Flowdock

Recent notes

RSS feed
April 25, 2009
4 thanks

Set :use_route to nil to let Rails pick the best route

Imagine the following case. You have two landing pages, one generic one, and an account specific one. The urls are as follows:

map.landing 'landing', :controller => 'landing', :action => 'index'
map.account_landing 'accounts/:account_id/landing', :controller => 'landing', :action => 'index'

Now imagine you want a path to the landing page, using the most specific route possible. If you have an account_id, use it, if not, skip it.

You could do

url_for(:controller => 'landing', :action => 'index', :account_id => current_account)

If current_account is set you’ll get “/accounts/:account_id/landing” if not, you’ll get “/landing”. However, that just looks ugly.

Enter :use_route => nil.

landing_path(:account_id => nil)                    # => '/landing'
landing_path(:account_id => 1)                      # => '/landing?account_id=1'
landing_path(:account_id => nil, :use_route => nil) # => '/landing'
landing_path(:account_id => 1, :use_route => nil)   # => '/accounts/1/landing'

Setting :use_route to nil, is equivalent to the earlier #url_for example.

April 24, 2009
2 thanks

have your to_param begin with the object's id

If you overwrite the to_param method in your model class such that it does not begin with its id, you can be in for a nasty surprise:

Example

class User
  def to_param
    self.login
  end
  ...
end

Let’s say you have a user called “bob”, than you might think this works:

>> bob = User.find(3)
=> #<User id: 3, login: "bob", ...>
>> User.find(bob.to_param)
ActiveRecord::RecordNotFound: Couldn't find User with ID=bob

But it’s not the reason being that Rails find method looks for a beginning number (d+) and uses that to look up the record (and ignores everything that comes after the last digit). So the solution is to have your to_param return something that begins with the object’s id, like so:

Example

class User
  def to_param
    "#{self.id}-#{self.login}"
  end
  ...
end

>> bob = User.find(3)
=> #<User id: 3, login: "bob", ...>

>> User.find(bob.to_param)

> # id: 3, login: “bob”, …>

>> bob.to_param

> “3-bob”

April 24, 2009
0 thanks

test

I need that program :)

April 23, 2009
5 thanks
April 23, 2009
6 thanks

Argument Ordering

Be aware that the order of arguments for this method is the opposite of File.join:

File.expand_path('foo', '/bar')   # => "/bar/foo"
File.join('foo', '/bar')          # => "foo/bar"
April 23, 2009
0 thanks

See Also: IO Class Methods

There are other more specific methods defined in the IO class: IO.open for files, IO.popen for pipes.

April 23, 2009
1 thank

Customize Formatting with a Subclass

Instead of passing in a formatter block, you can always create a subclass that defines the format:

require 'logger'

class MyLogger < Logger
  def format_message(severity, datetime, progname, msg)
    "[%s %s] %s\n" % [ severity, datetime.strtftime("%H:%M"), msg ]
  end
end

This can be easier than always passing the same formatter option.

April 23, 2009
2 thanks

Rails and Ruby 1.8.7 Extensions

Note that the use of Symbol#to_proc requires either Rails or Ruby 1.8.7. Prior versions will show:

['a', 'b', 'c'].collect(&:capitalize)
 #  => TypeError: wrong argument type Symbol (expected Proc)
April 23, 2009
5 thanks

Handy shorthand for array manipulation

You may write something like this:

>> ['a', 'b', 'c'].collect{|letter| letter.capitalize}
=> ["A", "B", "C"]

But it looks so much nicer this way:

>> ['a', 'b', 'c'].collect(&:capitalize)
=> ["A", "B", "C"]
April 23, 2009
1 thank

Using strings as association names

Beware, that using strings as association names, when giving Hash to :include will render errors:

The error occurred while evaluating nil.name

So, :include => [‘assoc1’, ‘assoc2’ ] will work, and :include => [ {‘assoc1’ => ‘assoc3’}, ‘assoc2’] won’t. Use symbols:

Proper form

:include => [ {:assoc1 => :assoc3}, ‘assoc2’]

April 23, 2009 - (v2.0.0 - v2.3.2)
0 thanks

Real HTML_ESCAPE VALUE

Real value:

HTML_ESCAPE = { '&' => '&',  '>' => '>',   '<' => '<', '"' => '"' }
April 21, 2009
3 thanks

Format not coming out properly?

Date, Time and DateTime may have different formats defined.

If you do:

@user.created_at.to_formatted_s(:long_ordinal)

You will get (or something):

April 16th, 2009 22:03 

But if you do:

@user.created_at.to_date.to_formatted_s(:long_ordinal)

You will get:

April 16th, 2009

So, be sure you know which one you are working with.

April 21, 2009
3 thanks

To throw an exception, use Kernel#raise

Other languages use the term throw for raising exceptions, but Ruby has a specific raise call for that.

April 21, 2009
7 thanks

Do not forget to add indexes

Don’t forget to add indexes to HATM table:

add_index :developers_projects, [:developer_id, :project_id]
April 21, 2009
1 thank

Merges with inherited values from super class

