Recent good notes

RSS feed
November 16, 2008 - (>= v2.1.0)
3 thanks

Application Helper for Fading Flash Messages

A simple helper method for showing the flash message. Includes optional fade in seconds (view needs javascript_include_tag defaults if you desire fade effect):

  def show_flash_message(options={})
    html = content_tag(:div, flash.collect{ |key,msg| content_tag(:div, msg, :class => key) }, :id => 'flash-message')
    if options.key?(:fade)
      html << content_tag(:script, "setTimeout(\"new Effect.Fade('flash-message');\",#{options[:fade]*1000})", :type => 'text/javascript')
    end
    html
  end

simply call in your views then using:

  <%= show_flash_message(:fade => 4) %>
October 23, 2008
5 thanks

Customizing attribute names in error messages

By default, the error messages translate the names of the attributes through String#humanize. The way to to change that is to override the ActiveRecord::Base.human_attribute_name method. For example, if you want to name a column in your database as <tt>:www_url</tt> and you want to say "Website" instead of "Www url" in the error message, you can put this into your model: class Person < ActiveRecord::Base def self.human_attribute_name(attribute_key_name) if attribute_key_name.to_sym == :www_url "Website" else super end end end Currently this seems to be the cleanest and easiest way. Unfortunately, human_attribute_name is deprecated and may stop working in a future release of Rails.
October 15, 2008
12 thanks

List of status codes and their symbols

Note that the :status option accepts not only an HTTP status code (such as 500), but also a symbol representing that code (such as :created), if that makes more sense to you. Here's a list of which symbols map to which numbers (derived from ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE):

 100 = :continue
 101 = :switching_protocols
 102 = :processing
 200 = :ok
 201 = :created
 202 = :accepted
 203 = :non_authoritative_information
 204 = :no_content
 205 = :reset_content
 206 = :partial_content
 207 = :multi_status
 226 = :im_used
 300 = :multiple_choices
 301 = :moved_permanently
 302 = :found
 303 = :see_other
 304 = :not_modified
 305 = :use_proxy
 307 = :temporary_redirect
 400 = :bad_request
 401 = :unauthorized
 402 = :payment_required
 403 = :forbidden
 404 = :not_found
 405 = :method_not_allowed
 406 = :not_acceptable
 407 = :proxy_authentication_required
 408 = :request_timeout
 409 = :conflict
 410 = :gone
 411 = :length_required
 412 = :precondition_failed
 413 = :request_entity_too_large
 414 = :request_uri_too_long
 415 = :unsupported_media_type
 416 = :requested_range_not_satisfiable
 417 = :expectation_failed
 422 = :unprocessable_entity
 423 = :locked
 424 = :failed_dependency
 426 = :upgrade_required
 500 = :internal_server_error
 501 = :not_implemented
 502 = :bad_gateway
 503 = :service_unavailable
 504 = :gateway_timeout
 505 = :http_version_not_supported
 507 = :insufficient_storage
 510 = :not_extended
October 10, 2008
3 thanks

Implemented in database adapters

These methods are not implemented in the abstract classes. Instead, all database adapters implement these separately, if the feature is supported.

October 9, 2008 - (v1.2.0 - v2.1.0)
4 thanks

Rendering nothing

If your controller action does not explicitly call render, Rails will, by default, attempt to locate and render the template corresponding to the action. It's not uncommon, for example with Ajax calls, to want to render nothing. This will circumvent the default rendering and prevent errors on missing templates. To render nothing simply do the following:

  render :nothing => true

Its important to note that this isn't the same as returning no HTTP response. In fact, this results in an HTTP response with a status code of 200 OK being sent back with a blank content body. Why does it matter? Well, you can still test your controller by asserting that a :success response was returned.

October 1, 2008
3 thanks
September 26, 2008 - (v1.2.6 - v2.1.0)
4 thanks

Pass the observed fields value as a parameter in the Ajax request

Use encodeURIComponent with with to pass an encoded value as a parameter (POST or GET) of the AJAX request. For example:

  <%= observe_field :company_id,
             :url  => {:action => 'facilities', :only_path => false},
             :with => "'company=' + encodeURIComponent(value)" %>

Also, setting only_path => false for the URL ensures that the full URL (including host and protocol) is used for the AJAX request.

September 25, 2008
4 thanks

All methods

  create_table :table do |t|
    t.column # adds an ordinary column. Ex: t.column(:name, :string)
    t.index # adds a new index.
    t.timestamps
    t.change # changes the column definition. Ex: t.change(:name, :string, :limit => 80)
    t.change_default # changes the column default value.
    t.rename # changes the name of the column.
    t.references
    t.belongs_to
    t.string
    t.text
    t.integer
    t.float
    t.decimal
    t.datetime
    t.timestamp
    t.time
    t.date
    t.binary
    t.boolean
    t.remove
    t.remove_references
    t.remove_belongs_to
    t.remove_index
    t.remove_timestamps
  end
September 25, 2008
5 thanks

