# bad use of metaprogramming
module MetaTimeDSL
{:second => 1,
:minute => 60,
:hour => 3600,
:day => [24,:hours],
:week => [7,:days],
:month => [30,:days],
:year => [364.25, :days]}.each do |meth, amount|
define_method "m_#{meth}" do
amount = amount.is_a?(Array) ? amount[0].send(amount[1]) : amount
self * amount
end
alias_method "m_#{meth}s".intern, "m_#{meth}"
end
end
Numeric.send :include, MetaTimeDSL
# Rewrite
module TimeDSL
def second
self * 1
end
alias_method :seconds, :second
def minute
self * 60
end
alias_method :minutes, :minute
def hour
self * 3600
end
alias_method :hours, :hour
def day
self * 86400
end
alias_method :days, :day
def week
self * 604800
end
alias_method :weeks, :week
def month
self * 18144000
end
alias_method :months, :month
def year
self * 31471200
end
alias_method :years, :year
end
Numeric.send :include, TimeDSL
module RefaMetaTimeDSL
{:second => 1,
:minute => 60,
:hour => 3600,
:day => [24,:hours],
:week => [7,:days],
:month => [30,:days],
:year => [364.25, :days]}.each do |meth, amount|
self.class_eval <<-RUBY
def r_#{meth}
#{amount.is_a?(Array) ? "#{amount[0]}.#{amount[1]}" : "#{amount}"}
end
alias_method :r_#{meth}s, :r_#{meth}
RUBY
end
end
Numeric.send :include, RefaMetaTimeDSL
module EvalMetaTimeDSL
def self.included(base)
base.class_eval do
[ [:e_second, 1],
[:e_minute, 60],
[:e_hour, 3600],
[:e_day, [24,:e_hours]],
[:e_week, [7,:e_days]],
[:e_month, [30,:e_days]],
[:e_year, [365.25, :e_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", meth
end
end
end
end
Numeric.send :include, EvalMetaTimeDSL
module GoodMetaTimeDSL
SECOND = 1
MINUTE = SECOND * 60
HOUR = MINUTE * 60
DAY = HOUR * 24
WEEK = DAY * 7
MONTH = DAY * 30
YEAR = DAY * 364.25
%w[SECOND MINUTE HOUR DAY WEEK MONTH YEAR].each do |const_name|
meth = const_name.downcase
class_eval <<-RUBY
def g_#{meth}
self * #{const_name}
end
alias g_#{meth}s g_#{meth}
RUBY
end
end
Numeric.send :include, GoodMetaTimeDSL
###############################
#
#
# Benchmarks
require "rubygems"
require "rbench"
TIMES = 100_000
RBench.run(TIMES) do
format :width => 65
column :times
column :bad_meta, :title => "Bad Neta"
column :no_meta, :title => "No Meta"
column :refa, :title => "Refactored"
column :eval_meta, :title => 'Refactor 2'
column :good_meta, :title => "Better Meta"
group("MetaTimeDSL") do
report "with 360.seconds" do
bad_meta { 360.m_seconds }
no_meta { 360.seconds }
refa { 360.r_seconds }
eval_meta { 360.e_seconds}
good_meta { 360.g_seconds }
end
report "with 360.minutes" do
bad_meta { 360.m_minutes }
no_meta { 360.minutes }
refa { 360.r_minutes }
eval_meta { 360.e_minutes }
good_meta { 360.g_minutes }
end
report "with 360.hours" do
bad_meta { 360.m_hours }
no_meta { 360.hours }
refa { 360.r_hours }
eval_meta { 360.e_hours }
good_meta { 360.g_hours }
end
report "with 360.days" do
bad_meta { 360.m_days }
no_meta { 360.days }
refa { 360.r_days }
eval_meta { 360.e_days }
good_meta { 360.g_days }
end
report "with 360.weeks" do
bad_meta { 360.m_weeks }
no_meta { 360.weeks }
refa { 360.r_weeks }
eval_meta { 360.e_weeks }
good_meta { 360.g_weeks }
end
report "with 18.months" do
bad_meta { 18.m_months }
no_meta { 18.months }
refa { 18.r_months }
eval_meta { 18.e_months }
good_meta { 18.g_months }
end
report "with 7.years" do
bad_meta { 7.m_years }
no_meta { 7.years }
refa { 7.r_years }
eval_meta { 7.e_years }
good_meta { 7.g_years }
end
end
end
#####
#
# | Bad Neta | No Meta | Refactored | Refactor 2 | Better Meta |
# --MetaTimeDSL----------------------------------------------------------------------------------
# with 360.seconds x100000 | 0.143 | 0.046 | 0.033 | 0.047 | 0.048 |
# with 360.minutes x100000 | 0.152 | 0.045 | 0.033 | 0.046 | 0.046 |
# with 360.hours x100000 | 0.127 | 0.044 | 0.032 | 0.045 | 0.048 |
# with 360.days x100000 | 0.144 | 0.047 | 0.070 | 0.045 | 0.048 |
# with 360.weeks x100000 | 0.129 | 0.044 | 0.070 | 0.046 | 0.047 |
# with 18.months x100000 | 0.143 | 0.045 | 0.071 | 0.045 | 0.048 |
# with 7.years x100000 | 0.143 | 0.046 | 0.089 | 0.054 | 0.050 |
#
####