ipatov_net - web-библиотека, если угодно - web-платформа [entries|archive|friends|userinfo]
ipatov_net

[ userinfo | ljr userinfo ]
[ archive | journal archive ]

web-библиотека, если угодно - web-платформа [Apr. 25th, 2008|08:52 pm]
Previous Entry Add to Memories Tell A Friend Next Entry
Я тут приуготавливаюсь сделать небольшой прорыв в web-технологиях :) Ну, во-первых мне это и самому интересно, во-фторых, я, как заядлый морфинист дельфинист вознамерился сделать нечто такое, чтобы оно с одной стороны было на Borland Delphi, а с другой - с полным, так сказать, функционалом, сопоставимым с серьёзными web-библиотеками. Для начала я (естественно!) кинулся на WebSnap (BD7), и... дада, сия библиотека меня расово не порадовала ну никак, по следующим причинам:
  1. Глючность. Не буду сейчас перечислять все еполадки, скажу что их достаточно для расового отвращения.

  2. Неполнота. Например, у них определён интерпейс IImageProducer, но реализации его НЕТЪ (а использование есть). Пришлось писать самому, это совсем не сложно было - но вот осадочек-с остался.

  3. Слабая портируемость на Kylix (ну... не то чтобы это сов7 нельзя, но как-то муторно, например все датамодули переделывать, ибо то же ADO или BDE в Линуксе не поддерживаются - можно, конечно, DBExpress, но оно не пошло у меня как то с MySQL, то ли у меня руки кривые, то ли MySQL5 оно поддерживать не хочет)

  4. Отсутствие модульности. Всё "сидит" в одной dll-ке (или одном exe-шничке), то есть если уж перекомпиляем - то фсё и зараз.

  5. ТОРМОЗА! Я вот только один пример приведу - страничка с 50-ю итерациями на скрипте в ней, генерится 0.5 секунды. А что Вы хотите? JavaScript + OLE Automation. Ффффторой ASP, ога.

  6. Какие-то глюки при множестве параллельных запросов. Возможно, по причине предыдущего пункта.



В общем, не обрадованъ ни разу. Хоть и написал уже на WebSnap-e почти действующий форум, по мотивам phpBB - но решил расово слить его в унитаз, а опосля разобрать на куски, если пойдут другие наработки. И они воспоследовали. Решил таки интересу ради и развития для написать свою собственную web-библиотеку (которая начала стремительно приобретать черты web-платформы), с кодовым названием DWE (Delphi Web Engine - бесхитростно, ога). Что для неё характерно в первую очередь?
  1. Изначальная кроссплатформенность (пишется на CLX, а не VCL) - не, ну местами, конечно приходится пользоваться непортируемым кодом. Но это дело я к минимуму свести стараюсь.

  2. Модульность. Сама по себе DWE - это только система управления модулями, что-то полезное делают отдельные DLL-ки, подгружаемые динамически.

  3. Компилируемость. А вот это совсем интересно - страничка со встроенными в ней скриптами в конечном итоге становится DLL-кой, которая и выдаёт результирующую HTMK.

  4. Объектность. Там всё сделано через объекты, очень такая серьёзная иерархия.

