Large Multi Route example?

808 views
Skip to first unread message

Jeremy Hinegardner

unread,
Dec 6, 2014, 6:48:02 PM12/6/14
to ruby...@googlegroups.com
Hey all,

Is there a good app that is out there that shows a good usage of the multi_route plugin? I’m attempting to use it in my application and it I think my notions of what should be doable are different than what it does.

For instance, Say I have an app that uses multiple roda apps and r.run to dispatch to them, and I want to convert it to use multi_route. This is an off the cuff example, and is representative of what the app I’d like to change to use multi_route.

multi_route attempted conversion (does not work): https://gist.github.com/copiousfreetime/74eea8d50a666c4b8122

It looks like when you are using multi_route, you must reference the top level App.route method, it doesn’t pass through to the other classes. 

Also, the context of execution within the route is the top level App, it is not the class where the route is defined, so that means that if you want to call methods within the class of the route you are defining then you need to make the class level methods and pass in a context. So if you had different plugins used in different routing contexts, you need to push them all up into the top level app. Also helper or such, they all end up in the top level app. That basically makes the top level app class a big mess of plugins and helpers which I’m not really a big fan of.

I’ve read http://roda.jeremyevans.net/rdoc/files/doc/conventions_rdoc.html and that doesn’t appear to answer what should be in those ‘routes/a.rb’ etc files. Just the routing block? If that is the case, then were are you putting the classes that implement the business logic at the route termination? 

Am I even explaining myself well?

enjoy,

-jeremy

-- 
Jeremy Hinegardner

Jeremy Evans

unread,
Dec 6, 2014, 8:21:17 PM12/6/14
to ruby...@googlegroups.com
On Saturday, December 6, 2014 3:48:02 PM UTC-8, Jeremy Hinegardner wrote:
Hey all,

Is there a good app that is out there that shows a good usage of the multi_route plugin? I’m attempting to use it in my application and it I think my notions of what should be doable are different than what it does.

Unfortunately, all of the large Roda apps where I use multi_route are proprietary apps, so unfortunately I don't have an open source example.
 
For instance, Say I have an app that uses multiple roda apps and r.run to dispatch to them, and I want to convert it to use multi_route. This is an off the cuff example, and is representative of what the app I’d like to change to use multi_route.

multi_route attempted conversion (does not work): https://gist.github.com/copiousfreetime/74eea8d50a666c4b8122

With the multi_route plugin, everything is in the same class.  Your example has App and B subclasses of Roda.  For this, multi_route won't work.  If you want to use multi_route, you need to combine the multiple classes into a single class.
 
It looks like when you are using multi_route, you must reference the top level App.route method, it doesn’t pass through to the other classes. 

Also, the context of execution within the route is the top level App, it is not the class where the route is defined, so that means that if you want to call methods within the class of the route you are defining then you need to make the class level methods and pass in a context. So if you had different plugins used in different routing contexts, you need to push them all up into the top level app. Also helper or such, they all end up in the top level app. That basically makes the top level app class a big mess of plugins and helpers which I’m not really a big fan of.

Well, then maybe the multi_route plugin is not what you want to use, and you should stick to using run.  Note that there is nothing wrong with using run to dispatch to separate Roda (or other rack) applications.
 
I’ve read http://roda.jeremyevans.net/rdoc/files/doc/conventions_rdoc.html and that doesn’t appear to answer what should be in those ‘routes/a.rb’ etc files. Just the routing block? If that is the case, then were are you putting the classes that implement the business logic at the route termination? 

Am I even explaining myself well?

I think so.  Hopefully this example will clear things up:

# app.rb
class App < Roda
  plugin :multi_route
  Dir['./routes/*.rb'].each{|f| require f}
  route do |r|
    r.multi_route
  end
end

# routes/a.rb:

App.route("a") do |r|
  # ...
end

# routes/b.rb

App.route("b") do |r|
  # ...
end

It may be possible to add another plugin with an API similar to multi_route that works similar to run (multi_run?).  Maybe an API like:

# app.rb

class App < Roda
  plugin :multi_run
  Dir['./routes/*.rb'].each{|f| require f}

  route do |r|
    r.multi_run
  end
