Rails tips and tricks, part 2

I’ve spent a few more minutes gathering up some more of what I learned at the Rails Edge conference, and here’s what I came up with for the second dose of tips and tricks in Rails.

Using returning

returning allows you to do some processing inside of a block of what you intend to return. And it will return the final state (so you can still modify it within the block). For instance, have you ever had code similar to this?

1
2
3
4
5
6
7
8
9
def get_vehicle_report(vehicle_id)
  vehicle = Vehicle.find(vehicle_id)
  report  = Report.new(vehicle_id, vehicle.year, vehicle.description)
  if report.something.nil?
    owner = vehicle.owner
    # ...
  end
  report
end

While it’s nice that Ruby will return the last executed line, saving you an extra word (return), there’s still a better way. Using returning, the method becomes:

1
2
3
4
5
6
7
8
9
def get_vehicle_report(vehicle_id)
  vehicle = Vehicle.find(vehicle_id)
  returning Report.new(vehicle_id, vehicle.year, vehicle.description) do |report|
    if report.something.nil?
      owner = vehicle.owner
      # ...
    end
  end
end

Not overly useful, but I personally think this is a cleaner way to handle the explicit return. And sorry for the poor excuse for a method, but you get the idea.

Using built-in Formatted Dates and Times

I was aware of this prior to the conference, but I’ll post it anyway. Did you know there are a few built-in options to get rid of strftime when formatting a datetime?

1
2
3
4
5
6
7
8
9
10
11
>> now = Time.now
>> now.to_s
#   => "Mon Mar 05 18:30:48 2007"
>> now.to_s(:db)
#   => "2007-03-05 18:30:48"
>> now.to_s(:short)
#   => "05 Mar 18:30"
>> now.to_s(:long)
#   => "March 05, 2007 18:30"
>> now.to_s(:rfc822)
#   => "Mon, 05 Mar 2007 18:30:48 -0600"

I’ve found the :db parameter to be particularly useful when writing fixtures, as it will be formatted to whatever database you happen to be using.

Formatting your own Dates and Times

But what if you don’t like the built-in options? Now this is really useful, and I was not aware of this prior to the conference.

1
2
3
4
5
6
7
8
9
10
11
# environment.rb
ActiveSupport::CoreExtenstions::Time::Conversions::DATE_FORMATS.merge!(
  :simple     => "%d %b %Y",
  :last_login => "Last login: %m/%d/%y at %I:%M %p"
)

# used the same as above
>> now.to_s(:simple)
#   => "05 Mar 2007"
>> now.to_s(:last_login)
#   => "Last login: 03/05/07 at 06:30 PM"

This is a much easier way to change-up your date formats all across your application. Oh yeah, something else I’ve learned that might be useful:

1
2
>> (now..then).to_s(:db)
#   => "BETWEEN '2007-03-05 18:30:48' AND '2007-03-06 08:00:00'"

Enumerable extensions

There’s not really a need for explanation, so I’ll just list a few examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## group_by
#  - Partition a collection based on the value returned by a block
>> (1..20).group_by {|n| n%3}
#   => {0=>[3,6,9,12,15,18],
#       1=>[1,4,7,10,13,16,19],
#       2=>[2,5,8,11,14,17,20]}

## index_by
#  - Convert a collection into a hash where keys are values returned by a block
>> (1..5).index_by {|n| n*10}
#   => {50=>5, 40=>4, 30=>3, 20=>2, 10=>1}

## sum
#  - Sum the values returned by the block
>> [1,3,5,7,9].sum
#   => 25
>> [1,3,5,7,9].sum {|n| n*n}
#   => 165
>> Orders.find(:all).sum {|o| o.total}
#   => 12345.67

Array extensions

I used to despise dealing with arrays, but now I’ve learned to embrace them and all they can do. Here are a few examples you should be aware of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## in_groups_of
#  - Partitions an array into chunks of N elements
>> %w{ a b c d e f g h i j }.in_groups_of(3)
#   => [["a", "b", "c"],
#       ["d", "e", "f"],
#       ["g", "h", "i"],
#       ["j", nil, nil]]

## to_sentence
#  - Like #join on steroids (sometimes useful for listing tags)
>> names = %w{ Ryan Paul Heath }
>> names.to_sentence
#   => "Ryan, Paul, and Heath"
>> names.to_sentence(:connector => "and also")
#   => "Ryan, Paul, and also Heath"

Arrays have now become something I look forward to using. It’s fun to manipulate them using the least amount of code possible.

Another nice thing you should know (and I’m sure you already do) is the ability to do math-like operations on arrays—and they do not have to be numerical.

1
2
3
4
5
6
7
8
9
10
>> bowl_o_fruit  = %w{ apples oranges pears grapes bananas papaya }
>> plate_o_fruit = %w{ bananas pineapples papaya peaches oranges }

## How many fruits are in the bowl and not on the plate?
>> bowl_o_fruit - plate_o_fruit
#   => ["apples", "pears", "grapes"]

## What are the common fruits?
>> bowl_o_fruit && plate_o_fruit
#   => ["oranges", "bananas", "papaya"]

As a real example, at work I’m inserting tree-structured content from the file system into the database (activating it). So naturally I have to compare what’s in the database versus what’s in the file system (Read: get a ‘set difference’ of the files in the database and the directory). Here’s what I’m doing:

1
2
3
4
5
6
7
8
9
10
def view_published
  is_administrator(SUPER_ADMIN)
  directory_courses = read_directory
  active_courses, @activated_courses = [], []
  Course.list_active_courses.each do |ac|
    active_courses << ac.folder_title
    @activated_courses << ac
  end
  @available_courses = directory_courses - active_courses
end

Then I’m able to use the available_courses and activated_courses in the view. That may not be the best way to do it, but it’s far better than anything in C/C++ or something with similar syntax (i.e., Java).

I guess this is long enough now. Hopefully there were a few goodies in there, and if nothing else, you were reminded of something you’ve once read, but forgot to use. Feel free to add any related tips and tricks in the comments, as I often check back to posts like this for reference.

Comments

01

Eltjo on Sat Sep 27 at 02:49AM

I got an error with R 2.1.1 with your solution to extend the DATE_FORMATS in environment.rb

Any suggestions?

eltjo@bash dot nl

02

Eltjo on Sat Sep 27 at 02:55AM

Found it at http://wiki.rubyonrails.org/rails/pages/HowToDefineYourOwnDateFormat

eltjo@bash dot nl

Have something to say?
Please rewrite the image text Are You Human? Hint: Are You Human? Formatting Tips

or

© 2009 Ryan Heath | Site Management A Ruby on Rails production.

Get in Touch