Recent notes

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

March 17, 2010
0 thanks

Validations

out of the box touch will run with validations

March 17, 2010
0 thanks

Create new Hash as subset of another a different way

or

only keys

old_hash = { :a => 'A', :b => 'B', :c => 'C', :d => 'D', :e => 'E', :f => 'F' }
only_keys = [ :a, :c, :f ]
new_hash = old_hash.delete_if { |k, v| !only_keys.include? k }

only values

old_hash = { :a => 'A', :b => 'B', :c => 'C', :d => 'D', :e => 'E', :f => 'F' }
only_values = [ 'A', 'D', 'G' ]
new_hash = old_hash.delete_if { |k, v| !only_values.include? v }

there are many ways to skin a cat :)

March 17, 2010 - (>= v1_8_7_72)
0 thanks

Create new Hash as subset of another

old_hash = {:a=>‘A’,:b=>‘B’,:c=>‘C’,:d=>‘D’,:e=>‘E’,:f=>‘F’}

only_keys = [:a,:c,:f]

new_hash = Hash[*old_hash.find_all{|k,v| only_keys.member?(k)}.flatten]

# => {:a=>“A”, :c=>“C”, :f=>“F”}

or for values

only_vals = [‘A’,‘D’,‘G’]

new_hash = Hash[*old_hash.find_all{|k,v| only_vals.member?(v)}.flatten]

# => {:a=>“A”, :d=>“D”}

March 16, 2010 - (>= v2.2.1)
0 thanks

config/environments/production.rb

old

ActionController::AbstractRequest.relative_url_root= "/exampleapp"

new

config.action_controller.relative_url_root= "/exampleapp"
March 16, 2010 - (v1_8_6_287 - v1_8_7_72)
0 thanks

Another Example

Delete all files in log

require 'FileUtils'
Dir["log/*"].each{|file| FileUtils.rm file}
March 13, 2010
3 thanks

Can be used with has_many associations

You can also use this to validate that a has_many association has a specified number of records on the other end:

has_many :members

validates_length_of :members, :minimum => 1
March 12, 2010
0 thanks

Complete Formatting Codes

%a - The abbreviated weekday name (“Sun”)

%A - The full weekday name (“Sunday”)

%b - The abbreviated month name (“Jan”)

%B - The full month name (“January”)

%c - The preferred local date and time representation

%C - Century (20 in 2009)

%d - Day of the month (01..31)

%D - Date (%m/%d/%y)

%e - Day of the month, blank-padded ( 1..31)

%F - Equivalent to %Y-%m-%d (the ISO 8601 date format)

%h - Equivalent to %b

%H - Hour of the day, 24-hour clock (00..23)

%I - Hour of the day, 12-hour clock (01..12)

%j - Day of the year (001..366)

%k - hour, 24-hour clock, blank-padded ( 0..23)

%l - hour, 12-hour clock, blank-padded ( 0..12)

%L - Millisecond of the second (000..999)

%m - Month of the year (01..12)

%M - Minute of the hour (00..59)

%n - Newline (n)

%N - Fractional seconds digits, default is 9 digits (nanosecond)

  • %3N millisecond (3 digits)

  • %6N microsecond (6 digits)

  • %9N nanosecond (9 digits)

%p - Meridian indicator (“AM” or “PM”)

%P - Meridian indicator (“am” or “pm”)

%r - time, 12-hour (same as %I:%M:%S %p)

%R - time, 24-hour (%H:%M)

%s - Number of seconds since 1970-01-01 00:00:00 UTC.

%S - Second of the minute (00..60)

%t - Tab character (t)

%T - time, 24-hour (%H:%M:%S)

%u - Day of the week as a decimal, Monday being 1. (1..7)

%U - Week number of the current year, starting with the first Sunday as the first day of the first week (00..53)

%v - VMS date (%e-%b-%Y)

%V - Week number of year according to ISO 8601 (01..53)

%W - Week number of the current year, starting with the first Monday as the first day of the first week (00..53)

%w - Day of the week (Sunday is 0, 0..6)

%x - Preferred representation for the date alone, no time

%X - Preferred representation for the time alone, no date

%y - Year without a century (00..99)

%Y - Year with century

%z - Time zone as hour offset from UTC (e.g. +0900)

%Z - Time zone name

%% - Literal “%” character

t = Time.now                        #=> 2007-11-19 08:37:48 -0600
t.strftime("Printed on %m/%d/%Y")   #=> "Printed on 11/19/2007"
t.strftime("at %I:%M%p")            #=> "at 08:37AM"
March 12, 2010
3 thanks

Complete Formatting Codes

NOTE: Some of these seem only to work for DateTime (e.g. %L, %N)

%a - The abbreviated weekday name (“Sun”)