end

# routes/a.rb

class A < App
  App.run("a", self)
  route do |r|
    # ...
  end
end

# routes/b.rb

class B < App
  App.run("b", self)
  route do |r|
    # ...
  end
end

Such a plugin would be a bit slower than multi_route, but would add more separation between the various routing subtrees.  Because the routing subtrees are subclasses of the main application (in this example, not actually required), you could add helpers to the main application that would be usable by all of the routing subtrees, but helpers added to a single routing subtree would not affect other routing subtrees. Thoughts on this?

Thanks,
Jeremy

Jeremy Hinegardner

unread,
Dec 7, 2014, 1:18:35 AM12/7/14
to ruby...@googlegroups.com
On December 6, 2014 at 18:21:18, Jeremy Evans (jeremy...@gmail.com) wrote:
On Saturday, December 6, 2014 3:48:02 PM UTC-8, Jeremy Hinegardner wrote:
Hey all,

Is there a good app that is out there that shows a good usage of the multi_route plugin? I’m attempting to use it in my application and it I think my notions of what should be doable are different than what it does.

Unfortunately, all of the large Roda apps where I use multi_route are proprietary apps, so unfortunately I don't have an open source example.

Well, I look forward to the day when we have some large example Roda Apps :-).

For instance, Say I have an app that uses multiple roda apps and r.run to dispatch to them, and I want to convert it to use multi_route. This is an off the cuff example, and is representative of what the app I’d like to change to use multi_route.

multi_route attempted conversion (does not work): https://gist.github.com/copiousfreetime/74eea8d50a666c4b8122

With the multi_route plugin, everything is in the same class.  Your example has App and B subclasses of Roda.  For this, multi_route won't work.  If you want to use multi_route, you need to combine the multiple classes into a single class.

Understood, That matches with the behavior I am seeing.

It looks like when you are using multi_route, you must reference the top level App.route method, it doesn’t pass through to the other classes. 

Also, the context of execution within the route is the top level App, it is not the class where the route is defined, so that means that if you want to call methods within the class of the route you are defining then you need to make the class level methods and pass in a context. So if you had different plugins used in different routing contexts, you need to push them all up into the top level app. Also helper or such, they all end up in the top level app. That basically makes the top level app class a big mess of plugins and helpers which I’m not really a big fan of.

Well, then maybe the multi_route plugin is not what you want to use, and you should stick to using run.  Note that there is nothing wrong with using run to dispatch to separate Roda (or other rack) applications.

I was wanting to switch to the multi_route so that there would be just one routing tree. I haven’t benchmarked, and maybe this is something to add to the r10k to understand the speed difference between a single multi_route system layout and a whole bunch of smaller roda apps that are effectively mounted within the routing tree and have sub routing trees of their own.

I was mostly pondering switching to multi_route since that appeared to be the recommended approach for larger applications.

I’ve read http://roda.jeremyevans.net/rdoc/files/doc/conventions_rdoc.html and that doesn’t appear to answer what should be in those ‘routes/a.rb’ etc files. Just the routing block? If that is the case, then were are you putting the classes that implement the business logic at the route termination? 

Am I even explaining myself well?

I think so.  Hopefully this example will clear things up:


Yup, that all clears it up fine.

It may be possible to add another plugin with an API similar to multi_route that works similar to run (multi_run?).  Maybe an API like:

# app.rb

class App < Roda
  plugin :multi_run
  Dir['./routes/*.rb'].each{|f| require f}

  route do |r|
    r.multi_run
  end
end

# routes/a.rb

class A < App
  App.run("a", self)
  route do |r|
    # ...
  end
end

# routes/b.rb

class B < App
  App.run("b", self)
  route do |r|
    # ...
  end
end

Such a plugin would be a bit slower than multi_route, but would add more separation between the various routing subtrees.  Because the routing subtrees are subclasses of the main application (in this example, not actually required), you could add helpers to the main application that would be usable by all of the routing subtrees, but helpers added to a single routing subtree would not affect other routing subtrees. Thoughts on this?

I could go for smething in that range. Some plugin that allows for the ease of organization of multi_route but uses individual roda apps. I’d have to think for a bit.

