Struct
让我创建一个带有参数的新类,并且有一些很好的语义。但是,参数不是必需的,它们的顺序需要参考定义:
Point = Struct.new(:x, :y)
Point.new(111, 222)
#=> <point instance with x = 111, y = 222>
Point.new(111)
#=> <point instance with x = 111, y = nil>
我喜欢类似于Struct的东西,但它使用关键字参数代替:
Point = StricterStruct.new(:x, :y)
Point.new(x: 111, y: 222)
#=> <point instance with x = 111, y = 222>
Point.new(x: 111)
#=> ArgumentError
这可能看起来像这样:
module StricterStruct
def self.new(*attributes)
klass = Class.new
klass.instance_eval { ... }
klass
end
end
但是在initialize
上定义klass
方法的大括号应该是什么:
attributes
中的符号数组的形式给出;和initialize
方法将它们分配给同名的实例变量答案 0 :(得分:6)
由于Ruby 2.0 +的新功能,我使用(令人惊讶的Pythonic)**kwargs
策略结束了:
module StricterStruct
def self.new(*attribute_names_as_symbols)
c = Class.new
l = attribute_names_as_symbols
c.instance_eval {
define_method(:initialize) do |**kwargs|
unless kwargs.keys.sort == l.sort
extra = kwargs.keys - l
missing = l - kwargs.keys
raise ArgumentError.new <<-MESSAGE
keys do not match expected list:
-- missing keys: #{missing}
-- extra keys: #{extra}
MESSAGE
end
kwargs.map do |k, v|
instance_variable_set "@#{k}", v
end
end
l.each do |sym|
attr_reader sym
end
}
c
end
end
答案 1 :(得分:1)
我可能误解了这个问题,但你在找这样的东西吗?
module StricterStruct
def self.new(*attributes)
klass = Class.new
klass.class_eval do
attributes.map!{|n| n.to_s.downcase.gsub(/[^\s\w\d]/,'').split.join("_")}
define_method("initialize") do |args|
raise ArgumentError unless args.keys.map(&:to_s).sort == attributes.sort
args.each { |var,val| instance_variable_set("@#{var}",val) }
end
attr_accessor *attributes
end
klass
end
end
然后
Point = StricterStruct.new(:x,:y)
#=> Point
p = Point.new(x: 12, y: 77)
#=> #<Point:0x2a89400 @x=12, @y=77>
p2 = Point.new(x: 17)
#=> ArgumentError
p2 = Point.new(y: 12)
#=> ArgumentError
p2 = Point.new(y:17, x: 22)
#=> #<Point:0x28cf308 @y=17, @x=22>
如果你想要更多的东西请解释,因为我认为这符合你的标准,至少我对它的理解。因为它定义了方法并且可以采用&#34;关键字&#34;(Hash
)参数并分配适当的实例变量。
如果您希望以与定义的顺序相同的顺序指定参数,只需删除排序。
也可能有更清洁的实施。
答案 2 :(得分:0)
听起来你正在寻找Ruby的内置OpenStruct:
require 'ostruct'
foo = OpenStruct.new(bar: 1, 'baz' => 2)
foo # => #<OpenStruct bar=1, baz=2>
foo.bar # => 1
foo[:bar] # => 1
foo.baz # => 2
foo.baz = 3
foo # => #<OpenStruct bar=1, baz=3>
我认为OpenStruct是Hash上的糖果涂层,我们可以在没有任何实际约束的情况下访问和分配实例,而不像使用普通访问器创建真正的类。我们可以假装它是哈希,或者是带方法的类。这是一个甜点打顶,不是它的地板蜡,不,它是一体的两件事!
答案 3 :(得分:0)
我也在寻找这个,最终偶然发现了这个确实如此的宝石:
https://github.com/etiennebarrie/kwattr
class FooBar
kwattr :foo, bar: 21
end
foobar = FooBar.new(foo: 42) # => #<FooBar @foo=42, @bar=21>
foobar.foo # => 42
foobar.bar # => 21
而不是
class FooBar
attr_reader :foo, :bar
def initialize(foo:, bar: 21)
@foo = foo
@bar = bar
end
end