Flowdock

Recent good notes

RSS feed
August 4, 2008
7 thanks

Example: find by associated table

Say you have tables “authors” and “books” and they have a one-to-many association.

You want authors who have written books with “cooking” in the title…

cookbook_authors = Author.find(:all, 
  :conditions => ['books.title LIKE ?', '%cooking%'], 
  :joins => [:books], 
  :order => 'authors.last_name' )

For many-to-many associations, it’s a similar pattern. Say you have tables “people” and “organizations” with a many-to-many association through the join table “organization_memberships”.

Ski Club members whose first name starts with “a”…

ski_club_members = Person.find(:all, 
  :conditions => ['first_name LIKE ? AND organizations.name = ?', 
    'a%', 'Ski Club'], 
  :joins => [:organizations], 
  :order => 'people.last_name' )
August 4, 2008
3 thanks

Detailed messages for a nested model

Detailed messages for a nested model

<%@address = @order.address%>
<%=error_messages_for :address%>
August 3, 2008 - (<= v2.1.0)
6 thanks

Cross browser issues

We use jQuery as our Javascript library of choice, but have to use a work around for full cross-browser support.

In jQuery you need to set the AJAX request headers as:

$.ajaxSetup({

beforeSend: function(xhr) {xhr.setRequestHeader(“Accept”, “text/javascript”);}

});

But we found that IE and Safari sends headers like: HTTP_ACCEPT=>“text/html, /, text/javascript”, with the javascript header last so this mucks up the respond_to block as it will always enter the first block (usually format.html) and never reach your format.js block.

We have a before filter called on required actions that forces the request format to be javascript if it is an xml_http_request?

def fix_xml_http_request
  if request.xml_http_request?
    request.format = :js
  end
end
July 31, 2008
8 thanks

Multiple filter methods with :only, :except

Notice that this methods accepts *filters param, so you can pass array of methods with :only or :except too

Example

before_filter [:authorize, :set_locale], :except => :login

July 30, 2008
19 thanks

Value parameter

You can add a value to your hidden field by using the :value parameter.

Example
hidden_field(:object, :field, :value => params[:requestval])
July 30, 2008
7 thanks

Using gmail SMTP server to send mail

First you would need to sign up with Google Apps, which is a very painless process:

http://www.google.com/a/cpanel/domain/new

Next you need to install a plugin that will allow ActionMailer to make a secure connection to google:

script/plugin install git://github.com/caritos/action_mailer_tls.git

We need this due to transport layer security used by google.

Lastly all you need to do is place this in your environment.rb file and modify it to your settings:

ActionMailer::Base.smtp_settings = {
 :address => "smtp.gmail.com",
 :port => 587,
 :domain => "your.domain_at_google.com",
 :authentication => :plain,
 :user_name => "google_username",
 :password => "password"
}
July 30, 2008
6 thanks

Different Method for Subdomains

@james

You can also access the subdomain via the subdomains array.

request.subdomains.first
July 29, 2008 - (v2.1.0)
16 thanks

Scoped using - more simple way

Regarding to the example from james, there is a more simple way to do this:

user.messages.update_all(:read => true)
July 28, 2008
15 thanks

Friendlier error message example

The default error messages can be a bit stale and off putting. Try somethings like this:

error_messages_for(
  :user, 
  :header_message => "Oops - We couldn't save your user!", 
  :message => "The following fields were a bit of a problem:", 
  :header_tag => :h1
)

You can also use error_messages_for as follows

<%  form_for User.new do |f| %>
  <%=  f.error_messages :header_message => "..." %>
<%  end  %>
July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

select_options_tag - no more worries...

no more explicit options_for_select calls..

def select_options_tag(name='',select_options={},options={})
  #set selected from value
  selected = ''
  unless options[:value].blank?
    selected = options[:value]
    options.delete(:value)
  end
  select_tag(name,options_for_select(select_options,selected),options)
end

select_options_tag(‘name’,[[‘oh’,‘no’]],:value=>‘no’)

July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

haml, an alternative to ERb

Want something nicer looking (and currently, faster!) than using ERb for your views? Have a look at haml (and it’s companion, sass, for stylesheets). It will make you feel all fuzzy on the inside, I promise :P.

