What's New in Edge Rails: Simply RESTful Support - And How to Use It

Posted by ryan
at 8:40 AM on Tuesday, August 01, 2006

See here from some syntactical changes that occurred after this post was published

Though I’ve been beaten to the punch on this one, it’s still worth a mention. Edge rails now has native support for the simply resful plugin functionality (though the implementation is not backwards-compatible as it now uses pluralized naming conventions in its mappings).

This addition continues the Rails march towards REST-vana and allows for easy declaration of REST-able controller methods. Where as edge Rails’s new Active Resource library provides the client-side part of the REST equation, simply RESTful provides the server-side handling of REST-based requests.

Basic Usage

The way it works is that you get a new map.resources method to use in your routes.rb config file that specifies which resource you want to RESTize. For example, assuming I have a standard users_controller I would add the following to my routes:

map.resources :users

Yeah, that’s it. And with that declaration, here are the requests that are mapped for me (all to the users controller):

GET: /users => [:action => 'index']
GET: /users.xml => [:action => 'index', :format => 'xml']
GET: /users/1 => [:action => 'show', :id => 1]
GET: /users/1;edit => [:action => 'edit', :id => 1]
GET: /users/1.xml => [:action => 'show', :id => 1, :format => 'xml']

POST: /users => [:action => 'create']
PUT: /users/1 => [:action => 'update', :id => 1]
DELETE: /users/1 => [:action => 'destroy', :id => 1]

Note that in true REST form, this one URL: /users/1 serves four different actions depending on request method (GET => show, POST => create, etc…). Beauty in simplicity…

It should be noted that Rails has to cheat to emulate the various HTTP request methods as most browsers don’t support sending PUT or DELETE requests. Rails does this by using the _method parameter in the various link_to helper methods

Advanced Usage

So REST is great for calling simple CRUD actions, but how do you invoke other actions while not stepping outside the friendly confines of the REST world? As was hinted at in the edit mapping above, you use the ‘;’ delimiter to denote a different action – along with telling map.resources what HTTP methods to use for the non-standard actions.


# Provided as default resource mappings
GET: /users/1;edit => [:action => 'edit', :id => 1]
GET: /users/1.xml;edit => [:action => 'edit', :id => 1, :format => 'xml']

# Update routes to handle non-standard CRUD actions
# This one says that a GET on 'filter' is accessing the collection
map.resources :users, :collection => { :filter => :get }

GET: /users;filter => [:action => 'filter']
GET: /users;filter?active=true => [:action => 'filter', :active => 'true']

# And this says that a POST to 'deactivate' is accessing a member item
# (i.e. a single item)
map.resources :users, :member => { :deactivate => :post }

POST: /users/1;deactivate => [:action => 'deactivate', :id => 1]

# Get funky and allow a PUT to 'new;admin'
map.resources :users, :new => { :admin => :put }

PUT: /users/new;admin => [:action => 'admin']

Hopefully this makes it pretty clear how you can layer your application functionality on top of this new RESTful routing. And for those looking for a real wow factor…

Wow Usage (i.e. Nested & Prefixed Routes)

...you can also specify path prefixes to your routes for trully spectacular routes:


# Give my user routes a prefix by group
map.resources :users, :path_prefix => "/groups/:group_id" 

GET: /groups/13/users/1 => [:controller => 'users', :action => 'show',
        :group_id => 13, :id => 1]

Another way you can achieve this nested routing is by nesting the resource mappings themselves


# Make both groups and users RESTable, with users as nested resources of groups
map.resources :groups do |group|
  group.resources :users
end

GET: /groups/13/users/1 => [:controller => 'users', :action => 'show',
      :group_id => 13, :id => 1]
GET: /users/1 => [:controller => 'users', :action => 'show', :id => 1]
GET: /groups/1 => [:controller => 'groups', :action => 'show', :id => 1]

Tidbits

With each resource mapping you specify, you also get a handy named route. So with:

map.resources :users, :collection => { :filter => :get }

You get a filter_users_url helper method:

