2010-07-09 3 views
5

Предположим, у меня есть объект FireNinja < Ninja в моей базе данных, который хранится с использованием одностраничного наследования. Позже я понимаю, что он действительно WaterNinja < Ninja. Какой самый чистый способ изменить его на другой подкласс? Еще лучше, мне бы хотелось создать новый объект WaterNinja и просто заменить старый FireNinja в БД, сохранив идентификатор.Изменение класса объекта в ActiveRecord

Редактировать Я знаю, как создать новый объект WaterNinja из моего существующего FireNinja, и я также знаю, что могу удалить старый и сохранить новый. То, что я хотел бы сделать, это mutate класс существующего элемента. Я делаю это, создавая новый объект и делая магию ActiveRecord для замены строки или делая какую-то сумасшедшую вещь для самого объекта или даже удаляя ее и повторно вставляя с тем же идентификатором, хотя это часть вопроса.

ответ

2

Вы должны сделать две вещи:

  • Создать WaterNinja < Ninja
  • В ninjas таблице запустить что-то вроде UPDATE ninjas SET (type = 'WaterNinja') WHERE (type = 'FireNinja')

Вот об этом.

Для выполнения преобразования во время выполнения это будет сделано, но я не рекомендую.

class Ninja 
    def become_another_ninja(new_ninja_type) 
    update_attribute(:type, new_ninja_type) 
    self.class.find(id) 
    end 
end 

@water_ninja = @fire_ninja.become_another_ninja('WaterNinja') 

Проблема в том, что @fire_ninja будет теперь холостые объекта.

+0

Похоже, вы отвечаете: «Как мне преобразовать все« FireNinjas »в« WaterNinjas »?» Ответ по-прежнему работает, хотя если я изменю условие на 'ID = myid'. Да, я подумал об этом.Мне также пришлось бы вытащить объект после обновления, чтобы установить некоторые настройки поля WaterNinja, но это нормально. Однако я надеялся на что-то более чистое. –

+0

Я понимаю, что вы имеете в виду. Я не рекомендую это делать, но это сделает трюк: 'ninja_id = @ ninja.id; @ ninja.update_attribute (: type, 'WaterNinja'); @ninja = WaterNinja.find (ninja_id) ' –

+0

Отредактировано, чтобы показать метод, который я описал. –

-1

Вам нужно будет определить метод WaterNinja#to_fireninja. Ruby не имеет возможности изменить класс объекта, сохраняя при этом один и тот же объект.

class WaterNinja < Ninja 
    def to_fireninja 
    FireNinja.new :name => name 
    end 
end 
+0

Это создаст новый объект, но когда я вызову save, он создаст другую строку в базе данных. Я хочу заменить мой сохранившийся экземпляр FireNinja моим новым объектом WaterNinja. –

+0

@ Исаак: Я не думаю, что это возможно, если вы не можете удалить WaterNinja и заставить ActiveRecord сохранить новый FireNinja с тем же идентификатором. – Adrian

+0

В один прекрасный момент на вопрос можно ответить, если бы вы могли сказать мне, как сохранить новый ниндзя с существующим идентификатором. Я был бы рад удалить и повторно вставить. –

0

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

http://en.wikipedia.org/wiki/Strategy_pattern

+0

Не обязательно. Google для шаблонов рефакторинга «заменить тип с полиморфизмом» и «заменить тип на стратегию». Использование стратегии охватывает некоторые случаи, но в других предпочтете наследование. Эти шаблоны также хорошо описаны в книге Рефакторинг (http://www.amazon.com/Refactoring-Ruby-Jay-Fields/dp/0321603508). – samuil

+0

У меня есть веские причины для использования наследования независимо от проблемы, которую я представляю выше. Я не строю changetypeofninja.com. –

7

Вы можете сделать свой FireNinja акт как WaterNinja делая

@ninja.becomes(WaterNinja) 

Если вы хотите навсегда изменить классы, то просто перезаписать атрибут типа.

@ninja.type = "WaterNinja" 
@ninja.save! 
+0

Это правильный ответ, принятый ответ не так хорош, потому что он не использует «становится», – Rob

Смежные вопросы