Flowdock

Recent good notes

RSS feed
March 10, 2009 - (>= v2.1.0)
8 thanks

Use lambda to avoid caching of generated query

If you’re using a named_scope that includes a changing variable you need to wrap it in a lambda to avoid the query being cached and thus becoming unaffected by future changes to the variable, example:

named_scope :translated, :conditions => { :locale => I18n.locale }

Will always return the same locale after the first hit even though I18n.locale might change. So do this instead:

named_scope :translated, lambda { { :conditions => { :locale => I18n.locale } } }

Ugly, but at least it’s working as we expect it…

March 5, 2009
7 thanks

String#match will match single token only

>> s = “{{person}} ate {{thing}}”

> “{{person}} ate {{thing}}”

>> r = /{{(.*?)}}/

> {{}}

>> s.match®.captures

> [“person”]

Using String#scan pulls out all tokens you were searching for:

>> s.scan®.flatten

> [“person”, “thing”]

March 4, 2009
10 thanks

Differences between normal or-assign operator

Differences between this method and normal memoization with ||=:

  • memoize works with false/nil values

  • Potential arguments are memoized

Take the following example:

def allowed?
  @allowed ||= begin
    # Big calculation
    puts "Worked"
    false
  end
end

allowed? # Outputs "Worked"
allowed? # Outputs "Worked" again

Since @allowed is set to false (this is also applicable with nil), the ||= operator will move on the the next statement and will not be short-circuited.

When you use memoize you will not have this problem.

def allowed?
  # Big calculation
  puts "Worked"
  false
end
memoize :allowed?

allowed? # Outputs "Worked"
allowed? # No output

Now, look at the case where we have parameters:

def random(max=10)
  @random ||= rand(max)
end

random     # => 4
random     # => 4 -- Yay!
random(20) # => 4 -- Oops!

Better use memoize again!

def random(max=10)
  rand(max)
end
memoize :random

random     # => 6
random     # => 6 -- Yay!
random(20) # => 12 -- Double-Yay!
random     # => 6 -- Head a'splode
March 4, 2009
5 thanks

Usage

This defines attr_accessors at a class level instead of instance level.

class Foo
  cattr_accessor :greeting
end

Foo.greeting = "Hello"

This could be compared to, but is not the same as doing this:

class Bar
  class << self
    attr_accessor :greeting
  end
end

Bar.greeting = "Hello"

The difference might not be apparent at first, but cattr_accessor will make the accessor inherited to the instances:

Foo.new.greeting #=> "Hello"
Bar.new.greeting # NoMethodError: undefined method `greeting' for #<Bar:0x18e4d78>

This inheritance is also not copy-on-write in case you assumed that:

Foo.greeting  #=> "Hello"
foo1, foo2 = Foo.new, Foo.new

foo1.greeting = "Hi!"

Foo.greeting  #=> "Hi!"
foo2.greeting #=> "Hi!"

This makes it possible to share common state (queues, semaphores, etc.), configuration (max value, etc.) or temporary values through this.

March 3, 2009
9 thanks

File class documentation

Most of the File class documentation is located in IO class docs. What you see here is what ‘ftools’ gives you.

February 27, 2009
6 thanks

Extend with an anonymous module

You can extend with an anonymous module for one-off cases that won’t be repeated:

belongs_to :container, :polymorphic => true, :extend => ( Module.new do
    def find_target
      ...
    end
  end )

The parentheses are important, will fail silently without them.

February 24, 2009
9 thanks

Specialized versions of find with method_missing

Check ActiveRecord::Base.method_missing for documentation on the family of “magic” find methods (find_by_x, find_all_by_x, find_or_create_by_x, etc.).

February 24, 2009 - (>= v2.2.1)
5 thanks

ATM does not work in Rails 2.3 Edge

add to test/spec_helper to make it work again…

#spec_helper / test_helper
include ActionController::TestProcess
February 23, 2009
6 thanks

Nested with_options

You can nest with_options blocks, and you can even use the same name for the block parameter each time. E.g.:

class Product
  with_options :dependent => :destroy do |product|
    product.with_options :class_name => 'Media' do |product|
      product.has_many :images, :conditions => {:content_type => 'image'}
      product.has_many :videos, :conditions => {:content_type => 'video'}
    end

    product.has_many :comments
  end
end
February 22, 2009
3 thanks

CAUTION! :frequency option description is misleading

To use event-based observer, don’t supply :frequency param at all. :frequency => 0 causes JS error.

Use this option only if time-based observer is what you need.

February 20, 2009
5 thanks

Static and dynamic attachments

You can attach static files directly:

attachment :content_type => "image/jpeg", :body => File.read("someimage.jpg")

and you can also define attachments dynamically by using a block:

attachment "text/csv" do |a|
  a.body = my_data.to_csv
end
February 18, 2009
4 thanks

Turn off for individual controllers/actions

To disable protection for all actions in your controller use skip_before_filter:

skip_before_filter :verify_authenticity_token

You can also pass :only and :except to disable protection for specific actions, e.g:

skip_before_filter :verify_authenticity_token, :only => :index
February 17, 2009
4 thanks

Date_select with assert_valid_keys

If you are using date_select with assert_valid_keys you have to allow 3 parameters named field(1i), field(2i) and field(3i).

For example with field

date_select("post", "written_on")

You have to allow following fields:

params[:post].assert_valid_keys( 
  'written_on(1i)', 'written_on(2i)', 'written_on(3i)'
)
February 17, 2009
7 thanks

