Dynamic Methods in Ruby with method_missing

December 21, 2010

Make it up as you go

One way Ruby is dynamic is that you can choose how to handle methods that are called, but don’t actually exist. If you have a lot of very similar methods, you can even use this to define them all at once! Ruby does this using the method_missing method, which you override in the classes where you need more dynamic method calling.

ActiveRecord’s dynamic find_all_by methods

Ruby on Rails uses method_missing with ActiveRecord’s find_all_by methods. There is no find_all_by_name method, but if your Person model has a name attribute, you can call Person.find_all_by_name('Bob') and it will return all the records that match that name.

Here’s a very simplified version of how Rails handles find_all_by requests:

class Person < ActiveRecord::Base
  def self.method_missing method_name, *args
    if method_name =~ /^find_all_by_(\w+)$/
      self.all(:conditions => {$1 => args[0]})
    end
  end
end

Using regular expressions, method_missing sees if the method name matches something we expect. It parses out the interesting parts, and uses them to look up the objects we’re searching for. This is a good use case, because the attributes of an ActiveRecord model aren’t known until runtime.

Dynamic methods for dynamic objects outside Rails

We can apply this same technique outside of Rails. Let’s create the world’s most dynamic Ruby class:

# lib/widget.rb
class Widget
  def method_missing sym, *args
    if sym =~ /^(\w+)=$/
      instance_variable_set "@#{$1}", args[0]
    else
      instance_variable_get "@#{sym}"
    end
  end
end

We’ve just created a Widget object that can have any attributes you want to give it. method_missing checks if the called method ends with an equal sign – if so, it assigns the value you passed, to an instance variable with that name. If there’s no equal sign, it tries to get the value of an instance variable by that name:

ruby-1.9.2-p0 > widget = Widget.new
 => #<Widget:0x0000010383f618> 
ruby-1.9.2-p0 > widget.name = 'Bob'
 => "Bob" 
ruby-1.9.2-p0 > widget.age = 30
 => 30 
ruby-1.9.2-p0 > widget.name
 => "Bob" 
ruby-1.9.2-p0 > widget.age
 => 30 

Use method_missing with methods that use blocks

You can also pass blocks to method_missing. Say we have an ActiveRecord model called Person, with name and age attributes. Let’s create something similar to find_all_by that gets the list of matching people, and runs them through the map method. We’ll call it map_by:

# app/models/person.rb
class Person < ActiveRecord::Base
  def self.method_missing method_name, *args, &block
    if method_name =~ /^map_by_(\w+)$/
      list = self.all(:conditions => {$1 => args[0]})
      list.map(&block)
    end
  end
end

If a method is called that can’t be found, method_missing will check to see if it matches our map_by pattern, perform an ActiveRecord search, and push the results through map with the block we supplied.

Now let’s see if it works, by grabbing the names of all people in our database age 30:

ruby-1.9.2-p0 > Person.create :name => 'Bob', :age => 30
 => #<Person id: 2, name: "Bob", age: 30, created_at: "2010-12-21 02:23:57", updated_at: "2010-12-21 02:23:57"> 
ruby-1.9.2-p0 > Person.create :name => 'John', :age => 29
 => #<Person id: 3, name: "John", age: 29, created_at: "2010-12-21 02:24:11", updated_at: "2010-12-21 02:24:11"> 
ruby-1.9.2-p0 > Person.create :name => 'Marsha', :age => 30
 => #<Person id: 4, name: "Marsha", age: 30, created_at: "2010-12-21 02:24:22", updated_at: "2010-12-21 02:24:22"> 
ruby-1.9.2-p0 > Person.map_by_age(30){|person| person.name}
 => ["Bob", "Marsha"] 

It works! Now I’m going to refactor the Person class to make it easier to add more dynamic methods in the future. I’ll even add an each_by handler so we can see multiple dynamic methods in action:

# app/models/person.rb
class Person < ActiveRecord::Base
  class << self
    def method_missing method_name, *args, &block
      case method_name
      when /^map_by_(\w+)$/ then map_by $1, args[0], &block
      when /^each_by(\w+)$/ then each_by $1, args[0], &block
      else super method_name, *args, &block
      end
    end
    
    def map_by attribute, value, &block
      list = self.all(:conditions => {attribute => value})
      list.map(&block)
    end

    def each_by attribute, value, &block
      list = self.all(:conditions => {attribute => value})
      list.each(&block)
    end
  end
end

I’ve done a few things. First, I changed our “if” conditional to a case statement, so that we can add to it in the future, and it will be clean and readable. I also moved the actual map_by code into its own method, for the same reason. And now, method_missing calls its parent method if it doesn’t find a match, to preserve inheritance.

You might also notice that instead of defining self.method_missing and self.map_by, I’ve wrapped these method definitions in a class << self block that essentially does the same thing. I think this is cleaner when you have several class methods.

method_missing can be used in any Ruby class, so long as you can anticipate dynamic methods that the users of your class might need, and preserve the chain of inheritance. This should be used sparingly, when you can cut down on method definitions by defining them dynamically. It’s easy to abuse this, and there is extra overhead involved. But for the right situations, method_missing can create shorter, more readable code.

Ruby’s defined? Operator

December 20, 2010

Even if you’ve used Ruby’s defined? operator on a daily basis, you may not understand how it works. I sure didn’t until recently, but it’s worth a look.

A refresher in memoization etiquette

If you’re acquainted with memoization, this might look familiar:

class Person
  attr_accessor :first_name, :last_name

  def full_name
    return @full_name if defined?(@full_name)
    @full_name = "#{first_name} #{last_name}"
  end
end

The full_name method above uses memoization – the return value of the method is calculated just once, on the first call. It’s stored in the instance variable @full_name, and used for subsequent calls to the method. I first discovered this technique digging through the code base for Thoughtbot’s shoulda gem. I’ve used it hundreds of times, and never really questioned how the defined? operator works until recently.

Question mark?

Ruby methods can contain some non-alphanumeric characters like “!” and “?”, and rubyists take advantage of this to add readability to our code. Methods ending in “!” typically mean one of two things: the method is altering its receiver, or it’s going to complain loudly if it fails (usually by raising an exception). By the same convention, methods ending in “?” are asking a question, and the answer is usually boolean (yes/no).

The defined? method follows this convention…sort of. I always assumed it returned true/false, but that’s only half the story. If the object in question is defined, defined? gives you a string description of the object. This equates to “true” in any conditional arguments. If the object is not defined, it returns nil, which equates to “false”.

Test anything. Almost.

So defined? works in any boolean context, but it also provides a little more info. And it works on just about anything. Classes:

ruby-1.9.2-p0 > defined? Person
 => nil 
ruby-1.9.2-p0 > class Person
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? Person
 => "constant" 

It works on methods:

ruby-1.9.2-p0 > def bark
ruby-1.9.2-p0 ?>  puts "woof"
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? bark
 => "method" 

And of course it works on variables of all kinds:

ruby-1.9.2-p0 > defined? @@a
 => nil 
ruby-1.9.2-p0 > @@a = 'a'
 => "a" 
ruby-1.9.2-p0 > defined? @@a
 => "class variable" 
ruby-1.9.2-p0 > defined? @b
 => nil 
ruby-1.9.2-p0 > @b = 'b'
 => "b" 
ruby-1.9.2-p0 > defined? @b
 => "instance-variable" 
ruby-1.9.2-p0 > defined? c
 => nil 
ruby-1.9.2-p0 > c = 'c'
 => "c" 
ruby-1.9.2-p0 > defined? c
 => "local-variable" 

I even tried other operators, just on a whim. But of course, this was too much to hope for :)

ruby-1.9.2-p0 > defined?(+)
SyntaxError: (irb):1: syntax error, unexpected ')'
	from /Users/bellmyer/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

defined? is an operator, not a method

