What's New in Edge Rails: Standard Internationalization Framework 5

Posted by ryan
at 12:41 PM on Tuesday, July 22, 2008



Internationalization (i18n) is a tough nut to crack and has long been handled in Rails by a variety of plugins. Thanks to a concerted effort by the Rails i18n team we now have a standardized framework for providing internationalization.

Rather than rehash what’s already out there, head over to Sven’s writeup of the new Rails Internationalization framework for the skinny.

It’s important to note that this does not provide actual language translations but merely a way for you to plug in other translations and internationalization implementations into your app.

tags: ruby, rubyonrails

What's New in Edge Rails: Get Your Metaclass 5

Posted by ryan
at 7:48 PM on Saturday, July 19, 2008



The ActiveSupport library now has a little nugget worth mentioning – a metaclass accessor available everywhere as metaclass.

Not sure what a metaclass (or singleton class, or eigen class) is? Let Ola explain it to you.

tags: ruby, rubyonrails

What's New in Edge Rails: Nested Model Mass Assignment 23

Posted by ryan
at 7:30 PM on Saturday, July 19, 2008



Nested models (nested forms by another name) describe the scenario when you want to create and modify values of nested attributes through a containing model. For instance, if you have an user model with many phone numbers:

1
2
3
4
5
6
7
8
9
class User < ActiveRecord::Base
  validates_presence_of :login
  has_many :phone_numbers
end

class PhoneNumber < ActiveRecord::Base
  validates_presence_of :area_code, :number
  belongs_to :user
end

you may want to be able to create the user and a group of phone numbers at the same time. This is what this looks like with the new mass assignment functionality of Rails keyed off of the :accessible option of the association declaration (:phone_numbers, in this case).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User < ActiveRecord::Base
  validates_presence_of :login
  has_many :phone_numbers, :accessible => true
end

ryan = User.create( {
  :login => 'ryan',
  :phone_numbers => [
    { :area_code => '919', :number => '123-4567' },
    { :area_code => '920', :number => '123-8901' }
  ]
})

ryan.phone_numbers.count #=> 2

A single hash of values being sent to User.create results in both a new user object and new associated phone numbers. Previously, you would have had to manually create your own phone_numbers= setter method on user to get this same functionality:

1
2
3
4
5
6
7
8
9
10
11
class User < ActiveRecord::Base

  ...

  def phone_numbers=(attrs_array)
    attrs_array.each do |attrs|
      phone_numbers.create(attrs)
    end
  end

end

Mass assignment now gives you this functionality for free.

This may not look like much, but it is a step in the direction of letting you use nested forms. Consider a user registration form where a user can enter their login name and their phone numbers in the same form (through the use of fields_for which will bundle nested model attributes into a single form):

1
2
3
4
5
6
7
8
<% form_for @user do |f| %>
  <%= f.text_field :login %>
  <% fields_for :phone_numbers do |pn_f| %>
    <%= pn_f.text_field :area_code %>
    <%= pn_f.text_field :number %>
  <% end %>
  <%= submit_tag %>
<% end %>

This form, when submitted to the following standard RESTful UserController, will correctly create the user and its associated phone numbers through the beauty of mass assignment.

1
2
3
4
5
6
7
8
9
10
class UserController < ApplicationController

  # Create a new user and their phone numbers with mass assignment
  def new
    @user = User.create(params[:user])
    respond_to do |wants|
      ...
    end
  end
end

Mass assignment can be used on all association types – :belongs_to, :has_one, :has_many and :has_and_belongs_to_many as long as the :accessible => true option is specified.

This is a very convenient addition to ActiveRecord, but the real zinger will come with full nested form support when you can create, update and delete these nested models directly from what is pushed down in the parameter hash of a form submission. This would allow for the functionality in this complex forms screencast with minimal hassle. What a fine day that would be.

tags: ruby, rubyonrails

What's New in Edge Rails: Easy Memoization

Posted by ryan
at 8:32 PM on Tuesday, July 15, 2008



Most people will recognize the pattern of memoization to provide a basic caching mechanism (that’s not a misspelling, it really doesn’t have an ‘r’) :