%A - The full weekday name (“Sunday”)

%b - The abbreviated month name (“Jan”)

%B - The full month name (“January”)

%c - The preferred local date and time representation

%C - Century (20 in 2009)

%d - Day of the month (01..31)

%D - Date (%m/%d/%y)

%e - Day of the month, blank-padded ( 1..31)

%F - Equivalent to %Y-%m-%d (the ISO 8601 date format)

%h - Equivalent to %b

%H - Hour of the day, 24-hour clock (00..23)

%I - Hour of the day, 12-hour clock (01..12)

%j - Day of the year (001..366)

%k - hour, 24-hour clock, blank-padded ( 0..23)

%l - hour, 12-hour clock, blank-padded ( 0..12)

%L - Millisecond of the second (000..999)

%m - Month of the year (01..12)

%M - Minute of the hour (00..59)

%n - Newline (n)

%N - Fractional seconds digits, default is 9 digits (nanosecond)

  • %3N millisecond (3 digits)

  • %6N microsecond (6 digits)

  • %9N nanosecond (9 digits)

%p - Meridian indicator (“AM” or “PM”)

%P - Meridian indicator (“am” or “pm”)

%r - time, 12-hour (same as %I:%M:%S %p)

%R - time, 24-hour (%H:%M)

%s - Number of seconds since 1970-01-01 00:00:00 UTC.

%S - Second of the minute (00..60)

%t - Tab character (t)

%T - time, 24-hour (%H:%M:%S)

%u - Day of the week as a decimal, Monday being 1. (1..7)

%U - Week number of the current year, starting with the first Sunday as the first day of the first week (00..53)

%v - VMS date (%e-%b-%Y)

%V - Week number of year according to ISO 8601 (01..53)

%W - Week number of the current year, starting with the first Monday as the first day of the first week (00..53)

%w - Day of the week (Sunday is 0, 0..6)

%x - Preferred representation for the date alone, no time

%X - Preferred representation for the time alone, no date

%y - Year without a century (00..99)

%Y - Year with century

%z - Time zone as hour offset from UTC (e.g. +0900)

%Z - Time zone name

%% - Literal “%” character

t = Time.now                        #=> 2007-11-19 08:37:48 -0600
t.strftime("Printed on %m/%d/%Y")   #=> "Printed on 11/19/2007"
t.strftime("at %I:%M%p")            #=> "at 08:37AM"
March 11, 2010
1 thank

Skips validations and callbacks

The method skips validations and callbacks. That is why it should be used with caution.

Code example

person.toggle :active
March 11, 2010
0 thanks

Use message param

The message param is invaluable in case test fails – if you use it to display relevant info, you will find out what went wrong much faster.

Reworking the silly example above:

assert some_list.include?(5)

will only tell you that

<false> is not true.

which isn’t terribly helpful, is it? But if you use message like that:

assert some_list.include?(5), "some_list = #{some_list.inspect}"

the output will be:

some_list = [1, 2].
<false> is not true.

which in most cases should give you strong hints as to why the test failed.

March 11, 2010
3 thanks

Available statuses

All the available statuses (extracted from SYMBOL_TO_STATUS_CODE hash) in a slightly more readable form:

:continue                        => 100
:switching_protocols             => 101
:processing                      => 102
:ok                              => 200
:created                         => 201
:accepted                        => 202
:non_authoritative_information   => 203
:no_content                      => 204
:reset_content                   => 205
:partial_content                 => 206
:multi_status                    => 207
:im_used                         => 226
:multiple_choices                => 300
:moved_permanently               => 301
:found                           => 302
:see_other                       => 303
:not_modified                    => 304
:use_proxy                       => 305
:temporary_redirect              => 307
:bad_request                     => 400
:unauthorized                    => 401
:payment_required                => 402
:forbidden                       => 403
:not_found                       => 404
:method_not_allowed              => 405
:not_acceptable                  => 406
:proxy_authentication_required   => 407
:request_timeout                 => 408
:conflict                        => 409
:gone                            => 410
:length_required                 => 411
:precondition_failed             => 412
:request_entity_too_large        => 413
:request_uri_too_long            => 414
:unsupported_media_type          => 415
:requested_range_not_satisfiable => 416
:expectation_failed              => 417
:unprocessable_entity            => 422
:locked                          => 423
:failed_dependency               => 424
:upgrade_required                => 426
:internal_server_error           => 500
:not_implemented                 => 501
:bad_gateway                     => 502
:service_unavailable             => 503
:gateway_timeout                 => 504
:http_version_not_supported      => 505
:insufficient_storage            => 507
:not_extended                    => 510
March 11, 2010
2 thanks

Good way to see what went wrong

Use the message parameter like that:

assert_response :success, @response.body

