Extending Rails 3 with Railties »
Created at: 07.10.2010 14:49, source: Engine Yard Blog, tagged: Uncategorized Rails 3 Railties
Gem plugins in Rails 3.0
Rails 3.0 is finally released, and with it comes a fantastic new way to extend Rails: Railties. Railties are the basis of the core components of Rails 3, and are the result of months of careful refactoring by Carlhuda. It is easier to extend and expand Rails than it has ever been before, without analias_method_chain in sight.
Unfortunately, while the system for extending and expanding Rails has been completely overhauled, the documentation hasn't been updated yet. The Rails Plugins Guide only covers writing plugins in the old Rails 2 style. Ilya Grigorik wrote a Railtie & Creating Plugins blog post, but just scratched the surface of what is possible with a Railtie plugin. This post covers writing Railtie plugins, hooking into the Rails initialization process, packaging Railtie plugins as gems, and using gem plugins in a Rails 3 application.
Creating Railtie plugins
Creating a Railtie is easy. Just create a class that inherits from::Rails::Railtie. Every subclass of Railtie is used to initialize your Rails application. Since ActionController, ActionView, and the other Rails components are also Railties, your plugin can function as a first-class member of the Rails application. It will have access to the same methods and context that are used by the official Rails components. Here is a sample minimal Railtie that will be loaded when your Rails application boots.
require 'rails'
class MyCoolRailtie < Rails::Railtie
# railtie code goes here
end
The Railtie documentation lists all of the methods that are available inside each Railtie class, but doesn't really go into depth about what you can use Railties to do. Here are some example Railties explaining how to use the Railtie methods (in alphabetical order) to customize and extend Rails.
console
Theconsole method allows your Railtie to add code that will be run when a Rails console is started.
console do
Foo.console_mode!
end
generators
Rails will require any generators defined inlib/generators/*.rb automatically. If you ship Rails::Generators with your Railtie in some other directory, you can require them using this method.
generators do
require 'path/to/generator'
end
rake_tasks
If you ship rake tasks for apps with your Railtie, load them using this method.rake_tasks do
require 'path/to/railtie.tasks'
end
initializer
Theinitializer method provides Railties with a lot of power. They create initializers that will be run during the Rails boot process, like the files put into config/initializers in the app directory. The initializer method takes two options, :after or :before, if there are specific initializers that you want to run before or after yours.
initializer "my_cool_railtie.boot_foo" do
Foo.boot(Bar)
end
initializer "my_cool_railtie.boot_bar",
:before => "my_cool_railtie.boot_foo" do
Bar.boot!
end
Rails configuration hooks
The biggest extension hook that Railties provide is somewhat unassuming: theconfig method. That method returns the instance of Railtie::Configuration that belongs to the application being booted. This opens up all sorts of interesting possibilities, since the config object is the same one that is made available inside a Rails application's environment.rb file. Here are some annotated examples of using config to change how a Rails application is initialized and configured.
after_initialize
This method takes a block that will be run after Rails is is completely initialized, and all of the application's initializers have run.app_middleware
This method exposes the MiddlewareStack that will be used to handle requests to your Rails application. You can use any of the methods defined on MiddlewareStack, includinguse and swap, to manage the Rails application's Rack middlewares. For example, if your Railtie included the Rack middleware MyRailtie::Middleware, you could add it to the Rails application middleware stack like this:
config.middlewares.use MyRailtie::Middleware
before_configuration
Code passed in a block to this method will be run immediately before the application configuration block insideapplication.rb is run. This is usually the best place to set default options that users of your plugin should be able to override themselves, as in the jquery-rails example below.
before_eager_load
The block passed tobefore_eager_load will be run before Rails requires the application’s classes. Eager load is never run in development mode. However, if you need to run code after Rails loads, but before any application code loads, this is the place to put it.
config.before_eager_load do
SomeClass.set_important_value = "RoboCop"
end
before_initialize
This method takes a block to be run before the Rails initialization process happens -- this is basically equivalent to creating an initializer, and setting it to run before the first initializer the app has.generators
This object holds the configuration for the generators that are invoked when you run therails generate command.
config.generators do |g|
g.orm :datamapper, :migration => true
g.template_engine :haml
g.test_framework :rspec
end
You can also use it to disable colorized logging in the console.
config.generators.colorize_logging = false
to_prepare
Last, but quite importantly,to_prepare allows you to do one-time setup. The block you pass to this method will run for every request in development mode, but only once in production. Use it when you need to set something up once before the app starts serving requests.
Examples
At this point, you're probably thinking "why would I actually want to do any of that stuff?". So, here are a few select examples of Railtie plugins packaged as gems.rspec-rails
The rspec-rails plugin ships with a set of rake tasks and generators that integrate the RSpec gem with Rails.module RSpec
module Rails
class Railtie < ::Rails::Railtie
config.generators.integration_tool :rspec
config.generators.test_framework :rspec
rake_tasks do
load "rspec/rails/tasks/rspec.rake"
end
end
end
end
This Railtie just does three things: First, it sets the generators that will be used for integration tests via the integration_tool method. Next, it sets the generators that will be used to generate model, controller, and view tests (via the test_framework method). Last, it loads the RSpec rake tasks to run RSpec tests instead of test-unit tests.
jquery-rails
The jquery-rails plugin ships with a generator that downloads and installs jQuery, the jquery-ujs script that enables Rails helpers with jQuery, and optionally installs jQueryUI as well.module Jquery
module Rails
class Railtie < ::Rails::Railtie
config.before_configuration do
if ::Rails.root.join('public/javascripts/jquery-ui.min.js').exist?
config.action_view.javascript_expansions[:defaults] =
%w(jquery.min jquery-ui.min rails)
else
config.action_view.javascript_expansions[:defaults] =
%w(jquery.min rails)
end
end
end
end
end
This Railtie only sets one setting, but checks for the jQueryUI library to determine the value to set. By using the config.before_configuration hook, it runs right before the application.rb config block runs. That means it has access to the Rails.root, which is needed to check for jQueryUI, and it means that users can still override javascript_expansion[:defaults] in their application.rb if they want something different than the new defaults that the plugin provides.
haml-rails
The haml-rails gem provides generators for views written in Haml instead of the default generated views that are written in ERB.module Haml
module Rails
class Railtie < ::Rails::Railtie
config.generators.template_engine :haml
config.before_initialize do
Haml.init_rails(binding)
Haml::Template.options[:format] = :html5
end
end
end
end
This Railtie simply changes the template engine that Rails invokes when you run rails generate, and then initializes Haml for Rails, and sets the Haml output format to HTML5.
Packaging up gem plugins
Railtie plugins are easy to turn into gem plugins for Rails. This makes them easy to distribute, manage, and upgrade. The first thing you need is a gem. If you don't have a gem yet, you can create a new gem easily using Bundler. Just runbundle gem my_new_gem and Bundler will generate a skeleton gem and gemspec that follow gem best practices. Once you have a gem, just make sure that your Railtie subclass is defined when lib/my_new_gem.rb is loaded. You can define the Railtie in a separate file and require that file, or define it directly. Last, add a dependency on the Rails gem (~>3.0) to your gemspec.
If your gem is also a plain Ruby library, and you don't want to depend on the Rails gem, then you can put your Railtie in a separate file, and conditionally require that file inside your main library file.
# lib/my_new_gem/my_cool_railtie.rb
module MyNewGem
class MyCoolRailtie < ::Rails::Railtie
# Railtie code here
end
end
# lib/my_new_gem.rb
require 'my_new_gem/my_cool_railtie.rb' if defined?(Rails)
This ensures that your gem can be loaded (without the Railtie) if it is loaded outside the context of a Rails application.
Now that your gem has a Railtie, you can build it and release it to Gemcutter. Once your gem is on Gemcutter, using it with Rails 3 applications is extremely easy -- just add the gem to your Gemfile. Bundler will download and install your gem when you run bundle install, Rails will load it, and the Rails::Railtie class takes care of the rest!more »
Rails Has Great Documentation »
Created at: 28.08.2010 21:08, source: Riding Rails - home, tagged: Activism Documentation Documentation Rails 3 Rails 3
To this day I still hear people complain that Rails has poor documentation. From where I’m sitting this seems far from the truth. Let me lay out the evidence piece by piece:
To learn Rails from scratch Michael Hartl recently finished his book Ruby on Rails Tutorial: Learn Rails by Example. The book teaches Rails 3 from the ground up and it’s available for FREE online. If you’d rather have a PDF or a book you can grab that as well (and he’s even working on some screencasts).
The source for the finalized book will be pushed to GitHub and released under a Creative Commons License shortly after Rails 3 is done. If you’d like to help translate the book to your language of choice, feel free to contact Michael and he’ll get in touch when it’s time to make it happen.
Rails Guides
If you’re not a Rails newbie don’t forget about the Rails Guides, which have been updated for Rails 3.
Rails API Docs
There are two main websites I use to do API lookups. The first is Rails Searchable API Doc, which has online and offline searchable documentation. The second is APIdock which is online only, but has the ability to comment and easily compare different versions of documentation.
Rails 3 Free Screencasts
If you’re more of a visual learner (like me) then there are plenty of free screencasts to teach you about Rails 3. About 2 months ago I produced the Rails 3 Screencasts, which will get you started.
Ryan Bates has also produced an incredible amount of Rails 3 screencasts over on Railscasts.com. Ryan has been producing Railscasts for over 3 1/2 years, isn’t that crazy?
There’s also a few good free screencasts over on Teach me to Code by Charles Max Wood.
Keeping on the Edge
If you find yourself wondering how to keep up with all of the newest features / libraries for Rails 3, both the Ruby5 Podcast and the Ruby Show are going strong. Don’t listen to audio? It doesn’t matter, just subscribe to the Ruby5 RSS feed and get links with descriptions to all the newest libraries, tutorials, and more. You might also want to checkout Peter Cooper’s new Ruby Weekly, a Ruby email newsletter.
Need to upgrade a big app to Rails 3?
Jeremy McAnally’s Rails 3 Upgrade Handbook PDF is just $12. There’s also a few paid screencasts for the upgrade over on Thinkcode.tv and BDDCasts.
Need a Book?
There’s a bunch of books that will be coming out after the release, most of which you can start reading now. The Rails 3 Way by Obie Fernandez, Rails 3 In Action by Ryan Bigg and Yehuda Katz, Beginning Rails by Cloves Carneiro Jr and Rida Al Barazi, and of course the Agile Web Development with Rails:fourth edition by Sam Ruby, Dave Thomas, and David Heinemeier Hansson.
In conclusion
No more complaining about lack of good documentation! Seriously. If you want even more Rails 3 content, check out the blog post by Kevin Faustino on 34 Ruby on Rails 3 resources to get you started.
more »
Announcing Rails Dispatch! »
Created at: 07.04.2010 20:00, source: Engine Yard Blog, tagged: News rails Rails 3
At Engine Yard, we’re always looking for new and fun ways to give back and help grow the community. Most recently, we organized the Ruby Summer of Code, designed to help foster student participation in open source development. Today, as the next project on our long list, we’re launching RailsDispatch.com.
Rails Dispatch will provide timely releases of up-to-date educational content, in the form of blog posts, tutorial videos, screencasts and more. We’ll be working with Engine Yard and community developers to put together the resources, and aiming for weekly content pushes from now until RailsConf. Until then the bulk of the content, in the spirit of the times, will focus on Rails 3, and the changes and new features it brings. As the content library grows, we’ll also be releasing new, interactive elements and community resources, so you definitely want to hop off the RSS feed and on to the site every little bit to check out what’s new.
Check out today’s release for the technical details on how Rails 3 makes life better, and an update to the famous Rails ‘blog in 15 minutes’ screencast.
As always, leave your feedback here, and check out Rails Dispatch!
more »
The Lowdown on Routes in Rails 3 »
Created at: 30.03.2010 21:00, source: Engine Yard Blog, tagged: Technology Rails 3 routes
Stop! I’d like to tell you something important, but it may be a bit shocking, so you should probably have a seat. Here goes: everything you knew about working with routes in Rails 2… is history! With Rails 3, you’ve got to roll up your sleeves, unlearn what you learned, and route the new way around. And this time, it’s faster, cleaner and a lot more Ruby-like.
In this post, we’ll walk through the underpinnings of Routes in Rails 3. They’ve been rewritten—for good reason—and after we get through the explanation, I’m confident you’ll agree.
Let’s start by looking at some code; here’s the new DSL, in its full glory:
resources :products do resource :category member do post :short end collection do get :long end end match "/posts/github" => redirect("http://github.com/rails.atom")
Now check out the old way of doing it:
map.resources :products, :member => {:short => :post}, :collection => {:long => :get} do |products| products.resource :category end
As you can see, the example from Rails 3 is much cleaner and more Rubyish. So let’s jump right in and walk through a quick overview of how you’d define different types of routes in Rails 3.
Default Route
The default route in Rails 3, match '/:controller(/:action(/:id))', is much more explicit, as the parenthesis denote optional parameters.
Regular Routes
Rather than defining different keys for controller and action, you just have catalog#view, which is pretty awesome.
match 'products/:id', :to => 'catalog#view'
In Rails 2, you would’ve done:
map.connect 'products/:id', :controller => 'products', :action => 'view'
Named Routes
Named Routes generate helpers like posts_url and posts_path, rather than manually defining the hash to action and controller in helper methods like link_to:
match 'logout', :to => 'sessions#destroy', :as => "logout"
The key :as specifies a name to generate helpers. In Rails 2, you would have done:
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
Empty Route
The root of the web site is the empty route. Whereas Rails 2 added a nice shortcut to it, Rails 3 simplifies things even further:
# Rails 3 root :to => 'welcome#show' # Rails 2 map.root :controller => "welcome", :action => 'show'
Shorthands
The revamped routes in Rails 3 sport some nice shortcuts to commonly used routes. There are two types of shorthands. First, the :to shorthand allows you to skip the :to key and directly designate the route to the matcher:
match "/account" => "account#index" match "/info" => "projects#info", :as => "info"
Second, the match shorhand allows you to define a path and controller with its action at the same time:
match "account/overview" # identical to match "account/overview", :to => "account#overview"
Verb Routes
While you can limit a route to an HTTP request through :via, it’s a nice added convenience to have Verb routes. Adding sugar on top, you can even use shorthands with them:
get "account/overview" # identical to match "account/overview", :to => "account#overview", :via => "get"
Keys
The match method (as well as the verb shorthands) take a number of optional keys.
:as
The :as key names the route. You can then use named route helpers wherever url_for is available (such as controllers, tests, and mailers). Resource routes (using the resources helper) automatically create named routes, as in Rails 2.3.
match "account/overview/:id", :as => "overview" # in your controller overview_path(12) #=> "/account/overview/12"
:via
Allows you to specify a set of verbs, so only those HTTP requests are accepted for a route.
match "account/setup", :via => [:get, :post]
Rack
Rack is a sweet interface to web servers that provides unified API to Ruby frameworks. Most if not all Ruby frameworks are built on top of Rack these days. The recent built-in support for Rack means your application is not bound to being Rails specific. You can have parts of your application handled by any Rack supported framework, be it Sinatra, Cramp or something else. You can skip the Rails stack altogether and pass on the request to a Rack app.
Here’s an example of a Sinatra app:
class HomeApp < Sinatra::Base get "/" do "Hello World!" end end Rizwan::Application.routes do match "/home", :to => HomeApp end
And here’s an example of a Rack app:
match "/foo", :to => proc {|env| [200, {}, ["Hello world"]] } match 'rocketeer.js' => ::TestRoutingMapper::RocketeerApp RocketeerApp = lambda { |env| [200, {"Content-Type" => "text/html"}, ["javascripts"]] }
Resourceful Routes
Since Rails 1.2, resourceful routes have been the preferred way to use the router. Recognizing this fact, the Rails core team has added some nice improvements. Take a look at this typical RESTful route in Rails 3:
resources :productsThis would generate all the neat helpers we have come to love and would also route the URLs accordingly. Just like before, you can also add multiple resources in a single line:
resources :products, :posts, :categories
More RESTful Actions
As you know, you’re not limited to the seven actions that RESTful architecture provides, but can also define more actions in a resource. Having said that, you might want to keep an eye open if you’re defining lots of actions in a single resource, as they can be turned into separate resources.
We can add RESTful actions to this resource in a couple of ways. Here’s a few collection RESTful actions inside a block:
resources :products do collection do get :sold post :on_offer end end
And take a look at this inline member RESTful action:
resources :products do get :sold, :on => :member end
Not only that, but you can also redefine to extend the scope of the default seven RESTful actions:
resources :session do collection do get :create end end
create actions, which usually only accepts POST requests, can now accept GET requests as well:
resource :session do get :create end
Nested Resources
In Rails 2, nested resources were defined by a block or by using a :has_many or :has_one key. Both of these have been superseded by a block, giving them a more Rubyish interface to defining associated resources.
Here’s a route for a project that has many tasks and people:
resources :projects do resources :tasks, :people end
Namespaced Resources
These are especially useful when defining resources in a folder; it doesn’t get much cleaner than this:
namespace :admin do resources :projects end
Renaming Resources
You can also rename resources through the :as key. This code uses :as in resourceful routes to change the products path to devices:
namespace :forum do resources :products, :as => 'devices' do resources :questions end end
Restricting Resources
Resources can be restricted to only specified actions.
resources :posts, :except => [:index] resources :posts, :only => [:new, :create]
Altering Path Names
You can define a different path name for a particular REST action. This helps you customize your RESTful routes. This code will route /projects/1/cambiar to the edit action.
resources :projects, :path_names => { :edit => 'cambiar' }
The Redirect Method
The newly added redirect method in Rails 3 provides a level of convenience not present before. For example, it can redirect to any path given and eventually, can also pass on to a full-blown URI, something previously accomplished by Rails plugins like redirect_routing.
Moreover, the redirect method also introduces generic actions. Unique to Rails 3, generic actions are a simple way to provide the same action to complex paths, depending on what’s passed to redirect.
This code will redirect /foo/1 to /bar/1s:
match "/foo/:id", :to => redirect("/bar/%{id}s")
This code will redirect /account/proc/john to /johns.
match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" }
Note that redirect cannot be used in a block as opposed to other constraints and scopes.
The Constraints Method
Constraints allow you to specify requirements for path segments in routes. Besides that, they also allow you to use a handful of methods to verify whether or not something matches a given criteria. Like a route that checks if the request is AJAX or not, for example.
In this code, we’re using a regular expression, and the route has been restricted to only allow one digit IDs to pass through:
match "/posts/show/:id", :to => "posts#index", :constraints => {:id => /\d/}
Scope
When the scope method is passed along a symbol, it assumes the symbol is a controller. When the argument is a string, the scope method prepends that string to the beginning of the path.
Scope can also have path_segments, which can be constrained, giving us greater flexibility in routes.
controller :articles do scope '/articles', :name_prefix => 'article' do scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do match '/:id', :to => :with_id end end end scope :posts, :name_prefix => "posts" do match "/:action", :as => "action" end scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do # See constraint here resources :rooms end
As you can see, when scope is given a string as its argument, it prepends to the path, something that was accomplished through path_prefix in Rails 2. name_prefix is essentially the same as before.
Optional Segments
Unlike all the previous versions of Rails, path segments can now be optional in routes. Optional segments don’t necessarily have to be path segments, which are passed as parameters to the action. The default route is a good example of optional segments in use. Here, both /posts/new and /posts will be redirected to the create action in posts controller, but /posts/edit will not work:
match 'posts(/new)', :to => 'posts#create'
This is an optional path scope that allows to have a prepended path before a resource:
scope '(:locale)', :locale => /en|pl/ do resources :descriptions end
Pervasive Blocks
As evident from the examples, routes in Rails 3 exhibit pervasive blocks for almost all the methods you’d normally want to pass a block to, helping you achieve DRY in routes.rb.
controller :posts do match 'export', :to => :new, :as => :export_request match '/:action' end
Right now, most Rails developers wouldn’t use all of these methods, but when the need arises—when you need to define more complex routes—you might appreciate having the above information handy. You don’t need to use a plugin or a hack when you know it’s built right in. With Rails 3, Routes rock at a whole new level.
Questions and comments welcome!
more »
Rails and Merb Merge: Rack (Part 6 of 6) »
Created at: 16.03.2010 19:00, source: Engine Yard Blog, tagged: Technology rails Rails 3
In this final segment, I’ll talk about how Rails 3 takes advantage of the Rack standard, leveraging it in ways that make sharing code between Ruby web applications easier. There are a ton of good blog posts and presentations on Rack itself, so I won’t go into too much detail beyond a brief summary.
What is Rack?
Rack defines a standard for interaction between web servers and Ruby web applications. It’s based on the CGI standard, but stripped of CGI’s global characteristics (such as its use of environment variables and standard output).
Servers send requests to Rack applications by calling their #call method with an environment Hash containing information as a number of standardized keys. For instance, it provides the requested path in the PATH_INFO variable. Applications send responses back to the server as an Array of three elements: the status code, a Hash of the headers, and the body. Applications may return any object that responds to #each as the body, and each iteration must return a String object.
Here are a couple of simple Rack applications:
# A Proc responds to #call proc do |env| [200, {"Content-Type" => "text/html"}, ["Hello world"]] end # Other objects that respond to #call work too class MyApp def self.call(env) [ 200, {"Content-Type" => "text/html"}, ["Hello world"] ] end end
Rails 2.3
In Rails 2.3, Rails became a fully valid Rack application, leveraging Rack in a number of places:
- ActionController::Dispatcher.new is the top-level Rack application
- The parameter parser is implemented as middleware
- The router is a Rack application that dispatches to controllers
- Each controller is a Rack application
This was a modest step toward leveraging Rack, and made Rails applications work anywhere Rack worked. However, the internals of some of these pieces were messy, and an overhaul would be needed to take full advantage of the Rack ecosystem and the power of Rack’s architecture.
Enter Rails 3: The Application
First, rather than having a global Dispatcher object pointing at a global router, Rails 3 introduces the idea of the Rails Application. The Application, an object holding the router and other configuration, is itself a Rack application.
The following is a valid config.ru.
# Set up load paths as desired require "action_controller/railtie" class FooController < ActionController::Base def bar self.response_body = "HELLO" end end class MyApp < Rails::Application config.session_store :disabled routes.draw do match "/foo" => "foo#bar" end end run MyApp
As you can see, a Rails application is a valid Rack application. The above config.ru file will run with rackup, Passenger, Glassfish, or any other Rack server.
ActionDispatch and Rack Middleware
Rails 3 introduces a new framework called ActionDispatch, a library extending the features of the Rack library for Rails’ use. This library can also be used standalone in any Rack application.
The new ActionDispatch::Request and ActionDispatch::Response objects now inherit from Rack::Request and Rack::Response. Integration tests now use Rack::Test and can now be used to test any Rack application.
Rails now includes a number of new general purpose middlewares:
- A middleware to run preparation callbacks, which Rails runs once in production mode, or once per request in development mode
- A middleware to set up and write cookies
- A middleware to clean up flash notices
- A middleware to handle HEAD requests
- A middleware to handle IP spoof checking
- A middleware to serve static assets
- Middlewares to handle rescuing exceptions down the stack
- Middlewares for various session stores
Rails also uses a number of middlewares that ship with the Rack library:
- A middleware to synchronize non-threadsafe requests
- A middleware to measure and set the runtime of requests
- A middleware to implement Send-File semantics in various web servers
- A middleware to handle PUT and DELETE requests coming in via POST requests
The middleware stack object, available as config.middleware, supports reordering these middlewares, or inserting new middleware relative to default ones.
One nice thing about implementing the functionality as middleware is that it forces them to be simple, standalone objects, and understanding, patching or even replacing them becomes much easier than trying to modify a monolithic controller or request object.
The Router
The Rails 3 router was written from the ground up by Josh Peek as the Rack::Mount project, which provides a general-purpose Rack router. Rack::Mount recognizes URLs and dispatches them to any Rack application. As a result, the router now natively supports matching any URL to any arbitrary Rack application–even a Sintra application.
Rails.application.routes.draw do match "/blog" => Rack::Blog # Someone should write it! end
The router is itself a Rack application, so it’s possible to mount a router inside the main router. Carl Lerche’s Astaire project uses this to provide the Sinatra routing DSL inside a Rails controller. The following is a valid config.ru:
$:.unshift File.expand_path('../../lib', __FILE__) require 'rubygems' require 'astaire' class OmgController < ActionController::Base include Astaire::DSL append_view_paths File.expand_path('../views', __FILE__) get "/goodbye" do render :text => "goodbye" end get "/hello" do render "hello" end end run OmgController
Astaire uses ActionDispatch::Routing::RouteSet internally, a Rails wrapper around Rack::Mount.
Actions
Because the router dispatches directly to Rack applications, each controller action is its own mini-application. To get a valid Rack application for a controller’s action, use the ArticlesController.action(:index) syntax.
The Rails router uses the syntax match "/articles" => "articles#index" to match a URL to a controller/action pair. That syntax is identical to match "/articles" => ArticlesController.action(:index).
Because actions are their own Rack applications, you can add middleware between the router and controller actions:
class ArticlesController < ApplicationController use MyMiddleware end
This will insert the middleware between the router and the dispatching of an action. The following is a valid config.ru:
class ArticlesController < ActionController::Base append_view_path "/path/to/views" def index render end # implicit actions, such as "articles/show" would work here end run ArticlesController.action(:index)
This endpoint would behave exactly like a normal action, including callbacks and rendering. In fact, it’s roughly identical to what happens internally inside the router.
You can also call into an action using this syntax:
env = Rack::MockRequest.env_for("/articles") status, headers, body = ArticlesController.action(:index).call(env) # Create a response object, if you want one response = ActionDispatch::Response.new(status, headers, body)
You could also use Rack::Test:
class TestArticles < Test::Unit::TestCase include Rack::Test::Methods def app ArticlesController.action(:index) end def test_get get "/articles" assert_equal "Hello world", last_response.body end end # This would include the application's middleware, # which you probably want as it includes sessions # and cookies class TestApplication < Test::Unit::TestCase include Rack::Test::Methods def app MyApplication end def test_get get "/articles" assert_equal "Hello world", last_response.body end end
You would probably use Rails integration tests, which wrap Rack::Test with some additional conveniences, in practice. It’s nice to know that there’s very little magic going on, and that all of the individual pieces of Rails are just standard Rack.
Conclusion
While Rails 2 had started down the path of supporting Rack, Rails 3 took a few dozen steps forward, deeply integrating the framework with the Rack standard. In addition to improving the general interaction with servers, Rails 3 takes much more advantage of the Rack library, exposes its own functionality as reusable Rack components, and wires up the stack using the Rack architecture. This means that Rails itself integrates well with tools that work with Rack applications, and can also incorporate other applications or middleware following the Rack standard.
Stay tuned for more Rails 3 content to come—the series may be over, but there’s plenty left to talk about.
more »

