Fastest/One-liner way to list attr_accessors in Ruby?

前端 未结 6 815
忘了有多久
忘了有多久 2020-12-02 14:16

What\'s the shortest, one-liner way to list all methods defined with attr_accessor? I would like to make it so, if I have a class MyBaseClass, any

相关标签:
6条回答
  • 2020-12-02 14:57

    Heres an alternative using a mixin rather than inheritance:

    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]
    
    0 讨论(0)
  • 2020-12-02 15:10

    Extract the attributes in to an array, assign them to a constant, then splat them in to attr_accessor.

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

    Now you can access them via the constant:

    puts SubClass.ATTRS #=> [:id, :title, :body]
    
    0 讨论(0)
  • 2020-12-02 15:14

    There is no way (one-liner or otherwise) to list all methods defined by attr_accessor and only methods defined by attr_accessor without defining your own attr_accessor.

    Here's a solution that overrides attr_accessor in MyBaseClass to remember which methods have been created using 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]
    
    0 讨论(0)
  • 2020-12-02 15:15

    Class definitions

    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
    

    Call as class methods

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

    Call as instance methods

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

    Both will output the same

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

    How to tell which are writer reader and accessor?

    attr_accessor is both attr_writer and attr_reader

    attr_reader outputs no = after the method name

    attr_writer outputs an = after the method name

    You can always use a regular expression to filter that output.

    0 讨论(0)
  • 2020-12-02 15:16

    Following up on Christian's response, but modifying to use 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
    
    0 讨论(0)
  • 2020-12-02 15:20

    It could be filtered with the equal sign matching logic:

    methods = (SubClass.instance_methods - SubClass.superclass.instance_methods).map(&:to_s)
    methods.select { |method| !method.end_with?('=') && methods.include?(method + '=') }
    #> ["id", "title", "body"]
    
    0 讨论(0)
提交回复
热议问题