filter_users_url #=> "/users;filter"

Don’t like the default naming? Fine, add your own prefix to the generated url name:

map.resources :users, :collection => { :filter => :get }, :name_prefix => 'my_'

Now:

my_filter_users_url #=> "/users;filter"

I think I’m in love.


tags: , , ,

What's New in Edge Rails: An Interactive Capistrano Shell

Posted by ryan
at 6:31 AM on Monday, September 11, 2006

This utility comes with several warnings about its experimental state and unknown future – but we’re not going to let that damper our inquisitiveness, are we? Introducing a way to execute capistrano commands from a shell environment (think script/console). So how does this Capistrano shell work?

First, start the capistrano shell w/

cap -v shell

Then enter commands to be executed on your remote environments. (These are mostly from the shell source comments itself)


# Execute on all servers
cap> echo ping

# Execute on specific servers
on myserver1.com,myserver2.com echo ping

# To execute based on roles
with app,db echo ping

# Execute a capistrano task
!deploy

# Execute multiple capistrano tasks
!setup deploy

# Combine all the goodness to execute a task on
# specific servers and roles
on myserver1.com !setup
with app !setup

Why would you want to use this shell? Well, you get the ability to simultaneously manipulate several environments from one shell – and all within the friendly confines of capistrano. You should find the rollback task to be especially comforting…

tags: , ,

What's New in Edge Rails: Around Filters are Here

Posted by ryan
at 4:42 AM on Friday, September 22, 2006

For the second time I’ve had my virtual hand slapped for trumpeting a poor usage of with_scope . For the record, I want to make it clear that one should be using the rich domain model that active record provides to operate on associated objects: user.assets.find() and user.asset.create() instead of scoping to a user as my and Roman’s examples implied. Just a case of not thinking hard enough to come up with an appropriate example. Sorry David, won’t happen again!

I’ve mentioned the Meantime filter plugin by Roman Le Negrate in the past as a very useful little utility – apparently the core team agrees as around filtering is now part of edge Rails.

Usage is pretty much the same as with meantime:


class AssetController < ActionController

  before_filter :verify_login
  around_filter :benchmark

  def assets
    @assets = Asset.find(:all)
  end

  def benchmark
    benchmark("Asset benchmark") { yield }
  end

end

You can also define a class to handle your filtering needs (whose filter method yields to the action block)


class AssetController < ActionController

  around_filter BenchmarkFilter   # or BenchmarkFilter.new
  ...

  class BenchmarkFilter
    def filter
      benchmark("Benchmarking...") { yield }
    end
  end

end

Or you can directly pass a Proc in as the filter:


class AssetController < ActionController

  around_filter (:only => :assets) do |controller,action_block| 
    controller.benchmark("Benchmarking...") { action_block.call }
  end
  ...
end

Since the around filter explicitly governs the execution of the action being requested, you can use them to skip the action completely. This would act the same as a before_filter that returned false.

tags: rubyonrails, rails

What's New in Edge Rails: Explicit Deprecation

Posted by ryan
at 4:55 AM on Friday, July 28, 2006

After a bit of a hiatus (due to little reportable action on the source tree), a deprecation tidbit was just committed. What’s this little nugget do? The ActiveSupport library now provides the ability to explicitly state what methods are deprecated – which will pump out a warning message when that method is called. This is a nice way of not breaking your codebase by removing deprecated methods, while still letting others know that they’re marked for future removal.

Here’s how it works – simply include the deprecation module in the class that has a deprecated method (in this example a User model object) – and mark the method symbol as deprecated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
require 'active_support/deprecation'

class User < ActiveRecord::Base

  # The method to deprecate
  def permitted?(role)
    roles.include?(role)
  end

  # Formally deprecate it!
  deprecate :permitted?

end

# This method will execute as normal - except now there's a warning
# message printed to your log file or to standard out:

User.new.permitted?('ADMIN') #=> false

# "Your application calls User##permitted?, which is now deprecated.
# Please see the API documents at http://api.rubyonrails.org/ for more information."

