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 ruby, english, daily_method | geen reacties
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 ruby, english, daily_method | geen reacties
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 ruby, english, daily_method | geen reacties
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 ruby, ruby on rails, english, daily_method | geen reacties
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 ruby, english, daily_method | geen reacties
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 ruby, english, daily_method | 2 reacties
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 ruby, english, daily_method | geen reacties
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 ruby, english, daily_method | geen reacties
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)
Geplaatst in ruby, english, daily_method | geen reacties