Flowdock

Recent notes

RSS feed
June 25, 2009
2 thanks

Antonym of empty?

The antonym of empty? is Enumerable#any? method:

[].empty?  #=> true
[].any?    #=> false
[1].empty? #=> false
[1].any?   #=> true

Be cautious however, if your array might contain nil’s or false’s:

[false, nil].any? #=> false
June 25, 2009 - (>= v2.3.2)
1 thank

Validate() is run always before one of the more specific validation methods

I did not see this mentioned explicitly anywhere.

The method validate is run always before a call to validate_on_create or validate_on_update is made.

Example:

class Foo < ActiveRecord::Base
  def validate
    puts 'In validate'
  end

  def validate_on_create
    puts 'In validate_on_create'
  end

  def validate_on_update
    puts 'In validate_on_update'
  end
end

Now, when creating a new Foo using script/console, the output is as follows:

In validate
In validate_on_create

and when updating a Foo, the output looks like:

In validate
In validate_on_update
June 22, 2009
3 thanks

Optional local assigns

When you have a partial with optional local assigns, for instance:

<%= render :partial => 'articles/preview' %>
<%= render :partial => 'articles/preview', :locals => { :show_call_out => true } %>

And you don’t want the partial to break when the local isn’t assigned, you can reference it through the local_assigns local variable instead of through the template binding:

<% if local_assigns[:show_call_out] %>
  <em><%= format @article.call_out %></em>
<% end %>
June 19, 2009
1 thank

Usage example

class Aa

 class_inheritable_accessor :test
end
=> [:test]

Aa.test = 10
=> 10

Aa.test
=> 10

Bb = Class.new(Aa)
=> Bb

Bb.test
=> 10

Bb.test = 5
=> 5

Bb.test
=> 5

Aa.test
=> 10
June 19, 2009
2 thanks

Re: How to test different responses

In addition to using:

@request.accept = "text/javascript" #=> request JS

as rubymaverick and nachocab suggested, you can also pass :format when calling your action, eg:

it "GET #most_viewed renders #most_viewed.js.rjs template if js requested" do
  get :most_viewed, :format => 'js'
  response.should render_template('most_viewed')
end
June 18, 2009
3 thanks

Not really deprecated

This isn’t really deprecated, it’s just relocated to ActiveRecord::AttributeMethods#read_attribute

June 18, 2009
11 thanks

Important note

It has been said that “it can be compared to, but isn’t the same thing as”:

class Bar
  class << self
    attr_accessor :greeting
  end
end

Which is true. However, they are “inherited” isn’t exactly the case. Rather, cattr_accessor uses class variables.

The problem with class variables in Ruby, is that a class variable is the same object across all subclasses of a class. Consider the following example of what happens with cattr_accessor:

class A
  @@foo = 'foo'

  def self.foo
    @@foo
  end
end

p A.foo # => "foo"

class B < A
end

p B.foo # => "foo"

class B
  @@foo = 'bar'
end

p B.foo # => "bar"

So far so good you might think. However, something you might not have expected is that the variable has now also changed in class A:

p A.foo # => "bar"

This is in my opinion almost never what you’d want. More probable is that you’d want the individual class instance to have an accessor. (Remember classes are objects in Ruby). I do the following in regular Ruby:

class A
  class << self
    attr_accessor :foo
  end

  self.foo = 'foo'
end

p A.foo # => "foo"

class B < A
end

p B.foo # => nil

class B
  self.foo = 'bar'
end

p B.foo # => "bar"

p A.foo # => "foo"

As you can see, this returns nil when a value hasn’t explicitly been set yet on the new class instance. If you’d like to have inheritance without messing with the superclasses variables, have a look at ActiveSupport’s class_inheritable_accessor, which does the same as I just explained, but creates a clone of the object and assigns it to the subclass whenever a class is inherited.

What I’d normally do in Ruby to fix the issue of it returning nil is to create the accessor manually and have it set the instance variable to the default if it’s nil:

class A
  class << self
    def foo
      @foo ||= 'foo'
    end
  end
end

class B < A
end

p B.foo # => nil

So to recap:

  • cattr_accessor uses class variables (@@foo), in which case the object is shared across all subclasses of a class. Use it mainly for static data, in which case you’d probably best use a constant.

  • class_inheritable_accessor (or what I showed) uses instance variables (@foo) at the Class instance level. These variables are not shared across all subclasses.

June 18, 2009
1 thank

Expensive method!

This method builds the a new hash every time it’s called, so be cautious not to use it in loops etc.

June 17, 2009
3 thanks

Skipping validation

