Created
March 9, 2011 16:14
-
-
Save atimin/862474 to your computer and use it in GitHub Desktop.
Пример приемов метапрограммирования на Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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