Usage examples

Basic usage:

User.should_receive(:find).with(:all, anything).and_return("hello world")

Now:

User.find(:all, :conditions => "foo")  #=> "hello world"

But you can also use blocks for more complex matching logic. For example:

User.should_receive(:find) { |*args|
  if args.size == 2
    "received two arguments"
  else
    "something else"
  end
}.at_least(:once)

Now:

User.find(:all, :conditions => "bar")  #=> "received two arguments"
User.find(5)                           #=> "something else"

Of course normally you’d return mocks instead of strings.

February 17, 2009
9 thanks

Empty elements

If you want to output an empty element (self-closed) like “br”, “img” or “input”, use the tag method instead.

February 16, 2009
6 thanks

Usage example

Some examples:

# Remove even numbers
(1..30).reject { |n| n % 2 == 0 }
# => [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]

# Remove years dividable with 4 (this is *not* the full leap years rule)
(1950..2000).reject { |y| y % 4 != 0 }
# => [1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]

# Remove users with karma below arithmetic mean
total = users.inject(0) { |total, user| total += user.karma }
mean = total / users.size
good_users = users.reject { |u| u.karma < mean }
February 13, 2009
3 thanks

New test syntax

You can use either one and even mix in the same test case if you want:

class Test < Test::Unit::TestCase
  # old way to define a test method (prefix with test_)
  def test_should_be_valid_without_content
    assert Comment.new.valid?
  end

  # new way to define a test
  test "should be valid without content" do
    assert Comment.new.valid?
  end
end
February 12, 2009
4 thanks

Real life use

If you’re wondering what the base64 format is used for, here are some examples:

Just note that the encoded (character) data is about 30% larger than un-encoded (binary) data.

February 12, 2009
4 thanks

Binary files

Another real important flag is b when dealing with binary files. For example to download an mp3 from the internet you need to pass the b flag or the data will be screwed up:

# Downloads a binary file from the internet
require 'open-uri'
url = "http://fubar/song.mp3"
open(url, 'rb') do |mp3|
  File.open("local.mp3", 'wb') do |file|
    file.write(mp3.read)
  end
end

Don’t say you haven’t been warned. :)

February 12, 2009
3 thanks

Other regular-expression modifiers

Likewise you can set Regexp::IGNORECASE directly on the regexp with the literal syntax:

/first/i
# This will match "first", "First" and even "fiRSt"

Even more modifiers

  • o – Perform #{} interpolations only once, the first time the regexp literal is evaluated.

  • x – Ignores whitespace and allows comments in * regular expressions

  • u, e, s, n – Interpret the regexp as Unicode (UTF-8), EUC, SJIS, or ASCII. If none of these modifiers is specified, the regular expression is assumed to use the source encoding.

Literal to the rescue

Like string literals delimited with %Q, Ruby allows you to begin your regular expressions with %r followed by a delimiter of your choice.

This is useful when the pattern you are describing contains a lot of forward slash characters that you don’t want to escape:

%Q(http://)
# This will match "http://"
February 12, 2009
4 thanks

Literal syntax

As you propably know you can create an Array either with the constructor or the literal syntax:

Array.new == []
# => true

But there is also another nice and concise literal syntax for creating Arrays of Strings:

["one", "two", "three"] == %w[one two three]
# => true

You can use any kind of parenthesis you like after the %w, either (), [] or {}. I prefer the square brackets because it looks more like an array.

February 12, 2009
4 thanks

Useful scenario

This can be quite useful, for example when writing a command line script which takes a number of options.

Example

Let’s say you want to make a script that can make the basic CRUD operations. So want to be able to call it like this from the command line:

> my_script create
> my_script delete

The following script allows you to use any abbreviated command as long as it is unambiguous.

# my_script.rb
require 'abbrev'

command = ARGV.first
actions = %w[create read update delete]
mappings = Abbrev::abbrev(actions)
puts mappings[command]

That means you can call it like this:

> my_script cr
> my_script d

And it will print:

create
delete
February 10, 2009
8 thanks

Security issue with non-HTML formats

Please note that using default to_xml or to_json methods can lead to security holes, as these method expose all attributes of your model by default, including salt, crypted_password, permissions, status or whatever you might have.

You might want to override these methods in your models, e.g.:

def to_xml
  super( :only => [ :login, :first_name, :last_name ] )
end

Or consider not using responds_to at all, if you only want to provide HTML.

February 10, 2009
3 thanks

Cheat Sheet

I have written a short introduction and a colorful cheat sheet for Perl Compatible Regular Expressions (PCRE) as used by Ruby’s Regexp class:

http://www.bitcetera.com/en/techblog/2008/04/01/regex-in-a-nutshell/

February 9, 2009
5 thanks
February 7, 2009 - (>= v2.2.1)
3 thanks

Deprecated

This method is deprecated. You should use:

I18n.translate('activerecord.errors.messages')
February 4, 2009
3 thanks

Multiline regexps

A shortcut for multiline regular expressions is

/First line.*Other line/m

(notice the trailing /m)

For example:

text = <<-END
  Hello world!
  This is a test.
END

text.match(/world.*test/m).nil?  #=> false
text.match(/world.*test/).nil?   #=> true
February 3, 2009
3 thanks

Possible gotcha

Please note that exists? doesn’t hold all the conventions of find, i.e. you can’t do:

Person.exists?(:conditions => ['name LIKE ?', "%#{query}%"]) # DOESN'T WORK!