Because you can enclose the object you want to check in parentheses (as in, defined?(@full_name)), you might be tempted to think it’s a method. It’s not, it’s a native operator. This is an important distinction, because it means defined? can’t be overridden:

ruby-1.9.2-p0 > x = 'test variable'
 => "test variable" 
ruby-1.9.2-p0 > defined? x
 => "local-variable" 
ruby-1.9.2-p0 > def defined? object
ruby-1.9.2-p0 ?>  puts "defined? has been overridden!"
ruby-1.9.2-p0 ?>  end
 => nil 
ruby-1.9.2-p0 > defined? x
 => "local-variable" 

I don’t get an error trying to override the operator with a method definition, but it doesn’t work, either. Honestly, Ruby is so permissive I half expected the override to work anyway! Another clue that you’re dealing with an operator is that it has no receiver. That’s why you give it the object, instead of calling it from a receiver, like the nil? method:

ruby-1.9.2-p0 > @x.nil?
 => true 
ruby-1.9.2-p0 > @x.defined?
NoMethodError: undefined method `defined?' for nil:NilClass
	from (irb):8
	from /Users/bellmyer/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

While defined? is most valuable (and most commonly used) in a boolean context, there may be meta-programming applications where you’d want to what type of “thing” you’re dealing with. While you can always use the .class method, you have to already know that the object is defined. In the world of meta-programming, that’s often a luxury you don’t have.

Ruby Method Permissions: The Differences Between Public, Protected, and Private

December 15, 2010

It’s easy to code in Ruby for years, and never pay attention to method permissions. I know, because I did it. I came from a C/C++ background, so I understood the concepts of public vs private. And I vaguely understood that Ruby’s protected fell somewhere in between. I just never really cared enough to look it up. Eventually I got curious. I’ll run down the different types of method visibility here.

Examples

It’s hard to think of a good, simple example to use for this. I’ll go with hobbits. They’re friendly to other species, but they have private lives, as well. Much of their privacy seems to be open to other hobbits, since they appear to be able to wander into each others’ houses in the shire at will. Of course, they also have a few choice secrets kept just to themselves. They have all the qualities of a well-rounded Ruby method. Let’s define their class here:

class Hobbit
  def initialize(name, rooms, has_ring)
    @name, @rooms, @has_ring = name, rooms, has_ring
  end

  def name
    @name
  end

  def name_of(hobbit)
    hobbit.name
  end

  def rooms_of(hobbit)
    hobbit.rooms
  end

  def hobbit_has_ring?(hobbit)
    hobbit.has_ring?
  end

  protected

  def rooms
    @rooms
  end

  private

  def has_ring?
    @has_ring
  end
end

Each hobbit has three attributes. Their name is public, the number of rooms in their house is protected, and whether or not they have The Ring is private. There are methods to read these attributes, and to try to read them from other hobbits. Now let’s create a couple hobbits to work with:

irb(main):001:0> require 'hobbit.rb'
=> true
irb(main):002:0> frodo = Hobbit.new('Frodo', 3, true)
=> #<Hobbit:0x10038b5c0 @rooms=3, @name="Frodo", @has_ring=true>
irb(main):003:0> samwise = Hobbit.new('Samwise', 2, false)
=> #<Hobbit:0x1003769e0 @rooms=2, @name="samwise", @has_ring=false>

Public Methods

This is the default method type in Ruby (and most languages), and it means that it can be called from anywhere, by any hunk of code that knows what the object is. We can easily find out the names of our hobbits:

irb(main):004:0> frodo.name
=> "Frodo"
irb(main):005:0> samwise.name
=> "Samwise"

Protected Methods

Now, what about the rooms in their houses? This info is accessible only through protected methods:

irb(main):006:0> frodo.rooms
NoMethodError: protected method `rooms' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from (irb):6
	from :0
irb(main):007:0> frodo.rooms_of(frodo)
=> 3
irb(main):008:0> frodo.rooms_of(samwise)
=> 2

