public
Last active

Sinatra like routes in Rails controllers

  • Download Gist
0_README.md
Markdown

Sinatra like routes in Rails controllers

A proof of concept of having Sinatra like routes inside your controllers.

How to use

Since the router is gone, feel free to remove config/routes.rb. Then add the file below to lib/action_controller/inline_routes.rb inside your app.

Then, all you need to do is to tell your Rails application what is the endpoint it should use. It is as easy as:

class Application < Rails::Application
  endpoint lambda { |env| ApplicationController.call(env) }
end

We use a lambda so the controller is reloaded in development. You could pass simply endpoint ApplicationController if you don't care about reloading.

Now, Rails will skip the router and go direct to ApplicationController. In there, simply do:

require "action_controller/inline_routes"

class ApplicationController < ActionController::Base
  include ActionController::InlineRoutes

  protect_from_forgery

  get "/hello" do
    render text: "Hello Gorgeous!"
  end
end

And that is it! You can use all Rails helpers, filters, views, etc.

How it works

Internally, ActionController::InlineRoutes is going to define an index action for you. So callbacks, middleware and everything works just fine and as if you were inside the index action.

In cases no route matches, the method not_found can be called, which you can then further customize or simply do nothing and let Rails default behaviour kick in.

You could even mix Rails conventional actions with this Sinatra routes style, but please don't!

TODO

  • Sinatra routes pattern matching does not work. You can't do "/posts/:id" yet, if you need this, fork it!

  • Add support to mount (like in Rails routers, also called forward in Sinatra(. This will allow you to organize your code in more than one controller. Again, fork it if you need it!

  • Make it a gem with proper README, release and docs!

License

Copyright (c) 2012 José Valim

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

inline_routes.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
module ActionController::InlineRoutes
extend ActiveSupport::Concern
 
included do
class_attribute :inline_routes
self.inline_routes = Hash.new { |h,k| h[k] = [] }
end
 
module ClassMethods
def call(env)
action(:index).call(env)
end
 
def get(path, &block)
define_route("GET", path, block)
end
 
def post(path, &block)
define_route("POST", path, block)
end
 
def put(path, &block)
define_route("PUT", path, block)
end
 
def delete(path, &block)
define_route("DELETE", path, block)
end
 
def patch(path, &block)
define_route("PATCH", path, block)
end
 
private
 
def define_route(verb, path, block)
current = inline_routes[verb] || []
current += [[path, block]]
self.inline_routes = inline_routes.merge(verb => current)
end
end
 
def index
unless run_inline_route
not_found
end
 
raise "inline route did not redirected nor rendered" unless performed?
end
 
private
 
def not_found
headers["X-Cascade"] = "pass"
self.response_body = ""
end
 
def run_inline_route
inline_routes[request.request_method].each do |path, block|
if request.path_info == path
instance_exec(&block)
return true
end
end
false
end
end

I <3 where this is going

But what is the advantage over the usual rails routes? Is this faster? Or is it just to appease those who like Sinatra?

@joelmoss just to appease.

me and @stefanpenner played with it a while ago https://github.com/gmarik/mini-rails

I like this. Routes are one area that I would prefer less metaprogramming auto-configured magic and more explicit declaration that is easily understood at a glance.

@josevalim I've pushed a simple gem for that yesterday. Supports pattern matching!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.