Archive for the ‘Rails’ Category

Dynamic Form Elements in Ruby on Rails

December 7, 2010

If I showed you a snippet from a form view, you’d probably recognize all the parts:

<div class="field">
  <%= f.label :first_name %><br />
  <%= f.text_field :first_name %>
</div>
<div class="field">
  <%= f.label :last_name %><br />
  <%= f.text_field :last_name %>
</div>
<div class="actions">
  <%= f.submit %>
</div>

You’d probably guess that the User model has at least two attributes: first_name and last_name. You’d normally be right, but that doesn’t have to be the case. What if I want the database to store first and last names separately, but I want the form to let them type their full name, and I’d split it up behind the scenes? Something like this:

<div class="field">
  <%= f.label :full_name, 'Name' %><br />
  <%= f.text_field :full_name %>
</div>

It’s easy to do, just by adding accessor methods to our model, like so:

class User < ActiveRecord::Base
  def full_name
    "#{first_name} #{last_name}"
  end

  def full_name= name
    self.first_name, self.last_name = name.split(/\s+/, 2)
  end
end

In the example above, we have a getter that spits out the first and last name put together. We also have a setter, that tries to intelligently split a full name into its parts.

Now that there is a full_name and full_name= method in our model, we can use it in our forms like any other attribute. This is because the attributes themselves are just sets of accessor methods that are added to your model by ActiveRecord, built using the database schema for that model’s table.

In other words, if your model’s table has a first_name field, ActiveRecord will automatically create the first_name and first_name= methods for you. It’s these methods (called accessors) that Rails form builders look for, whether they’re attributes stored in the database or not.

Ruby Enumerable Magic: Filters

December 6, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

A powerful part of the Enumerable module is the set of methods that help you filter a larger collection down to a smaller one, containing just the items you need. There are several methods to help.

entries (or to_a)

This is the most permissive of the Enumerable filter methods – it doesn’t filter out anything. In fact, if you use this on an array you won’t see any difference at all. entries returns the list of items in the collection. For an array, the array itself will be returned. This makes sense, when you see that this method is aliased as to_a.

But more complex collections like our Team class will provide a better example. Here’s the class, in case you missed it from previous posts:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

Let’s use entries:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team
=> #<Team:0x100391088 @members=["joshua", "gabriel", "jacob"]>
irb(main):005:0> team.entries
=> ["joshua", "gabriel", "jacob"]

You can see that team is a non-array collection, but calling entries returns the members list. To be honest, this isn’t immensely useful – but it’s good to know that it exists, and how it works.

select (or find_all)

In contrast to entries, select is arguably the most used enumerable filter. It returns a list of all the collection items that pass the test that you pass in as a block. For example:

irb(main):006:0> team.select{|member| member =~ /^j/}
=> ["joshua", "jacob"]

Here we want to filter the collection to just those items that start with the letter “j”. As with all Ruby methods that expect a block, we can use the unary ampersand operator to make our code shorter and sweeter:

irb(main):007:0> team.members[1].freeze
=> "gabriel"
irb(main):008:0> team.select(&:frozen?)
=> ["gabriel"]

In the example above, we “froze” the second member in the collection, “gabriel”. Then we called select on the collection, asking for just the frozen members.

reject

This is the opposite of select. It will only return items in the collection that *don’t* pass the test we give it. Let’s do the opposite of our last two tests:

irb(main):009:0> team.reject{|member| member =~ /^j/}
=> ["gabriel"]
irb(main):010:0> team.reject(&:frozen?)
=> ["joshua", "jacob"]

partition

This little method probably deserves a lot more use and recognition than it gets. You might say it’s the Bruce Campbell of enumerable methods. It’s like calling select AND reject on a collection. it gives you both lists back – the passers, and the failures:

irb(main):011:0> frozen, not_frozen = team.partition(&:frozen?)
=> [["gabriel"], ["joshua", "jacob"]]
irb(main):012:0> frozen
=> ["gabriel"]
irb(main):013:0> not_frozen
=> ["joshua", "jacob"]

grep

In our previous examples, we’ve used regular expressions in some of our filter methods. To be honest, I do this a lot. I didn’t realize that Enumerable provided a special method just for this use case until I researched this article. It works like select, except it only takes a regular expression pattern, no need to pass it a block. That means this snippet:

irb(main):014:0> team.select{|member| member =~ /^j/}
=> ["joshua", "jacob"]

Can be rewritten like this:

