Our Daily Method #9: String#+@

Geplaatst door Michiel de Mare vr, 15 feb 2008 08:00:00 GMT

It’s Friday so we’ll keep it short.


[String,Symbol].each{|c|c.class_eval {alias +@ to_sym}}

Is it a Symbol? Is it a String? Who gives a damn!


hash[+key]

Geplaatst in , ,  | geen reacties

Our Daily Method #8: Object.alias_class_method

Geplaatst door Remco van 't Veer do, 14 feb 2008 08:00:00 GMT

In an earlier daily method an alias is created for a class method. This is a bit tricky since the Module#alias_method method only operates in instance methodes. Fortunately a class is an instance of Class so we can use that instance as a context to use alias_method.

To extend an object instance we use the class<< notation. This adds a singleton/eigen/virtual/meta-class to the given instance which you can use to add methodes. Inside a class definition the self object points to the instance of the class begin defined. This allows us the do the following:


class Foo
  class << self
    alias_method :neu, :new
  end
end

This Foo class can now be instanciated using neu (note, new is just a class method). But this class<< stuff inside a class is a bit messy. I prefer using the following extension to Object:


class Object
  def self.alias_class_method(new_name, old_name)
    meta = class << self; self; end
    meta.send :alias_method, new_name, old_name
  end
end

Now we can write:


class Foo
  alias_class_method :neu, :new
end

For more information about eigenclasses see the discussion Nutter and Lam had on Ruby-core.

Geplaatst in , ,  | geen reacties

Our Daily Method #7: Enumerable#mjoin

Geplaatst door Michiel de Mare wo, 13 feb 2008 08:00:00 GMT

You all know flatten. Flatten takes an array and removes all structure from it. But sometimes you’ve got an array of arrays of arrays, and want to turn it into an array of arrays. What we need is a flatten-light: mjoin

module Enumerable
  def mjoin
    inject!([]) {|memo,x| a.each {|x| memo << x}}
  end
end
(read more about inject!).

When do you use this? Whenever you want to use map but you don’t always want to return exactly one value, and some or all of the values are arrays (which means you can’t use flatten).

Does that sound exceedlingly rare? In fact, it is a very common structure. A table is an array of arrays, and a join is an operation which returns zero or more results per row. And this also explains the name! (join, of course, is already taken – mjoin was inspired by this article).

I plan to talk more about using the table datastructure in the near future.

Geplaatst in , ,  | geen reacties

Our Daily Method #6: AR.find(..).each

Geplaatst door Remco van 't Veer di, 12 feb 2008 08:00:00 GMT

AR.find(..).each

Working with ActiveRecord it often bothers me I need to write each after doing a find-all. I can give find a block but is just ignored without any warning.

So today no new method but the removal of a method!


class ActiveRecord::Base
  def self.find_with_block_sensitivity(*args)
    r = find_without_block_sensitivity(*args)
    [r].flatten.each {|v| yield v} if block_given?
    r
  end

  class << self
    alias_method :find_without_block_sensitivity, :find
    alias_method :find, :find_with_block_sensitivity
  end
end

Now, instead of:


Article.find(:all).each {|article| puts article.title}

we can write:


Article.find(:all) {|article| puts article.title}

Look ma! No each! Also works for single object results.

Geplaatst in , , ,  | geen reacties

Our Daily Method #5: Enumerable#inject!

Geplaatst door Michiel de Mare ma, 11 feb 2008 07:20:00 GMT

Enumerable#inject is a terrific and flexible method – so flexible that I regularly abuse it:

Take for instance this fragment:


arr_of_arr.inject(Hash.new(0)) do |h,a|
  a.each {|x| h[x] += 1 }
  h
end

# I could simply use a local variable
h = Hash.new(0)
ar_of_ar.each do |h,a|
  a.each {|x| h[x] += 1 }
end

# Ruby 1.9 offers Object#tap
Hash.new(0).tap do |h|
  ar_of_ar.each do |a|
    a.each {|x| h[x] += 1 }
  end
end

And yet I like my inject-solution better – no local variables or superfluous blocks. Why not change inject by not returning the value of the block and using it in the next iteration, why not each time pass in the argument to inject. Let’s name this function inject!


module Enumerable
  def inject!(memo)
    each {|x| yield(memo,x) }
    memo
  end
end

ar_of_ar.inject!(Hash.new(0)){|h,a| a.each{|x| h[x] += 1}}

Geplaatst in , ,  | geen reacties

Our daily Method #4: Hash#excluding en Hash#only

Geplaatst door Remco van 't Veer vr, 08 feb 2008 08:00:00 GMT

This will look familiar:


u = params[:user]
attr = if u[:password].blank?
  u.reject do |k,_| 
    [:password, :password_confirmation].include?(k)
  end
end
User.update_attributes(attr || u)

But it smells like something reusable. I would like to write:


u = params[:user]
attr = if u[:password].blank?
  u.excluding(:password, :password_confirmation)
end
User.update_attributes(attr || u)

Easily to implement, including its little brother only, with:


class Hash
  def excluding(*keys)
    reject{|k,_| keys.include?(k)}
  end

  def only(*keys)
    reject{|k,_| !keys.include?(k)}
  end
end

Disclaimer: the above example doesn’t really work in Rails because params is a hash with indifferent access. Making an indifferent variant is up you! :)

Geplaatst in , ,  | 2 reacties

Our Daily Method #3: Hash.multi

Geplaatst door Remco van 't Veer do, 07 feb 2008 08:00:00 GMT

You have probably used it before; Hash.new with a block to make a two-dimensional hash;


map = Hash.new{|h,k| h[k] = {}}
map[:dragon][:strength] = 9

But sometimes you need a multi-dimensional hash;


def Hash.multi
  Hash.new{|h,k| h[k] = Hash.multi}
end

I use it in an application to construct a simple cache with complex keys;


CACHE = Hash.multi

def expensive_query(key)
  cache = CACHE[:query][auth_level][current_channel]
  unless cache.has_key?(key)
    cache[key] = Server.query(auth_level, current_channel, key) 
  else
    cache[key]
  end
end

Geplaatst in , ,  | geen reacties

Our Daily Method #2: Numeric#in

Geplaatst door Michiel de Mare wo, 06 feb 2008 06:00:00 GMT

Most web-applications do not appear to have a lot to do with random numbers, online poker sites excepted (I hope), but they are surprisingly useful nevertheless. That’s why I’m introducing a new notation to express chance.


# this certainly works
redirect_to home_url if rand < 0.2222

# this is better
redirect_to home_url if rand(9) < 2

# but not as beautiful as this
redirect_to home_url if 2.in 9

# the code
class Numeric 
  def in(other)
    rand(other) < self
  end
end

Geplaatst in , ,  | geen reacties

Our Daily Method #1: Range#coerce

Geplaatst door Michiel de Mare di, 05 feb 2008 08:00:00 GMT

We’d like to introduce a new blog category: Our Daily Method. We’ll demonstrate short, general purpose methods, which might be suitable for the standard library. We’re accepting requests!

We’ll kick off with Range#coerce. The problem is: you’ve got a value and you wish to ensure that it’s within a certain interval. Sounds ideal for a Range, but it’s missing from Ruby’s Range, although there’s include? (and in Ruby 1.9 cover?) to test whether the value is in the range.

Hence the following:

class Range
  def coerce(x)
    x < first ? first : x > last ? last : x
  end
end

(1..12).coerce(999)   # => 12

Geplaatst in , ,  | geen reacties

Oudere artikelen: 1 2