Каков правильный способ переопределить метод setter в Ruby on Rails?
Я использую Ruby on Rails 3.2.2, и я хотел бы знать, является ли следующее "правильным" / "правильным" / "уверенным" способом переопределить метод setter для моего атрибута класса.
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self[:attribute_name] = value
end
Вышеприведенный код работает, как ожидалось. Тем не менее, я хотел бы знать, если, используя приведенный выше код, в будущем у меня будут проблемы или, по крайней мере, какие проблемы "следует ожидать" / "может произойти" с Ruby on Rails. Если это неправильный способ переопределить метод setter, каков правильный путь?
Примечание. Если я использую код
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self.attribute_name = value
end
Я получаю следующую ошибку:
SystemStackError (stack level too deep):
actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
Ответы
Ответ 1
============================================ ===============================
Обновление: 19 июля 2017 г.
Теперь Документация Rails также предлагает использовать super
следующим образом:
class Model < ActiveRecord::Base
def attribute_name=(value)
# custom actions
###
super(value)
end
end
============================================ ===============================
Оригинальный ответ
Если вы хотите переопределить методы setter для столбцов таблицы при доступе через модели, это способ сделать это.
class Model < ActiveRecord::Base
attr_accessible :attribute_name
def attribute_name=(value)
# custom actions
###
write_attribute(:attribute_name, value)
# this is same as self[:attribute_name] = value
end
end
В документации Rails см. Переопределение прецедентов по умолчанию в документации Rails.
Итак, ваш первый метод - это правильный способ переопределить установщики столбцов в моделях Ruby on Rails. Эти аксессоры уже предоставлены Rails для доступа к столбцам таблицы в качестве атрибутов модели. Это то, что мы называем отображением ORR ActiveRecord.
Также имейте в виду, что attr_accessible
в верхней части модели не имеет ничего общего с аксессуарами. Он имеет совершенно другую функцию (см. этот вопрос)
Но в чистом Ruby, если вы определили accessors для класса и хотите переопределить setter, вам нужно использовать переменную экземпляра, как это:
class Person
attr_accessor :name
end
class NewPerson < Person
def name=(value)
# do something
@name = value
end
end
Это будет легче понять, если вы знаете, что делает attr_accessor
. Код attr_accessor :name
эквивалентен этим двум методам (getter и setter)
def name # getter
@name
end
def name=(value) # setter
@name = value
end
Также ваш второй метод выходит из строя, потому что он вызовет бесконечный цикл, когда вы вызываете тот же метод attribute_name=
внутри этого метода.
Ответ 2
Используйте ключевое слово super
:
def attribute_name=(value)
super(value.some_custom_encode)
end
И наоборот, чтобы переопределить читателя:
def attribute_name
super.some_custom_decode
end
Ответ 3
В рельсах 4
скажем, у вас в таблице возраст.
def age=(dob)
now = Time.now.utc.to_date
age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
super(age) #must add this otherwise you need to add this thing and place the value which you want to save.
end
Примечание:
Для новых пользователей в рельсах 4 вам не нужно указывать attr_accessible в модели. Вместо этого вы должны белым списком своих атрибутов на уровне контроллера с помощью метода разрешить.
Ответ 4
Я обнаружил, что (по крайней мере, для коллекций отношений ActiveRecord) работает следующий шаблон:
has_many :specialties
def specialty_ids=(values)
super values.uniq.first(3)
end
(Это захватывает первые 3 недвойных записи в переданном массиве.)
Ответ 5
Использование attr_writer
для перезаписывания сеттера
attr_writer: attribute_name
def attribute_name=(value)
# manipulate value
# then send result to the default setter
super(result)
end