If this fails (the response isn’t a success), it will display the response body along with the failure message, thus allowing you to quickly find out what went wrong. If the response is e.g. 500, there will probably be some exception stacktrace displayed in the body. And so on.

March 10, 2010
0 thanks

Specify your own template

You can specify you own template this way:

def notice
  ...
  @template = "some_other_name.html.erb"
end
March 8, 2010
0 thanks

Eagerness

Check out this simple example:

"Hello Ruby friend".sub(/^(.*)e/,  'X')  # => "Xnd"
"Hello Ruby friend".sub(/^(.*?)e/, 'X')  # => "Xllo Ruby friend"

The question mark turns the dotstar into non-eager mode which means it will halt on the first subsequent “e” rather than the last one. This comes in handy e.g. for Cucumber step definitions.

Okay, but not really nice:

/^I am using rvm "([^\"]*)" with gemset "(.*)"$/

Much more readable and consistent equivalent to the above:

/^I am using rvm "(.*?)" with gemset "(.*?)"$/
March 4, 2010
1 thank

AASM named scopes

If you are using the aasm plugin/gem, this will generate all named scopes for your various states.

Code example

Class Article <  ActiveRecord::Base

  include AASM

  aasm_initial_state :created

  aasm_state :published
  aasm_state :unpublished
  aasm_state :deleted
  aasm_state :created

  aasm_event :publish do
    transitions :to => :published, :from => [:created]
  end

  aasm_event :unpublish do
    transitions :to => :unpublished, :from => [:created, :published]
  end

  aasm_event :delete do
    transitions :to => :deleted, :from => [:published, :unpublished]
  end

  aasm_states.each { |s| named_scope s, :conditions => { :state => s.to_s } }  

end
March 4, 2010 - (>= 1.2.8)
5 thanks

stub_chain is very useful when testing controller code

or any other chained method call type that you’d like to stub, example:

in your controller:

def new
  @user = current_site.users.new
end

in your spec:

it "#new should assign a @user" do 
  u = mock("User")
  controller.stub_chain(:current_site, :users, :new).and_return(u)
  assigns[:user].should == u
end

whereas before you had to stub each chained method call separately:

it "#new should assign a @user" do 
  u = mock("User")
  users = mock("Users collection", :new => u)
  site = mock("Site", :users => users)
  controller.stub!(:current_site).and_return(site)
  assigns[:user].should == u
end

Please note that stub_chain was added to RSpec in version 1.2.6

March 4, 2010
3 thanks

Re: Caveat when using dynamic layouts

Since there’s no way to edit posts on here, I need to correct myself and say that what I posted before doesn’t work, since you can’t specify layout multiple times:

class OrdersController < BaseController
  layout :determine_layout, :only => :new
  layout "public", :except => :new
  # ...
end

So don’t do that. The only way to ensure that the other actions get the default theme is to drop :only/:except and do the conditions yourself:

class OrdersController < BaseController
  layout :determine_layout

private
  def determine_layout
    %w(new).include?(action_name) ? "some_layout" : "public"
  end
end

All this to say, beware of :only/:except – they aren’t as useful as you think they are.

March 3, 2010
3 thanks

Deprecated in 1.9.x!

Use FileUtils::copy instead. It is also in 1.8.x, FileUtils, so call that one instead.

March 3, 2010 - (>= v1_8_6_287)
4 thanks

makedirs(path) to create file path

mkdir will only create a single directory on an existing path. If you want to create a full path, like the `mkdir -p /full/path` command, use the makedirs method.

1.8: File.makedirs(path) 1.9: FileUtils.makedirs(path)

February 27, 2010
4 thanks

Caveat when using dynamic layouts

Worth noting that if you have a controller which inherits from another controller which has a layout, and in this child controller you’re determining the layout at runtime using a method for specific actions, the other actions you are excluding will not inherit the layout from the parent controller.

For example, if you’ve got this

class BaseController < ApplicationController
  layout "public"
end
class OrdersController < BaseController
  layout :determine_layout, :only => :new
  # index, show, new, create, edit, update, destroy ...
end

then OrdersController#index, #show, and #edit won’t get the “public” layout – in fact they won’t get a layout at all. So you’ll need to do this instead:

class OrdersController < BaseController
  layout :determine_layout, :only => :new
  layout "public", :except => :new
  # ...
end
February 26, 2010
3 thanks

default_scope on create

If you specify :conditions in your default_scope in form of a Hash, they will also be applied as default values for newly created objects. Example:

class Article
  default_scope :conditions => {:published => true}
end

Article.new.published? # => true

However:

class Article
  default_scope :conditions => 'published = 1'
end

Article.new.published? # => false
February 25, 2010
2 thanks

configuration no longer in environment.rb

configure session store in config/initializers/session_store.rb