Flowdock

Recent notes

RSS feed
November 6, 2008
1 thank

Compiling mysql gem in Leopard with MacPorts MySQL

Needs architecture and reference to mysql_config:

sudo env ARCHFLAGS="-arch i386" gem install mysql -- \
--with-mysql-config=/opt/local/lib/mysql5/bin/mysql_config
November 5, 2008
2 thanks

Rendering YAML

When you want to render XML or YAML you can use

render :xml, some_object.to_xml 

or

render :json, some_object.to_json

However there is no equivalent for YAML. What you can do is render just plain text with a correct content-type:

render :text => some_object.to_yaml, :content_type => 'text/yaml'

The content_type is debatable but this seems to be the most standard.

November 5, 2008
0 thanks

re: james' note incorrect

kieran is correct, my note is incorrect, it was not meant for ActionMailer::Base

November 5, 2008 - (v1.0.0 - v2.1.0)
1 thank

james' note incorrect

The render method in ActionMailer is infact a private method, in all versions (including the new Rails 2.2).

However, spectators note about @template works well. Thanks.

November 5, 2008
0 thanks

RERE: 1 render definition for many actions (cleaner)

@arronwashington. Ah, so that’s the motivation for using default_render, thanks for clearing that up, thanks :)

November 4, 2008
2 thanks

RE: 1 render definition for many actions (cleaner)

Hi James,

Unfortunately that doesn’t work if you use the

@zombies 

variable in the rendered template, which is why default_render was added as a patch AFAIK. :)

before_filter is called too soon, after_filter is called after an attempt at rendering is made, so default_render is the only option for this as far as I know.

November 4, 2008
0 thanks

re: Specifying :include no longer necessarily joins the association

I have seen how :include does not nessisarily perform a join during that SQL query, if you need the join to occur then, rather then tricking AR (“forcing”), use :joins instead of :include to ensure the joins occur.

November 4, 2008 - (v1.0.0 - v2.1.0)
1 thank

1 render definition for many actions (cleaner)

@arronwashington, you can just call render rather then using instance_eval to overwrite default_render, for example:

before_filter :render_filter, :only => [:zombies, :cool_zombies]

def zombies
  @zombies = Zombie.find(:all)
end

def cool_zombies
  @zombies = Zombie.find(:all, :conditions => { :eats_humans => false, :is_hippie => true })
end

protected

def render_filter
  # without instance_eval or overwriting default_render
  render :template => 'zombies/all'
end
November 4, 2008
0 thanks

1 render definition for many actions.

Overriding default_render in especially useful when you have many actions that all render the same thing.

before_filter :render_filter, :only => [:zombies, :cool_zombies]

def zombies
  @zombies = Zombie.find(:all)
end

def cool_zombies
  @zombies = Zombie.find(:all, :conditions => { :eats_humans => false, :is_hippie => true })
end

protected

def render_filter
  instance_eval do
    def default_render
      render :template => 'zombies/all'
    end
  end
end
November 2, 2008
2 thanks

params hash gets the model id automatically

The params hash gets automatically populated with the id of every model that gets passed to form_for. If we were creating a song inside an existing album:

URL:/albums/209/songs/new
form_for [@album, @song] do |f| 
  ...
  f.submit "Add"
end

The params hash would be:

params = {"commit"=>"Add", 
          "authenticity_token"=>"...",
          "album_id"=>"209",
          "song"=>{"song_attributes"=>{...}}
          }

So, in the songs_controller you could use this album_id in a before_filter:

before_filter :find_album
protected
def find_album
  @album = Album.find(params[:album_id])
end

If you only did this:

form_for @song do |f| 

You would get this params hash:

params = {"commit"=>"Add", 
          "authenticity_token"=>"...",
          "song"=>{"song_attributes"=>{...}}
          }  
November 2, 2008 - (v1_8_6_287)
1 thank

The reverse operation of split is join.

Given that String#split returns an array, its reverse operation is Array#join. Example:

"life is awesome".split
=>["life","is","awesome"]

["life","is","awesome"].join(" ")
=>"life is awesome"
October 31, 2008
0 thanks

By images's sub dirctionary to img tag

image_tag(“icons/edit.png”) # =>

<img src="/images/icons/edit.png" alt="edit" />
October 30, 2008
2 thanks

re: Customizing attribute names in error messages

You can use Module.alias_attribute to achieve the same result as overriding human_attribute_name. After aliasing use the new name in the validates_xxxx_of methods or ActiveRecord::Errors.add.

October 30, 2008
0 thanks

Anyone know the order the scopes assemble conditions?

It seems like last scope = first condition in sql. Can anyone confirm?

October 30, 2008
1 thank

can be useful to achieve attr_reader effect

e.g.

class Account < ActiveRecord::Base 

  def credit(amount)
    self.write_attribute(:balance, self.balance + amount)
    self.save!
  end

  def balance=(value)
    raise "You can't set this attribute. It is private."
  end

end

this allows fred.account.credit(5) whilst raising an error on fred.account.balance = 1000

October 30, 2008
7 thanks

Can be extended but only with a module

Although not documented, belongs_to does support Association Extensions however it doesn’t accept a block like has_many does. So you can’t do this:

class Account < ActiveRecord::Base
  belongs_to :person do
    def do_something_funky
      # Some exciting code
    end
  end
end

but you can do this:

module FunkyExtension
  def do_something_funky
    # Some exciting code
  end
end

class Account < ActiveRecord::Base
  belongs_to :person, :extend => FunkyExtension
end

And then call it like this:

@account = Account.first
@account.person.do_something_funky
October 30, 2008
2 thanks

Does very litle

This method simply returns the hash itself since HashWithIndifferentAccess by definition support symbolized key access. For a (regular rails) Hash there is ActiveSupport::CoreExtensions::Hash::Keys#symbolize_keys! that “Destructively convert all keys to symbols”.

October 29, 2008
3 thanks

A work-around for adding confirmation to image_submit_tag

Sometimes you may want to add a confirmation to image submit tags but this function does not allow it. To get over this limitation use a normal submit tag and set the src and type properties (set type to “image”)

Code example

submit_tag “Delete”, :confirm => “Are you sure?”, :src => “/images/trash.png”, :type => “image” %>

October 25, 2008
3 thanks
October 25, 2008
2 thanks

Alternative: use 1000.humanize

1.humanize == “1″ 1000000.humanize == “1.000.000″ 1000.12345.humanize == “1.000,12″

http://pragmatig.wordpress.com/2008/10/25/numbers-for-humans-humanize-for-numeric/

October 24, 2008
1 thank

module includes with callbacks

If you write a plugin or module that includes callbacks make sure to define the method and call super after you’re done with your business.

module CoolStuff

def self.included(base)
  super
  base.extend(ClassMethods)
  # the next line seems to clobber. instead opt for defining an inheritable method
  # base.after_save :chill
end

module ClassMethods
  # cool class methods
end

def chill
  self.cool = true
end

def after_save
  self.chill
  super # if you don't call super, bloggy won't run
end

end # yes I know this next line is a divisive issue but it’s common enough ActiveRecord::Base.send :include, CoolStuff

class Blog < ActiveRecord::Base

after_save :bloggy

def bloggy
  slugify_title
end

end

October 24, 2008
11 thanks

Prompt vs. Select

According to the docs in form_options_helper.rb

:include_blank - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.

:prompt - set to true or a prompt string. When the select element doesn’t have a value yet, this prepends an option with a generic prompt – “Please select” – or the given prompt string.

The main difference is that if the select already has a value, then :prompt will not show whereas the :include_blank always will.

October 24, 2008
0 thanks

Styling question

How do we style the select boxes and “:” somehow within this method?

Follwup: it seems in Rails 2.1, FormBuilder#time_select didn’t pass html_options to this method. and it’s fixed i

October 24, 2008
9 thanks

Back it up with a unique index

As mentioned briefly above, as well as using this validation in your model you should ensure the underlying database table also has a unique index to avoid a race condition.

For example:

class User < ActiveRecord::Base
  validates_uniqueness_of :login_name
end

The index can be specified in the migration for the User model using add_index like this:

add_index :users, :login_name, :unique => true

You do a similar thing when using the :scope option:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name, :scope => :account_id
end

Should have a migration like this:

add_index :people, [ :account_id, :user_name ], :unique => true

Note that both the attribute being validated (:user_name) and the attribute(s) used in the :scope (:account_id) must be part of the index.

For a clear and concise explanation of the potential for a race condition see Hongli Lai’s blog.

October 24, 2008
2 thanks

The human side of inflections

Rails 2.2 moves this functionality to the Inflector::Inflections class:

See the ticket and the code that allow the humanisation rules to be centralised in an app.

October 23, 2008
2 thanks

Customizing attribute names in error messages

This can be used to customize attribute names in error messages. See my note in ActionView::Helpers::ActiveRecordHelper#error_messages_for.

October 23, 2008
8 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 :www_url 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 23, 2008
1 thank

Mocking puts from RSpec

If you want to mock calls to puts from RSpec, do it from the class/module you are in:

module Foo
  def self.foo
    puts "hello"
  end
end

describe Foo do
  it "should write 'hello' when foo() is called" do
    Foo.should_receive(:puts).with("hello")  # Kernel and Object don't work in this case...
    Foo.foo
  end
end
October 22, 2008
10 thanks

Gotcha when defining :finder_sql or :counter_sql

When setting custom SQL statements in the :finder_sql or :counter_sql queries, if you need to inject attributes from the current object, such as the ID, make sure to disable string interpolation of the statement by using single quotes or %q().

Example:

has_many :relationships, :class_name => 'Relationship', :finder_sql => %q(
  SELECT DISTINCT relationships.*
  FROM relationships
  WHERE contact_id = #{id}
)

Surrounding this SQL with double-quotes or %Q() will expand #{id} too early, resulting in a warning about Object#id being deprecated and general brokenness.

October 22, 2008 - (>= v2.1.0)
2 thanks

HABTM relation

When you want to create a has_and_belong_to_many relation (og just a has_many :through) use this setup.

Example
class CreateCourses < ActiveRecord::Migration
  def self.up
    create_table :seasons do |t|
      t.integer :year
      t.string :period
    end

    create_table :courses do |t|
      t.string :courseCode
    end

    create_table :courses_seasons, :id => false do |t|
      t.references :course, :null => false
      t.references :season, :null => false
    end
    add_index :courses_seasons, [:course_id, :season_id], :unique => true

  end

  def self.down
    drop_table :seasons
    drop_table :courses
    drop_table :courses_seasons
  end
end