self = Потомок в Ruby? - PullRequest
       21

self = Потомок в Ruby?

1 голос
/ 10 ноября 2009

У меня есть текстовый журнал из игры с (например) двумя типами записей, а именно. Чат и Событие. По большей части они очень похожи, поэтому у меня есть класс LogEntry, определенный так;

class LogEntry < Array
  def initialize(str)
    super str.split
  end

  def parse
    LogEntry.parse self
  end

  def LogEntry.parse(entry)
    # Processes the elements that are in any Entry
    # Figure out whether it's a Chat entry or an Event entry
    # Returns an object of type LogChat or LogEvent
  end
end

LogChat и LogEvent расширяют LogEntry и выполняют дальнейшую обработку, соответствующую их домену. Все работает как положено;

chat = LogEntry.new("some chat")
event = LogEntry.new("some event")

chat.parse.class   # => LogChat
event.parse.class  # => LogEvent

Вопрос: Метод класса LogEntry.parse по существу возвращает проанализированную запись соответствующего класса. В этом контексте анализируемая запись является важным битом. Но мы могли бы переименовать метод экземпляра parse в what_type_should_i_be? Я хочу, чтобы объект воздействовал на эту информацию и 'self.become LogEntry.parse (self)'

Прямо сейчас, чтобы разобрать запись, я должен сделать это;

  entry = entry.parse

Я хочу продвинуть это дальше, чтобы получить тот же результат с;

  entry.parse

Я пробовал очевидное;

class LogEntry
  def parse
    self = LogEntry.parse(self)
  end
end

Все же я получаю ошибку Can't change the value of self. Кто-нибудь знает, как мне добиться этого?

Edit: Я изменил свои примеры, потому что многие ответы были сосредоточены на итерации по многим записям. Ответ Чака элегантно показывает, что эта ситуация не проблема.

В случае, если это вызывает у кого-то интерес, я наткнулся на Evil Ruby , который позволяет вам вмешиваться в `self.class '. Есть хорошая статья о Orielly под названием Ruby-код, который поглотит вашу душу! Я изучаю ее, чтобы узнать, предлагает ли она какие-либо ответы. (Правка: evil.rb хорошо назван! То, что низкий уровень «не кажется» подходящим для стабильного / долгосрочного распространения.)

Ответы [ 4 ]

2 голосов
/ 10 ноября 2009

Если вы можете разбить функциональность на различные модули, вы можете mutate extend() self как вам нравится:

class LogEntry
  ...
  def parse!       # This mutates self!
    case LogEntry.parse!
    when :chat
      self.extend MyApp::LogChat
    when :event
      self.extend MyApp::LogEvent
    else
      raise MyApp::Exception, "waaah"
    end
  end
end

Вам не нужно делать неуклюжий оператор case с повторными вызовами self.extend(), конечно, но вы поняли.

2 голосов
/ 10 ноября 2009

Я думаю, что фундаментальная проблема заключается в том, что each является неправильным методом здесь. Либо parse измените внутреннее состояние объекта, а не сам объект, либо используйте map! для замены объектов в коллекции новыми версиями.

entries.map! {|entry| entry.parse}

обновит объекты в массиве с результатом вызова parse для них, поэтому нет смысла делать странные вещи с self в методе parse.

0 голосов
/ 10 ноября 2009

У вас есть некоторая путаница в строке / массиве / LogEntry, но, если у вас все получилось, и в конце вы все еще хотите иметь подкласс Array, заменяющий его собственное содержимое, вам нужно использовать replace:

self.replace(LogEntry.parse(self))
0 голосов
/ 10 ноября 2009

для начала, ваши комментарии говорят, что LogEntry.parse возвращает объект LogChat или LogEvent. Итак, вы просите объект изменить себя на объект другого типа.

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

entries.each do |entry|
  some_type_log = entry.parse
  some_type_of_log.save!
end

EDIT: извините, хотел кое-что уточнить. Поскольку вы анализируете данные, являющиеся частью LogEntry, и хотите, чтобы запись анализировалась сама собой, нет необходимости передавать какие-либо параметры. просто оставьте метод разбора без параметров.

Если вы знаете, что это за тип логов, вы можете пропустить шаг и проанализировать его в пути. chat_entry = LogChat.new (: log => LogEntry)

затем создайте метод log, который будет вашим парсером, который будет обрабатывать элементы, связанные с чатом.

...