Friday, 20 February 2009

Moving to Rails 2.2.2

Having just made the move from 2.1.2 to 2.2.2, I hought I wold share some of the issues I came across. There was a bug in 2.1.2 which meant that the image_tag method appended a dot to the end of the image filename, which has been resolved, so that is a good start.

Partials
The way you use partials has been updated in Rails 2.2. While the changes are certainly an improvement, modifying code is not fun (I had about 50 changes to make; thank goodness functional testing will catch your mistakes).

The basic change is that render_partial and render_partial_collection now accept a single hash, rather than a string indicating the file and the important variable, and can be accessed through the render method (thanks to mcclelland for pointing that out). The hash should have the file mapped to the :partial key, and any variables you want in your partial go in a hash mapped to the :locals key (so immediately we can see this is an improvement; you can pass as many of these as you want). These are accessed like local variables in your partial (but really as method calls, I assume). Class variables can be accessed in your partial just as they can in normal views.

To render a collection, just map it to :collection. You can also map any HTML code you want between the elements of your collection to the :spacer key. Rails works out that if :partial is present, it has to render a partial, and if :collection is present, it has to render that partial as a collection, so I would use render for everything (and personally, I would have removed or made private render_partial and render_partial_collection given that we all have to change our code anyway).

Let us see how it has changed. In this first example, a single variable is passed, which I have called :list (so I do not have to change the partial itself):
# Old version
<%= render_partial 'list', @events %>

# New version
<%= render :partial => 'list', :locals => { :list => @events } %>

Here is a table that uses two partials. The first, for the headings, requires no variables. The second is passed an array mapped to :collection:
# Old version
<table width="100%">
<%= render_partial 'table_headings', nil %>
<%= render_partial_collection 'table_row', @samples %>
</table>

# New version
<table width="100%">
<%= render :partial => 'table_headings' %>
<%= render :partial => 'table_row', :collection => @samples %>
</table>


Error pages
There are three error pages in the public directory, which Rails uses when something goes wrong in the production environment (rather than giving error details). I think these were passed through ERb previously. This is no longer the case.

Testing
When running tests, if the system encounters an error, it just gives up, instead of noting the error, and moving on to the next test. I strongly suspect this is a bug.

If a user tries to do something when logged in, but without the required role, he gets redirected to the root page. In the controller, I specify a controller and an action. Previously, I could specify that in the redirect too, but not now. It took some seaching to find that I needed this:
assert_redirected_to "http://test.host/"


Another change in that Rails now generates tests in a different form:
#Old format
def test_my_method
some_code
end

#New format
test "my method" do
some_code
end

The old format still works - no need to change all your old tests. Rather than creating a whole new set of methods, you are now invoking the test method... which then defines a new method by prepending test_ to the name, and then doing exactly the same as it did previously.