As you can see, we can’t call the hobbits’ room methods directly, but one hobbit can call the protected rooms method for itself, or any other hobbit. In purely programming terms, public methods are globally accessible, where protected methods can only be called by other methods in the class.

Private Methods

What about ring status? This info is locked in private methods. How can we access this?

irb(main):013:0> frodo.has_ring?
NoMethodError: private method `has_ring?' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from (irb):13
	from :0
irb(main):014:0> frodo.hobbit_has_ring?(frodo)
NoMethodError: private method `has_ring?' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from ./hobbit.rb:19:in `hobbit_has_ring?'
	from (irb):14
	from :0
irb(main):015:0> frodo.hobbit_has_ring?(samwise)
NoMethodError: private method `has_ring?' called for #<Hobbit:0x100374c80 @rooms=2, @name="Samwise", @has_ring=false>
	from ./hobbit.rb:19:in `hobbit_has_ring?'
	from (irb):15
	from :0

We can’t access the has_ring? method directly. We can’t access it indirectly from another method, specifying the hobbit we’re interested in. Not even if that hobbit is ourselves! Let’s reopen our class and add the i_have_the_ring? method:

class Hobbit
  def i_have_the_ring?
     has_ring?
  end
end

Now frodo has a way to check if he has the ring:

irb(main):021:0> frodo.i_have_the_ring?
=> true

In Ruby, protected methods are still open to other objects of the same type. So as long as the “receiver” (hobbit in hobbit.rooms) is the same class as the calling object, method access is granted.

But private methods are not allowed to specify a receiver at all. I can’t say frodo.has_ring? or even self.has_ring? inside the frodo object at all. I’m only allowed to say has_ring? and the object will assume it’s calling its own method.

Summary

While public methods are fairly straightforward, the difference between protected and private can cause problems if you’re not aware of it. Ruby’s private implementation is even stricter than C’s, not allowing receivers. But protected balances that out with the ability to “walk into sibling objects’ houses”.

I tend to use protected methods when I don’t intend for them to be called directly, and I don’t write tests for them. I write tests for the public methods that call them. And I generally avoid private methods, because I can’t envision needing to block sibling-to-sibling access (although I probably should if I’m not testing them, right?) but I don’t want to spend hours debugging a method call that breaks the no-receiver rule.

Memoize Techniques in Ruby and Rails

December 13, 2010

The headline isn’t a typo. If you haven’t heard of “memoizing”, it’s the act of caching the result of a method so that when you call the method in the future, it doesn’t have to do all the processing again. I’ll show you a few different ways to do this, along with the pros and cons of each.

Setup

Let’s setup a sample rails app to play with. I’m using Rails 3, and we’ll generate a simple model to work with:

memoize$ rails g model user first_name:string last_name:string
      invoke  active_record
      create    db/migrate/20101204003605_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
memoize$ rake db:migrate
(in /Users/bellmyer/Desktop/bellmyer/blog/memoize)
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0013s
==  CreateUsers: migrated (0.0014s) ===========================================

The Standard Idiom

You’ve probably seen the simplest form of memoization, whether you called it that or not. Our user model has a first_name and last_name. Let’s say we want to create a full_name method to combine the two:

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

Let’s verify that it works as we expect:

memoize$ rails c
Loading development environment (Rails 3.0.1)
ruby-1.9.2-p0 > user = User.new :first_name => 'Bob', :last_name => 'Smith'
 => #<User id: nil, first_name: "Bob", last_name: "Smith", created_at: nil, updated_at: nil> 
ruby-1.9.2-p0 > user.full_name
 => "Bob Smith" 

Great! Now let’s try the simplest form of memoization:

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

The first time this method is called, @full_name doesn’t exist yet, so the code to the right of ||= is executed. The next time this method is called, the result of the method has already been stored in @full_name, so the method doesn’t need to recalculate it.

A Better Way

This quick-and-dirty method works well for a lot of stuff, but it doesn’t work in all cases. Let’s add another memoized method, and see if you can find the logical flaw:

  def has_full_name?
    @has_full_name ||= (!first_name.blank? && !last_name.blank?)
  end

