The problem with this approach is readability. If you look at the code vs Sinatra every time you want to figure out which route to edit, you have to traverse the tree as opposed to just scanning for the route you want. As an example, editing the route /artist/:id/album/:number requires you to first find the /artist/:id then scan down and look for /album/:number making sure you watch for the closing end because what if the route isn't defined? In Sinatra, you look for /artist/:id/album/:number. The solution to this is to put a comment on every route to denote where it is. I agree the tree methodology can be faster O(log N) (for GET and POST) vs O(N) but readability suffers with no comments.
Why couldn't Sinatra create some sort of underlying tree structure that could perform in O(log N) as opposed to O(N). That way you get the readability with the perf.
It couldn't create a tree in all instances because it allows full regexes in the routes. It could create a hybrid (split into trees for anything with a known static prefix), or go the full hog to a finite automata, though. But at least the latter is suddenly a big complexity increase.
a fairly trivial fix would be to have routes optionally defined as "[/artist/:id/album]/number" where the [...] prefix is not required, but is checked for correctness if present.
The basic design for Roda was taken from Cuba (it's a fork of Cuba), and I think if you read the Roda source, you wouldn't find it difficult to understand.
There are certainly parts of Sequel that are overly clever, I won't argue that. But compared to similar libraries, I don't think it's worse in that department.
I'm glad Roda is finally getting some publicity. I submitted it here when it was released.
I've converted about 15 apps from Sinatra to Roda, and a couple of Rails apps to Roda (working on converting my final Rails app). Personally, I've found that the biggest advantages come when the URL structure mirrors your application structure, and you have redundant code in many routes, as that type of code becomes simpler, faster, and DRYer with Roda.
Yes, you can use a before block with wildcard routes with Sinatra, but Sinatra will still traverse the routing list sequentially and recheck the full route for every entry in the list. Unfortunately, while I love Sinatra, have contributed patches to it, and have used it since 0.1.0, the approach doesn't scale well for sites with a lot of routes.
The code non-locality issue shouldn't be ignored, and is probably the largest issue with Roda, but the issue is probably going to happen with any approach that DRYs up the code to the same degree. It's true if you use a before block in Sinatra, or a before_filter in Rails.
The use of a routing tree really isn't an innovative approach (at least not innovated by me). It's been around in Ruby since Rum was released in 2008 (and maybe earlier elsewhere). However, it's not well known, and I hope that Roda brings it into the mainstream.
One of the best things about Roda that I don't think has been mentioned in the comments yet is the plugin system, which I borrowed from Sequel. This makes it easy to extend Roda beyond its very small core. For example, the multi_route plugin makes it easy to scale Roda to larger sites by splitting up routing subtrees into specific files. There are currently about 15 plugins, and I have ideas for quite a few more that I will implement after finishing up my current Rails->Roda conversion.
I apologize that the code on the website isn't syntax highlighted. If anyone can offer their design skills, I would greatly appreciate it.
First: This is pretty sweet, Jeremy. Glad to see this.
Second: How well does it scale for developing large sites (for whatever value of "large" seems useful for discussion).
One of the things I really like about Ramaze is that I can start with basically a Rack app, spin it off to a simple Ramaze app, and keep expanding and refactoring it as requirements call for.
Is there some app size or level of app complexity beyond which Roda would not be a sensible choice? You say it has a plugin system; is it really battle-ready? (I'm going to guess Yes because of who wrote it, but I'd like to hear any additional thoughts on it.)
I get the impression that Roda lends itself to building large sites that are a collection of sub-sites or services; you're never really building a single big monolithic app.
Side note: For fast syntax highlighting you could use embedded gists.
I don't work on truly huge sites, my largest is only about 10kloc. But it scales very well for the all of the sites I've tried, and I think based on the architecture it would scale to much larger sites.
Your workflow with Rack->Ramaze should work similarly when using Roda. Roda scales down well:
# config.ru
require 'roda'
Roda.route{'Hello world'}
run Roda
There is probably some level of complexity where Roda may not be a good choice. I'm not sure what that level is, but I don't expect to hit it on sites I work on. In any case it's probably best to split up such an app into multiple apps/services before that level is reached.
In terms of battle readiness, it's new and currently I think I'm the only person using it in production. I hesitate to call something battle ready until there are more people using it, but I'm very committed to it and you can expect the same level of support for Roda that Sequel receives.
Thanks for the tip on embedded gists, I'll look into that.
I don't really buy the "big win" over Sinatra ("much DRYer", no need to set ivars for each action) because you can just use a `before` block with wildcard routes.
before '/artist/:id*' do
@artist = Artist[params[:id]]
end
before '/artist/:id/album/:number*' do
@album = @artist.album_with_number(params[:number])
end
Welcome Roda and congrats for this new innovative routing idea. As much as I see some value in a tree-like routing definition in this nice comparison with Sinatra, http://roda.jeremyevans.net/why.html, Sinatra's routing approach still appears to be more readable and manageable. Maybe Sinatra could optimize the storage of the routing entries internally in a tree to make it as fast as Roda (if it doesn't already do).
As for readability, Roda kinda shoots itself in the code by not having any highlighting on that snippet, but generally yes, you want to keep dispatch trees to small apps. On the other hand, using P/WSGI apps you can stack apps inside one another, scaling trees without getting a moloch. Not clear on whether Roda supports that.
Roda supports stacking apps via r.run to dispatch to any other rack app. You can also use the multi-route plugin, which allows you to split up the routing into subtrees, but keeps the current state.
I love it. I think the concerns about the code being unmaintainable from the example are a bit unfair. There is nothing preventing a developer from moving the blocks out into separate files/classes for a more coder-friendly organization. How deep do you guys nest your javascript callbacks?
This also lead me to check out sequel, which I can't believe that I'm just now finding out about.
I've been using a web "framework" I wrote for a few years now. It now powers various projects, from the Chartboost Store backend, to the entire the entire backend codebase for my new startup, FreshPay.
I think this looks great. However, even in his simple example comparing Roda and Sinatra the code ends with 7 'end's. I can imagine how deeply nested a non-simple app can be. In my experience, the deeper you get in your nesting - the more complicated the code is to read.
It might be just me, but that is insanely hard to read. I MUCH prefer the Sinatra approach for that reason alone. Looking at the examples side-by-side I couldn't tell what was going on in Roda...at all.
My apologies if this is off-topic, but since I see mention of various frameworks ... it seems relevant.
I'm a .NET dev finally looking at Ruby again. I have a Rails book from a long while ago that I'm working through (well, planning on - Rails 4 in Action, still in MEAP) but looking at this data I'm wondering if that's the best idea.
Any recommendations on a framework for someone getting started?
If it helps, I've been using ASP.NET MVC since 1.0 if not slightly earlier.
It depends on your goals, but if they include simplicity I would strongly advise against rails. Coming from .NET, you may be looking for the breath of fresh air feeling ruby can provide, that I can now do things in 1 line that took 5 before and understand every part of my system -- if that is the case, Sinatra or another microframework might be the thing for you. It's also nice to be able to learn and start using something in a matter of hours rather than "working through a book"
It sounds like you may have come into Ruby from a similar background; any suggestions on a good way to start learning the language? There seems to be an interesting post on creating a very basic blog engine via Sinatra, but everything else I've found is ... lacking.
As far as my own goals, I actually like .NET (C#) as a language, and generally don't feel as though ASP.NET MVC is too complex. It has definitely gotten a bit more bloated in particular areas, but ... Shrug.
I wish that routes weren't stuck within strings like `/artist/:id`, but instead used some kind of pattern matching like `['artist' :id]`. It's better to use Ruby syntax instead of a quoted string of characters that can't be manipulated or verified as easily.
Definitely feels like an improvement on Cuba! I'm not a huge fan of the `is` method here, though. I feel like you could get the same thing from a terser DSL if you treated HTTP methods as terminators and only allowed `on` to filter the path.