Re: [Cppcms-users] template plugins
Brought to you by:
artyom-beilis
From: Artyom B. <art...@ya...> - 2012-01-05 11:02:08
|
Hi, First of all before I'll answer any specific points I'll summarize my answer. General Concepts Notes ======================= Skin = namespace View = class Template = class-member-function Even thou in general different "skins" are originally used for "skinning" purposes, it is not a mandatory way to see the things. Each plugin should have its own skin (or namespace) and its content can be rendered they way you need. Limitations =========== Currently cppcms_tmpl_cc does not generate separate h and cpp file and thus you can't derive views (classes) in one skin (namespac) from other. There is other way to reuses "shared-skin", I'll show it later. Making simple plugin ===================== There are two ways to do it. One, related to CppCMS 0.99.11 and other to the latest version in trunk. Lets start with current 0.99.11. Our main page <% skin basic_skin %> <% view basic_view uses content::basic_content %> <html> ... <body> ... <!--- aux part--> <% foreach p in plugings %> <% item %><%= p.rendered_aux_html_content | raw %><% end %> <% end %> ... <!--- Main part--> <% foreach p in plugings %> <% item %> <div id="plugin_<%=p.id %>" > <%= p.rendered_main_html_content | raw %> </div> <% end %> <% end %> ... </body> <% end view %> ... <% end skin %> Now this is what plugin's template should contain <% skin plugin_a %> <% view main_html uses plugin_a_content::some_content_b %> <% template render() %> <!-- Notice render function --> some plugin specific html that does something it needs <% end template %> <% view aux_html uses plugin_a_content::some_content_a %> <% template render() %> <!-- Notice render function --> some plugin specific html that does something it needs <% end template %> <% end view %> Now how we integrate all this. Let's say that our plugin should provide following API class plugin { public: virtual std::string main_html(cppcms::applicatin *app) = 0; virtual std::string aux_html(cppcms::application *app) = 0; }; Now when we render each plugin we do following: main_application.cpp content::basic_content c; // prepare c ... // render plugins 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); And plugin looks like this: 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(); } That's it... Now in CppCMS 0.99.12 (trunk) you can do following ================================================== There is a new command <% render ... %> http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_templates_comm#Rendering.other.views Our main page <% skin basic_skin %> <% view basic_view uses content::basic_content %> <html> ... <body> ... <!--- aux part--> <% foreach p in plugings %> <% item %><% render p->name(), "aux_html" with p->aux_html_content() %><% end %> <% end %> ... <!--- Main part--> <% 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 %> Where p is actually a pointer to plugin object. Now the plugin skin would look the same. skipping Now how we integrate all this. Let's say that our plugin should provide little bit different api: class plugin { public: virtual void prepare() = 0; virtual cppcms::base_content &main_html_content() = 0; virtual cppcms::base_content &aux_html_content() = 0; virtua std::string name() = 0; }; Now when we render each plugin we do following: basic_content { ... // pointers to plugins std::vector<plugin *> plugins; } main_application.cpp content::basic_content c; // prepare c ... for(p in plugins) p->prepare(); render("basic_skin","basic_view",c); And plugin looks like this: class my_plugin : public plugin { public: virtual void prepare() { ... setup main_ and 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(); } ========================================================== Of course there are more variansts (for 0.99.12/trunk). For example: plugin { void render_main(cppcms::application &); void render_aux(cppcms::application &); } Where plugin executed like render_main(cppcms::application &app) { app.render("plugin_a","main_html",my_content); } And in the template code you cakk <% foreach p in plugins %> <% item %> <% c++ p->render_main(content.app()); %> <% end item %> <% end foreach %> According to your imagination ========================================== Derivation ========================================== Now, reusing a skin. Consider that your plugin wants to extend basic_skin::basic_view (i.e. derive from it). And have control over entire page. It is much more tricky. You can't derive from it as cppcms_tmpl_cc does not generate header files, what you can do is to do follwoing Instead of writing <% skin basic_skin %> You use nameless skin <% skin %> And for example when you compile main 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 Notice basic_view.tmpl appears twice but once under skin=namespace "basic_skin" and the second time under skin=namespace plugin_a. This copies c++ code, and requires plugin to actually use application's skin code, it is not so good but it is tolerable, at least untill cppcms_tmpl_cc will support generation of h and cpp files. ============================ Now all the above should go to wiki :-) ========================================================== Now particular answers ================================ > >Oh, up to today I have been using 0.99.10. >I see you released 0.99.11 ten days ago: >http://art-blog.no-ip.info/cppcms/blog/post/90 >In your blog, you don't give any details on how / why the views have been >refactored, and the implication, if any, for existing code >(e.g. what is now possible that wasn't before?) > This class was not existing: http://art-blog.no-ip.info/cppcms_ref_v0_99/classcppcms_1_1views_1_1pool.html All the operations on skins should be done via cppcms::application, the code was bad and many parts where declared as private. Now it is publicly usable > > >So, back to my original question: can you guesstimate the release dates for >1.0 and 1.1? >>From yesterday's post, I guesstimate that 1.0.0 RC1 will be released this >month, and the final 1.0.0 in February at the latest >When would the first beta 1.1 be released? Early spring? >And the first 1.1.0 RC? >I don't mind developing against a beta release. I guess it'll be stable enough >for a live release of my own application late this year..... > I don't know, 1.0 probably in a month... 1.1 would start immediatly when it become stable as 1.2 probably few month afterward. > >> On the other hand if I spend now lots of time today on plugin framework >> and tomorrow on other very important feature I'll never release a stable >> version and this is not good for the project. It was in beta stage for >> too long time. > >Well, I was not asking for you to implement the whole plugin framework right >now, before the release 1.0.0. >However, it seems that I am missing a single method in order to complete my >own plugin framework: how to attach new template includes. And again, I am not >the first one to ask about it. > See below. >So, what I was asking is, in terms of additional code before the code freeze, >can we have that extra method to complete our own frameworks? (unless, again, >I am doing things the wrong way, and what I am asking is possible another >way...) > I hope the notes above answer on what you ask. >-- > >I started replying to the rest of your comments, but I guess the discussion >will be clearer if I do my best to state as clearly as possible what I am >after. > > [snip] > > Right now, the > important is that you understand what I am after. > The core of the application is defining the general layout of the template, and > tells where the content from different plugins shall be inserted. > I think example above clarifies this >> Only thing you should care is to make sure each as its own skin name and >> not collide with its other. > >(=> with *each* other) > >Err... This is a major departure from what I was trying to do. Maybe I don't >understand the way you use the term 'skin'. For me, 'skin' refers to the >general look and appearance and colour scheme of the web site. A plugin >implementing a block of content would output this content in a way that's >congruent with the site's skin/theme. > > ... >Again, for me skin == theme. Only one is operative for any page request. >Or do you mean that a single page can be assembled from the output of different >skins? > If you have doughs take a look on skin as separate unit, namespace not certainly the style. >http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_templates_gen >I quote from above: > "Each skin should implement the virtual function render(), > unless it is already implemented in its parent." >Do you mean that a skin can have parents?? >In the wiki page above, you mention a views hierarchy (which, I think, I >understand) but not a skin hierarchy! > > Ok this is a mistake, I ment "Each view should implemen", skins are namespaces they do not have inheritence. >For reference, my problem appears to me to be very, very similar to that of >Julian Pietron, > > [snip] > > I still fail to see how I can tell each plugin to render their specific >parts according to their own data and own templates, and to pass the result >back to the core application which will render the whole page and send it to >the user. > I think that code above explains it >How would >views_pool::static_instance().render(...) >interact with the core template/view/skin? It was changed since to: cppcms::views::pool::instance().render(skin_name,view_name,content,output_stream) It would render: a view in skin using content to the stream :-) I home it is clear now. > > >> > 4. Note: you can't load and unload two plugins simultaneously as it would >> > not be thread safe, > >I don't understand what you mean by "loading two plugins simultaneously". > I think it is out of date note. > >Basically, what follows is the implementation side of the simple example I >posted above: > >// core content.hpp >struct main : public cppcms::base_content { > std::string title; > std::string content; >} > >// plugin: plugin_content.hpp >#include "core/content.hpp" >namespace content { >struct example_page : public main { > std::string my_test; >}; >} // namespace content. > >As you see, the plugin content are sub-classes of the main content. See examples above. >In the thread mentioned above, Julian asked: > > [snip] > > >which obviously registers the view within the pool. > >And what I was originally asking, is a method to add a template to an already >registered skin/view. Not possible to do it directly today. See a workaround above. You need to create a new skin and reuse some templates. > >> > 2) On the other hand, you seemed to say that it was not possible, because >> > it was difficult to do it right, especially considering race conditions >> > in a multi- threaded web server, leading to a crash when one thread is >> > trying to render a sub-template from a plugin that was just disabled in >> > another thread. >> >> The cppcms::views::pool::render(...) is thread safe >> as it holds shared lock during any rendering process. If you >> would try to unload a skin during this period you would be blocked >> till **all** rendering operations will complete and >> then you'll be able to load/unload the view > >Julian asked: > >> > Would it be possible to register a view at runtime? > >You replied: > >> > Not sure. It would required making views loading >> > and unloading be thread safe and make the interaction >> > with actual DLL quite complicated. > > >But that was 18 months ago, so maybe things have changed since then... > DLLs are supported, and views loading is thread safe now. >If I can achieve the end result above, then I would have passed a very major >hurdle. > >Sorry for being slow to catch up with you, and for the length of this email. > I think the example above provides the solution. Correct me if I wrong. >Thanks, as always, > >Augustin. > > Best Regards, Artyom |