Главная  /  Правка  /  История  /   /  Область пользователей

Архитектура плагинов

Введение

У CppCMS будет полная встроенная поддержка плагинов в готовящейся к выходу версии 1.2, которая будет выпущена позже в этом году (2012).

Между тем, в CppCMS 1.0 уже есть все основы для поддержки плагинов. Темплейты уже полностью подключаемы (загрузка разделемого объекта и темплейта готова к использованию). Также есть новый класс booster::shared_object, позволяющий легко загружать динамические библиотеки.

Однако, в зависимости от архитектуры Вашего приложения, выбор точного решения может варьироваться. В следующих разделах будут рассмотрены различные варианты развития событий. Если здесь ничего не понятно, пожалуйста, спросите об этом в мейл-листе и вернитесь сюда для надлежащего исправления этой страницы.

Темплейты подключаемые с помощью разделяемого объекта.

У каждого разделяемого объекта темплейта есть глобальный конструктор и деструктор. Как только разделяемый объект загружен, вызывается конструктор и регистрирует себя в singleton'е cppcms::views::pool, становясь видимым для всех. При выгрузке он разрегистрируется с помощью глобального деструктора и больше не может больше использоваться. Это простая и прозрачная семантика.

Если плагин связан с кодом темплейта, он загрузит собственный темплейт:

libmy_plugin.so
my_application.cpp: // мой код { ... render("my_plugin_skin","some_view",some_content); } my_view.cpp <- my_view.tmpl
// этот код генерируется автоматически namespace { struct loader { loader() { cppcms::views::pool::instance().add(my_skin_generator()); } ~loader() { cppcms::views::pool::instance().remove(my_skin_generator());} } loader_instance; }

Поэтому, как только вы загрузите разделяемый объект, выполнится loader::loader() и зарегистрирует view, а когда будете выгружать разделяемый объект - loader::~loader() разрегистрирует его.

Удостоверьтесь, что у каждого темплейта есть собственное имя skin'а.

Темплейты с собственным фреймворком плагинов

Если Вы реализовали собственный фреймворк плагинов, есть два метода, позволяющие плагинам определить их собственные темплейты для создаваемого ими контента, интегрируемого в страницу, генерируемую главным приложением.

Использование потоков

Основное приложение определяет макет всей страницы:

<% skin basic_skin %>
<% view basic_view uses content::basic_content %>
<html>
  ...
<body>
  ...

  <!--- aux-часть-->

  <% foreach p in plugings %>
      <% item %><%= p.rendered_aux_html_content | raw %><% end %>

  <% end %>


  ...
  <!--- Main-часть-->

  <% foreach p in plugings %>
      <% item %>
        <div id="plugin_<%=p.id %>" >
        <%= p.rendered_main_html_content | raw %>

        </div>        

      <% end %>

  <% end %>

  ...


</body>
<% end view %>
...

<% end skin %>

Каждый плагин определяет темплейты для своего собственного содержимого:

<% skin plugin_a %>
<% view main_html uses plugin_a_content::some_content_b %>
<% template render() %>
  <!-- Указание render-функции -->
  какой-то конкретный html плагина
  делающий что-то, что ему нужно

<% end template %>
<% view aux_html uses plugin_a_content::some_content_a %>
<% template render() %>
  <!-- Указание render-функции -->
  какой-то конкретный html плагина
  делающий что-то, что ему нужно

<% end template %>

<% end view %>

<% end skin %>

Предположим, что наш плагин должен предоставлять следующее API:

class plugin {
public:
   virtual std::string  main_html(cppcms::applicatin *app) = 0;

   virtual std::string aux_html(cppcms::application *app) = 0;
 };

Содержимое каждого плагина рендерится примерно так:

// main_application.cpp

content::basic_content c;
// подготавливаем c
...
// рендерим плагины
for(int i=0;i<loaded_plugins.size();i++) {
     c.plugins[i].rendered_main_html_content = loaded_plugins[i]->main_html(this);
     c.plugins[i].rendered_aux_html_content = loaded_plugins[i]->aux_html(this);
} 

render("basic_skin","basic_view",c);

На стороне плагина, это выглядит примерно так:

std::string  main_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_b c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","main_html",c,buf);
    return buf.str();

}

std::string  aux_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_a c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","aux_html",c,buf);
    return buf.str();

}

С помощью команды 'render'