All methods

  change_table :table do |t|
    t.column # adds an ordinary column. Ex: t.column(:name, :string)
    t.index # adds a new index.
    t.timestamps
    t.change # changes the column definition. Ex: t.change(:name, :string, :limit => 80)
    t.change_default # changes the column default value.
    t.rename # changes the name of the column.
    t.references
    t.belongs_to
    t.string
    t.text
    t.integer
    t.float
    t.decimal
    t.datetime
    t.timestamp
    t.time
    t.date
    t.binary
    t.boolean
    t.remove
    t.remove_references
    t.remove_belongs_to
    t.remove_index
    t.remove_timestamps
  end
September 16, 2008
5 thanks

print standard-looking messages during migration

Within a migration file you can use the say_with_time method to print out informational messages that match the style of standard migration messages. See the say method also.

  say_with_time "migrate existing data" do
    # ... execute migration sql ...
  end
  #=> "-- migrate existing data"
  #=> "   -> 0.0299s"
September 12, 2008
13 thanks

Readable strftime

%a - The abbreviated weekday name (``Sun'')

%A - The full weekday name (``Sunday'')

%b - The abbreviated month name (``Jan'')

%B - The full month name (``January'')

%c - The preferred local date and time representation

%d - Day of the month (01..31) %H - Hour of the day, 24-hour clock (00..23)

%I - Hour of the day, 12-hour clock (01..12)

%j - Day of the year (001..366)

%m - Month of the year (01..12) %M - Minute of the hour (00..59)

%p - Meridian indicator (``AM'' or ``PM'')

%S - Second of the minute (00..60)

%U - Week number of the current year, starting with the first Sunday as the first day of the first week (00..53)

%W - Week number of the current year, starting with the first Monday as the first day of the first week (00..53)

%w - Day of the week (Sunday is 0, 0..6)

%x - Preferred representation for the date alone, no time

%X - Preferred representation for the time alone, no date

%y - Year without a century (00..99) %Y - Year with century

%Z - Time zone name %% - Literal ``%’’ character t = Time.now t.strftime("Printed on %m/%d/%Y") #=> "Printed on 04/09/2003" t.strftime("at %I:%M%p") #=> "at 08:56AM"

September 5, 2008
5 thanks

Custom collection local variable name

Regarding the previous note from hoodow about using :variable_name to create a custom local variable name when rendering a collection with a partial, the argument should be :as instead of :variable_name, so:

render :partial => "video_listing", :collection => @recommendations, :as => :video

September 2, 2008
5 thanks

Useful in migrations

The most common usage pattern for this method is probably in a migration, when just after creating a table you want to populate it with some default values, eg: class CreateJobLevels < ActiveRecord::Migration def self.up create_table :job_levels do |t| t.integer :id t.string :name t.timestamps end JobLevel.reset_column_information %w{assistant executive manager director}.each do |type| JobLevel.create(:name => type) end end def self.down drop_table :job_levels end end
August 29, 2008
6 thanks

Always pass a block

I highly recommend always taking a block and passing it back up the chain if you use alias_method_chain, even if the original method does not. Otherwise you're keeping anyone later in the chain from adding support for blocks.

http://tech.hickorywind.org/articles/2008/08/29/always-pass-a-block-when-using-alias_method_chain

August 28, 2008
3 thanks

Get year to show in descending order (Today to 1920 for example)

The way people think of time start and end would be 1920 to today. This made me think "but I want it show the current year first then down." Well it's as simple as swapping the start_year and end_year.

  date_select :date, :start_year => Date.current.year, :end_year => 1920

  # => 2008, 2007, 2006, 2005 ... 1920
August 27, 2008
6 thanks

Only attr_accessible attributes will be updated

If your model specified attr_accessible attributes, only those attributes will be updated.

Use attr_accessible to prevent mass assignment (by users) of attributes that should not be editable by a user. Mass assignment is used in create and update methods of your standard controller.

For a normal user account, for example, you only want login and password to be editable by a user. It should not be possible to change the status attribute through mass assignment.

  class User < ActiveRecord::Base
    attr_accessible :login, :password
  end

So, doing the following will merrily return true, but will not update the status attribute.

  @user.update_attributes(:status => 'active')

If you want to update the status attribute, you should assign it separately.

  @user.status = 'active'
  save
August 25, 2008 - (>= v2.1.0)
3 thanks

Rake tasks for gem dependencies

You can manage installation and other tasks for these dependencies with rake tasks, for example:

 rake gems:install              # Installs all required gems for this application
 rake gems:unpack               # Unpacks the specified gem into vendor/gems

To get all rake tasks about gems:

 rake -T gems
August 22, 2008
3 thanks

Doesn't add an index

Typically you will want to have an index on foreign keys but this method doesn't assume that. Outside of the create_table block you should follow this with add_index :

  add_index :table_name, :goat_id
  # and, if polymorphic:
  add_index :table_name, :goat_type
August 22, 2008 - (>= v2.1.0)
6 thanks

Specifying :include no longer necessarily joins the association

Before Rails 2.1, adding an :include=>[:association] in your find method caused ActiveRecord to generate SQL using a join. Since 2.1, it MAY NOT execute as a join.

The join executes a large query and returned potentially duplicate records for a one-to-many association. After 2.1, the query is broken down and eager-loaded using an additional query per association, passing the set of id's to load, and avoiding the duplicate rows.

