Самый быстрый/однострочный способ перечислить attr_accessors в Ruby?
Какой самый короткий, однострочный способ перечислить все методы, определенные с помощью 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
?
Ответы
Ответ 1
Нет способа (однострочный или другой), чтобы перечислять все методы, определенные 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]
Ответ 2
Извлеките атрибуты в массив, назначьте их constant, затем splat их до attr_accessor
.
class SubClass < MyBaseClass
ATTRS = [:id, :title, :body]
attr_accessor(*ATTRS)
end
Теперь вы можете получить к ним доступ через константу:
puts SubClass.ATTRS #=> [:id, :title, :body]
Ответ 3
Вот альтернатива, использующая mixin, а не наследование:
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]
Ответ 4
Следуя за христианским ответом, но изменяя использование 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
Определения классов
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 выводит an = после имени метода
Вы всегда можете использовать регулярное выражение для фильтрации этого вывода.