You can also denote deprecation of methods baseed on runtime conditions as well (useful when a method with certain arguments have been deprecated, but the method as a whole is still valid)


require 'active_support/deprecation'

class User < ActiveRecord::Base

  # Single arg method is now deprecated
  def to_s(format)
    if format
      ActiveSupport::Deprecation.issue_warning("to_s with a " + 
      "format is now deprecated, please use the no-arg call.")
      # ...
    end
    #...
  end  
end

# This method will execute as normal - except now there's a warning
# message printed to your log file or to standard out:

User.new.to_s(:long) #=> "Ryan William Daigle" 

# "to_s with a format is now deprecated, please use the no-arg call." 
# Please see the API documents at http://api.rubyonrails.org/ for more information." 

# However, this method will not have a deprecation warning:

User.new.to_s #=> "Ryan Daigle" 

It may not seem like much – but the ability to warn developers that a method is on the path to deprecation can save a lot of pain by making it more apparent which called methods are deprecated and not relying on developers to do their own homework.

We hate homework.

tags: ,

What's New in Edge Rails: ActiveResource Pulled from v1.2, World Weeps

Posted by ryan
at 8:07 AM on Monday, November 20, 2006

The REST client framework, ActiveResource, has been pulled from the up and coming Rails v1.2 release.

I’ve been using it to develop some REST client libraries and there are some refinements I can see being needed, but I don’t know if that’s the reasoning for its exclusion in the up-coming release. As a quick refresher – ActiveResource is the client framework that lets you invoke REST services from model objects. Think of it as ActiveRecord for REST.

However, get your REST panties out of their collective knot – you can still develop completely RESTful apps using the Simply RESTful server side functionality and the link helpers for enhancing the web tier with REST compliant requests.

Thanks to Tom of GiftHat for beating my feedreader to the punch on this one.

tags: , ,

What's New in Edge Rails: Prettier Error Pages!

Posted by ryan
at 4:40 PM on Tuesday, October 10, 2006

This may not seem like a biggie, but we all know you haven’t gotten around to prettying up your error pages yet..

The new, pretty, 404 error page

and

The new, pretty, 500 error page

It’s the little things sometimes…

tags: rubyonrails, rails

What's New in Edge Rails: Build URLs in Your ActionMailers (and other non-controllers)

Posted by ryan
at 5:03 PM on Wednesday, August 23, 2006

For a long time there was no good way to generate a URL within an ActionMailer. If you wanted to send a registration email when a user signs up for your application you would have had to pass in the url to be included in the email:


class MyController < ApplicationController
  def register
    # .. registration logic ...
    UserMailer.deliver_registration(user, home_url)
  end
end

class UserMailer < ActionMailer::Base
  def registration(user, url)
    @body = { 'user' => user, 'home' => url }
  end
end

This is hardly a hack, but it’s not as nice as it could be. It would be nice if the mailer itself could generate the url – or perhaps if the email template itself could generate the url? That day has come with the new ActionController::UrlWriter module.

Just include ActionController::UrlWriter in your mailer and you get access to all the familiar url_for and even the named url methods (home_url in this example):


class UserMailer < ActionMailer::Base
  include ActionController::UrlWriter
  def registration(user)
    @body = { 'user' => user, 'home' => home_url }
  end
end

tags: , ,

What's New in Edge Rails: Get Your RSS & Atom Feeds for Free

Posted by ryan
at 4:48 AM on Thursday, September 14, 2006

Resource feeder is no more, but the atom feed helper is here to replace it

Rails is all about some open-ness and interopability and it has become incrementally easier to achieve this with the new Resource Feeder plugin. This plugin gives you an easy way to create RSS and Atom feeds in your controllers from a collection of model objects with the use of the rss_feed_for and atom_feed_for methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PostsController < ApplicationController

  # Build an rss feed
  def rss
    render_rss_feed_for Post.find(:all, :order => 'created_at DESC',
                               :limit => 10)
  end

  # Build an atom feed
  def atom
    render_atom_feed_for Post.find(:all, :order => 'created_at DESC',
                                :limit => 10)
  end