irb(main):015:0> team.grep(/^j/)
=> ["joshua", "jacob"]

If you like passing blocks to methods (and who doesn’t??) then you’re in luck, because you can still do that with grep. If you pass a block, each collection item that matches the regular expression will be run through the block, and the results will be gathered up into the return array for you. Like this:

irb(main):016:0> team.grep(/^j/){|member| member.capitalize}
=> ["Joshua", "Jacob"]

grep found our matches, passed each one through capitalize, and gave us a list of the results.

detect (or find)

detect works a lot like select, except it stops looking as soon as it finds the first match. Unlike the other methods, it doesn’t return an array. It returns just the single item:

irb(main):017:0> team.detect{|member| member =~ /^j/}
=> "joshua"

By default, detect will return nil if no match is found. But you can pass a block, which it will run and return, if no match is found:

irb(main):018:0> team.detect(lambda{'turtles'}){|member| member =~ /^t/}
=> "turtles"

An alias for this method is find, which you’ll obviously want to avoid if you’re in a Rails application, since ActiveRecord has its own find method, and you don’t want to make your code harder to read.

Summary

Don’t be a fool, like I was, and wait until you write your own article about enumerables to brush up on all the filter methods. I remember the first time I changed the brakes on my own car. One nut would not come loose, and I was even using vise grips on it. After a few hours, I called a friend who brought over a tool that actually tightens the more you crank it (unlike vise grips where you determine the pressure beforehand) and it was off in under a minute. The first step in knowing the right tool for the job is knowing what tools you have at your disposal.

Ruby Enumerable Magic: Booleans

December 3, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

The most basic Enumerable methods are those that give you a simple yes/no answer. There are three of these boolean methods: all?, any?, and include?. Each gives you quick info about the collection you’re working on.

We’ll start out our Team class from previous articles:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

all?

This method tells you if *every* item in your collection matches the criteria you give it. Let’s test it out:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.all?{|member| member.length > 4}
=> true
irb(main):005:0> team.all?{|member| member.length > 5}
=> false

Here we’re checking to see if all members (which are just strings containing their names) meet a certain length. All of the names have more than four characters, so all? returns true. However, only two of the three names contain more than five letters, so all? returns false in the second test.

Whatever block you pass to all? will be evaluated for each item in the collection (members in this case) and the method will only return true if all items return true.

any?

all? is a little strict, like an uptight parent demanding “all” your dirty clothes be put in the hamper. Wouldn’t it be nice to get partial credit? any? promotes permissive searches and parenting by only requiring that one item in the collection meet the given criteria. As long as any item evaluates to true (using the block you pass in), the whole collection gets the thumbs up.

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.any?{|member| member == 'joshua'}
=> true
irb(main):005:0> team.any?{|member| member == 'javier'}
=> false

The example above is like a party. When you open the door and see three people standing there, you really only need to know *one* of them, and they’re all welcome. In the first example, if anybody in the team is named “joshua”, the whole collection is green-lighted for beer and awkward dancing. In the second example, if you don’t have a “javier” on your team, you’ll have to stay in the hallway, drinking the beer you brought yourself, and standing awkwardly.

include?

This is my favorite boolean enumerable method, simply because I always forget how to spell it. I always want to type includes? when the collection name is singular (like “team”). But it makes sense when the collection is plural, as in members.include?.

Our examples above can be shortened to use the include? syntax:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.include?('joshua')
=> true
irb(main):005:0> team.include?('javier')
=> false

First, note that include? doesn’t take a block – it takes one parameter, an item. This method will check the whole collection to see if that item is in there, and return true or false.

The Enumerable module contains a handful of handy boolean methods, but the real power comes in filtering collections down to the desired items. As luck would have it, that’s the topic of my next article.

Routing in Ruby on Rails 3

December 2, 2010

Be thee warned, this is a *long* article. However, it covers a lot of ground, is written in plain english, and comes with tons of code samples to make the subject matter as easy to digest as possible.

Routing has changed in Rails 3. It’s very different from 2.x, but it’s not actually difficult. This is one of those things where new users are almost better off, because they’re learning the simple way right off the bat, with no baggage. Whether you’re new to all of Rails or just Rails 3, this will get you up and running.

Static-y routes

Starting off with a fresh rails app, let’s generate our main static controller:

rails3_routing$ rails g controller home index about
      create  app/controllers/home_controller.rb
       route  get "home/about"
       route  get "home/index"
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      create    app/views/home/about.html.erb
      invoke  test_unit
      create    test/functional/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      create      test/unit/helpers/home_helper_test.rb

