Flowdock

Recent notes

RSS feed
May 17, 2010
2 thanks

Add has_keys? method to Hash class

class Hash

def has_keys?(*_keys)
  (_keys - self.keys).empty?
end

end

h = {1=>‘a’,2=>‘b’}

h.has_keys?(1,2) #-> true

h.has_keys?(1,3) #-> false

May 14, 2010
0 thanks

You must use the yielded object

A warning note (at least for v2.3.4): if you don’t use the yielded format object, you will get a cryptic error like this:

NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.call):
 app/controllers/comments_controller.rb:11:in `create'

So make sure you use it!

May 11, 2010
1 thank

No Layout, other options

While it renders to the same rules as render, you need to specify params.

You’d think this would work:

render_to_string "users/profile", :layout => false

You need to do this instead

render_to_string(:layout => "users/profile", :layout => false)
May 6, 2010
1 thank

has_and_belongs_to_many_with_deferred_save

Be aware that has_and_belongs_to_many saves association to join table immediately after assign. It does NOT wait for my_object.save. Hence if save does not get through validations (or fail for any other reason), associated records will still be in the database.

Here is a nice workaround: http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save

May 5, 2010
0 thanks

Avoiding the "multiple values for a block parameter" warning

As pointed out below, you can also have optional parameters. But you will get something like “warning: multiple values for a block parameter (0 for 1)” if you omit them.

You can avoid those warnings by passing *args and picking the parameters yourself:

define_method :that_method do |*args|

  foo = args[0] || 'my default'
  # ...
end

Now the warning will be gone. Just make sure you fetch your parameters from *args and assign a default value (unless you want them to default to nil).

May 4, 2010
0 thanks

Be careful about ActiveRecord::Base.include_root_in_json

If you have set ActiveRecord::Base.include_root_in_json = true, then when you do .to_json on an object, the output will begin with the class name rather than just a hash of attributes.

from the rails docs

konata = User.find(1)
ActiveRecord::Base.include_root_in_json = true
konata.to_json
=> { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
          "created_at": "2006/08/01", "awesome": true} }

if you try to pass this output as the argument to from_json, things will blow up.

User.new.from_json(konata.to_json[“user”]) will just pass in the attribute hash and will work.

Also, note that if you’ve use attr_accessible to limit mass assignment, you’ll have problems if you try pass in attributes that are not allowed to be mass assigned.

May 3, 2010 - (<= v1_8_7_72)
2 thanks

Changes self

This method changes the object/array the method is called on. For example:

a = ["a", "b", "c"]
b = ["x", "y", "z"]

a.concat(b)  #=> [a", "b", "c", "z", "y", "z"]
a #=> [a", "b", "c", "z", "y", "z"]

In this example the object A is modified, the method modifies the object, then returns the new object.

April 27, 2010
0 thanks

smtp syntax error 555 5.5.2

If You’re seeing a Net::SMTPFatalError (555 5.5.2 Syntax error ...) than You should check the email’s from header ! You probably have brackets while calling the from attribute setter :

Works in Rails < 2.3.3

def signup_notification(recipient)
  recipients      recipient.email_address_with_name
  subject         "New account information"
  from            %("My App" <no-reply@myapp.com>)
end

Works in Rails 2.3.5

def signup_notification(recipient)
  recipients      recipient.email_address_with_name
  subject         "New account information"
  from            'no-reply@myapp.com' # no <> brackets !
end

in Rails 2.3.3 the from email address will get wrapped with angle brackets, thus it must not have them within the address.

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
2 thanks

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
1 thank

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
3 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