z, ? | toggle help (this) |
space, → | next slide |
shift-space, ← | previous slide |
d | toggle debug mode |
## <ret> | go to slide # |
r | reload slides |
n | toggle notes |
Hi everyone, this lightning talk is about Roda. Roda is a new Ruby web framework that came out last year.
Roda is designed around the concept of a routing tree.
The routing tree concept was originally developed by the author of Rack back in 2009. He created a framework named Rum, which unfortunately was not released as a gem and never became well known.
The fundemental difference between a routing tree and other approaches to routing is that when using a routing tree, routing and request handling are integrated. As you are routing a request, you can also be handling a request.
Here is Hello World in Roda.
require 'roda'
Roda.route do |r|
"Hello World!"
end
For every request that is received, Roda yields to the block passed to Roda.route.
require 'roda'
Roda.route do |r|
"Hello World!"
end
The block is yielded a Rack::Request instance, and by convention, the block argument is named r.
require 'roda'
Roda.route do |r|
"Hello World!"
end
The return value of the block is used as the response. So this simple app returns Hello World for every request.
require 'roda'
Roda.route do |r|
"Hello World!"
end
Routing is performed in Roda by calling routing methods on the Rack::Request instance. Here, calling r.get with the string hello means that only GET requests for /hello will yield to the block.|All other requests will not yield to the block, and will result in a 404 not found response.
require 'roda'
Roda.route do |r|
r.get 'hello' do
"Hello World!"
end
end
You can nest routing methods inside each other, forming a tree, which is why this approach to routing is called a routing tree.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
Here, the block passed to r.on is yielded to only if the request path starts with /hello.|If the block is yielded to, /hello is stripped from the front of the path, and the routing process is repeated inside the block.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
So r.get here will only yield to the block if the request path is /hello/world.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
and since it returns Hello World, that will be returned as the response.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
If the request path is /hello/railsconf, it doesn’t yield to the first r.get call, and execution continues.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
Then execution gets to r.get railsconf, which will yield to the block and then return the Hello RailsConf response.
require 'roda'
Roda.route do |r|
r.on 'hello' do
r.get 'world' do
"Hello World!"
end
r.get 'railsconf' do
"Hello RailsConf"
end
end
end
At any point in any of these blocks, you can handle the current request. In this example, we set an instance variable as soon as the r.on block is taken.
require 'roda'
Roda.route do |r|
r.on 'hello' do
@greeting = 'Hello'
r.get 'world' do
"#{@greeting} World!"
end
r.get 'railsconf' do
"#{@greeting} RailsConf"
end
end
end
That instance variable can be used any of the route blocks in that branch of the routing tree.|This approach makes it unnecessary to have before filters all over your application in order to DRY code up.
require 'roda'
Roda.route do |r|
r.on 'hello' do
@greeting = 'Hello'
r.get 'world' do
"#{@greeting} World!"
end
r.get 'railsconf' do
"#{@greeting} RailsConf"
end
end
end
Let’s say you have an albums database and want to be able to edit attributes for albums. You have a route for each album, where a GET request shows a form for the album, and a POST request processes that form’s input.
require 'roda'
Roda.plugin :render
Roda.route do |r|
r.is 'albums/:id' do |album_id|
@album = Album.find(album_id.to_i)
r.get do
view(:album)
end
r.post do
@album.update(r['album'])
r.redirect
end
end
end
Instead of using a before filter or duplicating code in both methods, you just set the appropriate instance variable as soon as the branch is taken.
require 'roda'
Roda.plugin :render
Roda.route do |r|
r.is 'albums/:id' do |album_id|
@album = Album.find(album_id.to_i)
r.get do
view(:album)
end
r.post do
@album.update(r['album'])
r.redirect
end
end
end
This makes the @album instance variable available in both the GET and POST routes.
require 'roda'
Roda.plugin :render
Roda.route do |r|
r.is 'albums/:id' do |album_id|
@album = Album.find(album_id.to_i)
r.get do
view(:album)
end
r.post do
@album.update(r['album'])
r.redirect
end
end
end
Roda is designed to be very extensible, with a small core that only supports the bare minimum needed for routing.|The view method here renders a template, but it’s not included in Roda’s core.
require 'roda'
Roda.plugin :render
Roda.route do |r|
r.is 'albums/:id' do |album_id|
@album = Album.find(album_id.to_i)
r.get do
view(:album)
end
r.post do
@album.update(r['album'])
r.redirect
end
end
end
Roda uses a plugin system to enable functionality. The render plugin shown here adds support for rendering templates, including adding the view method.|By loading only the plugins you need, you avoid the memory and performance penalities of code that you don’t use.
require 'roda'
Roda.plugin :render
Roda.route do |r|
r.is 'albums/:id' do |album_id|
@album = Album.find(album_id.to_i)
r.get do
view(:album)
end
r.post do
@album.update(r['album'])
r.redirect
end
end
end
Roda ships with over 50 plugins to handle wide variety of use cases, so unlike when using most other small web frameworks, you don’t have to recreate the wheel.|Roda has plugins for features like asset packaging, template streaming, and sending emails using a routing tree.
Like other small web frameworks, Roda scales down well to handle small sites easily.
Unlike other small web frameworks, Roda scales up well, with multiple plugins designed to make it simple to structure large sites while still keeping the benefits of using a routing tree.|Roda makes it easy to start with a small site, and grow gradually into a large site.
In addition to making it simpler to write applications, Roda is the first ruby web framework designed with immutability in mind.|Roda apps are designed to be frozen in production, eliminating most possible thread safety issues in the framework.
require 'roda'
Roda.route do |r|
"Hello world!"
end
Roda.freeze
Roda makes it easy to secure your web applications, with plugins and options to avoid cross site scripting, cross site request forgery, click jacking, and insecure access.
require 'roda'
Roda.plugin :render, :escape=>true
Roda.plugin :csrf
Roda.plugin :default_headers,
'X-Frame-Options'=>'deny'
'Strict-Transport-Security'=>'max-age=16070400;'
Finally, Roda is also one of the fastest ruby web frameworks.
Using the r10k synthetic benchmarker, Roda is over 15x faster than Rails or Sinatra for applications with large numbers of routes. But synthetic benchmarks are pretty meaningless.|In real world applications with a couple hundred routes, I’ve found Roda can be 50% faster than Sinatra and twice as fast as Rails.
For applications with large numbers of routes, r10k has Sinatra using 70% more memory than Roda, and Rails using over three times as much memory.|In real world apps with a couple hundred routes, I’ve found Rails uses 2/3 more memory than Roda.
Have a slow Rails app that you want to speed up? Load the middleware plugin into Roda and load the Roda app as middleware in your Rails app.|Any route the Roda app doesn’t handle will call the Rails app, allowing you to speed up your bottlenecks while still using Rails for less performance sensitive code.
require 'roda'
Roda.plugin :middleware
# Rails
config.middleware.use Roda
Simplicitity, Extensibility, Reliability, and Performance. These are Roda’s four core values.
If you share those values and want to learn more about Roda, check out roda.jeremyevans.net for details, or just come talk to me. Thanks for listening!