end

So how do these feed methods know how to pull information from each individual resource of the collection? It uses a combination of options passed into the feed methods and specific properties of the resource. If you don’t pass any options into these feed methods your model objects should have title, description and created_at reader methods (and updated_at for atom).

Options

There are several options you can pass into these nice little feed methods to customize the title of the feed and most of the other feed elements. Here’s my attempt at enumerating all of them for RSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Feed options (w/ defaults):
options[:feed][:title]            # Pluralization of the model class, i.e. "Posts"
options[:feed][:link]            # Url to this record
options[:feed][:description] # omitted if not specified
options[:feed][:language]    # "en-us"
options[:feed][:ttl]              # 40

# Individual item options (w/ defaults).  These are the method symbols used to
# retrieve the proper values from each individual object:
options[:item][:title]            # :title
options[:item][:description] # :description
options[:item][:pub_date]   # :created_at

# General options:
options[:url_writer]            # an instance of UrlWriter

Just pass in these options to your feed methods as needed:

1
2
3
4
5
6
7
8
9
10
11
12
class PostsController < ApplicationController

  # Build an rss feed
  def rss
    render_rss_feed_for(Post.find(:all, :order => 'created_at DESC',
                               :limit => 10),
                    { :feed => {:title => "All posts"},
                      :item => {:title => :name,
                                :pub_date => :updated_at} })
  end

end

Feelin’ sassy? Try taking advantage of this guy :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PostsController < ActionController

  # View a collection of posts.  If "format" is specified
  # will return view of that type (of either rss or atom format)
  # /posts/list.rss => RSS
  # /posts/list.atom => Atom

  def list
    @posts = Post.find(:all, :order => 'created_at DESC',
                            :limit => 10)
    options = { :feed => {:title => "All posts"},
                    :item => {:title => :name,
                             :pub_date => :updated_at} }

    respond_to do |wants|
      wants.html
      wants.rss { render_rss_feed_for @posts, options }
      wants.atom { render_atom_feed_for @posts, options }
    end
  end
end

It just feels so right sometimes!

oh yeah, and to install this pup just do:

1
2
script/plugin install simply_helpful
script/plugin install resource_feeder

tags: rubyonrails, rails, rss, atom

While I Was Sleeping...

Posted by ryan
at 11:24 AM on Thursday, September 14, 2006

While I was being lazy Scott went ahead and posted a great write-up of the new Simply Helpful plugin that makes a few view-level operations more concise. Go ahead and check it out.

What's New in Edge Rails: ActiveResource Updates

Posted by ryan
at 11:56 AM on Tuesday, September 05, 2006

The maturation of ActiveResource continues with a pretty significant update with “full support for find/create/update/destroy” operations.

If you’re living on the bleeding edge and have had some issues getting ActiveResource to work as advertised you may want to check out the details of this update.

What's New in Edge Rails: Deprecated Finders

Posted by ryan
at 4:50 AM on Thursday, August 17, 2006

Those finder methods that have been marked for deprecation have now been formally deprecated using the nifty deprecation feature recently added to edge Rails. So, if you currently use any one of:

  • find_on_conditions
  • find_first
  • find_all

be prepared to get a little slap on the hand in the form of an error message: Your application calls ActiveRecord::Base##find_XXX, which is now deprecated. Please see the API documents at http://api.rubyonrails.org/ for more information.

Shame on you.

On a related note – using push_with_attributes is also deprecated. Use has_many :through instead.

tags: ,

What's New in Edge Rails: An Explicit Locals Hash

Posted by ryan
at 12:23 PM on Tuesday, September 05, 2006

In what is amounting to my most useless post yet – this feature has been reverted according to Bob .

With Rails components being shunned throughout the Rails world we are often left to use a set of view partials to abstract out commonly used display functionality. Sometimes this necessitates the use of variables (called ‘locals’) that may or may not be relevant in all view scenarios. For instance, if I have a partial that I use to display form text fields there may be a size parameter I want to pass in to limit the field’s size:


# my_form.rhtml
<%= render :partial => 'text_field', :locals => {:size => 5} %>

# _text_field.rhtml
<input type="text" size="<%= size %>" />

If I want the size parameter to be optional however, I could try to do this with a default value of 5:


# _text_field.rhtml
<input type="text" size="<%= size ? size : "5" %>" />

However, this fails if no size parameter is passed to the partial as size hasn’t been defined yet. With this update, however, you can query an explicit locals hash to determine the existance of a local variable:


# _text_field.rhtml
<input type="text" size="<%= locals[:size] ? size : "5" %>" />

Not a huge update, but if you’re using partials extensively (which I suspect most are) you’ve no doubt run into this issue.

And as Bojan has pointed out you could also use the nifty defined? method


# _text_field.rhtml
<input type="text" size="<%= defined?(size) ? size : "5" %>" />

tags: ,

What's New in Edge Rails: I Smell Version 1.2...

Posted by ryan
at 4:16 AM on Tuesday, November 14, 2006

It’s gotta be right around the corner, right?

What's New in Edge Rails: New Http Authentication Plugin - And a Plea to Contribute

Posted by ryan
at 11:13 AM on Monday, December 04, 2006

If you’re just not happy with some of the existing options out there for doing Http Authentication in Rails, we’ve got another entry into the market. There’s now an official Rails http authentication plugin in the Rails repo that makes this a simple proposition.

A few convenient methods are now available to you in your controllers to hold your hand: authenticate_or_request_with_http_basic that hands the basic username and password to a block for evaluation and request_http_basic_authentication that asks the client to authenticate itself using http basic authentication. Using them is pretty simple – their inclusion in an authentication filter seems to be the most intuitive use:


class DocumentsController < ActionController
  before_filter :verify_access

  def show
    @document = @user.documents.find(params[:id])
  end

  # Use basic authentication in my realm to get a user object.
  # Since this is a security filter - return false if the user is not
  # authenticated.
  def verify_access
    authenticate_or_request_with_http_basic("Documents Realm") do |username, password|
      @user = User.authenticate(username, password)
    end
  end
end

When the block of authenticate_or_request_with_http_basic evaluates to false, request_http_basic_authentication is invoked which requests that the user enter their credentials. You can also manually invoke request_http_basic_authentication if you are doing a mix of authentication methods and want to use http authentication only if session authentication fails etc…

So what’s with the “plea”? Well, digest authentication hasn’t been added to the plugin yet – though the structure and framework as all but been laid at your feet to contribute the digest logic yourself. Take a look at HttpAuthentication::Digest and see how easy it would be to contribute back to your favorite framework. Just fill in those tiny places that say stuff like # Fancy nouncing goes here and # You compute me and the world will revere you.

References

tags: , ,

What's New in Edge Rails: ActionMailer::Base.server_settings Deprecated

Posted by ryan
at 10:55 AM on Wednesday, January 31, 2007

Configuring your action mailer settings just got a little tweak that you’re going to want to pay attention to, especially if you’re running off edge. Instead of using ActionMailer::Base.server_settings you’re going to want to use ActionMailer::Base.smtp_settings. The syntax remains the same, it’s just a name change:

1
2
3
4
5
6
7
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
        :address                => "smtp.mymailserver.com",
        :authentication        => :login,
        :user_name                => "me",
        :password                => "password"
}

In the 1-2-stable rails branch the use of server_settings has been deprecated, while over in edge it’s been renamed outright and you’ll need to update your usage.

Also in the same update is the ability to specify your sendmail executable location and command line arguments options with the new ActionMailer::Base.sendmail_settings (if you’re using sendmail):

1
2
3
4
5
ActionMailer::Base.delivery_method = :sendmail
ActionMailer::Base.sendmail_settings = { 
  :location       => '/usr/sbin/sendmail', 
  :arguments      => '-i -t'
} 

See the ActionMailer::Base api for further option details.

tags: rubyonrails, rails