This method checks to see if the user has both a first and last name. At first, it looks like it will work as well as our first method. But what if the result is false? @has_full_name will be set to false, which means the right side of the equation will be run from scratch each time.

Instead of checking if @has_full_name equates to true or false, we need to check if @has_full_name has been defined, like so:

  def has_full_name?
    return @has_full_name if defined?(@has_full_name)
    @has_full_name = (!first_name.blank? && !last_name.blank?)
  end

Now we’re returning @has_full_name if it exists, and evaluating it otherwise. No more true/false gotchas. This method is more reliable, but it’s not as short and sweet, I’ll admit.

Memoization with Method Arguments

What if we have a method with arguments? We usually want the same input to produce the same output. If we enter the same arguments to a method over and over, we’d expect the same return value. So why not take our memoization a step further, and add caching for methods with arguments?

  def formal_name(salutation='Mr.', suffix=nil)
    @formal_name ||= {}
    return @formal_name[[salutation, suffix]] if @formal_name.has_key?([salutation, suffix])

    @formal_name[[salutation, suffix]] = "#{salutation} #{full_name} #{suffix}"
  end

This is a bit more complicated. Since this methd can be called with different arguments, we initialize a hash to store our cached results. We use Hash’s has_key? method to check if we already have a value for the given arguments. Let’s try it out:

memoize$ rails c
user Loading development environment (Rails 3.0.1)
ruby-1.9.2-p0 > user = User.new :first_name => 'Bob', :last_name => 'Smith'
 => #<User id: nil, first_name: "Bob", last_name: "Smith", created_at: nil, updated_at: nil> 
ruby-1.9.2-p0 > user.formal_name('Mr.', 'Jr.')
 => "Mr. Bob Smith Jr." 
ruby-1.9.2-p0 > user.first_name = 'John'
 => "John" 
ruby-1.9.2-p0 > user.formal_name('Mr.', 'Jr.')
 => "Mr. Bob Smith Jr." 

You know the memoization is working, because I changed the first name, and formal_name still gave us the same answer.

Using Rails’ Memoize Module

Our “better way” is bulletproof, and we’ve even added the ability to handle method arguments. But it’s a lot of work to do this for every method, and it gunks up the readability of the method. We have to wade through the caching code to figure out what the method really does.

If you’re using Rails, 2.2 or later, you can take advantage if its Memoize module to clean this up.
Let’s add a method that uses it:

class User < ActiveRecord::Base
  extend ActiveSupport::Memoizable

  # other methods...
  
  def initials(middle_initial)
    first_name[0] + middle_initial + last_name[0]
  end
  memoize :initials
end

It’s that easy! The memoize method takes care of things for you, but you have to extend your class with the module for it to work. And unlike the other memoization strategies discussed, this uses ActiveSupport, so it’s Rails-specific.

The good news is that if you’re using Rails for your project, you can use Memoizable anywhere. Let’s add a non-activerecord class in our lib folder:

# lib/my_number.rb
class MyNumber
  extend ActiveSupport::Memoizable
  
  attr_accessor :x
  
  def initialize(x)
    @x = x
  end
  
  def plus(y)
    @x + y
  end
  memoize :plus
end

And here’s the proof that it works:

memoize$ rails c
require 'myLoading development environment (Rails 3.0.1)
ruby-1.9.2-p0 > require 'my_number'
 => ["MyNumber"] 
ruby-1.9.2-p0 > num = MyNumber.new(5)
 => #<MyNumber:0x00000104434bf8 @x=5> 
ruby-1.9.2-p0 > num.plus 2
 => 7 
ruby-1.9.2-p0 > num.x = 0
 => 0 
ruby-1.9.2-p0 > num.plus 2
 => 7 

This is a much better solution if you’re using Rails, and I highly recommend it. The less code you type by hand, the less chance of adding a bug somewhere.

Things You Should Know

First, as mentioned above, the Memoizable module is only available in ActiveSupport, part of Rails. But the previous examples are pure Ruby, and can be used in any Ruby project.

