Ответ 1
Будет ли это работать для вас?
class Foo
def initialize(hash)
hash.each { |k,v| instance_variable_set("@#{k}", v) }
end
end
Возможный дубликат:
Создание идиоматического объекта в рубине
Есть много случаев, когда у меня есть метод initialize
, который выглядит следующим образом:
class Foo
def initialize bar, buz, ...
@bar, @buz, ... = bar, buz, ...
end
end
Есть ли способ сделать это с помощью простой команды, например:
class Foo
attr_constructor :bar, :buz, ...
end
где символы представляют собой имя переменных экземпляра (с духом/ароматом attr_accessor
, attr_reader
, attr_writer
)?
Мне было интересно, есть ли встроенный способ или более элегантный способ сделать что-то вроде этого:
class Class
def attr_constructor *vars
define_method("initialize") do |*vals|
vars.zip(vals){|var, val| instance_variable_set("@#{var}", val)}
end
end
end
чтобы я мог использовать его следующим образом:
class Foo
attr_constructor :foo, :bar, :buz
end
p Foo.new('a', 'b', 'c') # => #<Foo:0x93f3e4c @foo="a", @bar="b", @buz="c">
p Foo.new('a', 'b', 'c', 'd') # => #<Foo:0x93f3e4d @foo="a", @bar="b", @buz="c">
p Foo.new('a', 'b') # => #<Foo:0x93f3e4e @foo="a", @bar="b", @buz=nil>
Будет ли это работать для вас?
class Foo
def initialize(hash)
hash.each { |k,v| instance_variable_set("@#{k}", v) }
end
end
Я бы использовал OpenStruct
:
require 'ostruct'
class Foo < OpenStruct
end
f = Foo.new(:bar => "baz")
f.bar
#=> "baz"
Изменить: Ах, да, извините, неправильно поняли вас. Как насчет просто:
class Foo
def initialize(*args)
@baz, @buz = args
end
end
Интересный вопрос. Небольшое метапрограммирование должно позаботиться об этом.
module Attrs
def self.included(base)
base.extend ClassMethods
base.class_eval do
class << self
attr_accessor :attrs
end
end
end
module ClassMethods
# Define the attributes that each instance of the class should have
def has_attrs(*attrs)
self.attrs = attrs
attr_accessor *attrs
end
end
def initialize(*args)
raise ArgumentError, "You passed too many arguments!" if args.size > self.class.attrs.size
# Loop through each arg, assigning it to the appropriate attribute (based on the order)
args.each_with_index do |val, i|
attr = self.class.attrs[i]
instance_variable_set "@#{attr}", val
end
end
end
class Foo
include Attrs
has_attrs :bar, :buz
end
f = Foo.new('One', 'Two')
puts f.bar
puts f.buz
Конечно, недостатком этого является негибкость - вам нужно передать аргументы конструктора в определенном порядке. Конечно, как большинство языков программирования. Rails люди могут утверждать, что вы должны делать
f = Foo.new(:bar => 'One', :baz => 'Two')
который позволит вам проходить в attrs в любом порядке, а также удалять большую часть метапрограмм. Но это намного больше, чтобы напечатать.