I have another thought (that is not complete) about how each route terminates in a lambda/block and capturing the context at that point to dispatching to a particular object. 

Maybe what I’m thinking of is more if a complete routing tree for the application, and a execution context that changes based upon the current node in the routing tree.

I’m happy to continue with run for the time being. Mostly wanted to make sure I wasn’t doing something wrong wrt multi_route. In any case, it is probably good to think about how an application would look that is based more upon run than multi_route.

enjoy

-jeremy


Jeremy Evans

unread,
Dec 7, 2014, 12:32:12 PM12/7/14
to ruby...@googlegroups.com
The issue with changing the execution context during routing is that you can't change the class of an object, so you can't reuse the scope, request, or response objects if the app changes (all three use classes specific to the app).  With run, you are just passing the environment to any rack app using the standard rack API, so with Roda it has to create new scope, request, and response objects each time run is called.  With multi_route, since everything stays in the same class, you can reuse the objects. multi_route basically just instance_execs the block for the matching named route.
 

I’m happy to continue with run for the time being. Mostly wanted to make sure I wasn’t doing something wrong wrt multi_route. In any case, it is probably good to think about how an application would look that is based more upon run than multi_route.

Sounds good.   I'll probably add a multi_run plugin before the next release, to make things easier for people who want to use separate Roda/rack apps for the routing subtrees.

Thanks,
Jeremy

namt...@gmail.com

unread,
Dec 9, 2014, 8:37:34 AM12/9/14
to ruby...@googlegroups.com
Hello everyone

