Flowdock

Recent good notes

RSS feed
August 17, 2008
6 thanks

Explanation 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).

August 15, 2008
9 thanks

anything matcher

The anything matcher will match any ruby object:

1.should == anything
nil.should == anything
'string'.should == anything

var.should_receive(:method).with(param1, anything)
August 15, 2008
4 thanks

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?
August 15, 2008
8 thanks

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"
August 15, 2008
3 thanks

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:

August 15, 2008
3 thanks

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.

August 15, 2008
3 thanks

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.

August 15, 2008
4 thanks

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.

August 14, 2008 - (v1_8_6_287)
11 thanks

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"}
August 14, 2008
4 thanks

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.

August 14, 2008
13 thanks

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')
August 14, 2008
5 thanks

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'}
August 14, 2008 - (<= v2.1.0)
7 thanks

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).

August 14, 2008
6 thanks

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?))
August 13, 2008
7 thanks

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 %>
August 13, 2008
7 thanks

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).

August 13, 2008 - (v1.2.6 - v2.1.0)
3 thanks

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 => []
August 12, 2008
6 thanks

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
August 12, 2008 - (>= v2.1.0)
5 thanks

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)
August 12, 2008
12 thanks

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}" }
August 12, 2008
9 thanks

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}" }
August 11, 2008
10 thanks

RE: Using validates_format_of to validate URIs

Further to Olly’s note below, you can also specify the protocol to further limit the valid uri’s, else things like ‘ftp ://someurl.com’ (there’s only a space in there to get it to display on here) would be valid.

validates_format_of :uri, :with => URI.regexp(['http'])
August 11, 2008
8 thanks

Validations

update_attribute will not perform validations checks when the Validation module is included.

If you want to perform validations when updating, use update_attributes instead.

August 10, 2008
4 thanks

converts a Time object to a string in rfc822 / rfc2822 format

>> time = p.updated_at

> Fri Jun 20 14:05:30 +0100 2008

>> time.class

> Time

>> time.httpdate

> “Fri, 20 Jun 2008 13:05:30 GMT”

.

August 10, 2008
10 thanks

New way of calling partials on collections

You can directly call a partial on a collection of objects like this:

<%= render :partial => @users %>

This will call the partial _user.html.erb and populate a local variable ‘user’ within the partial. Then you can display the user partial in this way:

_user.html.erb:

Name: <%= user.name %> <br />
Email: <%= user.email %> <br />

The above render statement is equivalent to this code:

<% for user in @users %>
  <%= render :partial => 'user', :locals => { :user => user } %>
<% end %>
August 8, 2008
18 thanks

Using validates_format_of to validate URIs

You can use the validates_format_of with the regular expression in the URI library ensure valid URIs.

Example

require 'uri'

class Feed < ActiveRecord::Base
  validates_format_of :uri, :with => URI.regexp
end
August 8, 2008
7 thanks

Avoiding duplicate results when you do a join

When you use the :joins option you can get multiple instances of the same item. For example, say you want every User who owns one or more Lists. With the code below, if a user owns 5 lists, the User will show up five times in the results:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :order => 'users.username')

You can cause each found user to appear only once by using the :select option with “DISTINCT”:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :select => 'DISTINCT users.*'
  :order => 'users.username')
August 7, 2008
12 thanks

Function to Determine Layout

Sometimes its nice to have different layouts choosen automagicly:

class ApplicationController < ActionController::Base
 layout :determine_layout
 def determine_layout
   if is_admin?
     "admin"
   else
     "application"
   end
...
end
August 7, 2008 - (>= v2.1.0)
6 thanks

around_filter - working example

More detailed, working example of usage:

class HomeController < ApplicationController   
  around_filter :action1, :action2

  around_filter do |controller, action|
    logger.info "code block before action"
    action.call
    logger.info "code block after action"
  end

  def index
    logger.info "ACTION"
  end       

  private # filters should not be available for external URL

  def action1            
    logger.info "ACTION1 before yield"
    yield "ACTION1" 
    logger.info "ACTION1 after yield"
  end

  def action2            
    logger.info "ACTION2 before yield"
    yield "ACTION2" 
    logger.info "ACTION2 after yield"
  end       
end

Results (in log file):

ACTION1 before yield
ACTION2 before yield
code block before action
ACTION
Rendering home/index
code block after action
ACTION2 after yield
ACTION1 after yield 
August 5, 2008
17 thanks

Multipart form

Don’t forget to add :multipart => true if you have file upload in your form.

<% form_for "user", :html => { :multipart => true } do |f| %>