We can clean up our routes file so it looks something like this:

Rails3Routing::Application.routes.draw do
  get "home/index"
  get "home/about"

  root :to => "home#index"
end

The controller generator created the two static routes for us, and I made home/index the root (default) route for the application. Now I need to get rid of the default index file in the public directory:

rails3_routing$ ls public
404.html	500.html	images		javascripts	stylesheets
422.html	favicon.ico	index.html	robots.txt
rails3_routing$ rm public/index.html
rails3_routing$ ls public
404.html	500.html	images		robots.txt
422.html	favicon.ico	javascripts	stylesheets

If we don’t remove this, the app will show the public/index.html file as the home page. So far so good! If you fire up the server, you’ll see our home/index action is being called as our home page.

RESTful Routes

Let’s use the example of a blog application, and create the Post model, views, and controller to look at RESTful routing in Rails 3. We’ll use the scaffold generator to create the entire MVC (Model-View-Controller) stack in one shot:

rails3_routing$ rails g scaffold post title:string content:text
      invoke  active_record
      create    db/migrate/20101202051750_create_posts.rb
      create    app/models/post.rb
      invoke    test_unit
      create      test/unit/post_test.rb
      create      test/fixtures/posts.yml
       route  resources :posts
      invoke  scaffold_controller
      create    app/controllers/posts_controller.rb
      invoke    erb
      create      app/views/posts
      create      app/views/posts/index.html.erb
      create      app/views/posts/edit.html.erb
      create      app/views/posts/show.html.erb
      create      app/views/posts/new.html.erb
      create      app/views/posts/_form.html.erb
      invoke    test_unit
      create      test/functional/posts_controller_test.rb
      invoke    helper
      create      app/helpers/posts_helper.rb
      invoke      test_unit
      create        test/unit/helpers/posts_helper_test.rb
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

Looking at our routes.rb file, you can see we now have our first RESTful route, added automatically by Rails:

Rails3Routing::Application.routes.draw do
  resources :posts

  get "home/index"
  get "home/about"

  root :to => "home#index"
end

This doesn’t look like much – it only added a line to our routes file – but it packs a punch. For starters, it’s defining the seven basic RESTful routes:

  • GET index
  • GET show
  • GET new
  • POST create
  • GET edit
  • PUT update
  • DELETE destroy

These are the seven routes needed to list, view, add, update, and remove posts in our application. But what if we want to add custom routes to this controller? Maybe we want a couple actions called recent that shows only the latest 5 posts, and popular that shows the top 5 popular posts. Let’s also add an action called publish that will activate a post once it’s ready, so people can see it. Let’s add these to our routes:

  resources :posts do
    get 'recent', :on => :collection
    get 'popular', :on => :collection
    
    put 'publish', :on => :member
  end

Both recent and popular use http GET requests, because we’re not changing any data on the server. The publish action uses the http PUT request, to denote a route that does changes records on the server.

The publish action is a member action, meaning when we call this action, the route will have to contain the id of the record we want to work with, like this: “/posts/1/publish”. The other two actions are collection-oriented, meaning they work with the collection as a whole and we don’t need to pass a specific id to them. The recent route would look like “/posts/recent”.

When you start adding routes, the notation above may get tedious. Luckily, there’s a shortcut:

  resources :posts do
    collection do
      get 'recent'
      get 'popular'
    end
    
    member do
      put 'publish'
    end
  end

This is the preferred way to notate custom routes, whether you have many or just a couple. It’s easier to read, and easier to update with more routes in the future.

Nested Routes

Now let’s say we want to add comments to our application. Obviously every comment will be attached to a specific post, and we’d like the routes to reflect this. It’s not absolutely necessary, but it does add much needed structure in these trying and uncertain times. Let’s generate our comments:

rails3_routing$ rails g scaffold comment title:string content:text post_id:integer
      invoke  active_record
      create    db/migrate/20101202054608_create_comments.rb
      create    app/models/comment.rb
      invoke    test_unit
      create      test/unit/comment_test.rb
      create      test/fixtures/comments.yml
       route  resources :comments
      invoke  scaffold_controller
      create    app/controllers/comments_controller.rb
      invoke    erb
      create      app/views/comments
      create      app/views/comments/index.html.erb
      create      app/views/comments/edit.html.erb
      create      app/views/comments/show.html.erb
      create      app/views/comments/new.html.erb
      create      app/views/comments/_form.html.erb
      invoke    test_unit
      create      test/functional/comments_controller_test.rb
      invoke    helper
      create      app/helpers/comments_helper.rb
      invoke      test_unit
      create        test/unit/helpers/comments_helper_test.rb
      invoke  stylesheets
   identical    public/stylesheets/scaffold.css

