Ruby: методы, генерируемые attr_accessor - как их перебирать (в to_s - произвольный формат)? - PullRequest
8 голосов
/ 07 января 2010

Мне нужен класс, который имеет полуавтоматический метод to_s (для генерации XML на самом деле). Я хотел бы пройтись по всем автоматическим методам, установленным в моей строке attr_accessor:

class MyClass
    attr_accessor :id,:a,:b,:c
end

c=MyClass.new

Пока я делаю базовый:

c.methods - Object.methods

=> ["b", "b=", "c", "c=", "id=", "a", "a="]

Я столкнулся с несколькими проблемами:

  1. «id» может вызвать небольшую головную боль - поскольку у Object, похоже, уже есть «id».
  2. Вызов 'c.methods', приведенный выше, возвращает строки - я не получаю никаких других метаданных? (В Java «метод» - это объект, где я мог бы выполнить дальнейшее отражение).
  3. У меня есть отношения «один ко многим», с которыми мне приходится иметь дело («c» - это тип массива других типов объектов).

Это то, что я пытаюсь сделать: я хочу спроектировать простой объект, который имеет to_s, который будет создавать фрагмент XML: например.

<id> 1 </id>
<a> Title </a>
<b> Stuff </b>
<c>
    <x-from-other-object>
    <x-from-other-object>
    ....
</c>

А потом унаследовать мои классы данных от этого простого объекта: чтобы (надеюсь) я получил механизм для создания целого документа XML.

Я уверен, что и здесь заново изобретаю колесо ... так что приветствуются другие испытанные подходы.

Ответы [ 2 ]

13 голосов
/ 07 января 2010

Чтобы получить объекты метода из строки, вы можете использовать методы method или instance_method (где method будет вызываться для объекта и instance_method для класса). Единственная интересная информация, которую он дает вам, - это arity (в отличие от java, где также указываются типы возвращаемого значения и аргументы, что, конечно, невозможно в ruby).

Ваш заголовок предполагает, что вы хотите перебирать только методы, созданные attr_accessor, но ваш код будет перебирать каждый метод, определенный в вашем классе, что может стать проблемой, если вы захотите добавить дополнительные методы без доступа к вашему класс.

Чтобы избавиться от этой проблемы и проблемы с id, вы можете использовать свою собственную оболочку вокруг attr_accessor, в которой хранятся переменные, для которых она создала средства доступа, например:

module MyAccessor
  def my_attr_accessor *attrs
    @attrs ||= []
    @attrs << attrs
    attr_accessor *attrs
  end

  def attrs
    @attrs
  end
end

class MyClass
  extend MyAccessor
  my_attr_accessor :id,:a,:b,:c

  def to_s
    MyClass.attrs.each do |attr|
      do_something_with(attr, send(attr))
    end
  end
end

Для задачи 3 вы можете просто сделать

if item.is_a? Array
  do_something
else
  do_something_else
end
0 голосов
/ 26 ноября 2013

Я использую эту технику для преобразования пользовательских объектов в JSON. Может быть, фрагмент кода ниже будет полезен, так как вопрос был для реализации to_xml.

Здесь немного волшебства, используя self.included в модуле. Вот очень хорошая статья 2006 года о модуле с методами экземпляра и класса http://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html

Модуль предназначен для включения в любой класс для обеспечения функциональности to_json. Он перехватывает attr_accessor метод, а не использует свой собственный, чтобы требовать минимальных изменений для существующих классов.

to_json реализация основана на этом ответе

module JSONable
  module ClassMethods
    attr_accessor :attributes

    def attr_accessor *attrs
      self.attributes = Array attrs
      super
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end

  def as_json options = {}
    self.class.attributes.inject({}) do |hash, attribute|
      hash[attribute] = self.send(attribute)
      hash
    end
  end

  def to_json *a
    as_json.to_json *a
  end
end


class CustomClass
  include JSONable
  attr_accessor :b, :c 

  def initialize b: nil, c: nil
    self.b, self.c = b, c
  end
end

a = CustomClass.new(b: "q", c: 23)
puts JSON.pretty_generate a

{
  "b": "q",
  "c": 23
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...