The new method eliminates duplicates, but can incur more database overhead. If you are loading a very large set of records (more than a "page"), you may need to "force" the join or use find_by_sql instead.

When you specify a "table.column" syntax within a

 :conditions=>["child.name=?", name]

or

 :order=>'child.name'

then ActiveRecord will build the older, full query with the join because you are referencing columns from another table to build. This will cause the duplicate rows to reappear.

Whenever you reference a column from another table in a condition or order clause, ALWAYS use the table name to prefix the column, even if it not ambiguous among the tables involved. Otherwise the query will not be executed as a join and you will receive an SQL error referencing the "missing" column.

You can "force" a join by adding a reference to the other tables in your :conditions or :options parameters, even if the test or sort is irrelevant.

August 20, 2008
5 thanks

Iterate and join blocks

Following LacKac's idea, we can write render_join (useful to render a collection with a small chunks of code, where a render :partial + :spacer_template would be overkill):

  def render_join(collection, join_string, &block)
    output = collection.collect do |item|
      capture(item, &block)
    end.join(join_string)
    concat(output, block.binding)
  end

An example of use:

  <% render_join(@items, '<br />') do |item| %>
     <p>Item title: <%= item.title %></p>
  <% end %>
August 19, 2008 - (v2.1.0)
7 thanks

preload_associations manually

Usually you preload associations using <tt>:include => [ ... ]</tt>. In Rails 2.1 each association is fetched with a separate query. Something like: <tt>Post.find(:all, :include => [:tags, :user])</tt> will produce 3 queries - each for posts, tags and users. --- But sometimes you have a complex query, which uses <tt>:joins => :other_association</tt> and conditions between multiple tables, but not the ones you need to include. Then everything is mixed back in one query like in old versions of Rails. Another case may be when it is not possible to use <tt>:include</tt> at all, for example while using <tt>find_by_sql</tt>, but you still want/need to preload associated records. In rails 2.1 <tt>find</tt> uses <tt>preload_associations</tt> internally, when it is possible (There are no joins or conditions between tables). So then you can preload asociations manually from within your model: <tt>class Post < ActiveRecord::Base</tt> has_many :tags belongs_to :user ... def self.find_complex_with_includes posts = find_by_sql(...) # or find(:all, :joins => .....) preload_associations(posts, [:tags, :user]) posts end <tt>end</tt> and then do <tt>@posts = Post.find_complex_with_includes</tt>
August 19, 2008 - (v2.1.0)
5 thanks

Namespace or modules in routes

If you have grouped controllers into a module, e.g. admin then you can specify this in the routes using the namespace method:

  map.namespace :admin do |admin|
    admin.resources :categories
  end

which will map the categories resource giving urls like

/admin/categories/

/admin/categories/new

It will also generate the named routes such as new_admin_category_url and admin_category_path

August 19, 2008
6 thanks

script/generate syntax

To add a post_id field to a comments table, run this: script\generate migration add_post_id_to_comment post_id:integer See that it´s not the table name(plural), but the model name(singular),<br /> and post_id:references, does not works like in create_table. This is the generated migration: class AddPostIdToComment < ActiveRecord::Migration def self.up add_column :comments, :post_id, :integer end def self.down remove_column :comments, :post_id end end
August 17, 2008
3 thanks

Regexes with groups and split

When you use a Regex with capture groups, all capture groups are included in the results (interleaved with the "real" results) but they do not count for the limit argument.

Examples:

  "abc.,cde.,efg.,ghi".split(/.(,)/)
  => ["abc", ",", "cde", ",", "efg", ",", "ghi"]
  "abc.,cde.,efg.,ghi".split(/(.)(,)/)
  => ["abc", ".", ",", "cde", ".", ",", "efg", ".", ",", "ghi"]
  "abc.,cde.,efg.,ghi".split(/(.(,))/)
  => ["abc", ".,", ",", "cde", ".,", ",", "efg", ".,", ",", "ghi"]
  "abc.,cde.,efg.,ghi".split(/(.(,))/, 2)
  => ["abc", ".,", ",", "cde.,efg.,ghi"]
  "abc.,cde.,efg.,ghi".split(/(.(,))/, 3)
  => ["abc", ".,", ",", "cde", ".,", ",", "efg.,ghi"]
August 17, 2008
4 thanks

Using sweepers in script/runner

If you need to use some of your sweepers in a script/runner script or some rake task you can use this snipped:

  require 'action_controller/test_process'

  sweepers = [ProductSweeper, UserSweeper]

  ActiveRecord::Base.observers = sweepers
  ActiveRecord::Base.instantiate_observers

  controller = ActionController::Base.new
  controller.request = ActionController::TestRequest.new
  controller.instance_eval do
    @url = ActionController::UrlRewriter.new(request, {})
  end

  sweepers.each do |sweeper|
    sweeper.instance.controller = controller
  end

Your script will fire the ActiveRecord callbacks defined in that sweepers and you can use expire_cache, expire_fragment and also the routing helpers you have defined (hash_for_user_path, hash_for_product_path, etc.).

August 17, 2008
3 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
6 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)