Який правильний спосіб змінити метод сеттера в Ruby on Rails?


184

Я використовую Ruby on Rails 3.2.2, і я хотів би знати, чи наведено нижче "правильний" / "правильний" / "впевнений" спосіб змінити метод встановлення для атрибута мого класу.

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self[:attribute_name] = value
end

Наведений вище код, здається, працює як очікувалося. Однак я хотів би знати, чи в майбутньому, використовуючи вищезазначений код, у мене виникнуть проблеми або, принаймні, які проблеми "я повинен очікувати" / "може статися" з Ruby on Rails . Якщо це не правильний спосіб змінити спосіб встановлення, який правильний шлях?


Примітка : Якщо я використовую код

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

4
Мені подобається застосована термінологія "" належно "/" правильно "/" точно ". Коли ви даєте це 3 способи, це дійсно забезпечує відсутність неправильного тлумачення. Хороша робота!
Джей

5
@Jay - "Витонченість італізмів"; -)
Бачко

2
Щоб було зрозуміло, "рівень стека занадто глибокий" має на увазі той факт, що його рекурсивний дзвінок ... сам виклик.
Ніппісавр

Відповіді:


295

===================================================== ========================= Оновлення: 19 липня 2017 р.

Тепер документацію Rails також пропонують використовувати superтак:

class Model < ActiveRecord::Base

  def attribute_name=(value)
    # custom actions
    ###
    super(value)
  end

end

===================================================== ==========================

Оригінальний відповідь

Якщо ви хочете змінити методи встановлення для стовпців таблиці під час доступу через моделі, це зробити так.

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.

Отже, ваш перший метод - це правильний спосіб переосмислити налаштування стовпців у моделях Ruby on Rails. Ці аксесуари вже надаються Rails для доступу до стовпців таблиці як атрибутів моделі. Це ми називаємо відображення ActiveRecord ORM.

Також пам’ятайте, що attr_accessibleвгорі модель не має нічого спільного з аксесуарами. Він має зовсім іншу функціональність (див. Див Це питання )

Але в чистому Ruby, якщо ви визначили аксесуари для класу і хочете змінити сетер, вам доведеться використовувати змінну екземпляра, як це:

class Person
  attr_accessor :name
end

class NewPerson < Person
  def name=(value)
    # do something
    @name = value
  end
end

Це буде простіше зрозуміти, як тільки ви зрозумієте, що attr_accessorробить. Код attr_accessor :nameеквівалентний цим двом методам (геттер і сетер)

def name # getter
  @name
end

def name=(value) #  setter
  @name = value
end

Також ваш другий метод виходить з ладу, оскільки він спричинить нескінченний цикл, як ви викликаєте той самий метод attribute_name=всередині цього методу.


9
Для Rails 4 просто пропустіть, attr_accessibleоскільки його вже немає, і це має працювати
zigomir

11
Чому б не зателефонувати super?
Натан Ліліенталь

1
У мене склалося враження, що оскільки аксесуари та автори створюються динамічно, це superможе не спрацювати. Але, схоже, це не так. Я просто перевірив це, і він працює на мене. Також це питання задайте те саме
rubyprince

4
Є величезна гатча write_attribute. Конверсії будуть пропущені. Будьте в курсі, що write_attributeконверсії часових поясів будуть пропускатися з датами, які майже завжди будуть небажаними.
Тім Скотт

2
супер буде добре працювати, проте є деякі причини, що ви можете не хотіти нам цього. Наприклад, у монгоїдному самоцвіті є помилка, яку ви не можете натиснути на масив, якщо ви супер метод getter. Це помилка через те, як там керувати масивом в пам'яті. Також @name також поверне встановлене значення, а не викликає метод, який буде перезаписано. Однак у наведеному вище рішенні обидва будуть працювати чудово.
newdark-це

44

Використовуйте superключове слово:

def attribute_name=(value)
  super(value.some_custom_encode)
end

І навпаки, щоб перекрити читача:

def attribute_name
  super.some_custom_decode
end

1
Краща відповідь, ніж прийнята ІМО, оскільки вона підтримує виклик методу обмеженим однойменною назвою. Це зберігає успадковане переохолоджене поведінку на attribute_name =
Ендрю Шварц

Перезазначення методу getter стало небезпечним у Rails 4.2 через цю зміну: github.com/rails/rails/commit/… Раніше помічники форми називали б значення нетипізованого поля, а не викликали ваші власні користувачі. Тепер вони називають ваш метод, і тому вони створюватимуть заплутані результати у ваших формах залежно від того, як ви переосмислюєте значення.
Брендон Муїр

16

У рейки 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 в моделі. Натомість вам доведеться скласти білий список своїх атрибутів на рівні контролера, використовуючи дозвільний метод.


3

Я виявив, що (принаймні, для колекцій відносин ActiveRecord) працює наступний зразок:

has_many :specialties

def specialty_ids=(values)
  super values.uniq.first(3)
end

(Це захоплює перші 3 не повторюваних запису в пройденому масиві.)


0

Використання attr_writerдля перезапису сеттера attr_writer: attribute_name

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.