- Added some "template friendly" interfaces to request, and cookie classes - Fixed handing of "." and ".." in url mappermaster
@@ -415,6 +415,35 @@ add_custom_command( | |||
${CMAKE_CURRENT_SOURCE_DIR}/src/hello_world_skin2.tmpl | |||
${CMAKE_CURRENT_SOURCE_DIR}/src/hello_world_view1.tmpl) | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/tc_skin_a.cpp | |||
COMMAND ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
-o ${CMAKE_CURRENT_BINARY_DIR}/tc_skin_a.cpp | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin_a.tmpl | |||
DEPENDS | |||
${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin_a.tmpl) | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/tc_skin_b.cpp | |||
COMMAND ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
-o ${CMAKE_CURRENT_BINARY_DIR}/tc_skin_b.cpp | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin_b.tmpl | |||
DEPENDS | |||
${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin_b.tmpl) | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/tc_skin.cpp | |||
COMMAND ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
-o ${CMAKE_CURRENT_BINARY_DIR}/tc_skin.cpp | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin.tmpl | |||
DEPENDS | |||
${CMAKE_CURRENT_SOURCE_DIR}/bin/cppcms_tmpl_cc | |||
${CMAKE_CURRENT_SOURCE_DIR}/tests/tc_skin.tmpl) | |||
if(NOT DISABLE_SHARED) | |||
add_custom_command( | |||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/skin3.cpp | |||
@@ -435,6 +464,11 @@ if(NOT DISABLE_SHARED) | |||
endif(NOT DISABLE_SHARED) | |||
foreach(SKIN tc_skin_a tc_skin_b tc_skin) | |||
add_library(${SKIN} SHARED ${CMAKE_CURRENT_BINARY_DIR}/${SKIN}.cpp) | |||
target_link_libraries(${SKIN} ${CPPCMS_LIB}) | |||
endforeach() | |||
add_executable(hello_world src/hello_world.cpp skin1.cpp skin2.cpp) | |||
target_link_libraries(hello_world ${CPPCMS_LIB}) | |||
@@ -472,7 +506,10 @@ set(ALL_TESTS | |||
xss_test | |||
url_mapper_test | |||
copy_filter_test | |||
tc_test | |||
) | |||
foreach(TEST ${ALL_TESTS}) | |||
add_executable(${TEST} tests/${TEST}.cpp) | |||
target_link_libraries(${TEST} ${CPPCMS_LIB}) | |||
@@ -4,8 +4,10 @@ import os | |||
import re | |||
import sys | |||
variable_match=r"\*?([a-zA-Z][a-zA-Z0-9_]*\(?\)?)(((\.|->)([a-zA-Z][a-zA-Z0-9_]*\(?\)?))*)" | |||
str_match=r'"([^"\\]|\\[^"]|\\")*"' | |||
single_var_param_match=r'(?:-?\d+|"(?:[^"\\]|\\[^"]|\\")*")' | |||
call_param_match=r'(?:\(\)|\((?:' + single_var_param_match + r')(?:,' + single_var_param_match + r')*\))' | |||
variable_match=r"\*?([a-zA-Z][a-zA-Z0-9_]*"+ call_param_match +r"?)(((\.|->)([a-zA-Z][a-zA-Z0-9_]*" + call_param_match + r"?))*)" | |||
def interleave(*args): | |||
for idx in range(0, max(map(len,args))): | |||
@@ -80,8 +82,6 @@ class html_type: | |||
else: | |||
html_type_code='as_html' | |||
class view_block: | |||
pattern=r'^<%\s*view\s+(\w+)\s+uses\s+(:?:?\w+(::\w+)*)(\s+extends\s+(:?:?\w+(::\w+)?))?\s*%>$' | |||
@@ -494,6 +494,16 @@ class form_block: | |||
flags = 'cppcms::form_flags::%s,cppcms::form_flags::%s' % ( html_type_code, m.group(1)); | |||
output('{ cppcms::form_context _form_context(out(),%s); %s.render(_form_context); }' % (flags , ident)) | |||
class render_block: | |||
pattern=r'<%\s*render\s+(?P<name>[a-zA-Z]\w*)(\s+using\s+(?P<content>'+variable_match+r'))?\s*%>' | |||
def use(self,m): | |||
if m.group('content'): | |||
content = make_ident(m.group('content')) | |||
else: | |||
content = 'content'; | |||
global namespace_name | |||
output('content.app().render("%s","%s",out(),%s);"' % (content,namespace_name,content)) | |||
class filters_show_block(base_show): | |||
pattern=r'^<%\s*('+ variable_match + r'\s*(\|.*)?)%>$' | |||
@@ -518,6 +528,38 @@ def make_format_params(s,default_filter = 'escape'): | |||
error_exit("Seems to be wrong parameters list [%s]" % s_orig) | |||
return [] | |||
class cache_block: | |||
pattern=r'<%\s*cache\s+((?P<str>'+ \ | |||
str_match +')|(?P<var>'+ variable_match +r'))' + \ | |||
r'(\s+for\s+(?P<time>\d+))?(\s+on\s+miss\s+(?P<callback>[a-zA-Z]\w+)\(\))?' \ | |||
+ '\s*%>' | |||
type = 'cache' | |||
def use(self,m): | |||
if(m.group('str')): | |||
self.parameter = m.group('str') | |||
else: | |||
self.parameter = make_ident(m.group('var')); | |||
output('{ std::string _cppcms_temp_val') | |||
output(' if(content.app().cache().fetch_frame(%s,_cppcms_temp_val))' % self.parameter); | |||
output(' out() << _cppcms_temp_val;'); | |||
output(' else {') | |||
if(m.group('callback')): | |||
output(' '+make_ident(m.group('callback')+'();')) | |||
output(' cppcms::copy_filter _cppcms_cache_flt(out());') | |||
self.timeout = m.group('time'); | |||
def on_end(self): | |||
if self.timeout: | |||
output(' content.app().cache().store_frame(%s,_cppcms_cache_flt.detach(),%s);' \ | |||
% (self.parameter,self.timeout)) | |||
else: | |||
output(' content.app().cache().store_frame(%s,_cppcms_cache_flt.detach());' % self.parameter); | |||
output('}} // cache') | |||
class ngettext_block: | |||
pattern=r'^<%\s*ngt\s*('+str_match+')\s*,\s*('+str_match+')\s*,\s*('+variable_match+')\s*(using(.*))?\s*%>$' | |||
def use(self,m): | |||
@@ -553,9 +595,9 @@ class url_block: | |||
if m.group(3): | |||
params=make_format_params(m.group(4),'urlencode') | |||
if not params: | |||
output( "content.rendering_application().mapper().map(out(),%s);" % s) | |||
output( "content.app().mapper().map(out(),%s);" % s) | |||
else: | |||
output( "content.rendering_application().mapper().map(out(),%s, %s);" % (s , ', '.join(params))) | |||
output( "content.app().mapper().map(out(),%s, %s);" % (s , ', '.join(params))) | |||
class include_block: | |||
@@ -672,6 +714,7 @@ def main(): | |||
url_block(), \ | |||
foreach_block(), item_block(), empty_block(),separator_block(), \ | |||
include_block(), \ | |||
cache_block(), \ | |||
html_type(), form_block(), \ | |||
filters_show_block(), error_com() ]: | |||
m = re.match(c.pattern,x) | |||
@@ -43,18 +43,18 @@ namespace cppcms { | |||
/// Get the application that renders current | |||
/// content, throw cppcms_error if the application was not set | |||
/// | |||
application &rendering_application(); | |||
application &app(); | |||
/// | |||
/// Set the application that renders current | |||
/// | |||
/// Called automatically by application::render | |||
/// | |||
void rendering_application(application &app); | |||
void app(application &app); | |||
/// | |||
/// Resets the application | |||
/// | |||
void reset_rendering_application(); | |||
void reset_app(); | |||
private: | |||
struct _data; | |||
@@ -103,6 +103,10 @@ public: | |||
/// | |||
void secure(bool v); | |||
/// | |||
/// Check if cookie is not assigned - empty | |||
/// | |||
bool empty() const; | |||
cookie(); | |||
~cookie(); | |||
@@ -252,6 +252,10 @@ namespace http { | |||
/// | |||
cookies_type const &cookies(); | |||
/// | |||
/// Get cookie by its name, if not assigned returns empty cookie | |||
/// | |||
cookie const &cookie_by_name(std::string const &name); | |||
/// | |||
/// form-data GET part of request | |||
/// | |||
form_type const &get(); | |||
@@ -114,6 +114,11 @@ public: | |||
/// you call this function. | |||
/// | |||
std::string get(std::string const &key); | |||
/// | |||
/// Get a value for a session \a key. If it is not set, returns default_value | |||
/// | |||
std::string get(std::string const &key,std::string const &default_value); | |||
/// | |||
/// Get convert the value that is set for a key \a key to type T using std::iostream. For example you can | |||
@@ -70,7 +70,7 @@ namespace cgi { | |||
virtual ~acceptor(){} | |||
}; | |||
class connection : | |||
class CPPCMS_API connection : | |||
public booster::noncopyable, | |||
public booster::enable_shared_from_this<connection> | |||
{ | |||
@@ -205,11 +205,11 @@ namespace { | |||
public: | |||
rnd_guard(base_content &cnt,application *app) : cnt_(&cnt) | |||
{ | |||
cnt_->rendering_application(*app); | |||
cnt_->app(*app); | |||
} | |||
~rnd_guard() | |||
{ | |||
cnt_->reset_rendering_application(); | |||
cnt_->reset_app(); | |||
} | |||
private: | |||
base_content *cnt_; | |||
@@ -46,12 +46,12 @@ base_content const &base_content::operator=(base_content const &other) | |||
return *this; | |||
} | |||
void base_content::rendering_application(application &app) | |||
void base_content::app(application &app) | |||
{ | |||
app_ = &app; | |||
} | |||
application &base_content::rendering_application() | |||
application &base_content::app() | |||
{ | |||
if(!app_) { | |||
throw cppcms_error("Attempt to access to application that wasn't set"); | |||
@@ -59,7 +59,7 @@ application &base_content::rendering_application() | |||
return *app_; | |||
} | |||
void base_content::reset_rendering_application() | |||
void base_content::reset_app() | |||
{ | |||
app_ = 0; | |||
} | |||
@@ -27,6 +27,10 @@ | |||
namespace cppcms { namespace http { | |||
struct cookie::_data { }; | |||
bool cookie::empty() const | |||
{ | |||
return name_.empty() && value_.empty(); | |||
} | |||
std::string cookie::name() const { return name_; } | |||
void cookie::name(std::string v) { name_=v; } | |||
@@ -306,6 +306,17 @@ std::map<std::string,cookie> const &request::cookies() | |||
{ | |||
return cookies_; | |||
} | |||
static cookie const empty_cookie; | |||
cookie const &request::cookie_by_name(std::string const &name) | |||
{ | |||
cookies_type::const_iterator p = cookies_.find(name); | |||
if(p==cookies_.end()) | |||
return empty_cookie; | |||
else | |||
return p->second; | |||
} | |||
request::form_type const &request::get() | |||
{ | |||
@@ -282,6 +282,15 @@ void session_interface::clear() | |||
data_.clear(); | |||
} | |||
std::string session_interface::get(std::string const &key,std::string const &def) | |||
{ | |||
check(); | |||
data_type::const_iterator p=data_.find(key); | |||
if(p==data_.end()) | |||
return def; | |||
return p->second.value; | |||
} | |||
std::string session_interface::get(std::string const &key) | |||
{ | |||
check(); | |||
@@ -323,6 +323,13 @@ namespace cppcms { | |||
} | |||
} | |||
if(real_key == ".") | |||
real_key.clear(); | |||
else if(real_key == "..") { | |||
mapper = &mapper->parent(); | |||
real_key.clear(); | |||
} | |||
url_mapper *tmp = mapper->d->is_app(real_key); | |||
if(tmp) { | |||
// empty special key | |||
@@ -0,0 +1,92 @@ | |||
/////////////////////////////////////////////////////////////////////////////// | |||
// | |||
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU Lesser General Public License as published by | |||
// the Free Software Foundation, either version 3 of the License, or | |||
// (at your option) any later version. | |||
// | |||
// This program is distributed in the hope that it will be useful, | |||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
// GNU Lesser General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU Lesser General Public License | |||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
// | |||
/////////////////////////////////////////////////////////////////////////////// | |||
#ifndef CPPCMS_IMPL_DUMMY_API_H | |||
#define CPPCMS_IMPL_DUMMY_API_H | |||
#include "cgi_api.h" | |||
using cppcms::impl::cgi::io_handler; | |||
using cppcms::impl::cgi::handler; | |||
using cppcms::impl::cgi::callback; | |||
class dummy_api : public cppcms::impl::cgi::connection { | |||
public: | |||
dummy_api(cppcms::service &srv,std::map<std::string,std::string> env,std::string &output) : | |||
cppcms::impl::cgi::connection(srv), | |||
env_(env), | |||
output_(&output) | |||
{ | |||
} | |||
virtual std::string getenv(std::string const &key) | |||
{ | |||
std::map<std::string,std::string>::const_iterator p = env_.find(key); | |||
if(p==env_.end()) | |||
return std::string(); | |||
return p->second; | |||
} | |||
virtual std::map<std::string,std::string> const &getenv() | |||
{ | |||
return env_; | |||
} | |||
void async_read_headers(handler const &) | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
void async_read_eof(callback const &) | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
void async_write_some(void const *,size_t,io_handler const &h) | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
virtual void write_eof(){} | |||
virtual size_t write_some(void const *p,size_t s,booster::system::error_code &e) | |||
{ | |||
output_->append(reinterpret_cast<char const *>(p),s); | |||
return s; | |||
} | |||
virtual booster::aio::io_service &get_io_service() | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
bool keep_alive() | |||
{ | |||
return false; | |||
} | |||
void close(){} | |||
virtual void async_read_some(void *,size_t,io_handler const &h) | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
virtual void async_write_eof(handler const &h) | |||
{ | |||
throw std::runtime_error("dummy_api: unsupported"); | |||
} | |||
private: | |||
std::map<std::string,std::string> env_; | |||
std::string *output_; | |||
}; | |||
#endif |
@@ -0,0 +1,56 @@ | |||
<% c++ #include "tests/tc_test_content.h" %> | |||
<% skin tc_skin %> | |||
<% view test_default_master uses data::master %> | |||
<% template render() %>c<% end template %> | |||
<% end view %> | |||
<% view view_y uses data::master %> | |||
<% template render() %>view y<% end template %> | |||
<% end view %> | |||
<% view view_x uses data::master %> | |||
<% template render() %>view x<% end template %> | |||
<% end view %> | |||
<% view master uses data::master %> | |||
<% template render() %> | |||
TBD | |||
<% end template %> | |||
<% end view %> | |||
<% view master_tmpl uses data::master %> | |||
<% template a() %>A<% end %> | |||
<% template b(int x) %>x=<% x %><% end %> | |||
<% template c(int x,std::string const &y) %>x=<%x%> y=<%y%><% end %> | |||
<% template render() %> | |||
<% include a() %> | |||
<% include b(10) %> | |||
<% include b(integer) %> | |||
<% include c(10,"test") %> | |||
<% include c(integer,text) %> | |||
<% foreach x as data::master::integers_type::iterator in integers %><% separator %>,<% item %><% include b(x) %><% end %><% end %> | |||
<% end template %> | |||
<% end view %> | |||
<% view master_if uses data::master %> | |||
<% template render() %> | |||
<% if integer %>integer<%else%>!integer<% end %> | |||
<% if not integer %>!integer<%else%>!!integer<% end %> | |||
<% if empty text %>text empty<% else %>text not empty<% end %> | |||
<% if not empty text %>!text empty<% else %>!!text not empty<% end %> | |||
<% if (true) %>true<%end%> | |||
<% if (false) %>false<%end%> | |||
<% if (false) %>false<% elif (true) %>true<%end%> | |||
<% end template %> | |||
<% end view %> | |||
<% view master_url uses data::master %> | |||
<% template render () %> | |||
<% url "." %> | |||
<% url "." using integer %> | |||
<% url "." using integer,text %> | |||
<% url "/" using integer %> | |||
<% url "/" using integer,text %> | |||
<% url "/" using integer,text | raw %> | |||
<% url "/foo" %> | |||
<% end %> | |||
<% end view %> | |||
<% end skin %> |
@@ -0,0 +1,6 @@ | |||
<% c++ #include "tests/tc_test_content.h" %> | |||
<% skin tc_skin_a %> | |||
<% view master uses data::master %> | |||
<% template render() %>a<% end template %> | |||
<% end view %> | |||
<% end skin %> |
@@ -0,0 +1,6 @@ | |||
<% c++ #include "tests/tc_test_content.h" %> | |||
<% skin tc_skin_b %> | |||
<% view master uses data::master %> | |||
<% template render() %>b<% end template %> | |||
<% end view %> | |||
<% end skin %> |
@@ -0,0 +1,144 @@ | |||
#include "tc_test_content.h" | |||
#include <cppcms/service.h> | |||
#include <cppcms/http_response.h> | |||
#include <cppcms/json.h> | |||
#include <cppcms/url_mapper.h> | |||
#include "dummy_api.h" | |||
#include "test.h" | |||
class test_app : public cppcms::application { | |||
public: | |||
test_app(cppcms::service &srv) : | |||
cppcms::application(srv), | |||
srv_(srv) | |||
{ | |||
mapper().assign("foo","/foo"); | |||
mapper().assign("/"); | |||
mapper().assign("/{1}"); | |||
mapper().assign("/{1}/{2}"); | |||
} | |||
~test_app() | |||
{ | |||
release_context(); | |||
} | |||
void set_context() | |||
{ | |||
std::map<std::string,std::string> env; | |||
env["HTTP_HOST"]="www.example.com"; | |||
env["SCRIPT_NAME"]="/foo"; | |||
env["PATH_INFO"]="/bar"; | |||
env["REQUEST_METHOD"]="GET"; | |||
booster::shared_ptr<dummy_api> api(new dummy_api(srv_,env,output_)); | |||
booster::shared_ptr<cppcms::http::context> cnt(new cppcms::http::context(api)); | |||
assign_context(cnt); | |||
response().out(); | |||
output_.clear(); | |||
} | |||
std::string str() | |||
{ | |||
response().out() << std::flush; | |||
std::string result = output_; | |||
output_.clear(); | |||
return result; | |||
} | |||
void test_skins() | |||
{ | |||
std::cout << "- Testing different skins" << std::endl; | |||
data::master m; | |||
render("tc_skin_a","master",m); | |||
TEST(str()=="a"); | |||
render("tc_skin_b","master",m); | |||
TEST(str()=="b"); | |||
render("test_default_master",m); | |||
TEST(str()=="c"); | |||
} | |||
void test_views() | |||
{ | |||
std::cout << "- Testing different views" << std::endl; | |||
data::master m; | |||
render("view_x",m); | |||
TEST(str()=="view x"); | |||
render("view_y",m); | |||
TEST(str()=="view y"); | |||
} | |||
void test_templates() | |||
{ | |||
std::cout << "- Testing different template calls" << std::endl; | |||
data::master m; | |||
m.integer = 1; | |||
m.text = "str"; | |||
m.integers.push_back(21); | |||
m.integers.push_back(22); | |||
render("master_tmpl",m); | |||
TEST(str()=="\nA\nx=10\nx=1\nx=10 y=test\nx=1 y=str\nx=21,x=22\n"); | |||
} | |||
void test_if() | |||
{ | |||
std::cout << "- Testing conditions" << std::endl; | |||
data::master m; | |||
m.integer = 1; | |||
m.text = "x"; | |||
render("master_if",m); | |||
TEST(str()=="\ninteger\n!!integer\ntext not empty\n!text empty\ntrue\n\ntrue\n"); | |||
m.integer = 0; | |||
m.text = ""; | |||
render("master_if",m); | |||
TEST(str()=="\n!integer\n!integer\ntext empty\n!!text not empty\ntrue\n\ntrue\n"); | |||
} | |||
void test_url() | |||
{ | |||
std::cout << "- Testing url" << std::endl; | |||
data::master m; | |||
m.integer = 1; | |||
m.text = "/"; | |||
render("master_url",m); | |||
TEST(str()=="\n" | |||
"/\n" | |||
"/1\n" | |||
"/1/%2f\n" | |||
"/1\n" | |||
"/1/%2f\n" | |||
"/1//\n" | |||
"/foo\n"); | |||
} | |||
private: | |||
std::string output_; | |||
cppcms::service &srv_; | |||
}; | |||
int main() | |||
{ | |||
try { | |||
cppcms::json::value cfg; | |||
cfg["views"]["paths"][0]="./"; | |||
cfg["views"]["skins"][0]="tc_skin_a"; | |||
cfg["views"]["skins"][1]="tc_skin_b"; | |||
cfg["views"]["skins"][2]="tc_skin"; | |||
cfg["views"]["default_skin"]="tc_skin"; | |||
cppcms::service srv(cfg); | |||
test_app app(srv); | |||
app.set_context(); | |||
app.test_skins(); | |||
app.test_views(); | |||
app.test_templates(); | |||
app.test_if(); | |||
app.test_url(); | |||
} | |||
catch(std::exception const &e) | |||
{ | |||
std::cerr << "Fail " << e.what() << std::endl; | |||
return 1; | |||
} | |||
std::cout << "Ok" << std::endl; | |||
return 0; | |||
} | |||
@@ -0,0 +1,46 @@ | |||
#include <cppcms/view.h> | |||
#include <string> | |||
#include <vector> | |||
namespace data { | |||
struct master : public cppcms::base_content { | |||
int integer; | |||
std::string text; | |||
typedef std::vector<int> integers_type; | |||
integers_type integers; | |||
}; | |||
struct foo : public master { | |||
double real; | |||
}; | |||
struct bar : public master { | |||
typedef std::vector<std::string> set_type; | |||
set_type set; | |||
std::string text2html(std::string const &input) | |||
{ | |||
std::string output; | |||
for(unsigned i=0;i<input.size();i++) { | |||
switch(input[i]) { | |||
case '<': | |||
output+="<"; | |||
break; | |||
case '>': | |||
output+=">"; | |||
break; | |||
case '&': | |||
output+="&"; | |||
break; | |||
case '\r': | |||
break; | |||
case '\n': | |||
output+="<br />\n"; | |||
break; | |||
default: | |||
output+=input[i]; | |||
} | |||
} | |||
return output; | |||
} | |||
}; | |||
} // data |