Recent notes
RSS feedUse lambda to avoid caching of generated query
If you’re using a named_scope that includes a changing variable you need to wrap it in a lambda to avoid the query being cached and thus becoming unaffected by future changes to the variable, example:
named_scope :translated, :conditions => { :locale => I18n.locale }
Will always return the same locale after the first hit even though I18n.locale might change. So do this instead:
named_scope :translated, lambda { { :conditions => { :locale => I18n.locale } } }
Ugly, but at least it’s working as we expect it…
Memoize will not cache singleton methods
The following does not work:
class PersonType < ActiveRecord::Base extend ActiveSupport::Memoizable class << self def mister find_by_name('Mister') end memoize :mister end
I guess one could extend the superclass, Class, with Memoizable support, but that seems Evil.
Selected parameter needs an int
In order to pre-select an option, you can pass a fourth parameter. However, that parameter MUST be of integer type, so if you’re trying to set selected from the params hash, you must add to_i at the end of it.
<%= select_tag("job[state_id]", options_from_collection_for_select(State.find(:all), "id", "name", params[:state_id].to_i)) %>
String#match will match single token only
>> s = “{{person}} ate {{thing}}”
> “{{person}} ate {{thing}}”
>> r = /{{(.*?)}}/
> {{}}
>> s.match®.captures
> [“person”]
Using String#scan pulls out all tokens you were searching for:
>> s.scan®.flatten
> [“person”, “thing”]
Differences between normal or-assign operator
Differences between this method and normal memoization with ||=:
-
memoize works with false/nil values
-
Potential arguments are memoized
Take the following example:
def allowed? @allowed ||= begin # Big calculation puts "Worked" false end end allowed? # Outputs "Worked" allowed? # Outputs "Worked" again
Since @allowed is set to false (this is also applicable with nil), the ||= operator will move on the the next statement and will not be short-circuited.
When you use memoize you will not have this problem.
def allowed? # Big calculation puts "Worked" false end memoize :allowed? allowed? # Outputs "Worked" allowed? # No output
Now, look at the case where we have parameters:
def random(max=10) @random ||= rand(max) end random # => 4 random # => 4 -- Yay! random(20) # => 4 -- Oops!
Better use memoize again!
def random(max=10) rand(max) end memoize :random random # => 6 random # => 6 -- Yay! random(20) # => 12 -- Double-Yay! random # => 6 -- Head a'splode
Usage
This defines attr_accessors at a class level instead of instance level.
class Foo cattr_accessor :greeting end Foo.greeting = "Hello"
This could be compared to, but is not the same as doing this:
class Bar class << self attr_accessor :greeting end end Bar.greeting = "Hello"
The difference might not be apparent at first, but cattr_accessor will make the accessor inherited to the instances:
Foo.new.greeting #=> "Hello" Bar.new.greeting # NoMethodError: undefined method `greeting' for #<Bar:0x18e4d78>
This inheritance is also not copy-on-write in case you assumed that:
Foo.greeting #=> "Hello" foo1, foo2 = Foo.new, Foo.new foo1.greeting = "Hi!" Foo.greeting #=> "Hi!" foo2.greeting #=> "Hi!"
This makes it possible to share common state (queues, semaphores, etc.), configuration (max value, etc.) or temporary values through this.
Specify controller
If needed, you can also specify a controller.
redirect_to :controller => 'post', :action => 'index'
File class documentation
Most of the File class documentation is located in IO class docs. What you see here is what ‘ftools’ gives you.
Multiple associations on the same level
You can also specify multiple associations that are on the same level, like this:
konata = User.find(1) konata.to_json(:include => [:posts, :roles]) {"id": 1, "name": "Konata Izumi", "age": 16, "created_at": "2006/08/01", "awesome": true, "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"}, {"id": 2, author_id: 1, "title": "So I was thinking"}], "roles": [{"id":1, "user_id":1, "name": "Admin"}, {"id":2, "user_id":1, "name": "Moderator"}]}
Valid options changed in Rails v2.3.0 RC1
The valid options in Rails v2.3.0 RC1 are no longer
:connector and :skip_last_comma, but
:words_connector, :two_words_connector, and :last_word_connector:
>> %w(lorem ipsum dolor sit).to_sentence => "lorem, ipsum, dolor, and sit" >> %w(lorem ipsum dolor sit).to_sentence(:words_connector => ' + ') => "lorem + ipsum + dolor, and sit" >> %w(lorem ipsum).to_sentence(:two_words_connector => ' through ') => "lorem through ipsum" # No effect if more than two words >> %w(lorem ipsum dolor sit).to_sentence(:two_words_connector => ' through ') => "lorem, ipsum, dolor, and sit" >> %w(lorem ipsum dolor sit).to_sentence(:last_word_connector => ' or ') => "lorem, ipsum, dolor or sit"
used for testing
for example, to use the @message instance variable in a view test,
assigns[:message] = @message
you could type
assigns[:foo] = @message
and then message would be available to the view as @foo.
Exceptions while debugging
If the error wasn’t stored in a variable, you can still see it by looking at the global variable $ERROR_INFO.
Extend with an anonymous module
You can extend with an anonymous module for one-off cases that won’t be repeated:
belongs_to :container, :polymorphic => true, :extend => ( Module.new do def find_target ... end end )
The parentheses are important, will fail silently without them.
read_attribute?
The source if this method and other methods indicate the reading of read_attribute (protected)
there is no documentation about the insides of this method, the only entry listed is read_attribute (private) which is deprecated since 1.2.6?
Conflicts with Ruby 1.8.7
Using this with Rails < 2.2.x and Ruby 1.8.7 will create a conflict between ActiveSupport and Ruby, generating the following error:
>> '/'.first NoMethodError: undefined method `[]' for #<Enumerable::Enumerator:0x176b974> from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/string/access.rb:43:in `first'
So if using an older version of Rails with Ruby 1.8.7, use String to instead of String.first
Always a String
Remember that even if you ask for one field that is a number, a String will be returned:
Time.now.strftime("%j") #=> "055"
Specialized versions of find with method_missing
Check ActiveRecord::Base.method_missing for documentation on the family of “magic” find methods (find_by_x, find_all_by_x, find_or_create_by_x, etc.).
ATM does not work in Rails 2.3 Edge
add to test/spec_helper to make it work again…
#spec_helper / test_helper include ActionController::TestProcess
Test if one array includes the elements of another v2
Maybe a bit more readable way to write the previous snippet would’ve been
puts "yay" if [1, 2, 3].all? { |i| (1..9).include?(i) } # => "yay" puts "nope" if [1, 2, 3, 'A'].any? { |i| not (1..9).include?(i) } # => "nope"
Validate an optional URL field
Let’s say that you have an optional URL field to one of your models and you want to validate the URL. You can accomplish this by using the URI library:
require 'uri' # Put this at the beginning of your model file validates_each :url, :allow_blank => true do |record, field, value| begin valid = (URI.parse(value).scheme =~ /https?/) rescue URI::InvalidURIError valid = false end record.errors.add field, "not a valid url" unless valid end
If you want to add even more testing in there, just go ahead. For now, we just check that the link is to a HTTP resource, but you might have other requirements. This will allow stuff like “http://example” since “example” might be a valid intranet domain. If you want to check for a TLD in there, you can do so with a simple regexp.
For more information about the URI library, check out http://apidock.com/ruby/URI/
You can't have many :through with habtm
Imagine the following
a has_many b b has_and_belongs_to_many c a has_many c :through => b
a.b works fine
b.c works fine
a.c throws an error!
has_many :through where the through association is a habtm is not supported in Rails. The error is:
ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid source reflection macro :has_and_belongs_to_many for has_many :stories, :through => :subcategories. Use :source to specify the source reflection
Specifying the source reflection still won’t help you though, because this kind of has_many :through isn’t supported at all.
Nested with_options
You can nest with_options blocks, and you can even use the same name for the block parameter each time. E.g.:
class Product with_options :dependent => :destroy do |product| product.with_options :class_name => 'Media' do |product| product.has_many :images, :conditions => {:content_type => 'image'} product.has_many :videos, :conditions => {:content_type => 'video'} end product.has_many :comments end end
Check if value is included in array of valid values
If you want to check the value of an attribute using an array of valid values, the array has to be defined before the validation, so
validates_inclusion_of :name, :in => VALID_NAMES VALID_NAMES = %w(Peter Paul Mary)
won’t work, but
VALID_NAMES = %w(Peter Paul Mary) validates_inclusion_of :name, :in => VALID_NAMES
will.
CAUTION! :frequency option description is misleading
To use event-based observer, don’t supply :frequency param at all. :frequency => 0 causes JS error.
Use this option only if time-based observer is what you need.
Static and dynamic attachments
You can attach static files directly:
attachment :content_type => "image/jpeg", :body => File.read("someimage.jpg")
and you can also define attachments dynamically by using a block:
attachment "text/csv" do |a| a.body = my_data.to_csv end
Turn off for individual controllers/actions
To disable protection for all actions in your controller use skip_before_filter:
skip_before_filter :verify_authenticity_token
You can also pass :only and :except to disable protection for specific actions, e.g:
skip_before_filter :verify_authenticity_token, :only => :index