【问题标题】:Fastest/One-liner way to list attr_accessors in Ruby?在 Ruby 中列出 attr_accessors 的最快/单线方法?
【发布时间】:2011-01-30 01:10:06
【问题描述】:

列出用attr_accessor 定义的所有方法的最短的单行方法是什么?我想这样做,如果我有一个类MyBaseClass,任何扩展它的东西,我都可以得到子类中定义的attr_accessor。像这样的:

class MyBaseClass < Hash
  def attributes
    # ??
  end
end

class SubClass < MyBaseClass
  attr_accessor :id, :title, :body
end

puts SubClass.new.attributes.inspect #=> [id, title, body]

只显示attr_readerattr_writer 定义会怎样?

【问题讨论】:

    标签: ruby introspection


    【解决方案1】:

    可以用等号匹配逻辑过滤:

    methods = (SubClass.instance_methods - SubClass.superclass.instance_methods).map(&:to_s)
    methods.select { |method| !method.end_with?('=') && methods.include?(method + '=') }
    #> ["id", "title", "body"]
    

    【讨论】:

      【解决方案2】:

      类定义

      class MyBaseClass
        attr_writer :an_attr_writer
        attr_reader :an_attr_reader
      
        def instance_m
        end
      
        def self.class_m
        end
      end
      
      class SubClass < MyBaseClass
        attr_accessor :id
      
        def sub_instance_m
        end
      
        def self.class_sub_m
        end
      end
      

      作为类方法调用

      p SubClass.instance_methods - Object.methods    
      p MyBaseClass.instance_methods - Object.methods
      

      作为实例方法调用

      a = SubClass.new
      b = MyBaseClass.new
      
      p a.methods - Object.methods
      p b.methods - Object.methods
      

      两者输出相同

      #=> [:id, :sub_instance_m, :id=, :an_attr_reader, :instance_m, :an_attr_writer=]
      #=> [:an_attr_reader, :instance_m, :an_attr_writer=]
      

      如何区分 writer reader 和 accessor?

      attr_accessor 既是 attr_writer 又是 attr_reader

      attr_reader 在方法名之后输出 no =

      attr_writer 在方法名之后输出一个 =

      您始终可以使用正则表达式来过滤该输出。

      【讨论】:

        【解决方案3】:

        将属性提取到数组中,将它们分配给constant,然后将splat 分配给attr_accessor

        class SubClass < MyBaseClass
          ATTRS = [:id, :title, :body]
          attr_accessor(*ATTRS)
        end
        

        现在您可以通过常量访问它们:

        puts SubClass.ATTRS #=> [:id, :title, :body]
        

        【讨论】:

        • 不会是SubClass::ATTRS 吗?您的语法建议将 ATTRS 消息发送到 SubClass 类型的实例
        【解决方案4】:

        跟进 Christian 的回应,但修改为使用 ActiveSupport::Concern...

        module TrackAttributes
          extend ActiveSupport::Concern
        
          included do
            define_singleton_method(:attr_reader) do |*params|
              @attr_readers ||= []
              @attr_readers.concat params
              super(*params)
            end
        
            define_singleton_method(:attr_writer) do |*params|
              @attr_writers ||= []
              @attr_writers.concat params
              super(*params)
            end
        
            define_singleton_method(:attr_accessor) do |*params|
              @attr_accessors ||= []
              @attr_accessors.concat params
              super(*params)
            end
          end
        
          def attr_readers
            self.class.instance_variable_get('@attr_readers')
          end
        
          def attr_writers
            self.class.instance_variable_get('@attr_writers')
          end
        
          def attr_accessors
            self.class.instance_variable_get('@attr_accessors')
          end
        
        end
        

        【讨论】:

          【解决方案5】:

          这是使用混合而不是继承的替代方法:

          module TrackAttributes
            def attr_readers
              self.class.instance_variable_get('@attr_readers')
            end
          
            def attr_writers
              self.class.instance_variable_get('@attr_writers')
            end
          
            def attr_accessors
              self.class.instance_variable_get('@attr_accessors')
            end
          
            def self.included(klass)
              klass.send :define_singleton_method, :attr_reader, ->(*params) do
                @attr_readers ||= []
                @attr_readers.concat params
                super(*params)
              end
          
              klass.send :define_singleton_method, :attr_writer, ->(*params) do
                @attr_writers ||= []
                @attr_writers.concat params
                super(*params)
              end
          
              klass.send :define_singleton_method, :attr_accessor, ->(*params) do
                @attr_accessors ||= []
                @attr_accessors.concat params
                super(*params)
              end
            end
          end
          
          class MyClass
            include TrackAttributes
          
            attr_accessor :id, :title, :body
          end
          
          MyClass.new.attr_accessors #=> [:id, :title, :body]
          

          【讨论】:

          • 有没有办法通过 MyClass.attr_accessors 使用 mixins 获得相同的结果? @sepp2k 的继承方法允许两者。我可以考虑进行扩展和包含,但这似乎是多余的。有没有更好的办法?
          【解决方案6】:

          没有办法(单行或其他方式)列出 attr_accessor 定义的所有方法,并且只列出 attr_accessor 定义的方法而不定义您自己的 attr_accessor。

          这是一个解决方案,它覆盖 MyBaseClass 中的 attr_accessor 以记住使用 attr_accessor 创建的方法:

          class MyBaseClass
            def self.attr_accessor(*vars)
              @attributes ||= []
              @attributes.concat vars
              super(*vars)
            end
          
            def self.attributes
              @attributes
            end
          
            def attributes
              self.class.attributes
            end
          end
          
          class SubClass < MyBaseClass
            attr_accessor :id, :title, :body
          end
          
          SubClass.new.attributes.inspect #=> [:id, :title, :body]
          

          【讨论】:

          • 次要的 nitpick,但在第 5 行,您不需要调用 super(*vars),因为您传递的参数与该方法收到的参数相同;你可以不带任何参数或括号直接调用super,它会自动传递*vars
          • @MarkRushakoff:我认为这样会更明显。许多人认为supersuper() 是一回事(老实说,我不确定如果是这样我会不会更喜欢它)。
          • 默认情况下,super 传递参数(如果有,则阻止)背后的基本原理是,接口通常不会改变,因此它就是你想要的。我会说拥抱 Ruby 并让其他人也拥抱它,即使这意味着他们必须知道 super 与 super() 不同......
          • 有没有办法做到这一点,而不是使用继承(子类化),您可以使用模块来覆盖 attr_accessor,然后将模块用作其他类的混合?我尝试将 MyBaseClass 中的内容转换为 Module AttributeAccessors。然后在我想使用它的类中,我说extend AttributeAccessors 但是当我尝试访问类实例中的属性时,它会失败:NoMethodError: undefined method 'attributes' for Class:Class
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-06-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-28
          • 2018-08-27
          相关资源
          最近更新 更多