I am also looking to create a large app, I prefer to separate my app into modules that are self contained and can be easily extracted if need be in the future.
Is there an efficient way to achieve the below layout (you can ignore the naming I'm more interested in the structure).

Any pointer will greatly be appreciated.


foo_bar_app
├── modules
│   ├── Foo
│   │   ├── controllers
│   │   │   └── foo_router.rb                 # Foo Router
│   │   ├── Services
│   │   │   ├── foo_update_service.rb
│   │   │   └── foo_register_service.rb
│   │   ├── views
│   │   │   ├── foo_sub_layout.rb            # ModuleLayout
│   │   │   └── foo
│   │   │       └── index.rb                 # foo/Index
│   │   ├── config
│   │   │   └── foo_config.rb
│   │   ├── javascripts
│   │   │   └── application.js
│   │   └── stylesheets
│   │       └── application.css
│   ├── Bar
│   │   ├── controllers
│   │   │   └── bar_router.rb                # Bar router
│   │   │   └── zeebar_router.rb             # Zeebar router
│   │   ├── services
│   │   │   ├── bar
│   │   │   │   └── bar_removal_service.
│   │   │   └── zeebar
│   │   │       └── zeebar_removal_service.
│   │   ├── views
│   │   │   ├── bar_sub_layout.rb            # ModuleLayout
│   │   │   ├── bar
│   │   │   │   ├── index.rb                 # bar/Index
│   │   │   │   └── edit.rb                  # bar/Edit
│   │   │   ├── zeebar
│   │   │   │   ├── index.rb                 # zeebar/Index
│   │   │   │   └── edit.rb                  # zeebar/Edit
│   │   │   └── shared
│   │   │       └── bar_sub_layout.rb       
│   │   ├── config
│   │   │   └── foo_config.rb
│   │   ├── javascripts
│   │   │   └── application.js
│   │   └── stylesheets
│   │       └── application.css
│   │
│   ├── controllers
│   │   └── hardware_router.rb               # hardware router
│   ├── services
│   └── views
│       ├── app_layout.rb                    # App wide Layout
│       └── hardware
│           └── index.rb                     # Hardware/Index
├── application_router.rb                    # Main router
├── config
│   └── app_config.rb
└── public
    ├── favicon.ico
    ├── fonts
    │   └── cabin-medium.woff
    ├── images
    │   └── application.jpg
    ├── javascripts
    │   └── application.js
    └── stylesheets
        └── application.css

Jeremy Evans

unread,
Dec 9, 2014, 12:19:39 PM12/9/14
to ruby...@googlegroups.com
On Tuesday, December 9, 2014 5:37:34 AM UTC-8, namt...@gmail.com wrote:
Hello everyone

I am also looking to create a large app, I prefer to separate my app into modules that are self contained and can be easily extracted if need be in the future.
Is there an efficient way to achieve the below layout (you can ignore the naming I'm more interested in the structure).

Any pointer will greatly be appreciated.

It's hard to advise without knowing what your URL structure is.  But assuming your URLs reflect your application layout, your application_router.rb could look something like this:

Dir ['modules/**/controllers/*.rb'].each{|f| require f}
class App < Roda
  route do |r|
    on "hardware" do
      run Hardware # defined in modules/controllers/hardware.rb
    end
    on "foo" do
      run Foo # defined in modules/Foo/controllers/foo_router.rb
    end
    on "bar" do
      run Bar # defined in modules/Bar/controllers/bar_router.rb
    end
    on "zeebar" do
      run Zeebar # defined in modules/Bar/controllers/zeebar_router.rb
    end
  end
end

You could treat Hardware/Foo/Bar/Zeebar as standalone Roda apps in such a case.  This could be cleaned up when the multi_run plugin is implemented.

From the looks of your structure, you are gonig to want to use the render plugin in Hardware/Foo/Bar/Zeebar and the assets plugin in Foo/Bar/Zeebar.

That doesn't really address your entire question, but the scope is rather large.  If you have questions on how to accomplish specific tasks, please ask.

Thanks,
Jeremy

namt...@gmail.com

unread,
Dec 9, 2014, 2:24:20 PM12/9/14
to ruby...@googlegroups.com
Thanks for the reply.

Yes on the url's reflecting the application layout.

The example is adopted from a .Net mvc app layout that I have managed to use successfully to isolate related features into self contained modules which has simplified maintenance and development a lot.
I was impressed by Roda and would  love to replicate the same layout, where there are minimal app wide features such as master templates, master styles, master js, master routes etc for features that are cross cutting plus the self contained modules. I had read on the run option but got the impression that it might not be the most efficient.

That is why I wanted to get your opinion on the most efficient/fast way of achieving my type of setup.

When creating the multi_run plugin would you please consider such a modular layout,  I have found it to be one of the best ways to keep large apps maintainable, and easy for new dev's to dive into.

Thanks.

namt...@gmail.com

unread,
Dec 9, 2014, 2:43:46 PM12/9/14
to ruby...@googlegroups.com
Jeremy would you also elaborate on
 
From the looks of your structure, you are gonig to want to use the render plugin in Hardware/Foo/Bar/Zeebar and the assets plugin in Foo/Bar/Zeebar.

I do not have such a folder layout and cant figure out exactly what you mean

Thanks again.
Message has been deleted

Jeremy Evans

unread,
Dec 9, 2014, 2:49:16 PM12/9/14
to ruby...@googlegroups.com
On Tuesday, December 9, 2014 11:24:20 AM UTC-8, namt...@gmail.com wrote:
The example is adopted from a .Net mvc app layout that I have managed to use successfully to isolate related features into self contained modules which has simplified maintenance and development a lot.
I was impressed by Roda and would  love to replicate the same layout, where there are minimal app wide features such as master templates, master styles, master js, master routes etc for features that are cross cutting plus the self contained modules. I had read on the run option but got the impression that it might not be the most efficient.

run does add a little bit of overhead, but only a little.  For a large application the difference would probably not be noticeable.
 
That is why I wanted to get your opinion on the most efficient/fast way of achieving my type of setup.

When creating the multi_run plugin would you please consider such a modular layout,  I have found it to be one of the best ways to keep large apps maintainable, and easy for new dev's to dive into.

With multi_run (just recently committed), the code is similar to the example given earlier, except you register the applications using Roda.run, and dispatch to them based on prefix using r.multi_run:

Dir ['modules/**/controllers/*.rb'].each{|f| require f}
class App < Roda
  run "hardware", Hardware
  run "foo", Foo
  run "bar", Bar
  run "zeebar", Zeebar

  route do |r|
    r.multi_run
  end
end

I'm guessing this should work fine for your layout, as it allows you to treat each of your modules as its own rack application.

Thanks,
Jeremy
Reply all
Reply to author
Forward
0 new messages