Начиная с CppCMS 0.99.12 есть команда <% render ... %> См. Рендеринг других view.

Основное приложение определяет макет всей страницы:

<% skin basic_skin %>
<% view basic_view uses content::basic_content %>
<html>
  ...
<body>
  ...

  <!--- aux-часть-->

  <% foreach p in plugings %>
      <% item %><% render p->name(), "aux_html" with p->aux_html_content() %><% end %>

  <% end %>


  ...
  <!--- Main-часть-->

  <% foreach p in plugings %>
      <% item %>
        <div id="plugin_<%=p->id() %>" >
        <% render p->name(), "main_html" with p->main_html_content() %>
        </div>        

      <% end %>

  <% end %>

  ...


</body>
<% end view %>
...

<% end skin %>

, где p - указатель на объект плагина.

Темплейт плагина будет реализовываться как и в предыдущем разделе. См. выше.

Предположим, что наш плагин должен предоставлять следующее API:

class plugin {
public:
   virtual void prepare() = 0;

   virtual cppcms::base_content  &main_html_content() = 0;

   virtual cppcms::base_content  &aux_html_content() = 0;

   virtual std::string name() = 0;
};

Содержимое каждого плагина рендерится примерно так:

basic_content {
  ...
  // указатели на плагины
  std::vector<plugin *> plugins;
}

main_application.cpp:

content::basic_content c;
// подготавливаем c
...
for(p in plugins)
  p->prepare();
render("basic_skin","basic_view",c); 

На стороне плагина, это выглядит примерно так:

class my_plugin : public plugin {
public:
    virtual void prepare()
    {
      ... установки main_ и aux_...

    }
    virtual std::string name() { return "plugin_a"; }

    virtual cppcms::basic_content &main_html_content() { return main_; }

    virtual cppcms::basic_content &aux_html_content() { return aux_; }


private:

    plugin_a_content::some_content_b aux_;

    plugin_a_content::some_content_a main_;

};



std::string  main_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_b c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","main_html",c,buf);
    return buf.str();

}


std::string  aux_html(cppcms::applicatin *app)
{
    plugin_a_content::some_content_a c;
    ...
    std::ostringstream buf;
    app->render("plugin_a","aux_html",c,buf);
    return buf.str();

}

Вариации

Вариации на базе вышеуказанного, в зависимости от нужд Вашего приложения.

Определение плагина:

 plugin {
     void render_main(cppcms::application &);

     void render_aux(cppcms::application &);

 }

, где плагин выполняется как:

 render_main(cppcms::application &app)
 {
      app.render("plugin_a","main_html",my_content);

 }

и темплейт выглядит как:

 <% foreach p in plugins %>
  <% item %>
   <% c++ p->render_main(content.app()); %>
  <% end item %>
 <% end foreach %>

Повторное использование skin'а

В этом разделе рассматривается возможность плагина по повторному использованию skin'а, осуществляющаяся ядром приложения.

Предположим, что Ваш плагин хочет расширить basic_skin::basic_view (т.е. наследоваться от них) и иметь контроль над всей страницей.

Это намного хитрее.

Вы не можете наследоваться от них, т.к. cppcms_tmpl_cc не генерирует заголовочные файлы. Вместо этого, сделайте следующее.

Вместо записи: <% skin basic_skin %>, используйте неименованный skin: <% skin %>

Затем скомпилируйте основной skin:

Main view cpp:

 cppcms_tmpl_cc -s basic_skin basic_view.tmpl some_other_main_views.tmpl -o basic_skin.cpp
 g++ basic_skin.cpp -o libbasic_skin.so

 cppcms_tmpl_cc -s plugin_a basic_view.tmpl plugin_a.tmpl -o plugin_a.cpp
 g++ plugin_code.cpp plugin_a.cpp  -o libplugin_a.so

Обратите внимание, что basic_view.tmpl присутствует дважды, первый - под skin'ом=namespace, а второй раз - под skin'ом=namespace plugin_a.

Это дублирует c++-код и требует от плагина действительно использовать код skin'а приложения. Это не идеально, но терпимо, по крайней мере пока cppcms_tmpl_cc не станет поддерживать генерацию файлов .h и .cpp.

Проект

CppCMS является основой веб-разработки для выполнения ресурсоемких приложений.

Размещение

SourceForge.net Logo

Поддержать проект

CppCMS needs You


Навигация

Главная страница


Валидация CSS | Валидация XHTML 1.0