Skip to content

Instantly share code, notes, and snippets.

@atimin
Created March 9, 2011 16:14
Show Gist options
  • Save atimin/862474 to your computer and use it in GitHub Desktop.
Save atimin/862474 to your computer and use it in GitHub Desktop.
Пример приемов метапрограммирования на Ruby
class HashStub
# Значения атрибутов хранятся в хэше @data который
# инициализируется при создании объекта
def initialize
@data = {}
end
# Наша реализация method_missing
# name - имя метода который был не найден
# args - аргументы вызова метода собранные в массив
# Алгорим работы: если было присвоение атрибуту,
# т.е. имя метода завершается на "=",
# то сохраняем значение атрибута в хэше ключем значения будет
# имя метода без знака равно
# Если же было не присвоениеш, а чтение, то мы вытаскиваем значение
# из хэша по ключу имени метода
def method_missing(name, *args)
name = name.to_s
if name[-1].chr == '='
@data[name[0..-2].to_sym] = args[0]
else
@data[name.to_sym]
end
end
end
class EvalStub
# Если было первое присвоение атрибуту создадим переменную объекта
# и методы ацесоры, после чего вызовем метод присвоения
def method_missing(name, *args)
name = name.to_s
if name[-1].chr == '='
add_attr(name[0..-2])
send(name,args[0])
end
end
private
# Метод определяет перемению объекта и методы ацесоры к ней
# Например для name = attr
# получим переменную @attr
# и методы
# def attr=(val); @attr = val; end
# def attr; @attr; end
def add_attr(name)
var = name
get = name
set = name + "="
code = ""
code << "def #{set}(val); @#{var} = val; end\n"
code << "def #{get}; @#{var}; end"
instance_eval code
end
end
class DefineMethodStub
# Если было первое присвоение атрибуту создадим переменную объекта
# и методы ацесоры, после чего вызовем метод присвоения
def method_missing(name, *args)
name = name.to_s
if name[-1].chr == '='
add_attr(name[0..-2])
send(name,args[0])
end
end
private
# Метод определяет перемению объекта и методы ацесоры к ней
# Например для name = attr
# получим переменную @attr
# и методы
# def attr=(val); @attr = val; end
# def attr; @attr; end
def add_attr(name)
var = :"@#{name}"
get = :"#{name}"
set = :"#{name}="
self.class.send(:define_method, set) do |val|
instance_variable_set var,val
end
self.class.send(:define_method, get) do
instance_variable_get var
end
end
end
class StaticObject
def attr
@attr
end
def attr=(val)
@attr = val
end
end
if __FILE__ == $0
require 'benchmark'
TIMES = 10000
h = HashStub.new
h.attr = 0
e = EvalStub.new
e.attr = 0
d = DefineMethodStub.new
d.attr = 0
t = StaticObject.new
t.attr = 0
Benchmark.bmbm do |x|
x.report("Инициализация новых атрибутов с помощью HashStub") do
TIMES.times { |i| h.send(:"attr_#{i}=", i) }
end
x.report("Инициализация новых атрибутов с помощью EvalStub") do
TIMES.times { |i| e.send(:"attr_#{i}=", i) }
end
x.report("Инициализация новых атрибутов с помощью DefineMethodStub") do
TIMES.times { |i| d.send(:"attr_#{i}=", i) }
end
x.report("Запись значения в один и тот же атрибут c помощью HashStub") do
(TIMES*10).times { |i| h.send(:"attr=", i) }
end
x.report("Запись значения в один и тот же атрибут c помощью EvalStub") do
(TIMES*10).times { |i| e.send(:"attr=", i) }
end
x.report("Запись значения в один и тот же атрибут c помощью DefineMethodStub") do
(TIMES*10).times { |i| d.send(:"attr=", i) }
end
x.report("Запись значения в один и тот же атрибут статичного объекта") do
(TIMES*10).times { |i| t.send(:"attr=", i) }
end
x.report("Чтение значения одного и того же атрибута c помощью HashStub") do
(TIMES*10).times { |i| h.send(:"attr") }
end
x.report("Чтение значения одного и того же атрибута c помощью EvalStub") do
(TIMES*10).times { |i| e.send(:"attr") }
end
x.report("Чтение значения одного и того же атрибута c помощью DefineMethodStub") do
(TIMES*10).times { |i| d.send(:"attr") }
end
x.report("Чтение значения одного и того же атрибута статичного объекта") do
(TIMES*10).times { |i| t.send(:"attr") }
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment