Ответ 1
attr_accessor :pancakes
def after_initialize
return unless new_record?
self.pancakes = 11
end
Это гарантирует, что значение будет инициализировано некоторым значением по умолчанию только для новой записи.
Некоторый код, который у меня был с использованием attr_accessor_with_default
в модели рельсов, теперь дает мне предупреждение об устаревании, в котором я говорю "Использовать Ruby вместо этого!"
Итак, думая, что, возможно, в ruby 1.9.2
появился новый бит, который сделал дескриптор дескриптора attr_accessor
по умолчанию, я искал его, но я этого не вижу. Я видел несколько методов для переопределения attr_accessor
для обработки значений по умолчанию.
Это то, что они имеют в виду, когда они говорят мне "Использовать Ruby"? Или я должен сейчас писать полные геттеры/сеттеры? Или есть какой-то новый способ, который я не могу найти?
attr_accessor :pancakes
def after_initialize
return unless new_record?
self.pancakes = 11
end
Это гарантирует, что значение будет инициализировано некоторым значением по умолчанию только для новой записи.
Эта страница apidock предлагает просто сделать это в методе initialize.
class Something
attr_accessor :pancakes
def initialize
@pancakes = true
super
end
end
Не забудьте вызвать super
, особенно при использовании ActiveRecord или аналогичного.
Поскольку вы, вероятно, хорошо знаете свои данные, вполне допустимо предположить, что nil
не является допустимым значением.
Это означает, что вы можете покончить с after_initialize
, поскольку это будет выполнено для каждого создаваемого вами объекта. Как отметили несколько человек, это (потенциально) катастрофическое для производительности. Кроме того, вставка метода, как в примере, в любом случае устарела в Rails 3.1.
Чтобы использовать Ruby вместо этого, я бы использовал такой подход:
attr_writer :pancakes
def pancakes
return 12 if @pancakes.nil?
@pancakes
end
Итак, немного опустите магию Ruby и напишите свой собственный геттер. В конце концов, это делает именно то, что вы пытаетесь выполнить, и это хорошо и достаточно просто, чтобы кто-нибудь мог обернуть свою голову.
Нет ничего волшебного в 1.9.2 для инициализации переменных экземпляра, которые вы настроили с помощью attr_accessor
. Но есть after_initialize
обратный вызов:
Обратный вызов
after_initialize
будет вызываться всякий раз, когда создается объект Active Record, либо путем прямого использования нового, либо когда запись загружается из базы данных. Может быть полезно избежать необходимости прямого переопределения метода Active Recordinitialize
.
Итак:
attr_accessor :pancakes
after_initialize :init
protected
def init
@pancakes = 11
end
Это безопаснее, чем что-то вроде этого:
def pancakes
@pancakes ||= 11
end
потому что nil
или false
могут быть вполне допустимыми значениями после инициализации и предполагать, что они не могут вызвать некоторые интересные ошибки.
Мне интересно, будет ли работать с Rails-реализацией:
http://apidock.com/rails/Module/attr_accessor_with_default
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval( def #{sym}=(value) # def age=(value) class << self; attr_reader :#{sym} end # class << self; attr_reader :age end @#{sym} = value # @age = value end # end, __FILE__, __LINE__ + 1)
end
Это ооооолдовый вопрос, но общая проблема все еще возникает - и я оказался здесь.
Другие ответы разнообразны и интересны, но я обнаружил проблемы со всеми из них при инициализации массивов (особенно, поскольку я хотел иметь возможность использовать их на уровне класса, прежде чем инициализация была вызвана в экземпляре). У меня был успех:
attr_writer :pancakes
def pancakes
@pancakes ||= []
end
Если вы используете = вместо || =, вы обнаружите, что < оператор не может добавить первый элемент в массив. (Создается анонимный массив, ему присваивается значение, но он никогда не привязывался к @pancakes.)
Например:
obj.pancakes
#=> []
obj.pancakes << 'foo'
#=> ['foo']
obj.pancakes
#=> []
#???#!%$#@%FRAK!!!
Поскольку это довольно тонкая проблема и может вызвать несколько царапин на голове, я подумал, что стоит упомянуть здесь.
Этот шаблон должен быть изменен для bool, например, если вы хотите по умолчанию использовать false:
attr_writer :pancakes
def pancakes
@pancakes.nil? ? @pancakes = false : @pancakes
end
Хотя вы можете утверждать, что назначение не является строго необходимым при работе с bool.