Wrap text
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