В DWE сейчас есть такие вот "фичи":
  • Все данные представлены как вариации интерпейса IDWEValue, если объект поддерживает дополнительные интерпейсы - значит его данные можно представить как данные этого, описанного интерпейсом типа. Один и тот же объект может поддерживать несколько интерпейсов. В настоящее время поддерживаются следующие интерпейсы:
    1. IDWEValue - базовый интерфейс, поддерживает проверку поддержки дополнительных интерфейсов и удобное получение этих интерфейсов. Например, так
      Obj1.AsInteger.Value := ...
      Также поддерживает "подзначения" (значения типа IDWEValue, "принадлежащие" данному, по символическому имени) - но сам по себе объект может и не поддерживать "подзначений", тогда считается что у него их просто нетъ. Так вот, так сказать от 120 единиц и имеем "иерархию" объектов, к каждому из них можно обратиться через нотацию с точкою, например так
      Obj1['Obj2.Obj2b.Obj3'].AsDouble := ...

    2. IDWEVariantValue - значение типа Variant

    3. IDWEIntegerValue - значение типа Integer

    4. IDWEInt64Value - значение типа Int64

    5. IDWEDoubleValue - значение типа Double (с плавающей точкою)

    6. IDWEAnsiStringValue, IDWEWideStringValue, IDWEStringValue - строки в разных вариациях, а именно - без Unicode, с Unicode и принятые в системе "по умолчанию"

    7. IDWEDateTimeValue - значение типа дата-время, поддерживает обращение по отдельности к полям "год", "месяц", "час", "секунда" - ну и в том же духе.

    8. IDWEBooleanValue - булевское значение

    9. IDWEHandleValue - значение типа THandle

    10. IDWEMethod - значение типа "метод", в нём есть метод (простите за тавтологию) Invoke, получающий в качестве параметра объект типа IDWEValue, возвращающий в результате объект того же типа. Объект может из себя представлять иерархию, поэтому достаточно и одного параметра на вход, одного - на выход. "Стандартная" реализация этого интерфейса сделана через Delphi-event (callback по другому), но может быть реализовано и как разбор какого-нибудь скрипта, который лежит в том же объекте и к которому можно иметь доступ через интерпейс IDWEStringValue. Как-то так.

    11. IDWEArray - одномерный массив значений, работает одновременно и как список (с методами Next, Prev и т.д.), индексация массива - через целочисленный индекс.

    12. IDWEList - одномерный список значений, не поддерживает индексацию - доступ только через движение по списку и взятие "текущего" значения.

    13. IDWEStringList - "оболочка" для стандартного объекта TStringList (который вообще-то и как массив работает), полностью дублирует все его методы и свойства. Но вместо хранения со строкою опционального объекта, порождённого от TObject, хранит значения типа IDWEValue.

    14. IDWEStream - поток для досупа к данным (например, к файлам) - в общем, ввод-вывод. Яхвляется оболочкой для стандартного объекта TStream, но поддерживает ещё кое-что интересное, например хранение строк переменной длины, или хранение "упакованных" целочисленных значений (пакуются с расчётом на то, что они малы по величине - это для хранения, допустим, индексов в массивах, не очень больших).

    15. IDWEMemory - интерфейс прямого досупа к памяти объекта. Может и пригодится когда-нибудь.

    16. IDWEClass - т.н. "класс", обладающий заранее заданным и неизменным набором "подзначений", которые задаются в объекте типа IDWEClassTemplate (со значениями по умолчанию), на который у "класса" имеется ссылочка как на "шаблон".

    17. IDWEClassTemplate - "шаблон" класса, может быть "порождён" от другого шаблона, тогда шаблон наследует все "подзначения" своего родителя (и родителя родителя, и так далее...)

    18. IDWEBuffer - поддержка буферизации, есть методы Flush (актуализовать данные в некоем носителе) и Cancel (вернуться к значению, которе лежит в носителе в данный момент)

    19. IDWEWebEngine - самый главный "родительский" объект, поддерживает подключемые модули, генерацию html-страниц и всё такое

    20. IDWEModule - подключаемый модуль, поддерживает создание "шаблонов объектов" и порождение объектов от шаблонов, может иметь также "методы" (IDWEMethod) и прочие другие всякие подзначения, по вкусу.

    21. IDWEFile - объект-файл, поддерживает чтение даты модификации файла, узнавание его наличия, стирание существующего и тд.тп. Также оно и развитие интерпейса IDWEStream, только пердварительно его надобно открыть методом "Open".

    22. IDWEStore - объект, поддерживающий хранение его в каком-либо промеж уточном формате, то есть его можно куда-то сохранить, и откуда-то прочитать.

    23. IDWEStorage - объект, поддерживающий хранение объектов, поддерживающих интерпейс IDWEStore. В настоящее время объекты можно хранить в бинарном формате (в IDWEStream) или в текстовом (IDWEStringList). При желании можно сделать возможным хранение объекта, допустим, в базе данных.

    24. IDWELink - "ссылка" на другой объект, может быть сохранена в промежуточном хранилище - но для этого и объект-ссылка, и объект, на который ссылаются должны принадлежать общему "родителю" (а по идее, все должны принадлежать объекту типа IDWEWebEngine)

    25. IDWEGroup - "группа значений", вот этот объект поддерживает "подзначения", которые можно свободно изменять, удалять, добавлять и ссылатья на них по строковому имени. В свою очередь, подзначения могут быть сами группами (и это, так сказать, система, работающая от 120 единиц). Классы и шаблоны классов - "потомки" группы, с некоторыми своими изменениями, в частности, в классах нельзя удалять и добавлять новые значения (ибо пользователь класса должен знать, что все значения присутствуют как есть и тип их гарантирован).

    26. IDWELock - объект для синхронизации потоков (thread-ов), через него реализуются секции кода, которые не могут выполняться одновременно. Семафоры, одним словом, если по юниксоидному.

    27. IDWEConfig - объект, примерно реализующий функциональность ini-файлов

    28. IDWEWriteLock - объект опять-таки для синхронизации потоков, на этот раз с учётом того, каков требуется доступ - на чтение или на запись. Параллельное чтение допускается, чтение параллельно с записью или параллельные записи - нет.

    29. IDWEDocument - объект-генератор html-кода, это "потомок" интерфейса IDWEModule, но он умеет ещё и html-код генерить, да ещё и с поддержкой "контекста" (параметров генерации, которые можно задать перед генерацией), таким образом одна и та же страничка может быть сгенерена по-разному, например с разным SQL-запросом, всё это задаётся в "контексте" (который есть объект типа IDWEClass)

    30. IDWEWebContext - объект, реализующий хранение веб-контекста, как то запрос, ответ, и прочая такая информация.

    31. IDWEConnection - объект, реализующий некое "абстрактное" подключение к чему-либо (открытие файла, таблицы в БД, соединения TCP/IP и так далее)

    32. IDWESQLConnection - SQL-соединение к базе данных, в нём можно выполнять запросы и получать от него объекты-DataSet-ы. Порождён от IDWEConnection. В настоящее время поддерживаются ADO-подключения (как отдельный подгружаемый модуль), поддерживаются но не оттестированы :)

    33. IDWEDBSet - объект-набор данных (DataSet) из БД. Например, таблица или результаты SQL-запроса. Позволяет примерно то же, что и стаднартный TDataSet в делфях.

  • Генерация страничек делается следующим образом:

    1. Для начала проверяется, есть ли уже в наличие DLL-ка, которая генерит.

    2. Если её нет, либо дата её создания раньше даты модификации исходного файла скрипта, либо дата некоторых важных библиотек позже её даты (то есть перекомпилена была сама DWE) - то создаём её - сначала делаем *.pas файл, содержащий Delphi-код (а он создаётся с учётом содержащегося в файле-исходнике скрипта вперемешку с HTML-кодом). Затем компилим её компилятором "командной строки" dcc32.exe.

    3. Подгружаем эту dll-ку, создаём через вызов её функции объект типа IDWEModule и регистрим его унутри себя. Чтобы другие уже могли им пользоваться (сославшись на него по текстовому имени).

  • Текстовый формат хранения иерархии объектов, ну примерно таков:
    group = {
       A:integer = -1
       B:integer = 5
       C:double = 2,5
       D:string = AAA
       E:boolean = true
       #desc = some desc
       F:group = {
         F1:datetime = 25.04.2008 20:38:55
         F2:string_list = {
           store_values = true
           AAA
             empty
           BBB
             empty
           CCC
             integer = 0
         }
       }
       G:variant = currency:3,5
       H:array = {
         low_bound = 0
         element_type = integer
         integer = 1
         integer = 2
         integer = 3
         integer = 4
         integer = 5
       }
       I:link = F.F1
       J:datetime = 25.04.2008 20:38:55
       K:lock
       L:class_template = {
         _Parent:link
         _ClassName:string
       }
     }
    

  • Исходники тестовой dll-ки, сгенерёной из html-шаблона таковы:

    • content\test.html
      Params.AddSubValue('Date', CreateDateTimeValue);
      Params.AddSubValue('Int', CreateIntegerValue);
      <!Content>
      <html>
      <body>
      <!Code>
      Context['Date'].AsDateTime.Value:=Now;
      Context['Int'].AsInteger.Value:=Random(500);
      Engine.LoadConfig('test.cfg');
      <!End>
      Date = <%= Date %>
      Int = <%= Int %>
      </body>
      </html>
      
    • bin\test.pas
      library test;
      uses
        ShareMem, SysUtils, Classes, HTTPApp, IniFiles, Math, DateUtils, StrUtils,
        web_interface;
      
      type
        TDWEModule_test = class(TDWEDocument)
        public
          function Generate(WebContext : IDWEWebContext; Context : IDWEClass) : IDWEStringList;override;
        end;
      
      const
        ModuleClassName = 'TDWEModule_test';
        ModuleName = 'test';
      
      function ModuleFunc(WebEngine : IDWEWebEngine) : IDWEModule;stdcall;
      var
        Module : IDWEModule;
        Params : IDWEClassTemplateEditor;
      begin
        Module:=TDWEModule_test.Create(WebEngine, 'test');
        Params:=Module.CreateTemplate(nil, '_Params');
        Params.AddSubValue('Date', CreateDateTimeValue);
        Params.AddSubValue('Int', CreateIntegerValue);
        Params.MarkReadonly;
        Module.DoneInitialization;
        Result:=Module;
      end;
      
      function TDWEModule_test.Generate;
      var
        Content : IDWEStringList;
      
        procedure Write(S : String);
        var LS : String;
        begin
          if not Content.EOL then
          begin
            LS:=Content.Strings[Content.HighBound];
            LS:=LS+S;
            Content.Strings[Content.HighBound]:=LS;
          end else
          begin
            Content.Add(S);
          end;
        end;
      
        procedure Writeln(S : String);
        begin
          Write(S);
          Content.Add('');
        end;
      
      begin
        Content:=CreateStringList;
        Content.StoreValues:=false;
        Writeln('<html>');
        Writeln('<body>');
        Context['Date'].AsDateTime.Value:=Now;
        Context['Int'].AsInteger.Value:=Random(500);
        Engine.LoadConfig('test.cfg');
        Writeln('Date = '+Context['Date'].AsString.Value+'');
        Writeln('Int = '+Context['Int'].AsString.Value+'');
        Writeln('</body>');
        Writeln('</html>');
        Result:=Content;
      end;
      
      exports
        ModuleFunc name 'ModuleFunc';
      
      begin
        Randomize;
      end.
      





    • Гойспода программисты - Вываше мнение?

    LinkLeave a comment

    Comments:
    [User Picture]
    From:[info]antihubbard@lj
    Date:April 25th, 2008 - 12:25 pm
    (Link)
    наше мнение — выбросить делфи и заняться делом
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 25th, 2008 - 12:55 pm
    (Link)
    а чем плохи дельфы?
    [User Picture]
    From:[info]antihubbard@lj
    Date:April 25th, 2008 - 01:19 pm

    Re: Reply to your comment...

    (Link)
    холивар?
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 25th, 2008 - 02:16 pm

    Re: Reply to your comment...

    (Link)
    никак нет, я не фанат. просто объясните?
    скайп же вот умудрились на нём эстонцы напейсать.
    From:[info]karpol@lj
    Date:April 26th, 2008 - 12:02 am

    Re: Reply to your comment...

    (Link)
    эстонцы? вот ведь шифруются, а напейсано Люксембурк.
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 27th, 2008 - 05:18 am

    Re: Reply to your comment...

    (Link)
    ну у нас тоже напейсано Прага, а пишут в Питере
    [User Picture]
    From:[info]ipatov_net@lj
    Date:April 25th, 2008 - 02:16 pm

    Re: Reply to your comment...

    (Link)
    Почему нет? Возобновим традицию, подзабытую к сожалению :)
    [User Picture]
    From:[info]ipatov_net@lj
    Date:April 25th, 2008 - 02:12 pm
    (Link)
    Извините. У меня на той же дельфе уйма реально работающих проектов. И что-то ни заказчик, ни я, никто вообще на дельфу не жаловался. Заказчику вообще глубоко до бетонного столба, на чём я пишу. Был бы результат. Как-то так :)
    А рассуждать о том, что дельфи - мусор, это есть технический снобизм - "как же так, мы тут на C++ хуячим, поэтому мы бла-бла-бла". Может, всё-таки вопрос не в выборе средств, а в профессионализме того, кто ими пользуется?
    Да.
    [User Picture]
    From:[info]ipatov_net@lj
    Date:April 25th, 2008 - 02:29 pm
    (Link)
    А вообще, конечно, я есть очень технически вредный человек. В частности не люблю т.н. "общественное мнение" и "все так делают". Просто не люблю идти по чужим стопам, нет в этом ни доблести, ни подвига личного и душевного. И самоуважения от этого тоже, как-то нет.
    ...
    Да и из чистой технической вредности очень хочется сделать работающую систему с абсолютно "левой резьбой". Чтобы так сказать поборники стандартов не слишком много о себе думали ;)
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 25th, 2008 - 12:54 pm
    (Link)
    "я буду долго гнать велосипед,
    в глухих лугах его остановлю..." (ц)
    [User Picture]
    From:[info]ipatov_net@lj
    Date:April 25th, 2008 - 02:16 pm
    (Link)
    Для души полезно (с)
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 25th, 2008 - 02:17 pm
    (Link)
    "без сборщика мусора оттого только программировать нужно, что это ум в порядок приводит" почти (ц)
    [User Picture]
    From:[info]ipatov_net@lj
    Date:April 25th, 2008 - 02:19 pm
    (Link)
    Хуже. Хуже!
    Я вот в жизни ни разу с необходимостью сборки мусора не сталкивался ;)
    Обломок доисторический я что ли?
    Учился по классическим учебникам структурного программирования, ога ;)
    [User Picture]
    From:[info]zogmeister@lj
    Date:April 25th, 2008 - 03:40 pm
    (Link)
    "в хардовы недра ты, Ассемблер,
    проникни взора остротой!
    и, что содержит в них Фон Нейман,
    свои сокровища открой!" почти (ц)