ERb example

<div id="profile">
  <div class="left column">
    <div id="date"><%= print_date %></div>
    <div id="address"><%= current_user.address %></div>
  </div>
</div>

haml equivalent

#profile
  .left.column
    #date= print_date
    #address= current_user.address

Shifting to haml from ERb feels strange at first, but after about 20 minutes it starts to feel nice. A little longer and you’ll really start to notice your productivity (and of course, happiness) increase! :). I’ve starting shifting all new projects developed at our work office over to using haml (and sass), it’s been fantastic!

At first I came across a few things that I couldn’t do in haml, though every time a quick read of the overview doc page would show me a simple syntax for overcoming that issue! :) (which out of interest, is located here: http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html)

Give the tutorial a shot if you’re interested: http://haml.hamptoncatlin.com/tutorial

July 24, 2008
3 thanks

render_collection

You can wrap render in helpers. For example, render_collection. In app/helpers/application.rb:

module ApplicationHelper
  def render_collection(name, collection)
    render :partial => "shared/#{name}", :collection => collection
  end
end

In views:

<h2>Comments</h2>
<%= render_collection :comments, @photo.comments %>
July 24, 2008
8 thanks

render template file different from your action (method) name

In some cases you have to avoid rails magic that uses template names named as your ActionMailer method.

rails magic

def daily_notification
  # ...
end
# will look for daily_notification.erb

def weekly_notification
  # ...
end
# will look for weekly_notification.erb

your case

Just give necessary value to @template instance variable.

def setup
  # ...
  @template = 'notification'
end

def daily_notification
  # ...
end
# will look for notification.erb

def weekly_notification
  # ...
end
# will look for notification.erb
July 24, 2008 - (v2.1.0)
20 thanks

automatically generate scopes for model states

or better known as “throw on some more tasty meta-programming” :). Given an example of a model which has a state (String) which must from a set of defined values, e.g. pending, approved, denied.

class User < ActiveRecord::Base
  STATES = [ 'pending', 'approved', 'denied' ]

  validates_inclusion_of :state, :in => STATES

  # Define a named scope for each state in STATES
  STATES.each { |s| named_scope s, :conditions => { :state => s } }
end

This automatically defines a named_scope for each of the model states without having to define a named_scope manually for each state (nice and DRY).

July 23, 2008
3 thanks

options_for_select further example (using a collection and with a default value)

In this example, we are editing a collection of region records, each with its own select list of countries. (Region belongs_to :country.) If the region doesn’t have a country associated, then we want a default message of “unassigned”. Of course, if the region does have a country associated then we want that country displayed:

<% name = "region[" + region.id.to_s + "][country_id]" %>
<% id = "region_" + region.id.to_s %>

<%= select_tag(id, options_for_select([["unassigned" , "0" ]] +
                     Country.to_dropdown, region.country_id),

{:name => name} ) %> This give us:

<select id="region_3" name="region[3][country_id]">
  <option value="0">unassigned</option>
  <option selected="selected" value="12">England</option>
</select>