Now our routing file will have our comments routes, but they’re not nested inside our posts routes. Let’s fix that:

  resources :posts do
    resources :comments

    collection do
      get 'recent'
      get 'popular'
    end
    
    member do
      put 'publish'
    end
  end

Easy! We just moved the resource line into our posts block. In order to complete the process, I’ve added the necessary relationships in the models, before filter in the comments controller (to lookup the parent post) and updated the comments controller to perform all comment lookups from the perspective of the parent post. Feel free to view the source code for this article (link at the top of the page) to see how I did this.

Now if you’re editing a comment with id 100 that belongs to post 20, the route will look like: “/posts/20/comments/100/edit”.

Namespaced Routes

After a while, you might realize that your posts and comments need to be treated differently depending on the context. One way to do this is with namespacing. For example, you might want to create an admin section of the site where posts are shown with more options (re-ordering, editing, etc). While I won’t get into the permissions issues in this article, this is easy to do from a routing perspective.

First, we’ll create the namespaced controller:

rails3_routing$ rails g controller admin/posts index show new create edit update destroy
      create  app/controllers/admin/posts_controller.rb
       route  get "posts/destroy"
       route  get "posts/update"
       route  get "posts/edit"
       route  get "posts/create"
       route  get "posts/new"
       route  get "posts/show"
       route  get "posts/index"
      invoke  erb
      create    app/views/admin/posts
      create    app/views/admin/posts/index.html.erb
      create    app/views/admin/posts/show.html.erb
      create    app/views/admin/posts/new.html.erb
      create    app/views/admin/posts/create.html.erb
      create    app/views/admin/posts/edit.html.erb
      create    app/views/admin/posts/update.html.erb
      create    app/views/admin/posts/destroy.html.erb
      invoke  test_unit
      create    test/functional/admin/posts_controller_test.rb
      invoke  helper
      create    app/helpers/admin/posts_helper.rb
      invoke    test_unit
      create      test/unit/helpers/admin/posts_helper_test.rb

As you can see, calling the controller “admin/posts” caused all the newly created files to be put in their own admin directory. If we look in the routes file, we’ll see this ugliness:

  get "posts/index"

  get "posts/show"

  get "posts/new"

  get "posts/create"

  get "posts/edit"

  get "posts/update"

  get "posts/destroy"

The controller generator doesn’t know that we want RESTful, namespaced routes, so we’ll drop all those lines in favor of this:

  namespace :admin do
    resources :posts
  end

Note that we’re adding posts resources again, but this time they’re namespaced. They won’t interfere with our regular posts routes, because these routes will be prefixed with “/admin”. So editing the post with id “100″ in the admin section will have a route like this: “/admin/posts/100/edit”.

Things You Should Know

While our initial routes file is pretty simple and straightforward, it would quickly grow if this were a real app. How does the Rails 3 routing engine choose from conflicting or overlapping routes? It does this the same way Rails 2.x does – it works top to bottom. It does not try to find the *best* match (the way a google search might) – instead, it starts at the top of the routes file and stops when it finds the first acceptable match. Therefore, default routes should be pushed to the bottom.

