Flowdock

Recent notes

RSS feed
April 25, 2010
0 thanks

Avoid DoubleRenderError

One can not invoke render twice during an action. Thus if You have a complicated rendering logic but at the end would like to render some default content, or just would like to find out whether render has been called during the current action, use performed?. This also works with “empty” renderings such as head.

April 24, 2010
0 thanks

Rendering After Exception In respond_to() Block

Remember, format blocks set the response’s content type. This can present problems when handling errors.

class MediaController
  rescue_from  ActionController::MissingFile do |e|
    # User's browser probably wont display this 
    # Content-Type is application/x-shockwave-flash
    render :file => File.join(Rails.public_path, '404.html'), :status => 404 
  end

  # show details or stream video
  def show
    @media = Media.find params[:id]
    respond_to do |format|
      format.html
      format.flv { send_file @media.path, :disposition => 'inline' }
    end
  end
end

For these situations you must set :content_type when calling render:

render :file => File.join(Rails.public_path, '404.html'), :status => 404, :content_type => 'text/html' 
April 22, 2010
1 thank

Dynamic exists? methods

There are no dynamic exists? methods analogous to dynamic finders, which means that while you can do this:

Person.find_by_name('David')

you can’t do this:

Person.exists_by_name('David') # DOES NOT WORK

nor this:

Person.exists_by_name?('David') # DOES NOT WORK

However, you can simulate this with dynamic scope:

Person.scoped_by_name('David').exists?

You’ll have to admit that this is so much better than the plain old method:

Person.exists?(:name => "David")
April 22, 2010
0 thanks

nil Argument Raises An I18n::ArgumentError

You might want to do this:

module ActionView
 module Helpers
   module TranslationHelper
     def localize(*args)
       #Avoid I18n::ArgumentError for nil values
       I18n.localize(*args) unless args.first.nil?
     end
     # l() still points at old definition
     alias l localize
   end
 end

end

April 21, 2010
0 thanks

Mode Flags

RDONLY, TRUNC, etc… are defined in the File::Constants module which is include'd by IO and File.

IO.open fd, IO::RDONLY
File.open path, File::RDONLY

Though as pointed out above, they are interchangeable.

April 21, 2010
0 thanks

Errors Raised

Non IO errors (IOError) are contained in the Errno module. They are the same as those given in open(2), see:

http://www.kernel.org/doc/man-pages/online/pages/man2/open.2.html#ERRORS

Common Errors

  • Errno::ENOENT: No such file or directory

  • Errno::EACCES: Permission denied

  • Errno::EEXIST: File exists (i.e. IO::EXCL | IO::CREAT)

April 20, 2010
2 thanks

Does not work with polymorphic relations

If you have polymorphic relations, e.g.:

class Bookmark < ActiveRecord::Base
  belongs_to :thing, :polymorphic => true
  belongs_to :owner, :polymorphic => true
end

and you want to ensure that a thing can bookmarked by an owner at most once, you can’t do this:

validates_uniqueness_of :thing, :scope => :owner

Instead, you must use the real column names, e.g.:

validates_uniqueness_of :thing_id, :scope => [:thing_type, :owner_id, :owner_type]
April 16, 2010
1 thank

Also behaves like File#expand_path

You can also use URI.join to resolve relative and absolute links:

URI.join('http://example.com/', '/example').to_s
# => "http://example.com/example"

URI.join('http://example.com/example', 'test').to_s
# => "http://example.com/test"

URI.join('http://example.com/example/', 'test').to_s
# => "http://example.com/example/test"

URI.join('http://example.com/example/foo', '../css').to_s
# => "http://example.com/css"
April 16, 2010
5 thanks

Require file from the same folder

If you want to require file from the same folder, the simplest way is

require File.expand_path('../file-to-require', __FILE__)

If your file is /lib/book.rb

File.expand_path('../page', '/lib/book.rb') => '/lib/page.rb'
April 9, 2010
2 thanks

The :method goes in the :html option

When using a restful form helper and you want to use a method other than POST, remember to put the :method in the :html option.

e.g. To send a DELETE request instead of the usual POST (with a nested resource thrown in for good measure) use:

<% form_for [@post, @comment], :html => { :method => :delete } do |f| -%>
April 8, 2010
2 thanks

Bad example

Note that it would be better to avoid the alias_method line in the example and just call super.

April 3, 2010
0 thanks

:autosave => false vs. :autosave => nil

The documentation above mentions that :autosave => true always saves the association and that it’s “off” by default. What it doesn’t mention what they mean by “off”.

  • :autosave => nil (the default “off” behavior) will still autosave the association if it has changed or is a new record.

  • :autosave => false seems to prevent autosaving of the association, even if it has changed.

I’ve found :autosave => false to be useful behavior when trying to prevent cyclical dependency loops; there are likely other useful use cases out there.

April 3, 2010 - (>= v2.2.1)
3 thanks

Careful with this method.

Despite the name and description, it will actually update any changed fields on the model rather than just the desired attribute.

def update_attribute(name, value)
  send(name.to_s + '=', value)
  save(false)
end

See? Use update_all and pass in the model ID as a condition, instead.

April 1, 2010
0 thanks

Fetching records when column is set to nil or false

If you want to fetch all records when one column (boolean) is set to nil or false, try this:

Project.all(:conditions => "archived IS NULL OR archived = 'f'")
April 1, 2010
2 thanks

Doesn't return nil on empty array when param is given

This does not return nil if the array is empty and n is given.

[].shift(2) # => []

a = []
a.shift(2) # => []
a # => []
March 31, 2010
0 thanks

Always gracefully degrade if JS isn't available

If you always want to degrade when JS isn’t available you can add something like to environment.rb

module ActionView
  module Helpers
    module PrototypeHelper
      def link_to_remote(name, options = {}, html_options = nil)
         html_options ||= {}
         html_options[:href] ||= options[:url]
         link_to_function(name, remote_function(options), html_options || options.delete(:html))
      end
    end
  end
end
March 31, 2010
3 thanks

Interpolating

Note that to interpolate, the sequences must be inside single quotes:

# replace /ll/ with itself
'hello'.gsub(/ll/, '\0') # returns 'hello'
'hello'.gsub(/ll/, "\0") # returns 'he\000o'
March 29, 2010
0 thanks

Wrong example

In the authentication filter example above, the time condition should be reversed: we only want to find the user if time is still in the future (because it’s the valid-until time).

So the example should look like this:

id, time = @verifier.verify(cookies[:remember_me])
if time > Time.now
  self.current_user = User.find(id)
end
March 28, 2010
0 thanks

at_exit in sintra

in main.rb of sinatra:

at_exit { Application.run! if $!.nil? && Application.run? }
March 25, 2010
0 thanks

How to test callback methods

When testing callback methods, try to test the callback chain separate from its implementation.

Say this is your model:

class Project

  belongs_to :owner
  has_many :milestones

  after_save :create_milestones
  after_save :notify_owner

  private

  def notify_owner
    owner.project_created!
  end

  def create_milestones
    milestones.create(:name => 'Milestone 1')
  end

end

You should write your spec like this:

describe Project do

  describe 'create_milestones' do
    it 'should create an initial milestone' do
      project = Project.new
      project.milestones.should_receive(:create)
      project.send(:create_milestones)
    end
  end

  describe 'notify_owner' do
    it 'should notify its owner' do
      project = Project.new(:owner => mock_model(User))
      project.owner.should_receive(:project_created!)      
      project.send(:notify_owner)
    end
  end

  describe 'after_save' do
    it 'should run the proper callbacks' do
      project = Project.new
      project.should_receive(:create_milestones)
      project.should_receive(:notify_owner)
      project.run_callbacks(:after_save)
    end
  end

end

