@@ -616,6 +616,18 @@ add_custom_command( | |||
${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin.tmpl) | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/tc_plugin.cpp | |||
COMMAND ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
-o ${CMAKE_CURRENT_BINARY_DIR}/tc_plugin.cpp | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_plugin.tmpl | |||
DEPENDS | |||
${CMAKE_CURRENT_BINARY_DIR}/tc_sep_skin.h | |||
${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin.tmpl | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_plugin.tmpl | |||
) | |||
@@ -638,14 +650,15 @@ if(NOT DISABLE_SHARED) | |||
COMPILE_DEFINITIONS "${SKIN3_DEFS}") | |||
target_link_libraries(skin3 ${CPPCMS_LIB}) | |||
foreach(SKIN tc_skin_a tc_skin_b tc_skin tc_sep_skin_a tc_sep_skin_b tc_sep_skin) | |||
foreach(SKIN tc_skin_a tc_skin_b tc_skin tc_sep_skin_a tc_sep_skin_b tc_sep_skin tc_plugin) | |||
add_library(${SKIN} SHARED ${CMAKE_CURRENT_BINARY_DIR}/${SKIN}.cpp) | |||
target_link_libraries(${SKIN} ${CPPCMS_LIB}) | |||
if(IS_WINDOWS) | |||
set_target_properties(${SKIN} PROPERTIES COMPILE_DEFINITIONS DLL_EXPORT) | |||
endif() | |||
endforeach() | |||
target_link_libraries(tc_plugin tc_sep_skin) | |||
add_library(plugin SHARED tests/test_plugin_so.cpp) | |||
target_link_libraries(plugin ${CPPCMS_LIB}) | |||
if(IS_WINDOWS) | |||
@@ -288,7 +288,7 @@ def make_ident(val): | |||
else: | |||
return "content." + val | |||
def print_using_block_start(class_name,variable_name,content_name): | |||
def print_using_block_start(class_name,variable_name,content_name,temp_and_view=None): | |||
global output_template | |||
if content_name: | |||
content=make_ident(content_name) | |||
@@ -299,19 +299,31 @@ def print_using_block_start(class_name,variable_name,content_name): | |||
output_template(r'{') | |||
if guard: | |||
output_template(r' cppcms::base_content::app_guard _g(%s,content);' % content); | |||
output_template(r' %s %s(out(),%s);' % ( class_name, variable_name, content)); | |||
if temp_and_view: | |||
output_template(r' cppcms::views::view_lock _vl(%s,out(),%s); %s &%s = _vl.use_view<%s>();' % ( temp_and_view, content, class_name, variable_name, class_name)); | |||
else: | |||
output_template(r' %s %s(out(),%s);' % ( class_name, variable_name, content)); | |||
def print_using_block_end(): | |||
global output_template | |||
output_template('}') | |||
class using_block: | |||
pattern=r'^<%\s*using\s+(?P<class>(?:\w+::)*\w+)(?:\s+with\s+(?P<content>' + variable_match + r'))?\s+as\s+(?P<name>[a-zA-Z_]\w*)\s*%>$' | |||
pattern=r'^<%\s*using\s+(?P<class>(?:\w+::)*\w+)(?:\s+with\s+(?P<content>' + variable_match + r'))?\s+as\s+(?P<name>[a-zA-Z_]\w*)' \ | |||
+ r'(?P<from>\s+from' \ | |||
+ r'(\s*(?P<fst_str>'+ str_match +r')|(?:\s+(?P<fst_var>' + variable_match + r')))' \ | |||
+ r'(\s*,\s*((?P<snd_str>'+ str_match +r')|(?P<snd_var>' + variable_match + r')))?' \ | |||
+ r')?' \ | |||
+ '\s*%>$' | |||
basic_pattern = 'using' | |||
basic_name = 'using' | |||
type='using' | |||
def use(self,m): | |||
print_using_block_start(m.group('class'),m.group('name'),m.group('content')) | |||
if m.group('from'): | |||
temp_and_view = get_render_names(m); | |||
else: | |||
temp_and_view = None | |||
print_using_block_start(m.group('class'),m.group('name'),m.group('content'),temp_and_view) | |||
global stack | |||
stack.append(self) | |||
def on_end(self): | |||
@@ -669,6 +681,31 @@ class form_block: | |||
def on_end(self): | |||
self.format_input(self.command_type,self.ident) | |||
def get_render_names(m): | |||
first_str = m.group('fst_str') | |||
first_var = m.group('fst_var') | |||
if first_var: | |||
first = make_ident(first_var) | |||
else: | |||
first = first_str | |||
second_str = m.group('snd_str') | |||
second_var = m.group('snd_var') | |||
if second_var: | |||
second = make_ident(second_var) | |||
else: | |||
second = second_str | |||
if first and second: | |||
template_name = first | |||
view_name = second | |||
else: | |||
global namespace_name | |||
template_name = '"' + namespace_name + '"' | |||
view_name = first; | |||
return template_name + ', ' + view_name | |||
class render_block: | |||
pattern=r'^<%\s*render\s+' \ | |||
+ r'((?P<fst_str>'+ str_match +r')|(?P<fst_var>' + variable_match + r'))' \ | |||
@@ -685,34 +722,14 @@ class render_block: | |||
content = 'content'; | |||
guard=False | |||
first_str = m.group('fst_str') | |||
first_var = m.group('fst_var') | |||
if first_var: | |||
first = make_ident(first_var) | |||
else: | |||
first = first_str | |||
second_str = m.group('snd_str') | |||
second_var = m.group('snd_var') | |||
if second_var: | |||
second = make_ident(second_var) | |||
else: | |||
second = second_str | |||
if first and second: | |||
template_name = first | |||
view_name = second | |||
else: | |||
global namespace_name | |||
template_name = '"' + namespace_name + '"' | |||
view_name = first; | |||
temp_and_view = get_render_names(m); | |||
output_template('{') | |||
if guard: | |||
output_template(r'cppcms::base_content::app_guard _g(%s,content);' % content) | |||
output_template(r'cppcms::views::pool::instance().render(%s,%s,out(),%s);' % (template_name,view_name,content)) | |||
output_template(r'cppcms::views::pool::instance().render(%s,out(),%s);' % (temp_and_view,content)) | |||
output_template('}') | |||
@@ -113,6 +113,43 @@ namespace cppcms { | |||
std::string name_; | |||
booster::hold_ptr<data> d; | |||
}; | |||
/// | |||
/// \brief A class that allows to use the view withing the internal lock used inside pool class | |||
/// | |||
/// It is similar in its operation in creating the view class similarly to pool::render() but | |||
/// not calling base_view::render member function. | |||
/// | |||
/// It is used with `<% using ... from ... %>` CppCMS template | |||
/// | |||
/// \ver{v1_2} | |||
class CPPCMS_API view_lock : public booster::noncopyable { | |||
public: | |||
/// | |||
/// Create a view and lock pool's internal lock | |||
/// | |||
view_lock(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content); | |||
/// | |||
/// Delete the view and unlock the pool's lock | |||
/// | |||
~view_lock(); | |||
/// | |||
/// Shortcut to dynamic_cast<View &>(view()) | |||
/// | |||
template<typename View> | |||
View &use_view() | |||
{ | |||
return dynamic_cast<View &>(view()); | |||
} | |||
/// | |||
/// Get the underlying view object | |||
/// | |||
base_view &view(); | |||
private: | |||
struct _data; | |||
booster::hold_ptr<base_view> view_; | |||
booster::hold_ptr<_data> d; | |||
}; | |||
/// | |||
/// \brief This is a singleton object that holds all views in the process. Any view | |||
@@ -164,6 +201,12 @@ namespace cppcms { | |||
static pool &instance(); | |||
private: | |||
friend class view_lock; | |||
void lock(); | |||
void unlock(); | |||
// called on locked object | |||
base_view *create_view(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content); | |||
pool(); | |||
~pool(); | |||
@@ -130,9 +130,8 @@ void pool::remove(generator const &g) | |||
d->generators.erase(name); | |||
} | |||
void pool::render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) | |||
base_view *pool::create_view(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) | |||
{ | |||
booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
data::generators_type::iterator s=d->generators.find(skin); | |||
if(s==d->generators.end()) | |||
throw cppcms_error("cppcms::views::pool: no such skin:" + skin); | |||
@@ -143,11 +142,51 @@ void pool::render(std::string const &skin,std::string const &template_name,std:: | |||
if(t==reg_skin.end()) | |||
throw cppcms_error("cppcms::view::pool: no suck view:" + template_name + " is registered for skin: " + skin); | |||
std::auto_ptr<base_view> v; | |||
v = t->second->create(template_name,out,&content); | |||
if(!v.get()) | |||
throw cppcms_error("cppcms::views::pool: no such view " + template_name + " in the skin " + skin); | |||
return v.release(); | |||
} | |||
void pool::lock() | |||
{ | |||
d->lock.shared_lock(); | |||
} | |||
void pool::unlock() | |||
{ | |||
d->lock.unlock(); | |||
} | |||
struct view_lock::_data {}; | |||
view_lock::view_lock(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) | |||
{ | |||
pool &p = pool::instance(); | |||
p.lock(); | |||
try { | |||
view_.reset(p.create_view(skin,template_name,out,content)); | |||
} | |||
catch(...) { | |||
p.unlock(); | |||
throw; | |||
} | |||
} | |||
view_lock::~view_lock() | |||
{ | |||
view_.reset(); | |||
pool::instance().unlock(); | |||
} | |||
base_view &view_lock::view() | |||
{ | |||
return *view_; | |||
} | |||
void pool::render(std::string const &skin,std::string const &template_name,std::ostream &out,base_content &content) | |||
{ | |||
booster::shared_lock<booster::recursive_shared_mutex> guard(d->lock); | |||
{ | |||
std::auto_ptr<base_view> v; | |||
v = t->second->create(template_name,out,&content); | |||
if(!v.get()) | |||
throw cppcms_error("cppcms::views::pool: no such view " + template_name + " in the skin " + skin); | |||
booster::hold_ptr<base_view> v(create_view(skin,template_name,out,content)); | |||
v->render(); | |||
} | |||
} | |||
@@ -0,0 +1,10 @@ | |||
<% c++ #include "tc_sep_skin.h" %> | |||
<% skin plugin %> | |||
<% view master_plugin uses data::master extends tc_skin::master_api %> | |||
<% template m1() %>plugin::master_plugin::m1 <%= text %><% end %> | |||
<% end view %> | |||
<% view helper_plugin uses data::helper extends tc_skin::helper_api %> | |||
<% template h1() %>plugin::helper_plugin::h1 <%= x %><% end %> | |||
<% end view %> | |||
<% end skin %> |
@@ -216,8 +216,47 @@ TBD | |||
<% include show_x(h.y) using helper_helper with h %> | |||
<% include show_y(h.x) using helper_helper with h %> | |||
<% include show_integer() using helper_master %> | |||
<% end %> | |||
<% end %> | |||
<% end template %> | |||
<% end view %> | |||
<% view master_api uses data::master %> | |||
<% template m1() %><% end %> | |||
<% end view %> | |||
<% view helper_api uses data::helper %> | |||
<% template h1() %><% end %> | |||
<% end view %> | |||
<% view master_plugin uses data::master extends master_api %> | |||
<% template m1() %>skin::master_plugin::m1 <%= text %><% end %> | |||
<% end view %> | |||
<% view helper_plugin uses data::helper extends helper_api %> | |||
<% template h1() %>skin::helper_plugin::h1 <%= x %><% end %> | |||
<% end view %> | |||
<% view test_master_plugin uses data::master %> | |||
<% template render() %> | |||
<% using master_api as m from "master_plugin" %><% include m1() from m %><% end using %> | |||
<% using master_api as m from name1 %><% include m1() from m %><% end using %> | |||
<% using master_api as m from "plugin","master_plugin" %><% include m1() from m %><% end using %> | |||
<% using master_api as m from "plugin",name1 %><% include m1() from m %><% end using %> | |||
<% using master_api as m from skin1,"master_plugin" %><% include m1() from m %><% end using %> | |||
<% using master_api as m from skin1,name1 %><% include m1() from m %><% end using %> | |||
<% end template %> | |||
<% end view %> | |||
<% view test_helper_plugin uses data::master %> | |||
<% template render() %> | |||
<% using helper_api with h as m from "helper_plugin" %><% include h1() from m %><% end using %> | |||
<% using helper_api with h as m from name1 %><% include h1() from m %><% end using %> | |||
<% using helper_api with h as m from "plugin","helper_plugin" %><% include h1() from m %><% end using %> | |||
<% using helper_api with h as m from "plugin",name1 %><% include h1() from m %><% end using %> | |||
<% using helper_api with h as m from skin1,"helper_plugin" %><% include h1() from m %><% end using %> | |||
<% using helper_api with h as m from skin1,name1 %><% include h1() from m %><% end using %> | |||
<% end template %> | |||
<% end view %> | |||
<% end skin %> |
@@ -480,6 +480,38 @@ public: | |||
); | |||
} | |||
void test_using_from() | |||
{ | |||
std::cout << "- Testing using from" << std::endl; | |||
data::master c; | |||
c.text = "Text"; | |||
c.h.x=13; | |||
c.skin1="plugin"; | |||
c.name1="master_plugin"; | |||
render("test_master_plugin", c ); | |||
compare_strings(str(),"\n" | |||
"skin::master_plugin::m1 Text\n" //from "master_plugin" include m1() | |||
"skin::master_plugin::m1 Text\n" //from name1 include m1() | |||
"plugin::master_plugin::m1 Text\n" //from "tc_plugin","master_plugin" include m1() | |||
"plugin::master_plugin::m1 Text\n" //from "tc_plugin",name1 include m1() | |||
"plugin::master_plugin::m1 Text\n" //from skin1,"master_plugin" include m1() | |||
"plugin::master_plugin::m1 Text\n" //from skin1,name1 include m1() | |||
""); | |||
c.skin1="plugin"; | |||
c.name1="helper_plugin"; | |||
render("test_helper_plugin", c ); | |||
compare_strings(str(),"\n" | |||
"skin::helper_plugin::h1 13\n" //from "helper_plugin" include h1() | |||
"skin::helper_plugin::h1 13\n" //from name1 include h1() | |||
"plugin::helper_plugin::h1 13\n" //from "tc_plugin","helper_plugin" include h1() | |||
"plugin::helper_plugin::h1 13\n" //from "tc_plugin",name1 include h1() | |||
"plugin::helper_plugin::h1 13\n" //from skin1,"helper_plugin" include h1() | |||
"plugin::helper_plugin::h1 13\n" //from skin1,name1 include h1() | |||
""); | |||
} | |||
private: | |||
std::string output_; | |||
@@ -503,6 +535,7 @@ int main(int argc,char **argv) | |||
cfg["views"]["skins"][0]="tc_sep_skin_a"; | |||
cfg["views"]["skins"][1]="tc_sep_skin_b"; | |||
cfg["views"]["skins"][2]="tc_sep_skin"; | |||
cfg["views"]["skins"][2]="tc_plugin"; | |||
} | |||
else { | |||
std::cout << "Using shared header/body" << std::endl; | |||
@@ -532,6 +565,8 @@ int main(int argc,char **argv) | |||
app.test_cache(); | |||
app.test_using_render(); | |||
app.test_gettext(); | |||
if(separate) | |||
app.test_using_from(); | |||
} | |||
catch(std::exception const &e) | |||
{ | |||
@@ -17,6 +17,7 @@ namespace data { | |||
struct master : public cppcms::base_content { | |||
int integer; | |||
std::string text; | |||
std::string skin1,skin2,name1; | |||
helper h; | |||
typedef std::vector<int> integers_type; | |||
integers_type integers; | |||