While we’re on the subject, don’t use default routes. Named routes form a much more secure and stable application. There shouldn’t be routes you’re NOT expecting. One exception can be short urls. If you want members to have a short url the way Twitter does (http://twitter.com/kconrails for instance) then you’ll need a default route, placed at the bottom of the routes file, to be tried after all other routes have been checked. If you had a members controller, and the member name should route to the show action, the route might look like this:

match ':member' => 'members#show', :as => :shorturl

Finally, when in doubt, check your existing routes with this rake task:

rails3_routing$ rake routes
(in /Users/bellmyer/Desktop/bellmyer/blog/rails3_routing)
      admin_posts GET    /admin/posts(.:format)                      {:action=>"index", :controller=>"admin/posts"}
      admin_posts POST   /admin/posts(.:format)                      {:action=>"create", :controller=>"admin/posts"}
   new_admin_post GET    /admin/posts/new(.:format)                  {:action=>"new", :controller=>"admin/posts"}
  edit_admin_post GET    /admin/posts/:id/edit(.:format)             {:action=>"edit", :controller=>"admin/posts"}
       admin_post GET    /admin/posts/:id(.:format)                  {:action=>"show", :controller=>"admin/posts"}
       admin_post PUT    /admin/posts/:id(.:format)                  {:action=>"update", :controller=>"admin/posts"}
       admin_post DELETE /admin/posts/:id(.:format)                  {:action=>"destroy", :controller=>"admin/posts"}
    post_comments GET    /posts/:post_id/comments(.:format)          {:action=>"index", :controller=>"comments"}
    post_comments POST   /posts/:post_id/comments(.:format)          {:action=>"create", :controller=>"comments"}
 new_post_comment GET    /posts/:post_id/comments/new(.:format)      {:action=>"new", :controller=>"comments"}
edit_post_comment GET    /posts/:post_id/comments/:id/edit(.:format) {:action=>"edit", :controller=>"comments"}
     post_comment GET    /posts/:post_id/comments/:id(.:format)      {:action=>"show", :controller=>"comments"}
     post_comment PUT    /posts/:post_id/comments/:id(.:format)      {:action=>"update", :controller=>"comments"}
     post_comment DELETE /posts/:post_id/comments/:id(.:format)      {:action=>"destroy", :controller=>"comments"}
     recent_posts GET    /posts/recent(.:format)                     {:action=>"recent", :controller=>"posts"}
    popular_posts GET    /posts/popular(.:format)                    {:action=>"popular", :controller=>"posts"}
     publish_post PUT    /posts/:id/publish(.:format)                {:action=>"publish", :controller=>"posts"}
            posts GET    /posts(.:format)                            {:action=>"index", :controller=>"posts"}
            posts POST   /posts(.:format)                            {:action=>"create", :controller=>"posts"}
         new_post GET    /posts/new(.:format)                        {:action=>"new", :controller=>"posts"}
        edit_post GET    /posts/:id/edit(.:format)                   {:action=>"edit", :controller=>"posts"}
             post GET    /posts/:id(.:format)                        {:action=>"show", :controller=>"posts"}
             post PUT    /posts/:id(.:format)                        {:action=>"update", :controller=>"posts"}
             post DELETE /posts/:id(.:format)                        {:action=>"destroy", :controller=>"posts"}
       home_index GET    /home/index(.:format)                       {:controller=>"home", :action=>"index"}
       home_about GET    /home/about(.:format)                       {:controller=>"home", :action=>"about"}
             root        /(.:format)                                 {:controller=>"home", :action=>"index"}

It will give you a list of all available routes that have been setup in your application. It also gives you the route names. The first word of each line is the route name. The first named route in our example is “admin_posts”. You can append “_url” or “_path” in views and controllers to get either that action’s url with or without the hostname, respectively.

Also note that in the routes listed above, any symbols that are NOT in parentheses are required. For example, on line 10 above, post_comments_path would require post_id to be passed in. You can do this with either the id, or the object itself. For example

post_comments_path(@post)
post_comments_path(@post.id)

This is a lot to play with, which is why I like including code samples with articles like this. Download (link at the top of the page) and play around with the routes until you're comfortable. After and hour or so, you won't need to lookup the Rails specs every time you want to make a routing change.

Ruby Enumerable Magic: Unary Ampersand Operator

December 1, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’ve probably seen this example in my previous post, or in other Ruby code:

irb(main):001:0> ['a', 'b', 'c'].map(&:capitalize)
=> ["A", "B", "C"]

This is a cool trick, which equates to:

['a', 'b', 'c'].map{|letter| letter.capitalize}

Most Ruby developers have seen this idiom, and even used it, but you may not know how it works. This is *not* strictly a piece of Enumerable magic, even though this is probably where you see it used most often. We can all harness this power, but much like the Force it can be used for good or evil.

There are really two pieces of magic used together to make this possible. The first is the unary ampersand operator. Contrary to what you might think, there is no “&:” operator in Ruby. What you’re seeing in my first example above is actually “&” and “:capitalize”, pushed together. That first character is the unary ampersand. The second piece is a Ruby symbol that is being passed to the operator.

When Ruby sees the unary ampersand on the last argument of a method, it tries to convert it to a proc (an executable block of code), and run it. It does this by calling to_proc on the object. This is where the second piece of magic comes in. Rails 1.1 added a to_proc method to the Symbol class, so that you could pass a symbol to the unary ampersand (ie, &:capitalize) and it would convert it for you. Ruby 1.8.7 rolled this change into Ruby itself, so you can use it outside of Rails.

Digging into “The Why” can be fun, but what I really wanted to know is how I could harness this Ruby voodoo (or, if you will, “rooboo”) for my own selfish gain. That was, by far, the hardest part of putting together this guide. The good news is, once I figured it out, it’s easy to pass on.

Let’s use the example of a Translator class with one public method: speak. This class has a method for each of the languages it knows, and a catch-all method (Ruby’s method_missing) for languages it doesn’t understand:

class Translator
  def speak &language
    language.call(self)
  end
  
  protected
  
  def french
    'bon jour'
  end
  
  def spanish
    'hola'
  end
  
  def turkey
    'gobble'
  end
  
  def method_missing *args
    'awkward silence'
  end
end

This is awesome! Let’s play with it in irb:

irb(main):001:0> require 'translator.rb'
=> true
irb(main):002:0> translator = Translator.new
=> #<Translator:0x10038ff08>
irb(main):003:0> translator.speak(&:spanish)
=> "hola"
irb(main):004:0> translator.speak(&:turkey)
=> "gobble"
irb(main):005:0> translator.speak(&:italian)
=> "awkward silence"

The key to this magic is in the speak method:

  def speak &language
    language.call(self)
  end

There’s a lot going on here. When Ruby converts :turkey into a proc from a symbol, it makes the assumption that a receiver will be passed into the proc, and the proc will be called *on* that receiver. In this case, we pass self into the proc above. This is like calling self.turkey. Gobble.

Is this a super useful example? Maybe not. But it lets you play with the smallest possible working example, and get comfortable. If you have any questions, please leave a comment below.

Ruby Enumerable Magic: The Basics

November 30, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’re probably familiar with Ruby’s Enumerable module, even if you don’t know it by that name. It adds neat methods to arrays, like map, inject, select, and so on. You might have thought (like I did for a long time) that these methods were *array* methods, but they’re not.

Ruby’s arrays use Enumerable, and you can as well, in any class you want. Of course, the class should represent a collection of things, or all the iterative methods in Enumerable wouldn’t make much sense. Let’s say we have a Team class, that manages a group of members:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

Enumerable requires that your class contain an each method that serves up the items in the collection. All the other Enumerable methods rely on this. Now we can use the map method, for instance:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.map{|member| member.capitalize}
=> ["Joshua", "Gabriel", "Jacob"]

Now we can call any Enumerable methods on our team itself, and it will assume we want to work with the members array within. Enumerable can be a powerful mix-in to your own classes. We can even take this a step further, and clean up our call to map:

team.map(&:capitalize)

If you haven’t seen this before, it’s called the unary ampersand operator, and it’s the subject of my next post.

Nested Comments in Ruby on Rails, Part 1: Models

October 23, 2010

  1. The Model Layer
  2. Controllers and Views
 

YouTube has a pretty cool comment system. You can comment on videos, but you can also reply to comments other people have posted. In essence, you commenting on comments!

If you’d like something similar in your app, you might be tempted to create PostComments and CommentComments, or something similar. A better approach is to use polymorphic associations. Polymorphism makes it possible for a comment to belong to a post, or another comment, or any number of things. And it’s easier than you think.

Note: all examples will be in Rails 3. If you’re not familiar, that’s okay. The example code is available from github, in my examples of nested comments.

Let’s start by creating a Post model:

  rails g model post title:string body:text
  rake db:migrate

We’re keeping it simple, and we’re not going to bother with user authentication for these examples. Our post model is pretty straightforward, so we didn’t need to modify the migration file at all. Now let’s create our comments:

  rails g model comment title:string body:text commentable_id:integer commentable_type:string

This is where some of the magic comes in. We can’t say our comments “belong to posts” because they can belong to anything. So we can’t add a “post_id” field. Instead, we come up with a name for our association: “commentable”. We add a commentable_id field to store the id of the object this comment belongs to. And we add a commentable_type field to store the type of object this comment belongs to – ‘Post’, ‘Comment’, whatever. With these two pieces of info, Rails can figure out the rest and make your life easier.

Before we can add comments to our database, however, we need to make a small change the code in the “self.up” part of our migration:

  def self.up
    create_table :comments do |t|
      t.string :title
      t.string :body
      t.integer :commentable_id
      t.string :commentable_type

      t.timestamps
    end
    
    add_index :comments, [:commentable_id, :commentable_type]
  end

We’ve added an single index on the combination of the commentable fields, which will speed up our app. Now we can migrate the changes again:

  rake db:migrate

Now let’s setup our associations in our models, starting with posts:

  class Post < ActiveRecord::Base
    has_many :comments, :as => :commentable
  end

Normally if you tell Post that it has_many comments, Post would expect the comments table to have a “post_id” field. It doesn’t, because we’re using polymorphism. So we tell it the name we gave our polymorphic association: “commentable”.

Now for the slightly more complicated comment model:

  class Comment < ActiveRecord::Base
    belongs_to :commentable, :polymorphic => true
    has_many :comments, :as => :commentable
  end

First, we’re setting up the “belongs to” side of polymorphism. Our comment belongs to “commentable”, the name we gave our polymorphic association. We also tell it that this is polymorphic. Otherwise, Rails would look for a model called Commentable.

Finally, we’re saying that comments have many comments, the same way set did it for posts.

You might thing that after this special setup, using polymorphic associations would be more difficult as well. The good news is, the hard part is over and everything else works the same as any other “belongs_to” or “has_many” association. Let’s go into the rails console to try it out:

  post = Post.create :title => 'First Post'
  => #<Post id: 1, title: "First Post", body: nil, created_at: "2010-10-23 16:56:13", updated_at: "2010-10-23 16:56:13"> 

  comment = post.comments.create :title => 'First Comment'
  => #<Comment id: 1, title: "First Comment", body: nil, commentable_id: 1, commentable_type: "Post", created_at: "2010-10-23 16:56:40", updated_at: "2010-10-23 16:56:40"> 

  reply = comment.comments.create :title => 'First Reply'
  => #<Comment id: 2, title: "First Reply", body: nil, commentable_id: 1, commentable_type: "Comment", created_at: "2010-10-23 16:59:28", updated_at: "2010-10-23 16:59:28"> 

We’re able to add a comment to our post, and add a comment to that comment! Rails does the work of filling in the commentable_id and commentable_type fields, just as it would have filled in the post_id field if comments could only belong to posts.

Please check out the nested comments example code on github, which includes tests. Download it play around with it, and see how it works. In the next part, I’ll be looking at how to use nested comments in your controllers and views.

Common Addresses Using Polymorphism and Nested Attributes in Rails

October 19, 2010

Have you ever wanted to take an object that is common to a lot of models – like addresses – and DRY up your code? If you’re really concerned about design, or if the object itself is complex, you certainly want to make changes in one place and have them apply to everywhere in your app.

You can use a combination of polymorphism, nested attributes, and shared views to accomplish this easily. If it seems complicated at first, try it a couple times and you’ll see it’s no big deal. Let’s get started.

Polymorphism

This is the concept that something like an address can belong to more than one type of thing. Maybe your app has customers, employees, and locations, which all need addresses and you’d like them to appear uniform from one form to the next. Let’s create a migration and model for a simple address.

In your migration, we need the address fields you plan to use, plus the two fields that make polymorphism possible in rails: `object_id` and `object_type`. This is how rails will know what object type and id each address belongs to:

    create_table :addresses do |t|
      t.string :line1
      t.string :line2
      t.string :city
      t.string :state
      t.string :zip
      t.integer :addressable_id
      t.string :addressable_type

      t.timestamps
    end
 
    add_index :addresses, [:addressable_type, :addressable_id], :unique => true

In your model, we need to tell `address` that is belongs to other things polymorphically:

class Address < ActiveRecord::Base
  belongs_to :addressable, :polymorphic => true
end

Nested Attributes

Now let’s make the customer model “contain” an address. In addition to the `has_one` association, we’re going to tell rails that customer forms might also contain fields for the customer’s address as well:

class Customer < ActiveRecord::Base
  has_one :address, :as => :addressable
  accepts_nested_attributes_for :address
end

Shared Views

Next, we’ll want to make a partial to store the address form, that can be called from our other views. Put this in `app/views/shared/_address.html.erb`:

<p>
  <%= f.label :line1, 'Address 1' %><br />
  <%= f.text_field :line1 %>
</p>

<p>
  <%= f.label :line2, 'Address 2' %><br />
  <%= f.text_field :line2 %>
</p>

<p>
  <%= f.label :city %><br />
  <%= f.text_field :city %>
</p>

<p>
  <%= f.label :state %><br />
  <%= f.text_field :state, :size => 2 %>
</p>

<p>
  <%= f.label :zip, 'Zip Code' %><br />
  <%= f.text_field :zip %>
</p>

Where does the `f` come from in the view above? It’s passed in by any view that wants to use it. Let’s use the new customer form as an example:

<% form_for(@customer) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  
  <% f.fields_for :address do |address| %>
    <%= render :partial => 'shared/address', :locals => {:f => address} %>
  <% end %>
  
  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>

The `fields_for` call above is the secret sauce, calling our shared partial and passing the form object `address` into the partial as the variable `f`. When you view the new customer page, you’ll see the address fields included as if they were part of the customer form directly. The real power is that you can repeat these steps for all other models that have an address, and they’ll use the same database table, model, and shared view for addresses.

…And Other Junk

There’s one last step. In the `new` action of your `customers` controller, you need to build the address object onto the customer object, so rails knows it’s there:

  def new
    @customer = Customer.new
    @customer.build_address
  end

That’s all it takes to get polymorphism, nested attributes, and shared views working together to DRY up your code, data modeling, and views. And DRYer code equals happier coders! For more info on polymorphism and nested attributes, visit your friendly neighborhood documentation:

http://wiki.rubyonrails.org/howtos/db-relationships/polymorphic
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Testing Visitors in Devise with RSpec

October 16, 2010

We’re hot and heavy in the middle of Rails Rumble, and I’m taking a break to publish a problem I just had with the new Devise authentication engine. I love it so far, by the way, but I ran into a problem when I started testing for *authorization*. Authentication – verifying that a user is who they say they are – went just fine. Authorization – verifying that an authenticated user has access to a certain resource – is where I ran into trouble. Devise tries to make it easy to test as a certain member:

# spec/controllers/profiles_controller_spec.rb
require 'spec_helper'

include Devise::TestHelpers

describe ProfilesController do
  describe "as a member" do
    before do
      @member = Factory.create :user
      sign_in @member
    end

    ...
  end
end

I can sign in as a certain user, and test that things work okay. But what if I want to test as a visitor, not logged in? I want to verify that the visitor is redirect to the sign_in page when they try to access a members-only resource. It sounds easy – just don’t sign in, right? This gives this error:

undefined method `authenticate!' for nil:NilClass

That’s because it’s trying to authenticate, and it’s missing something behind the scenes in Deviseland. But we can trick Devise (or fix it, whatever) by signing out. So:

# spec/controllers/profiles_controller_spec.rb
require 'spec_helper'

include Devise::TestHelpers

describe ProfilesController do
  describe "as a member" do
    before do
      sign_out :user
    end

    ...
  end
end

I hope this helps others, whether you’re under a Rumble-esque time crunch or not!

Lazy Loading Ruby Methods the Smart Way

September 3, 2010

I just spent an hour trying to solve a seemingly simple problem. Luckily, test-driven development is the reason I spotted this problem at all! Here’s where I started:

  class Survey < ActiveRecord::Base
    SUBMISSION_STATII = ['submitting', 'submitted']

    def in_submission?
      SUBMISSION_STATII.include? self.status
    end
  end

I want the in_submission? method to cache its response in an instance variable, so it doesn’t have to run the check every time. I plan on this method getting called a lot. I wrote my code to test, using shoulda and mocha:

  should "cache the result" do
    Survey.SUMISSION_STATII.expects(:include?).with('new').returns(false).once
    survey = Survey.new :status => 'new'

    survey.in_submission?
    survey.in_submission?
  end

This is simply verifying that the status is checked for inclusion only once, even if we call the method multiple times. And here’s the code that I thought should satisfy the test:

  def in_submission?
    @in_submission ||= SUBMISSION_STATII.include?(self.status)
  end

I only added the @in_submission ||= part. This is my standard trick, but in this case it failed. Why? because whenever @in_submission is set to false, it will trigger the full code to run again next time. No “caching” happens. We should check whether the instance variable is defined, not whether it equates to true:

  def in_submission?
    return @in_submission if defined?(@in_submission)
    @in_submission = SUBMISSION_STATII.include?(self.status)
  end

Sure, we lose our sweet one-liner, but we gain an hour of productivity when our tests pass the first try. Or even worse, you didn’t bother to test caching at all, so it’s just slowing down your app and possibly even mucking things up. But you are using TDD, aren’t you?


Follow

Get every new post delivered to your Inbox.