Recent notes
RSS feedExplanation about :dependent option
It may seem that :dependent option is only used when the object that has the collection is destroyed, but it is also used every time a associated object is deleted, so if you use
object.collection.delete(associated_object)
your object will be deleted, destroyed or nullified, depending on the value of :dependent option.
With has_many :through associations this option is ignored at least in versions up to 2.1.0, so even if you set :dependent option to :destroy, your join objects will be deleted, not firing any callbacks you have set on destroy events.
If you need to act when your join model is deleted you can use a sweeper or an observer and the association callbacks like this:
# product.rb class Product has_many :categorizations has_many :categories, :through => :categorizations, :before_remove => :fire_before_remove_in_categorizations private def fire_before_remove_in_categorizations(category) categorization = self.categorizations.find_by_category_id(category.id) categorization.class.changed categorization.class.notify_observers(:before_remove, categorization) end end # categorization_sweeper.rb # do not forget to load this sweeper during initialization class CategorizationSweeper < ActionController::Caching::Sweeper observe Categorization def before_remove(categorization) # expire_cache, expire_fragment, whatever end end
One thing you should be aware of it is that you are using before_remove, so you have to be careful because your record may be not be removed (another callback raising an exception or the database not deleting the record) so you can not be sure your object will be delete. Expiring caches is safe because even if your record is not destroyed your cache will be regerated correctly.
You can not use after_remove, because at that point the join model do not exists anymore, so you can not fire its callbacks. But you have the model id and the associated model id, so if you do not need expiring caches maybe you can use this approach (expiring caches can be only done in a sweeper or in a controller, but with after_remove you are bound to your model).
collection update
in the FirmsController
@firm.people.update(params[:people].keys,params.values)
in the View
<% form_for(@firm) do |f| %>
<%= f.error_messages %> <%= f.text_field :name %> <%@firm.people.each do |person|%> <%fields_for "people[]", person do |pf|%> <%= pf.text_field :name %> <%end%> <%= f.submit "Save" %>
<%end%>
collection update
in the FirmsController
@firm.people.update(params[:people].keys,params.values)
in the View
<% form_for(@firm) do |f| %>
<%= f.error_messages %> <%= f.text_field :name %> <%@firm.people.each do |person|%> <%fields_for "people[]", person do |pf|%> <%= pf.text_field :name %> <%end%> <%= f.submit "Save" %>
<%end%>
Examples of Setting the Prefix and Suffix
Both of the following will work for setting the prefix or suffix:
class Mouse < ActiveRecord::Base self.table_name_prefix = 'forum_' end
and
class Mouse < ActiveRecord::Base def self.table_name_prefix 'forum_' end end
Cheking if a number is prime?
It’s a class for generating an enumerator for prime numbers and traversing over them.
It’s really slow and will be replaced in ruby 1.9 with a faster one.
Note: if you just want to test whether a number is prime or not, you can use this piece of code:
class Fixnum def prime? ('1' * self) !~ /^1?$|^(11+?)\1+$/ end end 10.prime?
Optional Argument for detect/find [Not Documented]
detect/find’s optional argument lets you specify a proc or lambda whose return value will be the result in cases where no object in the collection matches the criteria.
classic_rock_bands = ["AC/DC", "Black Sabbath","Queen", "Ted Nugent and the Amboy Dukes","Scorpions", "Van Halen"] default_band = Proc.new {"ABBA"} classic_rock_bands.find(default_band) {|band| band > "Van Halen"} => "ABBA"
or
random_band = lambda do fallback_bands = ["Britney Spears", "Christina Aguilera", "Ashlee Simpson"] fallback_bands[rand(fallback_bands.size)] end classic_rock_bands.find(random_band) {|band| band > "Van Halen"} => "Britney Spears"
Convert a Hash to an Array of Arrays using map
Although you‘ll always have to_a and it‘s faster, this trick is too cool to ignore…
Convert a Hash to an Array of Arrays using Enumerable#map
Convert a Hash to an Array of Arrays using map
Although you’ll always have to_a and it’s faster, this trick is too cool to ignore…
When the block is omitted, collect or map uses this implied block: {|item| item}, which means when applied on an hash without a block, collect/map returns an array containing a set of two-item arrays, one for each key/value pair in the hash. For each two-item array, item 0 is the key and item 1 is the corresponding value.
burgers = {"Big Mac" => 300, "Whopper with cheese" => 450, "Wendy's Double with cheese" => 320} burgers.map => [["Wendy's Double with cheese", 320], ["Big Mac", 300], ["Whopper with cheese", 450]]
see also:
Using any? on Empty Arrays and Hashes
When applied to an empty array or hash, with or without a block, any? always returns false. That’s because with an empty collection, there are no values to process and return a true value.
Testing Arrays for nils with Enumerable#all?
When the block is omitted, all? uses this implied block: {|item| item}.
Since everything in Ruby evaluates to true except for false and nil, using all? without a block on an array is effectively a test to see if all the items in the collection evaluate to true (or conversely, if there are any false or nil values in the array).
Using all? without a block on a hash is meaningless, as it will always return true.
Enumerable#all? and Hashes
When used on a hash and a block is provided, all? passes each key/value pair as a two-element array to the block, which you can “catch” as either:
-
A two-element array, with the key in element 0 and its corresponding value in element 1, or
-
Two separate items, the first being the key, the second being the corresponding value.
Using all? on Empty Arrays and Hashes
When applied to an empty array or hash, with or without a block, all? always returns true. That’s because with an empty collection, there are no values to process and return a false value. so, watch out, if your array or hash is empty for any reason you will get a true which might not be what you expect it to be.
Convert an Array to a Hash
The Hash.[] method converts an even number of parameters to a Hash. (The Hash[] method depends on the Hash class, but don’t confuse the method with the class itself). For example:
Hash['A', 'a', 'B', 'b'] # => {"A"=>"a", "B"=>"b"}
You can convert an array to a hash using the Hash[] method:
array = ['A', 'a', 'B', 'b', 'C', 'c'] hash = Hash[*array] # => {"A"=>"a", "B"=>"b", "C"=>"c"}
The * (splat) operator converts the array into an argument list, as expected by Hash[].
You can similarly convert an array of arrays to a Hash, by adding flatten:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = Hash[*array.flatten] # => {"A"=>"a", "B"=>"b", "C"=>"c"}
This also comes in handy when you have a list of words that you want to convert to a Hash:
Hash[*%w( A a B b C c )] # => {"A"=>"a", "B"=>"b", "C"=>"c"}
Calls attribute setter for each key/value in the hash
This is a convenience to set multiple attributes at the same time. It calls the “setter” method
self.attribute=(value)
for each key in the hash. If you have overridden the setter to add functionality, it will be called.
This also allows you to create non-table attributes that affect the record. For instance, a full_name=() method could parse the string and set the first_name=() and last_name() accordingly.
Testing an options hash receives certain parameters
This method is very useful for testing methods that use the ruby idiom of accepting a hash with configurable options.
class Example def self.find(options = {}) ... end end
We can use hash_including to ensure that certain options are passed in when mocking it.
Example.should_receive(:find).with(hash_including(:conditions => 'some conditions')) Example.find(:conditions => 'some_conditions', :order => 1) # => Passes expectation Example.find(:order => 1) # => Fails expectation
This can also be used to great effect with the anything matcher. For example:
hash_including(:key => anything) hash_including(anything => 'value')
Convert an Array of Arrays to a Hash using inject
Converting an array of arrays to a hash using inject:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = array.inject({}) do |memo, values| memo[values.first] = values.last memo end hash # => {'A' => 'a', 'B' => 'b', 'C' => 'c'}
with password md5 encrypted
If you are afraid to let your plain password on the code, you can do this instead:
require 'digest' class AdminController < ApplicationController before_filter :authenticate def authenticate authenticate_or_request_with_http_basic('Administration') do |username, password| md5_of_password = Digest::MD5.hexdigest(password) username == 'admin' && md5_of_password == '5ebe2294ecd0e0f08eab7690d2a6ee69' end end end
where ‘5ebe2294ecd0e0f08eab7690d2a6ee69’ is the md5 of the word ‘secret’.
You can get your own with this free webservice: <br /> http://qi64.appspot.com/md5/secret (replace ‘secret’ with your secret word).
Use Hpricot to customise error fields
I like to use Hpricot to add error information to my form fields. Here’s an example:
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if html_tag =~ /<(input|label|textarea|select)/ error_class = 'error' nodes = Hpricot(html_tag) nodes.each_child { |node| node[:class] = node.classes.push(error_class).join(' ') unless !node.elem? || node[:type] == 'hidden' || node.classes.include?(error_class) } nodes.to_html else html_tag end end
This will only apply the CSS class ‘error’ to elements that aren’t hidden inputs and don’t already have the error class.
Sample output:
<div> <label class="error" for="user_email">Email</label> <input name="user[email]" size="30" class="error" type="text" id="user_email" value="" /> </div>
Optional classes
This piece of syntax saves me allot of time. Note the if statement.
Code example
content_tag(:div, "Hello World", :class => ("active" if i_am_an_active_item?))
end_year
date_select supports end_year, too.
date_select("user", "birthday", :start_year => 1940, :end_year => Date.current.year - 13)
Update element after remote call
Not mentioned in the documentation, you can add :update option to the remote_form_for and pass the id of element you’d like to update after ajax action as you do with link_to_remote, for example:
<% remote_form_for "comment", :update => "form" } do |f| %> # your form here <% end %>
Or
<% remote_form_for "comment", :update => {:success => "form", :failure => "errors"} do |f| %> # your form here <% end %>
Re: Helper method taking a block
The same using the ActionView::Helpers::TagHelper#content_tag and ActionView::Helpers::CaptureHelper#capture methods:
def render_tree(collection, &block) concat( content_tag(:ul, collection.collect { |item| content_tag(:li, capture(item, &block)) }.join("\n") ), block.binding ) end
The benefit is that it’s easier to improve with html attributes (just add a hash of options to the content_tag call) and it makes just one call to concat (which probably makes it faster).
Insertion/Deletion callbacks
All ActiveRecord associations except for has_many :through support callbacks for pre- and post-insertion/deletion via the following, self-documenting parameters:
Adding to an Association
:before_add
:after_add
Removing from an Association
:before_remove
:after_remove
The flexibility that these callbacks offer is quite handy, but I’ll demonstrate with a silly example: logging each insertion and deletion.
class Ship < ActiveRecord::Base
has_many :pirates, :after_add => :say_hello, :before_remove => :say_goodbye private def say_hello(pirate) STDOUT.write("hello #{pirate.name} ") end def say_goodbye(pirate) STDOUT.write("goodbye #{pirate.name} ") end end
Now, we’ll see confirmation when we add/remove Pirates in the console from our ship (and yes, this must be the ghetto):
>> jolly_roger = PirateShip.new
=> #<Ship id: nil, created_at: nil, updated_at: nil> >> jolly_roger.pirates << Pirate.create(:name => 'Black Bart') hello Black Bart => [#<Pirate id: 1, name: "Black Bart", created_at: "2008-07-29 14:41:13", updated_at: "2008-08-11 11:51:25"> >> jolly_roger.pirates.first.delete goodbye Black Bart => []
Helper method taking a block
Following the similar egzample by autonomous, here’s a simpler version when you just need to write a flexible helper method that takes a block.
For example, suppose you have a method that renders a tree:
def render_tree(ary, &block) concat("<ul>", block.binding) for elem in ary concat("<li>", block.binding) yield elem concat("</li>", block.binding) end concat("</ul>", block.binding) end
You can use it in your view, eg:
<% render_tree(@objects) do |elem| -%> <%= elem.title -%> <%= link_to 'delete', elem -%> <% end -%>
that would return for egzample:
<ul> <li> Test title <a href="delete">/elems/1</a> </li> </ul>
Testing concat
To test such helper methods, use the following pattern (a utility method added to your Rspec/unit test suite:
def render_for(root, options = {}) _erbout = '' render_tree(root, options) do |node| _erbout.concat(node.title) end _erbout end
and test like this (RSpec example):
it "should return abc" do render_for(object).should == 'abc' end
Overview of all routes
To see all defined routes type in your console:
rake routes
This produces (eg.):
reorder_toolbox_items PUT /toolbox_items reord {:controller=>"toolbox_items", :action=>"reorder"} channels GET /channels {:controller=>"channels", :action=>"index"} ... etc.
Overview of all routes
To see all defined routes type in your console:
rake routes
This produces (eg.):
reorder_toolbox_items PUT /toolbox_items reord {:controller=>"toolbox_items", :action=>"reorder"} channels GET /channels {:controller=>"channels", :action=>"index"} ... etc.
polymorphic_url and namespaces and nested resources
You can use polymorphic_url with namespaces and nested resources using array as parameter:
polymorphic_url([:admin, @post])
will return:
admin_post_url(@post)
Overriding the default div class="fieldWithErrors"
By default fields that are invalid are wrapped in:
<div class="fieldWithErrors"> <input type="text" name="blah"> </div>
To override and wrap in spans instead of divs place the following in your environment.rb:
ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "<span class=\"fieldWithErrors\">#{html_tag}</span>" }
or to not use wrapping at all:
ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "#{html_tag}" }