Here is some more advice on how to test callback methods in Rails:

http://gem-session.com/2010/03/how-to-test-callback-methods-in-rails

March 25, 2010
0 thanks

Using models in your migration

Here is some advice how to call your models in a migration without shooting yourself in the foot:

http://gem-session.com/2010/03/how-to-use-models-in-your-migrations-without-killing-kittens

Basically you can inline models into your migrations to decouple them from changes in your model:

class AddCurrentToVendor < ActiveRecord::Migration

  class Vendor < ActiveRecord::Base
  end

  class Article < ActiveRecord::Base
    has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at'
  end

  def self.up
    add_column :vendors, :current, :boolean
    Article.all.each do |article|
      article.vendors.first.andand.update_attribute(:current, true)
    end
  end

  def self.down
    remove_column :vendors, :current
  end
end
March 25, 2010
0 thanks

RE: Replacing with "\" and match — a simple solution

Thanks. No, I am not trying to quote for a regex. It was mostly an approach thing since I came into contact with the behavior previously when I played around. After doing some tests, I figured I should spare any other adventurers that part. :-)

March 25, 2010
0 thanks

How to test custom error pages

Here is some advice for testing custom error pages using Webrat and Cucumber:

http://gem-session.com/2010/03/testing-your-custom-error-pages-with-webrat-and-cucumber

March 24, 2010
0 thanks

Time.now in UTC

A quick way to get the current time in UTC is:

Time.new.utc # => Wed Mar 24 14:38:19 UTC 2010
March 24, 2010
1 thank

Setting primary key from hash

If you try to specify the value for your primary key (usually “id”) through the attributes hash, it will be stripped out:

Post.new(:id => 5, :title => 'Foo') #=> #<Post @id=nil @title="Foo">

You can solve this by setting it directly, perhaps by using a block:

Post.new(:title => "Foo") { |p| p.id = 5 } #=> #<Post @id=5 @title="Foo">

This behavior is something you’d probably only have a problem with when you have custom primary keys. Perhaps you have a User model with a primary key of “name”…

class User < ActiveRecord::Base
  set_primary_key :name
end

User.new(params[:user]) # This will never work

You can solve this on a case-to-case basis by calling attributes= directly with the “ignore protected” option:

User.new { |user| user.send(:attributes=, params[:user], false) } # BAD BAD BAD!

You should not do the above example, though. If you do, all protected attributes are ignored, which is very, very bad when you only care about the primary key.

I’d recommend one of the following instead:

# Option 1 – Always allow primary key. Avoid with models created by users
class User
  private
    def attributes_protected_by_default
      super - [self.class.primary_key.to_s]
    end
end

# Option 2 – Add a new method for this case
class User
  def self.new_with_name(attributes = nil)
    new(attributes) { |u| u.name = attributes[:name] }
  end
end

As always when something is hard to do in Rails: Think about your design? Is it recommended? Is it sound? Do you really need to have a custom primary key?

March 23, 2010
1 thank

named_scopes and Acts as State Machine

As of AASM 2.1 named_scope(s) are defined by default for each state of a model.

March 19, 2010
1 thank

singular_class_name method missing

If this method is undefined in ActionView::Base, add to your ApplicationController:

class ApplicationController < ActionController::Base
  helper_method :singular_class_name
end
March 18, 2010
2 thanks

collect_with_index

Use Object#enum_for if you need to collect with index:

require 'enumerator'

['a', 'b', 'c'].enum_for(:each_with_index).collect do |item, index| 
  "#{index}: #{item}" 
end

See also: Enumerable#each_with_index

March 18, 2010 - (v1_8_6_287 - v1_8_7_72)
0 thanks

collect_with_index

Use Object#enum_for if you need to collect with index:

require 'enumerator'

%w{foo bar}.enum_for(:each_with_index).collect do |item, index| 
  "#{index}: #{item}" 
end

See also: Enumerable#each_with_index