Report abuse

# dohzya's comment is on target - the overhead here is in restoring 
# the context (stack frame) that was captured by 
# define_method, and handling the amount.is_a?(Array) case.
#
# Just lifting the Array case above define_method fails pretty badly, 
# as the methods don't get defined on Numeric until the end. (Also
# note: the meta-programming code relies on the TimeDsl code in
# this example - m_days calls TimeDsl's version of hours)
#
# What's really needed (if you want to go meta) is something closer
# to ActiveRecord's method:

module Meta2TimeDSL
  def self.included(base)
    base.class_eval do
      [ [:m2_second, 1], 
        [:m2_minute, 60], 
        [:m2_hour, 3600], 
        [:m2_day, [24,:m2_hours]], 
        [:m2_week, [7,:m2_days]], 
        [:m2_month, [30,:m2_days]], 
        [:m2_year, [365.25, :m2_days]]].each do |meth, amount|
          amount = amount.is_a?(Array) ? amount[0].send(amount[1]) : amount
          define_method meth do 
            self * amount
          end
          alias_method "#{meth}s".intern, meth
        end
    end
  end
end
Numeric.send :include, Meta2TimeDSL

#A couple notes:
# - The hash has been changed to an array; this is needed because 
#   Hash.each does not return elements in the order they were defined 
#   (it's not specified what order they appear in, by design).
# - This gives some speedup, (0.09s vs 0.13s, see below), but still has overhead due to the closure environment.

# A version using eval fixes the performance problems:

module Meta3TimeDSL
  def self.included(base)
    base.class_eval do
      [ [:m3_second, 1], 
        [:m3_minute, 60], 
        [:m3_hour, 3600], 
        [:m3_day, [24,:m3_hours]], 
        [:m3_week, [7,:m3_days]], 
        [:m3_month, [30,:m3_days]], 
        [:m3_year, [365.25, :m3_days]]].each do |meth, amount|
          amount = amount.is_a?(Array) ? amount[0].send(amount[1]) : amount
          eval "def #{meth}; self*#{amount}; end"
          alias_method "#{meth}s".intern, meth
        end
    end
  end
end
Numeric.send :include, Meta3TimeDSL

