Browse Source

- Updated URL mapper to handle mounted applications

- Improved handling if URLs in writing them to output
- Updated MB example
master
Artyom Beilis 13 years ago
parent
commit
c5825fd76d
12 changed files with 188 additions and 124 deletions
  1. +2
    -2
      bin/cppcms_tmpl_cc
  2. +38
    -35
      cppcms/application.h
  3. +1
    -0
      cppcms/url_mapper.h
  4. +1
    -0
      cppcms/view.h
  5. +3
    -0
      examples/message_board/apps/forums.cpp
  6. +22
    -28
      examples/message_board/apps/mb.cpp
  7. +1
    -1
      examples/message_board/apps/mb.h
  8. +6
    -1
      examples/message_board/apps/thread.cpp
  9. +1
    -1
      examples/message_board/view/forums.tmpl
  10. +30
    -27
      src/application.cpp
  11. +53
    -6
      src/url_mapper.cpp
  12. +30
    -23
      tests/url_mapper_test.cpp

+ 2
- 2
bin/cppcms_tmpl_cc View File

@@ -553,9 +553,9 @@ class url_block:
if m.group(3):
params=make_format_params(m.group(4),'urlencode')
if not params:
output( "out()<<content.rendering_application().url(%s);" % s)
output( "content.rendering_application().mapper().map(out(),%s);" % s)
else:
output( "out()<<content.rendering_application().url(%s, %s);" % (s , ', '.join(params)))
output( "content.rendering_application().mapper().map(out(),%s, %s);" % (s , ', '.join(params)))


class include_block:


+ 38
- 35
cppcms/application.h View File