You don’t want to use memoization everywhere. Here are some situations where it’s not appropriate:

  • The method is simple, and memoizing it won’t save you much (there is some overhead involved).
  • The method’s output needs to change over the life of its object
  • The method is unlikely to be called again with the same parameters.
  • There are so many combinations of parameters that will be called, it will eat up too much memory to store all the results.

Ruby Enumerable Magic: Aggregates

December 10, 2010

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

This final article in my series about the Enumerable module details the methods that don’t quite fit elsewhere, but deal with the collection as a whole. As such, they each return just one object, not an array.

inject

If you haven’t used this method before, it’s loads of fun. It’s a cumulative method that uses each item in the collection to form a “final answer”. The classic example is finding the sum of the numbers in an array:

irb(main):001:0> [1, 2, 3].inject{|sum, num| sum += num}
=> 6

Another use might be finding the initials in a name:

irb(main):008:0> ['Jaime', 'Lee', 'Bellmyer'].inject(''){|initials, name| initials += name[0,1]}
=> "JLB"

In this example, I had to pass the starting string (”, or blank) to inject. Otherwise, it takes the first element as a whole, and starts adding onto it. I don’t like this behavior, and I suspect it works this way because inject assumes you’re trying to sum elements in some way. You also need to pass an initial value to inject when you want the result to be a different class than the inputs:

irb(main):009:0> [1, 2, 3].inject(0.0){|sum, i| sum += i.to_f}
=> 6.0

min and max

These methods behave largely like you’d expect. The items in the collection have to have the <=> method defined, just like the sorting methods, since finding the min and max requires sorting. So strings and numbers behave like you’d expect:

irb(main):010:0> ['joshua', 'gabriel', 'jacob'].min
=> "gabriel"
irb(main):011:0> ['joshua', 'gabriel', 'jacob'].max
=> "joshua"
irb(main):012:0> [1,2,3].min
=> 1
irb(main):013:0> [1,2,3].max
=> 3

And you can also pass your own custom sorting block, like you can with the sort method:

irb(main):014:0> ['joshua', 'gabriel', 'jacob'].min{|a,b| a.reverse <=> b.reverse}
=> "joshua"
irb(main):015:0> ['joshua', 'gabriel', 'jacob'].max{|a,b| a.reverse <=> b.reverse}
=> "gabriel"
irb(main):016:0> [1,2,3].min{|a,b| a*(-1) <=> b*(-1)}
=> 3
irb(main):017:0> [1,2,3].max{|a,b| a*(-1) <=> b*(-1)}
=> 1

In conclusion

What a long journey it has been. I’d like to thank myself for completing my longest series of articles, in the most timely manner yet. And I’d like to thank you if you suffered through all of it for the sake of knowledge. Please feel free to ask any questions you might have, and I’ll do my best to answer them promptly.

And thus concludes our two-week look at the Enumerable module.

Ruby String Interpolation

December 8, 2010

String interpolation is the process of converting placeholders inside a string to the values they represent, in order to produce a dynamic string. You probably use it all the time without realizing just how cool it works under the hood, and how you can leverage this power in your own classes.

I came from a Perl environment, where variables look like $variable, and so string interpolation was pretty simple:

$person = 'World'
print "Hello, $person!"    # prints "Hello, World!"

Perl just recognized any alphanumeric word starting with a dollar sign as a variable, and replaced it inline. Ruby variables don’t start with a dollar sign though, so it’s a little more typing. Here’s how you include variables:

person = 'World'
puts "Hello, #{person}!"    # prints "Hello, World!"

Ruby uses the idiom #{} to denote something that needs to be interpreted. But this extra typing comes with a huge benefit. You can include more than just strings, you can include entire expressions! Say our variable isn’t already capitalized, and we want to do that inline:

person = 'world'
puts "Hello, #{person.capitalize}!"  # prints "Hello, World!"