Tomcat problem
Having got everything working fine in the development environment, and all tests passing, I was confident to move to the production environmemt...
19-Feb-2009 14:40:58 org.apache.catalina.core.ApplicationContext log
SEVERE: unable to create shared application instance
org.jruby.rack.RackInitializationException: undefined method `cache_template_loading=' for ActionView::Base:Class
...
19-Feb-2009 11:23:00 org.apache.catalina.core.ApplicationContext log
SEVERE: Exception caught
java.lang.NullPointerException
at org.jruby.rack.DefaultRackDispatcher.process(DefaultRackDispatcher.java:32)
at org.jruby.rack.RackFilter.doFilter(RackFilter.java:51)

Oh dear. After much searching (and updating JRuby from 1.1.4 to 1.1.6), I found this forum thread. The solution was to remove the following line from config/environment/production.rb (may also be in config/environment.rb):
config.action_view.cache_template_loading = true


A has_one problem
An obscure bug I came across for the has_one/belongs_to relationship. When I attempted to access one model from the other, I got a NoMethodError exception, and a complaint that I was tryong to do nil.include?. My has_many/belongs_to relationships worked fine. The problem was related to setting the time zone. I have no idea how that can be, but I was not the only one:
http://www.nabble.com/Strange-error...-td21313953.html
http://www.railsformers.com/article/activerecord-timezone-settings-bug

Anyway, the solution is to delete or comment out the old time zone configuration in config/environment.rb, and put in a new one.
  #config.time_zone = 'UTC'
config.active_record.default_timezone = :utc


Additional: Rails 2.3.2 has been out for a while, and I have tried using that. The only noticeable difference was application.rb was renamed to application_controller.rb. Right up until I tried to deploy on Tomcat and it all stopped working. There is a word around here, but I have a suspicion this assumes you have created your project with a more recent version of Rails than I did. Anyway, it did not work for me, so I am back on 2.2.2.

Further addition: Going back to 2.2.2 caused my unit tests to fail mysteriously, with rake just giving up (though functionals and integration tests were fine, and each unit test on its own was fine). It turned out that new models I had added with 2.3.2 had an additional test in test/units/helpers, and this was upsetting the rake task (possibly because I had deleted the associated helper files).

Struggling with Ruby: Contents Page

Friday, 13 February 2009

The Ruby Proc

In Ruby a Proc (short for procedure) is a block of code, bound to a variable (closely related to a block, discussed here). As is always the way in Ruby, a Proc is an object, and so can be created with the new method, though, as discussed later, it is generally preferable to use the lambda method. Here are some examples (expanding on those in the Ruby documention):
# A Proc can have one, none or many arguments
times7 = Proc.new {|n| n * 7 }
statement = Proc.new { 'from statement' }
multiply3 = Proc.new {|x, y| x * y }

The code in the Proc object can be invoked by using the call method.
p times3.call(12)               #=> 36
p times5.call(5) #=> 25
p times7.call(8) #=> 56
p times3.call(times5.call(4)) #=> 60

p statement.call #=> "from statement"
p multiply.call(4, 3) #=> 12

You can pass around Proc objects like any other object:
def gen_times(factor)
return Proc.new {|n| n*factor }
end

class ProcTest
# Class variable is a Proc
@@times13 = Proc.new {|n| n * 13 }

# Method uses various Proc objects
def test
times11 = Proc.new {|n| n * 11 }
p times11.call(3)
p @@times13.call(7)
times2 = gen_times(2)
p times2.call(19)
end

# Method uses a Proc passed as an argument
def test_argument prc
p prc.call(4)
end
end

# ProcTest object instantiated, and methods called
pt = ProcTest.new
pt.test
pt.test_argument Proc.new {|x| x + 5}

Instead of using call, you can invoke the code using square brackets notation. The following are equivalent:
multiply.call(4, 3)
multiply[4, 3]


Proc.new vs lambda
If you call a Proc with too few arguments, Ruby will pad them out with the nil object, so multiply.call(14) above would invoke the Proc code with 14 and nil (which would generate an error in this case). Any extra arguments are simply discarded.

The Kernal has a method lambda (there is also a method proc, which reportedly does the same as Proc.new, but I found it identical to lambda) which will also give a Proc object, but in this case the Proc will raise an ArgumentError is the argument count is wrong.
count_nils_new = Proc.new {|x, y, z|
"#{x.nil?} #{y.nil?} #{z.nil?}"
}

count_nils_lambda = lambda {|x, y, z|
"#{x.nil?} #{y.nil?} #{z.nil?}"
}

# The 4 is quietly discarded
p count_nils_new.call(1, 2, 3, 4)

# The method is sent 1, 2, nil
p count_nils_new.call(1, 2)

# The method is sent nil, nil, nil
p count_nils_new.call

# This is fine
p count_nils_lambda.call(1, 2, 3)

# These will all generate an ArgumentError
p count_nils_lambda.call(1, 2, 3, 4)
p count_nils_lambda.call(1, 2)
p count_nils_lambda.call

Proc objects have an arity method that can be used to determine how many arguments the Proc is expecting.

Using return in a Proc
Using an explicit return in a Proc object created with Proc.new (but not lambda) will cause the calling method to return that value.
def with_return_and_new
prc = Proc.new { return 'This is printed' }
prc.call
'Never seen'
end

def no_return_with_new
prc = Proc.new { 'no_return_with_new' }
prc.call
'This is printed'
end

def with_return_and_lambda
prc = lambda { return 'with_return_and_lambda' }
prc.call
'This is printed'
end

p with_return_and_new
p no_return_with_new
p with_return_and_lambda

It seems that the Proc object is returning not just the value, but the return command too. In general, therefore, it is a bad idea to use an explicit return inside a Proc object defined with Proc.new (if only because the effect will be confusing to anyone with out a good understanding of Ruby peculiarities). This is discussed more here:
http://innig.net/software/ruby/closures-in-ruby.rb

API for the Proc object:
http://www.ruby-doc.org/core/classes/Proc.html

See also:
http://eli.thegreenplace.net/2006/04/18/understanding-ruby-blocks-procs-and-methods/
http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html

Struggling with Ruby: Contents Page

Sunday, 8 February 2009

The Singleton in Ruby

When I first read about singletons in Ruby, I assumed this was a reference to the singleton pattern; perhaps the classic example of a pattern, and in my opinion not much use (actually Ruby has a Singleton module for doing just this). However, in Ruby the singleton is something else entirely.

The singleton is a method that is attached to a single instance of a class. Here is a simple example:
s1 = 'what'
s2 = 'where'
s3 = 'when'

def s1.hello
p "Hello world"
end

class << s2
def hello
p "Hello world"
end
end


s1.hello
s2.hello
begin
s3.hello
rescue NoMethodError
p $!
end

p s1.singleton_methods
# => ["hello"]
p s2.singleton_methods
# => ["hello"]
p s3.singleton_methods
# => []

Three strings are created. The first two have singleton methods added to them in slightly different ways. The singleton methods can be invoked on that specific instance, but not on any other object of that class. Finally, the singleton_methods method is invoked to show how these methods can be listed.

Technically, the object itself does not get a new method. Objects can only hold variables, not methods. Rather, the object has a metaclass, or virtual class, and this metaclass is where the new method is. This is what class << s2 is accessing.

Class methods
You can also add singleton methods to the class, where they become class methods. The logic here is that the class is itself an object, and these methods are being added to a single instance of the Class class. This code illustrates three ways to add a singleton to a class.
def String.goodbye1
p "Goodbye world (1)"
end

class String
def self.goodbye2
p "Goodbye world (2)"
end
end

class String
class << self
def goodbye3
p "Goodbye world (3)"
end
end
end

p String.singleton_methods
# => ["goodbye3", "goodbye2", "goodbye1"]

It is interesting to note that "String" is actually a constant that holds a Class object, and this is why it is capitalised.

Note: Some believe that class << self is bad; this suggests there may be more going on here.

The singleton_methods method returns all methods for the class that are not inherited from a superclass. Use singleton_methods false to exclude methods from modules.

See also:
http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html

http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html

Struggling with Ruby: Contents Page

Tuesday, 3 February 2009

Modules

A module is a collection of methods and constants, much like a method. However, a module cannot be instantiated. Instead, a module is added to an existing class or object to create a "mixin". Here is an example of a module, with both a method and a constant defined:
module TestModule
TEST_CONST = 1000

def hello
p "Hello"
end
end

The are two ways to add a module to your class, using the keywords include or extend:
class TestClass1
include TestModule
end

class TestClass2
extend TestModule
end

The include statement causes all the methods in the module to be added as instance methods, and also allows the constants to be accessed through the method. In contrast, the extend statement adds all the methods as class methods, and does nothing with the constants. Let us look at the constants first:
p TestModule::TEST_CONST
p TestClass1::TEST_CONST
begin
p TestClass2::TEST_CONST
rescue NameError
p $!
end

The class methods. TestClass2 had the module added using extend, so the method in the module is a class method:
begin
TestClass1.hello
rescue NoMethodError
p $!
end
TestClass2.hello

Finally the instance methods. TestClass1 had the module added using include, so the method in the module is an instance method. Note that you can add a module to an object at run time, but in that case the extend keyword is used:
# Create some instances
tc1 = TestClass1.new
tc2 = TestClass2.new
tc2too = TestClass2.new

# Methods accessible at the instance level
tc1.hello
begin
tc2.hello
rescue NoMethodError
p $!
end

# Module added at runtime
tc2.extend TestModule
tc2.hello
begin
tc2too.hello
rescue NoMethodError
p $!
end

I think the logic here is that extend is used for singletons; a single class or a single instance of the class.

Instantiation
Ruby will invoke the included or extended methods in your module, if they exist, when the module is included or extended respectively. These have to be defined as class methods - even though this is not a class. As far as I know, this is the only time you do tat for a module. Both methods should take a single parameter; the object or class to which the module is being added. An example:

module TestModule
def self.included base
p "I am being included by #{base}"
end

def self.extended base
p "I am being extended by #{base}"
end
end

class TestClass1
include TestModule
end

class TestClass2
extend TestModule
end

p 'Classes now defined'

tc2 = TestClass2.new
tc2.extend TestModule

# Output
#
# => "I am being included by TestClass1"
# => "I am being extended by TestClass2"
# => "Classes now defined"
# => "I am being extended by #<TestClass2:0x987a33>"

See also:
http://www.juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/

Struggling with Ruby: Contents Page

Saturday, 24 January 2009

The View Part 4 - Using Select in Forms

Last time around, I discussed forms, I am now going to focus on the select widget, as I found this particular mysterious at first. I am going to assume you have already read the page on forms.

Using f.select
The easiest way to use select is inside a FormBuilder block. While most FormBuilder code is in form_helper.rb, the select is in form_options_helper.

Suppose you have a column for integers in your database table (let us say "status", for example, in a table called "posts"). You want the user to be able to select an option from a drop-down list to set the value of the column. First, you need a set of options, and this is best defined in your model. This could be an array or a hash. If you use a hash, you can assign values to options yourself, but generally an array will be sufficient. I am going to use a hash, so in posts.rb, there will be this constant defined:
STATUS_OPTIONS = {'Read' => 1, 'Unread' => 2, 'Deleted' => 12}

Then, in the view, you just need a f.select. It might look something like this (with other fields removed for clarity):
<% form_for(@post) do |f| %>
<p>
Please select:
<%= f.select(:status, Post::STATUS_OPTIONS) %>
</p>
<p>
<%= f.submit "Update" %>
</p>
<% end %>

The f.select takes two parameters, the first being the name of the column (or any method as a symbol), the second is the array or hash. That is all you need to do. Rails will handle the saving and setting of the options for you.

Be aware that if you use an array, Rails will ignore the index. The value returned from the column method must be a value in the array, rather than a number for the index, and similarly what is set will be value, not the index. Personally, I found that annoying, so created a new method that would accept an array, and build a select element using the indices with the values.
# See actionpack/lib/action_view/helpers/form_options_helper.rb
module ActionView
module Helpers
class FormBuilder
def array_select(method, choices, options = {}, html_options = {})
h = {}
choices.each_index { |i| h.store(choices[i], i)}
@template.select(@object_name, method, h, objectify_options(options), @default_options.merge(html_options))
end
end
end
end


Using select_tag
The select_tag method is a bit poor, as it does not accept an array or hash, demanding instead a string with each entry surrounded by "<option>" and "</option>". Why it was not designed to accept an array and a value I cannot imagine. Instead, you have to use the options_for_select helper method, like this:
<%= select_tag("post[status]",
options_for_select(Post::STATUS_OPTIONS,
@post.status))
%>

However, there is also a select method that does the job.
<%= select(:post, :status, Post::STATUS_OPTIONS) %>

I imagine this was a later addition to Rails.

NOTE: I have read that select_tag should be used for GET commands, and select for POST (see here).

Submit on change to a select
Sometimes, you want the user to be able to select from a list, and to be taken straight to a new web page, without having to click on a button. This is pretty easy, with a bit of JavaScript. I set up a helper method to do that:
def submit_on_change
{:onchange => 'submit()'}
end

You can then add that method to your select or select_tag. Note that select takes two optional hashes, and you want to use the second, so I have put in an empty hash in that case.
<%= f.select :status, Post::STATUS_OPTIONS, {}, submit_on_change %>

<%= select_tag "post[status]",
options_for_select(Post::STATUS_OPTIONS,
@post.status),
submit_on_change %>


Another select example
Here is an example of using a select box to choose a web page. The web pages are set up in the model:
HELP_PAGES = {'Main' => 'index', 'Ruby Basics' => 'ruby', 'Ruby Classes' => 'classes'}

In the view, this code will set up the select (note that there is no submit button; be aware that any user with JavaScript disabled will not be able to navigate using this):
<% form_tag( {:action => :help, }, :method => :get) do %>
<%= select_tag "page", options_for_select(Post::HELP_PAGES),
submit_on_change %>
<% end %>

In the controller, the chosen page is handled:
def help
@page = params[:page]
# etc...
end


The collection_select method
Let us suppose you have one table associated with another, and want to be able to have the user select a record from one table for a record in the other. Let us go back to the archetypal blog application: Posts can be associated with a category (so a post belongs_to a category; a category has_many posts and the post table has a column called "category_id"). The user clicks on new post, writes his throughts, then can select from a list of categories from a drop-down list. How do we create such a thing?

This is what the collection_select method is for. As with select, there are two forms, one associated with a FormBuilder object, the other not.
<%= f.collection_select(:category_id,
Category.find(:all), :id, :name) %>
<%= collection_select(:post, :category_id,
Category.find(:all), :id, :name) %>

Note that the second form requires an extra parameter specifying the table we are modifying. The next parameter, :category_id in the example, is a method that is called to set the value; generally that will be the name of the column in the table you are modifying.

The next parameter is an array (kind of) of ActiveRecords; this is the list of options that will be available to the user. The next parameter, :id, is the method used by Rails to get values for each option of the select, while the next parameter determines the display name for the options. In effect, these two are the column names in the other table. To generate the list of options in the example, Rails iterates through the array of categories, and for each member it calls the "id" method to set the value, and the "name" method to set the text that is displayed.

As with select, there are two optional parameters for hashes of options.

As it turns out, you are not restricted to ActiveRecords. I tried it with this TestClass:
class TestClass
def initialize id, name
@id = id
@name = name
end
attr_reader :id, :name
end

Setting up an array:
  TEST_ARRAY = [
TestClass.new(12, 'First of all'),
TestClass.new(54, 'Middle'),
TestClass.new(32, 'Last and finally'),
]

And then using the collection_select like this:
<%= collection_select(:comment, :post_id,
Comment::TEST_ARRAY, :id, :name) %>

However, I have no idea why you would want to do that, rather than using a hash with select.

Selecting Dates
There are are set of methods to help you handle dates. If you are inside a FormBuilder block, just use date_select like this (Rails will even do this for you when you generate views):
<%= f.date_select :birthday %>
ActiveRecord will handle the rest. Outside FormBuilder you can use date_select or select_date (why not date_select_tag, which would be more consistent?). I found date_select easier to set up, but the values in the hash are not trivial to handle. I found some useful code here:

# Reconstruct a date object from date_select helper form params
def build_date_from_params(field_name, params)
Date.new(params["#{field_name.to_s}(1i)"].to_i,
params["#{field_name.to_s}(2i)"].to_i,
params["#{field_name.to_s}(3i)"].to_i)
end

date = build_date_from_params(:published_at, params[:article])


The API:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html

See also:
http://shiningthrough.co.uk/Select+helper+methods+in+Ruby+on+Rails

Struggling with Ruby: Contents Page

Friday, 23 January 2009

The View Part 3 - Using Forms

The basic component of a form for a view on Rails is the form_for method in ActionView::Helpers::FormHelper. This takes an object as an argument, and applies that object to the form components in a block. The variabe f is a FormBuilder object.
form_for(@post) do |f|
f.label :name
f.text_field :name
f.label :body
f.text_area :body
f.submit "Update"
end

Actually, it is not quite as simple as that, as you need to mix in the HTML, so in your view, the above becomes:
<% form_for(@post) do |f| %>
<p>
<%= f.label :name %>

<%= f.text_field :name %>
</p>
<p>
<%= f.label :body %>

<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Update" %>
</p>
<% end %>

It is a good idea to put in <%= f.error_messages %> as the second line in your form (and indeed Rails will do this for you), as this will display error messages for you (for example, if the user's input fails validation).

There are a number of methods available for the FormHelper object that will place widgets on the web page. These ones are listed in the API:
check_box
file_field
hidden_field
label
password_field
radio_button
text_area
text_field

The check_box method should be associated with a Boolean field. The hidden_field is useful for data you do not want the user to have access to, but which has to saved with the rest of the fields (but if a field is missing from the form, the record will retain the previous value).



Using radio_button
Suppose you have a column for integers in your database table (let us say "status", for example, in a table called "posts"). You want the user to be able to select an option.

First, you need a set of options, and this is best defined in your model. I am going to use a hash, so in posts.rb, there will be this constant defined:
STATUS_OPTIONS = {'Read' => 1, 'Unread' => 2, 'Deleted' => 12}

Then, in the view, you just need something like this:
<% Post::STATUS_OPTIONS.each_pair do |k, v| %>
<%= k + f.radio_button("status", v) %>
<% end %>

Ruby will iterate through the hash, Post::STATUS_OPTIONS. For each element, it will create a radio button.

Buttons that do not submit
Sometimes you want a button that does not submit the form. You can still use the submit method, just set the :type to map to "button. Here is an example of how to create a button that will invoke a JavaScript function called calc.
<%= f.submit 'Calc', { :onclick => 'calc()', :type => "button", } %>


Other Forms
Sometimes you want to create a form that is not associated with a particular record, such as a search form. For this you use methods from the ActionView::Helpers::FormTagHelper module. The same sort of methods are available, but with _tag as a suffix.

Use form_tag to create the basic form. You can give it a URL segment, or the usual URL parameters. The form_tag method seems to default to POST, so for this show example, I had to specify the method as GET.
<% form_tag({:action => :show, :id => 1}, :method => :get) do %>
<%= submit_tag 'Show 1' %>
<% end %>

Or using the URL:
<% form_tag '/posts/update/1' do %>
<%= submit_tag 'Update 1' %>
<% end %>

Here is a more interesting example, doing the same as the first example (just for illustration - there is no good reason to not do it the other way), with radio buttons. Note that the method is now PUT for update. Also, the tag name is of the form post[status] (for the model called "post", and the field called "status"). When Rails receives the request, the value of status will be put in a hash called post, which will go inside the params hash. This is the standard Rails technique, and is what happens in the earlier example, behind the scenes. This means the controller does not need changing.
<% form_tag({:action => :update, :id => @post.id},
:method => :put) do %>
<p>
<%= label_tag 'Name' %>

<%= text_field_tag 'post[name]', @post.name %>
</p>
<p>
<%= label_tag 'Body' %>

<%= text_area_tag 'post[body]', @post.body %>
</p>
<% i = 0
Post::STATUS_OPTIONS.each_pair do |k, v| %>
<%= k + radio_button_tag('post[status]', v,
@post.status == v) %>
<% end %>
<%= submit_tag "Update" %>
<% end %>

Hopefully tomorrow I will post about select.

The API:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html

Struggling with Ruby: Contents Page

Wednesday, 21 January 2009

Ruby Methods Part 4 - Calling Methods

Ruby has three (at least) ways to call a method on an object. This code illustrates their use. First, a class is defined with four methods, one of which is private, one is a class method and another takes a parameter. The class is instantiated, and then the methods accessed using the various techniques.
# Define a class with three methods
class MethodTest
def public_method
p 'In public_method'
end

def method_with_argument x
p "In public2_method - #{x}"
end

def self.class_method
p 'In class_method'
end

private
def private_method
p 'In private_method'
end
end

# Create instance of class
mt = MethodTest.new

# Invoke methods with the dot operator
mt.public_method
mt.method_with_argument('hello')
MethodTest.class_method
begin
mt.private_method
rescue NoMethodError
p $!
end

# Invoke methods with send
mt.send :public_method
mt.send :method_with_argument, 'Hello'
MethodTest.send :class_method
mt.send :private_method

# Invoke methods as objects
mt.method(:public_method).call
mt.method(:method_with_argument).call 'Hello'
MethodTest.method(:class_method).call
mt.method(:private_method).call

The dot operator
I guess this is the most familiar technique, and is common to other languages, like Java and C++. Private methods are not accessible, and instead a NoMethodError is raised (and a message is produced informing you that the method is private).

The send method
The send method is a part of the Object class and so is available to all objects. It invokes the named method (must be a symbol). As the named method is now being invoked from within the object, this means that all the methods are available, including private and protected methods.

Note that you can also use __send__, in case you have overwritten the send method. Overwriting __send__ will generate a warning that it is a bad idea; Rails uses __send__ a lot, for example, on the assumption that no one would be stupid enough to over-write it.

A good example of using the send method is where you want to access database columns in a large table, where the columns are numbered sequentially, say column0, column1, column2, etc. Rails will handle the generation of methods that will allow @mytable.column1 = 5 and x = @mytable.column1, but how do you get the total of the columns? Let us suppose ten such columns.
total = 0
10.times {|i|
total += send("column" + i.to_s)
}

Strangely, Ruby is quite a stickler for types. In Java or C# you could write "column" + i, but Ruby requires the to_s method to explicitly convert to a string. The send method sends a message (in OO talk) to the named method, column0, column1, etc. To assign a value, you need to append an equals sign to the method name. This loop assigns zero to each column:
10.times {i
send("column" + i.to_s + "=", 0)
}

The send method is a variable length method; just send it the right number of parameters for the method you are invoking.

The method object
Everything in Ruby is an object, including methods. You can access the method object with my_object.method(:my_method). Here is an example of using the method object for the length method of string
s = "string"
puts s.method(:length).class # => Method
puts s.method(:length).call # => 6
puts s.method(:length).methods.sort

# => ["==", "===", "=~", "[]", "__id__", "__send__", ...

As seen earlier, the method that the object represents can be invoked using the call method. As with send, this allows you to access private and protected methods. By the way, you can convert a method to a Proc using the to_proc method.

One important practical difference between using send and using the Method object is that the latter will only work on methods that are actually defined. It will not work for method calls that go though method_missing, including calls to column names for ActiveRecord or calls to the various find methods in Rails. This is because the method has to be defined to become an object.

API for the Method object:
http://www.ruby-doc.org/core/classes/Method.html


Struggling with Ruby: Contents Page