# Yielding the benchmarks shown below: 
# Rehearsal ------------------------------------------------------------------
# metaprogramming 360.seconds      0.140000   0.000000   0.140000 (  0.141081)
# metaprogramming(2) 360.seconds   0.090000   0.000000   0.090000 (  0.093958)
# metaprogramming(3) 360.seconds   0.040000   0.000000   0.040000 (  0.046542)
# no metaprogramming 360.hours     0.050000   0.000000   0.050000 (  0.048048)
# metaprogramming 360.minutes      0.130000   0.010000   0.140000 (  0.130627)
# metaprogramming(2) 360.minutes   0.090000   0.000000   0.090000 (  0.092048)
# metaprogramming(3) 360.minutes   0.050000   0.000000   0.050000 (  0.046882)
# no metaprogramming 360.minutes   0.040000   0.000000   0.040000 (  0.047463)
# metaprogramming 360.hours        0.130000   0.000000   0.130000 (  0.131119)
# metaprogramming(2) 360.hours     0.090000   0.000000   0.090000 (  0.092674)
# metaprogramming(3) 360.hours     0.050000   0.000000   0.050000 (  0.047141)
# no metaprogramming 360.hours     0.040000   0.000000   0.040000 (  0.045937)
# metaprogramming 360.days         0.140000   0.000000   0.140000 (  0.137041)
# metaprogramming(2) 360.days      0.090000   0.000000   0.090000 (  0.093388)
# metaprogramming(3) 360.days      0.040000   0.000000   0.040000 (  0.049240)
# no metaprogramming 360.days      0.050000   0.000000   0.050000 (  0.050279)
# metaprogramming 360.weeks        0.130000   0.000000   0.130000 (  0.136106)
# metaprogramming(2) 360.weeks     0.090000   0.000000   0.090000 (  0.099243)
# metaprogramming(3) 360.weeks     0.040000   0.000000   0.040000 (  0.051029)
# no metaprogramming 360.weeks     0.050000   0.000000   0.050000 (  0.050768)
# metaprogramming 18.months        0.130000   0.000000   0.130000 (  0.158195)
# metaprogramming(2) 18.months     0.090000   0.000000   0.090000 (  0.099450)
# metaprogramming(3) 18.months     0.050000   0.000000   0.050000 (  0.055940)
# no metaprogramming 18.months     0.050000   0.000000   0.050000 (  0.051529)
# metaprogramming 7.years          0.130000   0.000000   0.130000 (  0.170961)
# metaprogramming(2) 7.years       0.100000   0.000000   0.100000 (  0.104808)
# metaprogramming(3) 7.years       0.050000   0.000000   0.050000 (  0.054334)
# no metaprogramming 7.years       0.050000   0.000000   0.050000 (  0.050485)
# --------------------------------------------------------- total: 2.230000sec
# 
#                                      user     system      total        real
# metaprogramming 360.seconds      0.130000   0.000000   0.130000 (  0.141990)
# metaprogramming(2) 360.seconds   0.090000   0.000000   0.090000 (  0.098228)
# metaprogramming(3) 360.seconds   0.050000   0.000000   0.050000 (  0.049056)
# no metaprogramming 360.hours     0.050000   0.000000   0.050000 (  0.047961)
# metaprogramming 360.minutes      0.130000   0.000000   0.130000 (  0.130837)
# metaprogramming(2) 360.minutes   0.090000   0.000000   0.090000 (  0.093146)
# metaprogramming(3) 360.minutes   0.050000   0.000000   0.050000 (  0.048804)
# no metaprogramming 360.minutes   0.040000   0.000000   0.040000 (  0.053469)
# metaprogramming 360.hours        0.130000   0.000000   0.130000 (  0.131478)
# metaprogramming(2) 360.hours     0.090000   0.000000   0.090000 (  0.093532)
# metaprogramming(3) 360.hours     0.040000   0.000000   0.040000 (  0.047129)
# no metaprogramming 360.hours     0.040000   0.000000   0.040000 (  0.046554)
# metaprogramming 360.days         0.130000   0.000000   0.130000 (  0.131920)
# metaprogramming(2) 360.days      0.100000   0.000000   0.100000 (  0.094566)
# metaprogramming(3) 360.days      0.040000   0.000000   0.040000 (  0.046710)
# no metaprogramming 360.days      0.050000   0.000000   0.050000 (  0.047298)
# metaprogramming 360.weeks        0.130000   0.000000   0.130000 (  0.131556)
# metaprogramming(2) 360.weeks     0.090000   0.000000   0.090000 (  0.093834)
# metaprogramming(3) 360.weeks     0.050000   0.000000   0.050000 (  0.047080)
# no metaprogramming 360.weeks     0.050000   0.000000   0.050000 (  0.047726)
# metaprogramming 18.months        0.130000   0.000000   0.130000 (  0.138385)
# metaprogramming(2) 18.months     0.090000   0.000000   0.090000 (  0.094485)
# metaprogramming(3) 18.months     0.050000   0.000000   0.050000 (  0.047307)
# no metaprogramming 18.months     0.050000   0.000000   0.050000 (  0.047689)
# metaprogramming 7.years          0.130000   0.000000   0.130000 (  0.132349)
# metaprogramming(2) 7.years       0.100000   0.000000   0.100000 (  0.098260)
# metaprogramming(3) 7.years       0.050000   0.000000   0.050000 (  0.051457)
# no metaprogramming 7.years       0.050000   0.000000   0.050000 (  0.048856)

# Essentially, the eval version is identical to the handwritten version.
# Whether it is more readable, however, is another thing entirely. I can't
# see using this kind of structure for something like time which is naturally 
# fixed (it seems unlikely that a week will not be 7 days in the future...), but
# it could be useful for more variable situations (constants defined in YAML, 
# or in a DB somewhere).
#
#--Matt