Ruby ROXML - как получить массив для отображения его XML? - PullRequest
0 голосов
/ 16 мая 2010

Я пытаюсь создать объект Messages, который наследует Array. Сообщения соберут группу объектов Message. Я пытаюсь создать вывод XML с ROXML, который выглядит следующим образом:

<messages>
    <message>
        <type></type>
        <code></code>
        <body></body>
    </message>
    ...
</messages>

Однако я не могу понять, как получить объекты сообщений в объекте сообщений для отображения в XML. Вот код, с которым я работал:

require 'roxml'

class Message
  include ROXML

  xml_accessor :type
  xml_accessor :code
  xml_accessor :body
end

class Messages < Array
  include ROXML

  # I think this is the problem - but how do I tell ROXML that
  # the messages are in this instance of array?
  xml_accessor :messages, :as => [Message]

  def add(message)
    self << message
  end

end


message = Message.new
message.type = "error"
message.code = "1234"
message.body = "This is a test message."

messages = Messages.new
messages.add message

puts messages.length
puts messages.to_xml

Это выводит:

1
<messages/>

Итак, объект сообщения, который я добавил к сообщениям, не отображается. У кого-нибудь есть идеи? Или я поступаю неправильно?

Спасибо за любую помощь.

1 Ответ

2 голосов
/ 17 мая 2010

Я не думаю, что вы хотите, возможно. Вы каким-то образом пытаетесь получить доступ к внутреннему состоянию класса Array, что не только невозможно, потому что в большинстве реализаций эти внутренние компоненты скрыты во время выполнения C / C ++ / Java / .NET / Objective-C / ABAP, но также просто плохая идея и плохой объектно-ориентированный дизайн.

Дело в том, что Messages на самом деле не Array, поэтому он не должен наследоваться от Array. Скажите мне: действительно ли вы на 100% уверены, что ваш Messages класс способен добросовестно выполнять контракты всех 81 методов на Array? И что assoc, rassoc, rindex и transpose даже означают применительно к Messages?

Вам было бы намного лучше использовать делегирование вместо наследования здесь. Это дает вам красивую именованную сущность, которую вы можете передать xml_accessor:

require 'forwardable'
require 'roxml'

class Messages
  extend Forwardable
  include ROXML

  class << self; alias_method :[], :new end

  xml_reader :messages, :as => [Message]

  def initialize(*messages) @messages = messages end

  def_delegators :messages, :length, :<<
end

Примечание: я также изменил здесь несколько других вещей. Например, я лично считаю, что объект должен быть действительным и пригодным для использования после его создания. В вашей версии кода Message в основном недействителен после его создания и становится действительным только после вызова сеттеров type=, code= и body=:

class Message
  include ROXML

  class << self; alias_method :[], :new end

  xml_reader :type, :body
  xml_reader :code, :as => Integer

  def initialize(type=nil, code=nil, body=nil)
    @type, @code, @body = case opts = type
    when Hash
      opts[:type], opts[:code], opts[:body]
    else
      type, code, body
    end
  end
end

Вот немного расширенный пример использования:

msgs = Messages[Message['error', 1234, 'This is a test message.'], Message[]]

msgs << Message[
  type: 'warning', 
  code: 4815162342, 
  body: 'This is another test message.'
]

puts msgs.to_xml
# => <messages>
# =>   <message>
# =>     <type>error</type>
# =>     <body>This is a test message.</body>
# =>     <code>1234</code>
# =>   </message>
# =>   <message/>
# =>   <message>
# =>     <type>warning</type>
# =>     <body>This is another test message.</body>
# =>     <code>4815162342</code>
# =>   </message>
# => </messages>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...