@@ -185,67 +185,70 @@ namespace cppcms {
///
void render(std::string skin,std::string template_name,std::ostream &out,base_content &content);


///
/// Render a template \a template_name of default skin using this application as content.
/// Register an application \a app as child. Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
///
/// Side effect requires: output stream for response class, causes all updated session
/// data be saved and all headers be written. You can't change headers after calling this function.
void add(application &app);
///
void render(std::string template_name);
/// Register an application \a app as child and mount it into url dispatched calling
/// dispatcher().mount(regex,app,part);
///
/// Render a template \a template_name of \a skin skin using this application as content
/// Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
///
/// Side effect requires: output stream for response class, causes all updated session
/// data be saved and all headers be written. You can't change headers after calling this function.
///
void render(std::string skin,std::string template_name);



void add(application &app,std::string const &regex,int part);

///
/// Register an application \a app as child. Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
/// Register an application \a app as child and mount it into:
///
void add(application &app);
/// - url_dispatcher calling dispatcher().mount(regex,app,part);
/// - url_mapper calling mapper().mount(name,url,app);
///
/// Register an application \a app as child. Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
/// Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
///
/// All URL that match regular expression \a regex would be passed to the child for match. Matched part
/// \a part would be used by child for matching.
///
/// For example:
void add(application &app,std::string const &name,std::string const &url,std::string const &regex,int part);
///
/// \code
/// add(users,"^/users(.*)$",1")
/// \endcode
/// Register an application \a app as child and mount it into
/// url_mapper calling mapper().mount(name,url,app);
///
/// For URL /users/moshe would pass only "/moshe" to URL dispatched of \a users object
/// Ownership of app is not transfered to parent, however
/// it would shared it's parent reference count.
///
void add(application &app,std::string regex,int part);
///
void add(application &app,std::string const &name,std::string const &url);

///
/// Register an application \a app as child. Ownership of app is transfered to parent
///
void attach(application *app);
///
/// Register an application \a app as child. Ownership of app is transfered to parent
/// Register an application \a app as child and mount it into
/// url_dispatcher calling dispatcher().mount(regex,*app,part);
///
/// Ownership of app is transfered to parent.
///
void attach(application *app,std::string const &regex,int part);
///
/// Register an application \a app as child and mount it into
/// url_mapper calling mapper().mount(name,url,*app);
///
/// Ownership of app is transfered to parent.
///
/// All URL that match regular expression \a regex would be passed to the child for match. Matched part
/// \a part would be used by child for matching.
void attach(application *app,std::string const &name,std::string const &url);
///
/// For example:
/// Register an application \a app as child and mount it into:
///
/// \code
/// add(users,"^/users(.*)$",1")
/// \endcode
/// - url_dispatcher calling dispatcher().mount(regex,*app,part);
/// - url_mapper calling mapper().mount(name,url,*app);
///
/// For URL /users/moshe would pass only "/moshe" to URL dispatched of \a users object
/// Ownership of app is transfered to parent.
///
void attach(application *app,std::string regex,int part);
void attach(application *app,std::string const &name,std::string const &url,std::string const &regex,int part);

///
/// Get the parent of the application, if the application is the topmost class in hierarchy,


+ 1
- 0
cppcms/url_mapper.h View File

@@ -44,6 +44,7 @@ namespace cppcms {
void root(std::string const &r);

void assign(std::string const &key,std::string const &url);
void assign(std::string const &url);

void set_value(std::string const &key,std::string const &value);
void clear_value(std::string const &key);


+ 1
- 0
cppcms/view.h View File

@@ -23,5 +23,6 @@
#include <cppcms/base_view.h>
#include <cppcms/views_pool.h>
#include <cppcms/application.h>
#include <cppcms/url_mapper.h>
#endif


+ 3
- 0
examples/message_board/apps/forums.cpp View File

@@ -26,6 +26,9 @@ namespace apps {

forums::forums(cppcms::service &srv) : master(srv)
{
mapper().assign("{1}"); // with id
mapper().assign(""); // default
dispatcher().assign(".*",&forums::prepare,this,0);
}

void forums::prepare_content(data::forums &c,std::string const &page)


+ 22
- 28
examples/message_board/apps/mb.cpp View File

@@ -10,39 +10,33 @@
namespace apps {


mb::mb(cppcms::service &s) :
cppcms::application(s)
mb::mb(cppcms::service &s) : cppcms::application(s)
{
mapper().root(settings().get<std::string>("mb.root"));

// Forums
forums *forums_ptr = new forums(s);
attach(forums_ptr);
dispatcher().assign("(/(\\d+)?)?",&forums::prepare,forums_ptr,2);
mapper().assign("forums","/");
mapper().assign("forums","/{1}");


// Flat thread
flat_thread *flat_ptr = new flat_thread(s);
attach(flat_ptr);
dispatcher().assign("/flat/(\\d+)",&flat_thread::prepare,flat_ptr,1);
mapper().assign("flat_thread","/flat/{1}");

// Tree thread
tree_thread *tree_ptr = new tree_thread(s);
attach(tree_ptr);
dispatcher().assign("/tree/(\\d+)",&tree_thread::prepare,tree_ptr,1);
mapper().assign("tree_thread","/tree/{1}");
attach( new forums(s),
"forums",
"/{1}",
"(/(\\d+)?)?",2);

attach( new flat_thread(s),
"flat_thread",
"/flat/{1}",
"/flat/(\\d+)",1);


attach( new tree_thread(s),
"tree_thread",
"/tree/{1}",
"/tree/(\\d+)",1);

attach( new reply(s),
"comment",
"/comment/{1}",
"/comment/(\\d+)",1);
// Generic mapping
mapper().root(settings().get<std::string>("mb.root"));
mapper().assign("user_thread","/{method}/{1}");
// Reply
reply *repl_ptr = new reply(s);
attach(repl_ptr);
dispatcher().assign("/comment/(\\d+)",&reply::prepare,repl_ptr,1);
mapper().assign("comment","/comment/{1}");
}

} // apps

+ 1
- 1
examples/message_board/apps/mb.h View File

@@ -3,11 +3,11 @@

#include <cppcms/application.h>


namespace apps {

class mb : public cppcms::application {
public:

mb(cppcms::service &s);

};


+ 6
- 1
examples/message_board/apps/thread.cpp View File

@@ -68,7 +68,8 @@ bool thread_shared::prepare(data::thread_shared &c,int id)

flat_thread::flat_thread(cppcms::service &s) : thread_shared(s)
{

dispatcher().assign(".*",&flat_thread::prepare,this,0);
mapper().assign("{1}");
}


@@ -125,6 +126,8 @@ void make_tree(data::tree_t &messages,std::map<int,std::map<int,data::msg> > &co

tree_thread::tree_thread(cppcms::service &s) : thread_shared(s)
{
dispatcher().assign(".*",&tree_thread::prepare,this,0);
mapper().assign("{1}");
}


@@ -166,6 +169,8 @@ void tree_thread::prepare(std::string sid)

reply::reply(cppcms::service &srv) : thread_shared(srv)
{
dispatcher().assign(".*",&reply::prepare,this,0);
mapper().assign("{1}");
}

void reply::prepare(std::string smid)


+ 1
- 1
examples/message_board/view/forums.tmpl View File

@@ -11,7 +11,7 @@
<h2><% gt "No Topics" %></h2>
<% end %>
<h2><% gt "Create New Disussion" %></h2>
<form action="" method="post" >
<form action="<% url "/forums" %>" method="post" >
<% form as_p form %>
</form>
<% end template %>


+ 30
- 27
src/application.cpp View File

@@ -166,11 +166,26 @@ void application::add(application &app)
if(app.parent()!=this)
app.parent(this);
}
void application::add(application &app,std::string regex,int part)
void application::add(application &app,std::string const &regex,int part)
{
add(app);
dispatcher().mount(regex,app,part);
}

void application::add(application &app,std::string const &name,std::string const &url)
{
add(app);
mapper().mount(name,url,app);
}

void application::add(application &app,std::string const &name,std::string const &url,std::string const &regex,int part)
{
add(app);
dispatcher().mount(regex,app,part);
mapper().mount(name,url,app);
}


void application::attach(application *app)
{
d->managed_children.push_back(app);
@@ -208,10 +223,21 @@ void application::main(std::string url)
}
}

void application::attach(application *app,std::string regex,int part)
void application::attach(application *app,std::string const &regex,int part)
{
d->managed_children.push_back(app);
add(*app,regex,part);
attach(app);
dispatcher().mount(regex,*app,part);
}
void application::attach(application *app,std::string const &name,std::string const &url)
{
attach(app);
mapper().mount(name,url,*app);
}
void application::attach(application *app,std::string const &name,std::string const &url,std::string const &regex,int part)
{
attach(app);
dispatcher().mount(regex,*app,part);
mapper().mount(name,url,*app);
}

void application::render(std::string template_name,base_content &content)
@@ -226,29 +252,6 @@ void application::render(std::string skin,std::string template_name,base_content
service().views_pool().render(skin,template_name,response().out(),content);
}

void application::render(std::string template_name)
{
base_content *cnt = dynamic_cast<base_content *>(this);
if(!cnt) {
throw cppcms_error( "Can't use application::render(std::string) when the application "
"is not derived from base_content");
}
render(template_name,*cnt);
}

void application::render(std::string skin,std::string template_name)
{
base_content *cnt = dynamic_cast<base_content *>(this);
if(!cnt) {
throw cppcms_error( "Can't use application::render(std::string,std::string) when the application "
"is not derived from base_content");
}
render(skin,template_name,*cnt);
}




void application::render(std::string template_name,std::ostream &out,base_content &content)
{
rnd_guard g(content,this);


+ 53
- 6
src/url_mapper.cpp View File

@@ -24,6 +24,8 @@

#include <stdlib.h>

#include <iostream>

namespace cppcms {
struct url_mapper::data
{
@@ -52,12 +54,25 @@ namespace cppcms {
{
by_key_type::const_iterator kp = by_key.find(key);
if(kp == by_key.end())
throw cppcms_error("url_mapper: key " + key + " not found");
throw cppcms_error("url_mapper: key `" + key + "' not found");
by_size_type::const_iterator sp = kp->second.find(params_no);
if(sp == kp->second.end())
throw cppcms_error("url_mapper: invalid number of parameters for " + key);
return sp->second;
}
url_mapper *is_app(std::string const &key) const
{
by_key_type::const_iterator kp = by_key.find(key);
if(kp == by_key.end())
return 0;
by_size_type::const_iterator sp = kp->second.begin();
if(sp==kp->second.end())
return 0;
if(sp->second.child)
return &sp->second.child->mapper();
return 0;
}

url_mapper &child(std::string const &name)
{
@@ -109,8 +124,12 @@ namespace cppcms {
}
}


if(parent) {
parent->mapper().map(output,this_name,ss.str());
std::string url = ss.str();
filters::streamable stream_url(url);
filters::streamable const *par_ptr=&stream_url;
parent->mapper().d->map(this_name,&par_ptr,1,data_helpers,output);
}

}
@@ -119,10 +138,6 @@ namespace cppcms {

void url_mapper::assign(std::string const &key,std::string const &url)
{
real_assign(key,url,0);
}
void url_mapper::real_assign(std::string const &key,std::string const &url,application *child)
{
if( key.empty()
|| key.find('/') != std::string::npos
|| key ==".."
@@ -130,6 +145,14 @@ namespace cppcms {
{
throw cppcms_error("cppcms::url_mapper: key may not be '' , '.' or '..' and must not include '/' in it");
}
real_assign(key,url,0);
}
void url_mapper::assign(std::string const &url)
{
real_assign(std::string(),url,0);
}
void url_mapper::real_assign(std::string const &key,std::string const &url,application *child)
{

data::entry e;
std::string::const_iterator prev = url.begin(), p = url.begin();
@@ -183,7 +206,25 @@ namespace cppcms {
throw cppcms_error("cppcms::url_mapper the application mapping should use only 1 parameter");
}
e.parts.push_back(std::string(prev,p));

e.child = child;

if(child) {
if(d->by_key.find(key)!=d->by_key.end())
throw cppcms_error( "cppcms::url_mapper: mounted application key `" + key +
"' can't be shared with ordinary url key");
}
else {
data::by_key_type::iterator p = d->by_key.find(key);
if(p!=d->by_key.end()) {
data::by_size_type::const_iterator p2 = p->second.find(1);
if(p2!=p->second.end() && p2->second.child)
{
throw cppcms_error( "cppcms::url_mapper: ordinary url key `"+key+
"can't be shared with mounted application key");
}
}
}
d->by_key[key][max_index] = e;
}

@@ -258,6 +299,12 @@ namespace cppcms {
size_t end = key.find('/',pos);
if(end == std::string::npos) {
real_key = key.substr(pos);
url_mapper *tmp = mapper->d->is_app(real_key);
if(tmp) {
// empty special key
real_key.clear();
return *tmp;
}
return *mapper;
}
size_t chunk_size = end - pos;


+ 30
- 23
tests/url_mapper_test.cpp View File

@@ -125,7 +125,8 @@ void basic_test()
struct test1 : public cppcms::application {
test1(cppcms::service &s) : cppcms::application(s)
{
mapper().assign("default","/");
mapper().assign("/default");
mapper().assign("somepath","/");
mapper().assign("page","/{1}");
mapper().assign("preview","/{1}/preview");
mapper().assign("bylang","/{lang}");
@@ -138,7 +139,7 @@ struct test2 : public cppcms::application {
cppcms::application(s),
bee(s)
{
mapper().assign("default","/");
mapper().assign("somepath","/");
mapper().assign("page","/{1}");
mapper().assign("preview","/{1}/preview");
mapper().mount("bee","/bee{1}",bee);
@@ -161,7 +162,7 @@ struct test_app : public cppcms::application {
mapper().mount("foo","/foo{1}",foo);
mapper().mount("bar","/bar{1}",bar);
mapper().mount("foobar","/foobar{1}",bee);
mapper().assign("default","/test");
mapper().assign("somepath","/test");
mapper().root("xx");

bee.bee.mapper().set_value("lang","en");
@@ -169,31 +170,32 @@ struct test_app : public cppcms::application {
void test_hierarchy()
{
std::ostringstream ss;
mapper().map(ss,"default");
mapper().map(ss,"somepath");
TEST(value(ss) == "xx/test");

foo.mapper().map(ss,"default");
foo.mapper().map(ss,"somepath");
TEST(value(ss) == "xx/foo/");
foo.mapper().map(ss,"page",1);
TEST(value(ss) == "xx/foo/1");
foo.mapper().map(ss,"preview",1);
TEST(value(ss) == "xx/foo/1/preview");

bar.mapper().map(ss,"default");
bar.mapper().map(ss,"somepath");
TEST(value(ss) == "xx/bar/");
bar.mapper().map(ss,"page",1);
TEST(value(ss) == "xx/bar/1");
bar.mapper().map(ss,"preview",1);
TEST(value(ss) == "xx/bar/1/preview");

bee.mapper().map(ss,"default");

bee.mapper().map(ss,"somepath");
TEST(value(ss) == "xx/foobar/");
bee.mapper().map(ss,"page",1);
TEST(value(ss) == "xx/foobar/1");
bee.mapper().map(ss,"preview",1);
TEST(value(ss) == "xx/foobar/1/preview");

bee.bee.mapper().map(ss,"default");
bee.bee.mapper().map(ss,"somepath");
TEST(value(ss) == "xx/foobar/bee/");
bee.bee.mapper().map(ss,"page",1);
TEST(value(ss) == "xx/foobar/bee/1");
@@ -212,31 +214,36 @@ struct test_app : public cppcms::application {
}
void test_mapping()
{
TEST(u(*this,"/default")=="xx/test");
TEST(u(*this,"/foo/default")=="xx/foo/");
TEST(u(*this,"/foobar/bee/default")=="xx/foobar/bee/");
TEST(u(*this,"/somepath")=="xx/test");
TEST(u(*this,"/foo/somepath")=="xx/foo/");
TEST(u(*this,"/foo/")=="xx/foo/default");
TEST(u(*this,"/foo")=="xx/foo/default");
TEST(u(*this,"/foobar/bee/")=="xx/foobar/bee/default");
TEST(u(*this,"/foobar/bee")=="xx/foobar/bee/default");
TEST(u(*this,"/foobar/bee/bylang")=="xx/foobar/bee/en");
TEST(u(*this,"default")=="xx/test");
TEST(u(*this,"foo/default")=="xx/foo/");
TEST(u(*this,"foobar/bee/default")=="xx/foobar/bee/");
TEST(u(*this,"somepath")=="xx/test");
TEST(u(*this,"foo/somepath")=="xx/foo/");
TEST(u(*this,"foobar/bee/somepath")=="xx/foobar/bee/");
TEST(u(*this,"foobar/bee/bylang")=="xx/foobar/bee/en");
TEST(u(bar,"/default")=="xx/test");
TEST(u(bar,"/foo/default")=="xx/foo/");
TEST(u(bar,"/foobar/bee/default")=="xx/foobar/bee/");
TEST(u(bar,"/somepath")=="xx/test");
TEST(u(bar,"/foo/somepath")=="xx/foo/");
TEST(u(bar,"/foobar/bee/somepath")=="xx/foobar/bee/");
TEST(u(bar,"/foobar/bee/")=="xx/foobar/bee/default");
TEST(u(bar,"/foobar/bee")=="xx/foobar/bee/default");
TEST(u(bar,"/foobar/bee/bylang")=="xx/foobar/bee/en");
TEST(u(bee.bee,"/default")=="xx/test");
TEST(u(bee.bee,"/foo/default")=="xx/foo/");
TEST(u(bee.bee,"/foobar/bee/default")=="xx/foobar/bee/");
TEST(u(bee.bee,"/somepath")=="xx/test");
TEST(u(bee.bee,"/foo/somepath")=="xx/foo/");
TEST(u(bee.bee,"/foobar/bee/somepath")=="xx/foobar/bee/");
TEST(u(bee.bee,"/foobar/bee/bylang")=="xx/foobar/bee/en");
TEST(u(bee.bee,"default")=="xx/foobar/bee/");
TEST(u(bee.bee,"somepath")=="xx/foobar/bee/");
TEST(u(bee.bee,"bylang")=="xx/foobar/bee/en");
TEST(u(bee.bee,"../default")=="xx/foobar/");
TEST(u(bee.bee,"../../default")=="xx/test");
TEST(u(bee.bee,"../somepath")=="xx/foobar/");
TEST(u(bee.bee,"../../somepath")=="xx/test");
TEST(u(bee.bee,"bylang")=="xx/foobar/bee/en");
TEST(u(foo,"../foobar/bee/bylang")=="xx/foobar/bee/en");
}


Loading…
Cancel
Save