Ruby will run the code inside the braces, and display that. But it gets even better: Ruby will interpret any type of expression, and convert it to a string for you:

  puts "4 = #{4}"    # prints "4 = 4"
  puts "2 + 2 = #{2+2}"  # prints "2 + 2 = 4"

Even if the code inside the braces isn’t a string, Ruby will convert inline. How does it do this? It calls the to_s method on the result of the expression. The expression 2 + 2 equates to 4, which is a Fixnum object. This object, like every other in Ruby, has a to_s method.

Another big benefit of this is that you can decide how your own objects will be displayed in strings. Let’s say you have a user class:

class User
  attr_accessor :first_name, :last_name

  def initialize first, last
    self.first_name = first
    self.last_name = last
  end

  def to_s
    "#{first_name} #{last_name}"
  end
end

We’ve just told Ruby that when our user object is converted to a string, we want it to show the full name (first and last). Let’s try it out:

user = User.new 'Jaime', 'Bellmyer'
puts "Hello, #{user}!"    # prints "Hello, Jaime Bellmyer!"

This is one of many instances where knowing how ruby handles things under the hood can allow you create powerful code of your own. Enjoy!

Ruby Enumerable Magic: New Collections

December 8, 2010

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

The Enumerable module offers several methods to create a new collection out of an existing one, by applying code to each item in the original collection.

map

This is the most common and straightforward method in the Enumerable module. It takes the original collection, applies the given block to each item within, and returns an array of the results:

irb(main):001:0> ['joshua', 'gabriel', 'jacob'].map{|name| name.capitalize}
=> ["Joshua", "Gabriel", "Jacob"]
irb(main):002:0> ['joshua', 'gabriel', 'jacob'].map(&:capitalize)
=> ["Joshua", "Gabriel", "Jacob"]

As with all Ruby methods that require a block, we can pass a block itself, or use the unary ampersand operator to pass a symbol that will be converted to a block and run on the object.

sort

This method will return the same, unaltered items in the collection, but in a sorted order. It does this using each item’s <=> method. Objects like strings choose to be sorted alphabetically. Number classes choose to sort numerically. The important thing is that the items in the collection must have this method defined.

Now for an example:

irb(main):003:0> ['joshua', 'gabriel', 'jacob'].sort
=> ["gabriel", "jacob", "joshua"]
irb(main):004:0> [3, 1, 5].sort
=> [1, 3, 5]

You can also pass a block that will be used to sort items, if the default sort is not good enough. For instance, we could choose to sort strings alphabetically, but starting from last letter to first:

irb(main):005:0> ['joshua', 'gabriel', 'jacob'].sort{|a,b| a.reverse <=> b.reverse}
=> ["joshua", "jacob", "gabriel"]

sort_by

A shorter way to sort based on the reversed version of strings like we did above, is to use the sort_by method, and passing either a block or using the unary ampersand operator to pass a symbol:

irb(main):007:0> ['joshua', 'gabriel', 'jacob'].sort_by{|name| name.reverse}=> ["joshua", "jacob", "gabriel"]
irb(main):008:0> ['joshua', 'gabriel', 'jacob'].sort_by(&:reverse)
=> ["joshua", "jacob", "gabriel"]

This is like calling map before calling sort. It will sort the results of the map method, not the collection items themselves.

zip

Honestly, I haven’t found a good use case for this method, but I also didn’t know it existed until I did the research for this article. It has nothing to do with compression – a better name for it might have been “zipper”. Have you ever noticed on the highway when two lanes merge, a zipper effect is created? People instinctively take turns getting in line, much like the teeth of a zipper.

This method works in a similar fashion:

irb(main):010:0> [1, 2, 3].zip([4, 5, 6])
=> [[1, 4], [2, 5], [3, 6]]
irb(main):011:0> [1, 2, 3].zip([4, 5, 6], [7, 8, 9])
=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

It will take the original collection, and merge in the given array or arrays, as shown above. While I don’t yet know a great use case, I’ll keep my eyes peeled now that I understand how it works.

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.


Follow

Get every new post delivered to your Inbox.