使用预定义定义和自定义块自定义Ruby Struct

6kkfgxo0  于 2022-10-15  发布在  Ruby
关注(0)|答案(2)|浏览(230)

给出以下节目,在其中我想:
1.使用一些密钥创建一个Struct
1.提供一些默认定制
1.允许传递块以进行进一步定制

module Magic
    def self.MagicStruct(keys_array, &block)
        Struct.new(*keys_array) do
            @keys = keys_array
            def self.magic_class_method
                puts "constructed with #{@keys}"
            end

            def magic_instance_method
                puts "instance method"
            end

            # --- DOESN'T WORK, CONTEXT IS OUTSIDE OF MODULE --- #
            # yield if block_given?

            instance_eval(&block) if block_given?
        end
    end
end

Foo = Magic.MagicStruct([:a, :b, :c]) do
    puts "class customizations executing..."

    def self.custom_class_method
        puts "custom class method"
    end

    def custom_instance_method
        puts "custom instance method"
    end
end

Foo.magic_class_method  # works
Foo.custom_class_method # works

x = Foo.new({a: 10, b: 20, c: 30})
x.magic_instance_method  # works
x.custom_instance_method # fails

产出:

class customizations executing...
constructed with [:a, :b, :c]
custom class method
instance method
Traceback (most recent call last):
`<main>': undefined method `custom_instance_method' for #<struct Foo a={:a=>10, :b=>20, :c=>30}, b=nil, c=nil> (NoMethodError)

为什么self.custom_class_method被正确地添加到Foo类,而custom_instance_method却没有?这种用法在the Struct documentation中有明确的说明,所以我恐怕这里遗漏了一些作用域或上下文问题。
我更喜欢保持良好的def method() ... end语法,而不是求助于在定制块中使用define_method("method")的严格要求,这确实有效。

6ojccjat

6ojccjat1#

在Ruby中有“当前类”的概念,它是def等关键字的目标。
使用instance_eval时,当前类设置为self.singleton_class。换句话说,def xdef self.x是等价的。在您的代码中,custom_instance_method定义在新创建的Struct的Singleton类上,使其成为类方法。
使用class_eval时,当前类设置为self。由于此方法仅在类上可用,因此它会将当前类设置为您对其调用该方法的类。换句话说,def x将定义一个可用于该类的所有对象的方法。这就是你想要的。
有关更多详细信息,请参见my other answer

r55awzrz

r55awzrz2#

This是我编写的一个gem,我相信它做得最多,如果不是您想要的全部的话(虽然我不太确定是否要添加类方法,但我不得不尝试一下)。虽然创建的Struct不可变的,但您可以查看代码并更改它以满足您的需要;它非常简单。您感兴趣的应该是here,这基本上等于您在下面看到的内容。模块的扩展使用instance_eval,我相信这也是您想要的:


# https://github.com/gangelo/immutable_struct_ex/blob/main/lib/immutable_struct_ex.rb

# Defines the methods used to create/manage the ImmutableStructEx struct.

module ImmutableStructEx
  class << self
    # Most likely changing this method name to #create in subsequent version, but alas...
    def new(**hash, &block)
      Struct.new(*hash.keys, keyword_init: true, &block).tap do |struct|
        return struct.new(**hash).tap do |struct_object|
          struct_object.extend Comparable
          struct_object.extend Immutable
        end
      end
    end
  end
end

使用详情可在README.md in the github repository中找到。
我希望这能有所帮助,至少在一定程度上如此。

相关问题