Unlike the save method, you can’t pass false to update_attributes to tell it to skip validation. Should you wish to do this (consider carefully if this is wise) update the attributes explicitly then call save and pass false:

@model_name.attributes = params[:model_name]
@model_name.save false
June 16, 2009
0 thanks

:discard_month implicitly sets :discard_day to true

This may not be the behaviour that you want, and setting :discard_day => false doesn’t change this. One way of getting around this is to hide the month field using CSS e.g.

#some_date_field_2i {
  display:none;
}

If you use the :default option for the date_select, the correct default month will be passed through to the controller. Using this with :discard_year will give you a dropdown with only the day, but preserve the month and year as provided by :default.

June 13, 2009 - (>= v2.3.2)
0 thanks
June 12, 2009
3 thanks

cattr_accessor_with_default

Class attribute assessors are neat if you want to set up modifiable constant-like varibles. This is how you’d normally set it up:

module MyPlugin
  class Conf
    @@awesome_level = 'huge'
    cattr_accessor :awesome_level
  end
end

Then you can call and modify it like this:

>> MyPlugin::Conf.awesome_level
=> 'huge'
>> MyPlugin::Conf.awesome_level = 'massive'
>> MyPlugin::Conf.awesome_level
=> 'massive'

If you have a pile of those accessors I’d do something like this (there might be a better way, but it works):

module MyPlugin
  class Conf
    def self.cattr_accessor_with_default(name, value = nil)
      cattr_accessor name
      self.send("#{name}=", value) if value
    end

    cattr_accessor_with_default :awesome_level, 'huge'
    cattr_accessor_with_default :speed_level, 'insane'
    cattr_accessor_with_default :indifferent_level
    cattr_accessor_with_default :craziness_level, 'nuts'
  end
end

This way you declare accessor and it’s optional default value on the same line

June 12, 2009
0 thanks

Reusing shared examples

share_examples_for “a shape” do

   it "should have a color" do
     @shape.color.should == :black
   end
 end

describe "a circle" do
  before(:all) do
    @shape = Circle.new
  end 
  it_should_behave_like "a shape"
end
June 12, 2009
0 thanks

A stub with argument and return value

it “should use a dummy method with argument and return value” do

 dummy = mock("dummy").stub!(:emulate)
 dummy.should_receive(:emulate).with(:something).and_return("Done! sir!")
 dummy.emulate(:something).should == "Done! sir!"
end
June 12, 2009
1 thank

Conditions work for lower-level validate methods too

I don’t think this is mentioned in the docs anywhere, or else I couldn’t find it: Because validate, validate_on_create, and validate_on_update are ActiveSupport::Callbacks, their symbol forms support conditions just like validates_presence_of and company:

validate :permaname_must_be_unique, :if => :normal_entry?
validate_on_create :posted_at_must_be_valid_timestamp, :unless => Proc.new {|e| e.posted_at.nil? }
validate_on_update :title_must_not_contain_apostrophes, :if => :title_starts_with_a_b?
June 11, 2009
0 thanks

Not really helpful

When you’re trying to construct a specialized path name for a partial based on a record type you’re probably better off writing your own helper.

def topic_partial_path(topic)
  ['admin', topic.class.table_name, "#{topic.class.table_name.singularize}_as_topic"].join('/')
end
June 11, 2009
4 thanks

Keeping the flash object on multiple redirects

If your controllers are redirecting more than once, the flash contents will be lost. To avoid it, execute flash.keep before each redirection.

Check ActionController::Flash::FlashHash for more handy methods (discard, now, …)

June 11, 2009
2 thanks
June 10, 2009
2 thanks

[:a, :b, :c].try([1]) ? The answer is No.

Correct way is this:

[:a, :b, :c].try(:at, 1)
June 10, 2009
0 thanks

list of predefined variables

$! The exception information message set by ‘raise’. $@ Array of backtrace of the last exception thrown.

