Самый быстрый / однострочный способ перечислить attr_accessors в Ruby? - PullRequest
30 голосов
/ 21 марта 2010

Какой самый короткий, однострочный способ перечисления всех методов, определенных с 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_reader и attr_writer?

Ответы [ 5 ]

42 голосов
/ 04 июня 2015

Извлечение атрибутов в массив, присвоение их константе , затем splat их к attr_accessor.

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

Теперь вы можетеполучить доступ к ним через константу:

puts SubClass.ATTRS #=> [:id, :title, :body]
39 голосов
/ 21 марта 2010

Невозможно (однострочно или иначе) перечислить все методы, определенные attr_accessor, и только методы, определенные attr_accessor, без определения собственного attr_accessor.

Вот решение, которое переопределяет attr_accessor в MyBaseClass, чтобы запомнить, какие методы были созданы с помощью 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]
6 голосов
/ 21 марта 2014

Вот альтернатива, использующая миксин, а не наследование:

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]
2 голосов
/ 19 августа 2016

Определения классов

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=]

Как узнать, кто автор и читатель?

attr_accessor является одновременно attr_writer и attr_reader

attr_reader выводит no = после имени метода

attr_writer выводит = после имени метода

Вы всегда можете использовать регулярное выражение для фильтрации этого вывода.

2 голосов
/ 29 апреля 2014

Отслеживание ответа Кристиана, но изменение для использования 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
...