Rails - я против @
Я следую руководству Майкла Хартла RoR, и он освещает основы шифрования паролей. Это модель пользователя, которая в настоящее время стоит:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-][email protected][A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
self.encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
Я уже писал предыдущий вопрос о before_save
, и получается, что то, что я случайно сделал, написано моим encrypt_password как:
def encrypt_password
@encrypted_password = encrypt(password)
end
Я понимаю, что если self.encrypted_password устанавливает атрибут encrypted_password, но почему @encrypted_password не делает этого? В ответе на предыдущий пост о before_save
не работает кто-то сказал, что переменная экземпляра была "забыта" после того, как метод закончился тем, как я его первоначально закодировал - почему это так? Может кто-нибудь объяснить, как я и @работают по-другому в контексте вышеприведенного кода?
ПРИМЕЧАНИЕ. Я уже рассмотрел сообщения здесь и здесь, но они оба говорят, что "self" вызывает метод attribute =
, и я даже не понимаю, как этот метод может существовать здесь, так как я его никогда не создавал или не объявлял encrypted_password w/attr_accessor
. Поэтому я все еще смущен, и это не повторная публикация этих вопросов.
Ответы
Ответ 1
Аксессоры для encrypted_password
были автоматически добавлены Rails для вас, потому что поле с этим именем существует в таблице users
.
Любое поле, которое вы добавите в таблицу, будет автоматически доступно через self.field_name
.
Вот где учебник Майкла Хартла создает поле encrypted_password
в таблице users
.
Посмотрите также на user_spec.rb
(листинг 7.3) на связанной странице, где автор тестирует наличие поля encrypted_password
.
ОБНОВЛЕНО:
Как указывает @mu, @
используется для переменных экземпляра Ruby (иначе "iv" ). Но encrypted_password
- это "атрибут", определенный Rails, и не является переменной экземпляра.
Если вы запустите User.find(1).instance_variables
, вы увидите, что есть iv под названием @attributes
, который имеет тип Hash
.
Внутри, где iv хранится encrypted_password
. Rails определил методы доступа для encrypted_password
, который получает/устанавливает данные для этого
атрибут в @attributes
Hash
.
Обратите внимание, что вы также можете получить/установить данные через @attributes["encrypted_password"]
, вызванный из класса User
(но методы доступа являются удобным способом сделать именно это).
Ответ 2
Если вы позволите мне, я хотел бы перефразировать ответ.
Я объяснил в этом сообщении, что, как только вы создадите модель (rails-) с тем же (единственным) именем в качестве одного из (множественных) табуляций вашей базы данных, "магия" рельсов создаст сеттеры и геттеры, чтобы изменить записи в таблице.
Это связано с тем, что ваша модель наследует все методы из ActiveRecord:: Base Class, который определяет базовые CRUD-аксессоры (Create, Read, Update, Delete).
Ключевой момент, связанный с вашим вопросом, заключается в том, что вы не знаете, как rails реализует переменную экземпляра, связанную с столбцом таблицы базы данных,. И не следует.:) Все, что вам нужно знать, это то, что в этот момент у вас есть сеттеры и геттеры, доступные CRUD (создайте, прочитайте, обновите, удалите) в столбце базы данных "encrypted_password".
В вашем примере, возможно, rails использует переменную экземпляра, называемую @encrypted_password, возможно, rails использует переменную hash-instance, называемую @attributes [ "encrypted_password" ], или, может быть, rails использует переменную экземпляра, называемую @you_will_never_guess_encrypted_password.
-
И хорошо, что вы не знаете о поведении внутренних рельсов с переменными экземпляра. В 2019 году дальнейшая разработка Rails может привести к тому, что структура будет использовать переменную @complex-hash-instance для хранения значения encrypted_password.
Фактически лучший подход заключается в том, чтобы позволить рельсам управлять своим "private" "делом";) с переменными экземпляра и просто использовать методы getter и setter, которые он предоставляет вам.
Таким образом, ваше приложение будет работать с encrypted_password в следующем столетии (надеюсь, что ^^).
Итак, если вы используете @encrypted_password, он может работать с некоторой "воображаемой" версией рельсов, и он больше не будет работать с другими версиями рельсов. На самом деле с текущей версией рельсов это не работает.
-
Вторая ключевая точка заключается в том, что если вы хотите использовать регенерацию " encrypted_password" Rails, созданную для столбца таблицы базы данных encrypted_password, вы префикс ее " self", чтобы сообщить Ruby:" ok Я хочу использовать метод encrypted_password моей переменной Пользователь.
В Ruby метод вызывается путем передачи его имени получателю.
Вы пишете это следующим образом:
my_receiver.my_method
В вашем случае мы передаем метод encrypted_password в переменную экземпляра Пользователь. Но мы не знаем, как будет называться эта переменная экземпляра, поэтому мы используем слово self, чтобы сообщить Ruby: "Я говорю о любой переменной экземпляра User класс, который вызывает метод encrypted_password".
Например, мы могли бы назвать нашу переменную экземпляра "toto":
toto = User.new
поэтому toto.encrypted_password
отобразит зашифрованный пароль, а сам в этом случае в нашем коде будет ссылаться на.
Однако, благодаря Ruby, , если вы не даете никакого приемника при вызове метода, Ruby предположит, что вы передаете его в self.
Ссылка: Руководство для прагматичного программиста
Итак, в вашем примере вам даже не нужно ставить "я". как префикс.
Вы могли бы написать это так:
class User < ActiveRecord::Base
def encrypt_password
encrypted_password = encrypt(password)
end
end
Надеюсь, это поможет прояснить этот интересный вопрос.
Ответ 3
TL; DR -
Всегда пишите self.widget_count = 123
, если вы намерены сохранить widget_count обратно в базу данных.
(Но, пожалуйста, прочитайте длинные ответы, потому что полезно знать.)