$& The string matched by the last successful pattern match in this scope. $` The string to the left of the last successful match. $‘ The string to the right of the last successful match. $+ The last bracket matched by the last successful match. $1 to $9 The Nth group of the last successful regexp match. $~ The information about the last match in the current scope.

$= The flag for case insensitive, nil by default. $/ The input record separator, newline by default. $\ The output record separator for the print and IO#write. Default is nil. $, The output field separator for the print and Array#join. $; The default separator for String#split.

$. The current input line number of the last file that was read. $< The virtual concatenation file of the files given on command line. $> The default output for print, printf. $stdout by default. $_ The last input line of string by gets or readline.

$0 Contains the name of the script being executed. May be assignable. $* Command line arguments given for the script sans args. $$ The process number of the Ruby running this script. $? The status of the last executed child process. $: Load path for scripts and binary modules by load or require.

$“ The array contains the module names loaded by require. $DEBUG The status of the -d switch. $FILENAME Current input file from $<. Same as $<.filename. $LOAD_PATH The alias to the $:. $stderr The current standard error output. $stdin The current standard input. $stdout The current standard output. $VERBOSE The verbose flag, which is set by the -v switch. $-0 The alias to $/. $-a True if option -a (”autosplit“ mode) is set. Read-only variable. $-d The alias to $DEBUG. $-F The alias to $;. $-i If in-place-edit mode is set, this variable holds the extension, otherwise nil. $-I The alias to $:. $-l True if option -l is set (”line-ending processing“ is on). Read-only variable. $-p True if option -p is set (”loop“ mode is on). Read-only variable. $-v The alias to $VERBOSE. $-w True if option -w is set.

Source: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Pre-defined_Variables

June 10, 2009 - (v1.0.0 - v2.3.2)
5 thanks

Have the check_box checked by default

To have the check box checked by default, pass either :checked => true or :checked => 'checked' in the options. See ActionView::Helpers::InstanceTag#to_check_box_tag for details.

June 8, 2009 - (v2.2.1 - v2.3.2)
5 thanks

This is ON by default in :has_many

When defining a has_many relationship this behaviour is on by default. See has_many documentation, look for the :validate flag.

June 6, 2009
8 thanks

add index with :quiet=>true option for indices that are possibly already added

# Allows you to specify indices to add in a migration that will only be created if they do not # already exist, or to remove indices only if they already exist with :quiet=>true module ActiveRecord::ConnectionAdapters::SchemaStatements

def add_index_with_quiet(table_name, column_names, options = {})
  quiet = options.delete(:quiet)
  add_index_without_quiet table_name, column_names, options
rescue
  raise unless quiet and $!.message =~ /^Mysql::Error: Duplicate key name/i
  puts "Failed to create index #{table_name} #{column_names.inspect} #{options.inspect}"
end
alias_method_chain :add_index, :quiet

def remove_index_with_quiet(table_name, column_names, options = {})
  quiet = options.delete(:quiet)
  raise "no options allowed for remove_index, except quiet with this hack #{__FILE__}:#{__LINE__}" unless options.empty?
  remove_index_without_quiet table_name, column_names
rescue
  raise unless quiet and $!.message =~ /^Mysql::Error: Can't DROP/i
  puts "Failed to drop index #{table_name} #{column_names.inspect}"
end
alias_method_chain :remove_index, :quiet

end

June 4, 2009
2 thanks

A typical usage for a mock

You want to use a mock when you’re testing a behaviour of one of your methods that interacts with some outside world service (eg. an FTP server).

it "should login to ftp server" do
  ftp = mock('Ftp server', :null_object => true)
  Net::FTP.should_receive(:new).and_return(ftp)
  ftp.should_receive(:login).with('username', 'password')
  some_obj.connect
end

def connect
  session = Net::FTP.new('server.com')
  session.login('username', 'password')
  session.close
end
June 4, 2009
5 thanks

A catch-all format

If you’d like to specify a respond_to only for 1 or a few formats and render something else for all other formats, eg: (action.rss returns a feed but action.html or action.js should just render 404), use format.all:

respond_to do |format|
  format.rss { render_rss }
  format.all { render_404 }
end

Rails will render an empty string for all formats that don’t specify a response explicitly.

June 4, 2009
2 thanks

example

Ruby style

Dir[File.join(RAILS_ROOT, 'vendor', 'plugins', '*')]

Rails style

Dir[Rails.root.join('vendor', 'plugins', '*')]
June 4, 2009 - (v2.3.2)
1 thank

Security hole in 2.3.2

This method has a security hole in Rails 2.3.2. See http://weblog.rubyonrails.org/2009/6/3/security-problem-with-authenticate_with_http_digest for explanation.

Rails 2.3.3 should fix the problem.

June 3, 2009
7 thanks

ActiveRecord::RecordNotSaved can be triggered by accidental false return values in callbacks

You may have this exception raised if any of the defined callbacks such as ActiveRecord::Base#before_save or ActiveRecord::Base#before_create return false.

This can happen accidentally. For example:

class MyModel < ActiveRecord::Base
  before_save :assign_default_foo

protected
  def assign_default_foo
    self.foo = false
  end
end

Since assign_default_foo leaves a false value on the stack, the model will not be saved. A way around this is to simply leave nil or an empty return instead:

class MyModel < ActiveRecord::Base
  before_save :assign_default_foo

protected
  def assign_default_foo
    self.foo = false
    nil
  end
end