http://www.spacevatican.org/2008/8/19/fun-with-class-variables

“When you set a class_inheritable_array or a class_inheritable_hash you are actually concatenating (or merging) with the value inherited from the super class.”

Code example

class Base
  class_inheritable_hash :attrs
  self.attrs = {:name => 'Fred'}
end

class Derived < Base
  self.attrs = {:export => 'Pain'}
end

Derived.attrs #=> {:name => 'Fred', :export => 'Pain'}
April 21, 2009 - (>= v2.3.2)
5 thanks

strip_tags method not functioning in controllers, models, or libs

It comes up with an error about white_list_sanitizer undefined in the class you’re using it in. To get around this, use:

ActionController::Base.helpers.strip_tags('string')

To shorten this, add something like this in an initializer:

class String
  def strip_tags
    ActionController::Base.helpers.strip_tags(self)
  end
end

then call it with:

'string'.strip_tags
April 21, 2009 - (>= v2.3.2)
3 thanks

sanitize method not functioning in controllers, models, or libs

It comes up with an error about white_list_sanitizer undefined in the class you’re using it in. To get around this, use:

ActionController::Base.helpers.sanitize('string')

To shorten this, add something like this in an initializer:

class String
  def sanitize
    ActionController::Base.helpers.sanitize(self)
  end
end

then call it with:

'string'.sanitize
April 20, 2009
2 thanks

Cycle with first and last

I needed a cycle that was also aware of the first and last items in the collection. This is adapted from a snippet I found while Googling:

def cycle_with_first_last(object, collection, options = { })
  addition = ""
  addition += " #{options[:first]}" if object == collection.first
  addition += " #{options[:last]}"if object == collection.last
  cycle(options[:odd], options[:even]) + addition
end

Just put that in your helpers…

April 16, 2009
1 thank

Extracting the First Element

To extract the first element from an Array, use shift:

array = [ 1, 2, 3 ]           # => [ 1, 2, 3 ]
array.first                   # => 1
array                         # => [ 1, 2, 3 ]
array.shift                   # => 1
array                         # => [ 2, 3 ]
April 16, 2009
1 thank

Extracting the Last Element

To remove the last element from the Array, use pop:

array = [ 1, 2, 3 ]           # => [ 1, 2, 3 ]
array.last                    # => 3
array                         # => [ 1, 2, 3 ]
array.pop                     # => 3
array                         # => [ 1, 2 ]
April 16, 2009
12 thanks

Parameters for Hash#inject

When running inject on a Hash, the hash is first converted to an array before being passed through.

The typical Enumerable#inject approach would be to simply capture the value:

array.inject(...) do |c, v|
end

In the case of a Hash, v is actually a key/value pair Array. That is the key is v.first and the value is v.last, however using the pair this way is awkward and can lead to confusion.

Better to simply expand the parameters in the block definition:

hash.inject(...) do |c, (k, v)|
end

Where c is the traditional carry variable and k/v represent key and value respectively.

April 16, 2009 - (>= v2.0.0)
2 thanks

Example

This function can be used to pass the ID of selected item, for example:

# with select or collection_select helpers:
{ :onchange => remote_function(:url => { :action => 'do_smth' }, :with => "'id=' + $('the_id').value") }

# and grab ID in controller action as usually: 
YourModel.find(params[:id])
April 16, 2009
2 thanks

Various use cases

Example

user = User.new
user.name = 'Akhil Bansal'
user.save

user =  User.new(:name => 'Akhil')
user.save

User.new do |u|
  u.name = 'Akhil'
  u.save
end
April 16, 2009
0 thanks

Known unknowns

In case it isn’t obvious - this is what you use when you’re fleshing out all the tests that you haven’t written yet. eg if you have a set of twenty tests for a complex piece of functionality, and just want to write out the “should” declarations (or equivalent), so you don’t forget all the corner cases… then fill out the tests themselves. Putting an assert_fail makes sure you notice if you forget to come back and fill in the body of a test.

April 15, 2009 - (>= v2.3.2)
0 thanks

Testing HTTP Digest authentication

Testing HTTP Digest authentication is a bit tricky. I wrote a post describing how to accomplish it.

http://lightyearsoftware.com/blog/2009/04/testing-http-digest-authentication-in-rails/

Note also that Digest auth is broken for REST actions using PUT or DELETE. There is an open Lighthouse ticket for this, #2490:

rails.lighthouseapp.com/projects/8994/tickets/2490-http-digest-auth-uses-wrong-request-method-for-put-delete

April 15, 2009
0 thanks

Using global $! to address exception

@noxyu3m: Your code is actually syntactically wrong. The global is called $!

Your code should have been:

def create
  @model = Model.new(params[:model)
  @model.save!
rescue
  logger.error($!.to_s)
end

Although I would prefer

def create
  @model = Model.new(params[:model)
  @model.save!
rescue ActiveRecord::RecordInvalid
  logger.error($!.to_s)
end

to only catch expected exceptions, just like the documentation proposed.

April 14, 2009 - (v2.2.1 - v2.3.2)
2 thanks