NB: we’re using the handy acts_as_dropdown plugin (http://delynnberry.com/projects/acts-as-dropdown/) but we could just as easily prepare the select list with map / collect as above.

July 23, 2008 - (<= v2.1.0)
10 thanks

:only, :except and passing in multiple parameters

To specify that the filter should be applied to or excluded from given controller actions, use the :only and :except parameters. To pass in multiple controller actions use an array:

before_filter :authorize, :except => [:index, :show]
before_filter :authorize, :only => :delete
July 23, 2008
4 thanks

Keep your controllers clear

When you use redirect_to or render with flash[:notice] or flash[:error], you can define some helper methods in your ApplicationController (or somewhere you want):

class ApplicationController < ActionController::Base

  protected

    %w(notice error).each do |message|
      class_eval <<-END_EVAL
        def redirect_#{message}(url, message)
          flash[:#{message}] = message
          redirect_to url
        end

        def render_#{message}(action, message)
          flash[:#{message}] = message
          render :action => action
        end
      END_EVAL
    end
end

Now you have four methods - redirect_notice, redirect_error, render_notice and render_error.

July 23, 2008
5 thanks

Custom annotation types

For group work you may need something more than FIXME, OPTIMIZE and TODO. Just create new rake file and place it to lib/tasks:

require 'source_annotation_extractor'

task :notes do
  SourceAnnotationExtractor.enumerate "WTF|OMG", :tag => true
end

namespace :notes do
  desc "Enumerate all WTF annotations"
  task :wtf do
    SourceAnnotationExtractor.enumerate "WTF"
  end

  desc "Enumerate all OMG annotations"
  task :omg do
    SourceAnnotationExtractor.enumerate "OMG"
  end
end

or create an array of new types and generate tasks dynamicaly.

July 23, 2008
14 thanks

Pass id collections with check box tags

It can be useful to pass a collection of ids to a controller, especially in the case of a has_many relationship, for example:

User has_many Roles

In your view you could have something like:

<ul>
  <% @roles.each do |role| %>
      <li>
        <%= check_box_tag 'role_ids[]', role.id -%>
        <%= h role.name -%>
      </li>
  <% end %>
</ul>

Note the square brackets after role_ids - this is important for passing a collection through to the controller.

If you place this in a form and submit it, you can expect to see a param passed into the controller that looks like:

"role_ids"=>["1", "2", "3"]
July 23, 2008 - (<= v2.1.0)
9 thanks

Easy and effective admin authentication

Great for use within an AdminController (in which all other administrative controllers inherit from AdminController).

class AdminController < ApplicationController
  before_filter :authenticate

  def authenticate
    authenticate_or_request_with_http_basic('Administration') do |username, password|
      username == 'admin' && password == 'password'
    end
  end
end
July 23, 2008 - (<= v2.1.0)
9 thanks

perform update_all scoped within a has_many collection

For example: having two models, User and Message (user has_many messages, each message has a boolean flag called ‘read’). You want to mark all messages as read for a particular user.

Mark all messages as read for a particular user

Message.update_all({:read => true}, {:id => user.messages})
July 23, 2008
6 thanks

Loading fixtures in migrations

This helper is wrapper around Fixtures#create_fixtures and just load fixtures from specified directory (db/migrate/data by default):

class ActiveRecord::Migration
  def self.load_data(filename, dir = 'db/migrate/data')
    Fixtures.create_fixtures(File.join(RAILS_ROOT, dir), filename)
  end
end

It is usefull for tables with data like country list:

class CreateCountries < ActiveRecord::Migration
  def self.up
    create_table :countries do |t|
      t.string :name, :code, :null => false
      t.timestamps
    end
    load_data :countries
  end

  def self.down
    drop_table :countries
  end
end
July 22, 2008
4 thanks

Example

In your migration:

def self.up
  add_column :accounts, :is_admin, :boolean, :default => 0
end
July 22, 2008
4 thanks

:conditions examples

:conditions => {:login => login, :password => password}

:conditions => [‘subject LIKE :foo OR body LIKE :foo’, {:foo => ‘woah’}]

(from the book “The Rails Way”)

July 22, 2008 - (>= v2.1.0)
11 thanks

Migration helpers

You can add your own migration helpers as references:

Code example

class ActiveRecord::ConnectionsAdapters::TableDefinition
  def counter_caches(*args)
    args.each { |col| column("#{col}_count", :integer, :default => 0) }
  end
end

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :first_name, :last_name, :email
      t.counter_caches :photos, :messages
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end
July 22, 2008
19 thanks

select_tag with options_for_select example

An example of using options_for_select with select_tag

select_tag 'user_id', options_for_select(@users.collect{ |u| [u.name, u.id] })

This would generate something like:

<select id="user_id" name="user_id">
  <option value="1">Brad</option>
  <option value="2">Angie</option>
  <option value="3">Jenny</option>
</select>
July 22, 2008
19 thanks

Nested resources in form_for

If you like doing things RESTfully and have a model relationship like:

Post_ has many Comments_

Then you can construct a form_for within your view to mirror this relationship when creating comments:

form_for [@post, @comment] do |f|
  ...
end

You also need to make sure your routes reflect this relationship:

map.resources :post, :has_many => [:comments]
July 22, 2008
6 thanks

selected

select :languages, :language, [‘en’, ‘de’, ‘fr’], {:selected=> ‘en’}