1
2
3
4
5
6
class Person < ActiveRecord::Base
  def social_security
    @social_security ||= decrypt_social_security
  end
  ...
end

The big problem with this common type of memoization is that you’ve littered your method implementation with caching logic. Caching is best applied in a transparent manner – and ActiveSupport now lets you easily insert memoization into your classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person < ActiveRecord::Base

  def social_security
    decrypt_social_security
  end

  # Memoize the result of the social_security method after
  # its first evaluation (must be placed after the target
  # method definition).
  #
  # Can pass in multiple symbols:
  #  memoize :social_security, :credit_card
  memoize :social_security
  ...
end

@person = Person.new
@person.social_security  # decrypt_social_security is invoked
@person.social_security  # decrypt_social_security is NOT invoked

memoize transparently aliases the method and stores the value of your method’s first evaluation in an instance variable – giving you the same functionality of the unrefined var ||= ... implementation with much less clutter.

This implementation also handles the case where you freeze your User object before ever calling the social_security method. Using conventional memoization, calls to social_security would give you a Can’t modify instance variable error instead of happily evaluating as you would want.

This implementation also will not execute the target method more than once if the original execution resulted in nil or false – which is a flaw of the conventional pattern. You can, however, force the target method to be invoked with the optional reload parameter:

1
2
3
# Force invocation of target method, i.e. "decrypt_social_security"
# is invoked independent of there being a cached value
@person.social_security(true)

So start giving memoize some play – it’s just the right thing to do.

Update: You can now use unmemoize_all and memoize_all to undo and redo your memoized properties.

tags: ruby, rubyonrails

What's New in Edge Rails: Easy Join Table Conditions

Posted by ryan
at 4:38 PM on Monday, July 07, 2008



For an application with anything above a moderate level of domain complexity it’s quite likely that you’ve had to perform a query utilizing a join table:

1
2
3
4
5
6
7
8
9
10
11
class Article < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :articles
end

# Get all the users that have published articles
User.find(:all, :joins => :article,
  :conditions => ["articles.published = ?", true])

It always makes me feel slightly embarrassed to have to resort to using String snippets to specify my query logic, and this little bit is no exception. Well, now we can specify conditions on a join table in a more concise manner:

1
2
3
# Get all the users that have published articles
User.find(:all, :joins => :article,
  :conditions => { :articles => { :published => true } })

Note how you’re able to specify the join-table conditions as a hash whose key corresponds to the table or association name of the join table? You can now let Rails worry about forming the correct SQL condition even across complex joins.

However, don’t let the ease of this feature make you use it over a properly associated domain-model. For instance, this join query:

1
2
3
# Get all articles for a given user
Article.find(:all, :joins => :user,
  :conditions => { :users => { :id => @user.id } })

is more appropriately represented as:


@user.articles

tags: ruby, rubyonrails

What's New in Edge Rails: Custom Length Validation Tokenizer

Posted by ryan
at 8:41 PM on Sunday, July 06, 2008



The various ActiveRecord validation methods are some of the hardest working bits of ActiveRecord and yet they get so little love. In what may be a little-noticed change, you can now specify how validates_length_of parses the attribute value. While the default behavior is to just count the number of characters in the attribute value, you can now also do something like this to make sure the value has a minimum number of words:

1
2
3
validates_length_of :article, :minimum => 10,
  :too_short => "Your article must be at least %d words in length.",
  :tokenizer => lambda {|str| str.scan(/\w+/) }

Just pass a proc to the :tokenizer option that chops the attribute value into its relevant parts for measure by the length validation routine. You can do most anything, like ensure a minimum or maximum number of vowels, digits etc…

tags: ruby, rubyonrails

What's New in Edge Rails: Collection Partial Variable Naming

Posted by ryan
at 8:12 PM on Sunday, July 06, 2008



From the little-but-useful department comes a new addition to Rails that lets you explicitly name the local variable exposed to a partial template when using a collection partial. So, for instance, in this statement:


render :partial => 'employees', :collection => @workers, :as => :person

each element of the workers collection will be exposed as person within the employees template. No longer are you hostage to your template name.

tags: ruby, rubyonrails