Я следую руководству Майкла Хартла по RoR, и в нем рассматриваются основы шифрования паролей. Это модель User в ее нынешнем виде:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+@[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 и @ в контексте приведенного выше кода?
ПРИМЕЧАНИЕ: Я уже просматривал сообщения здесь и здесь , но они оба говорят, что «self» вызывает метод attribute =
, и я даже не понимаю, как этот метод мог существовать здесь, поскольку я никогда не создавал его и не объявлял encrypted_password w / attr_accessor
. Так что я все еще в замешательстве, и это не повторная публикация этих вопросов.
Средства доступа для 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
(но методы доступа являются удобным способом сделать это).
Если позволите, я бы перефразировал ответ.
Я объяснил в этом посте , что как только вы создаете (rails-) модель с тем же (единственным) именем, что и одно из (множественных) имен таблиц вашей базы данных, «волшебство» of rails создаст сеттеры и геттеры для изменения записей вашей таблицы.
Это потому, что ваша модель наследует все методы от ActiveRecord :: Base Class, который определяет основные средства доступа CRUD (Create, Read, Update, Delete).
Ключевым моментом, связанным с вашим вопросом, является то, что вы не знаете, как rails реализует переменную экземпляра, связанную с вашим столбцом таблицы базы данных, И вы не должны. :) Все, что вам нужно знать, это то, что в этот момент у вас есть сеттеры и геттеры, доступные для CRUD (создание, чтение, обновление, удаление) столбца вашей базы данных «encrypted_password».
В вашем примере, возможно, rails использует переменную экземпляра с именем @encrypted_password, возможно, rails использует переменную хеш-экземпляра с именем @attributes ["encrypted_password"], или, возможно, rails использует переменную экземпляра с именем @you_will_never_guess_encrypted_password.
-
И хорошо, что вы не знаете о поведении внутренних рельсов с переменными экземпляра. В 2019 году дальнейшее развитие Rails может привести к тому, что платформа будет использовать @ complex-hash-instance-variable для хранения значения encrypted_password.
На самом деле лучший подход - позволить rails управлять своим «личным» «делом»;) с переменными экземпляра и просто использовать методы getter и setter, которые он вам предоставляет. Так что ваше приложение все еще будет работать с encrypted_password в следующем столетии (я надеюсь, что ^^).
Так что если вы используете @encrypted_password, он может работать с какой-то «воображаемой» версией rails и больше не будет работать с другими версиями rails. На самом деле с текущей версией рельсов это не работает.
-
Второй ключевой момент заключается в том, что когда вы хотите использовать метод получения « encrypted_password », созданный для столбца таблицы базы данных encrypted_password, вы добавляете его префикс с помощью « self », чтобы сообщить Ruby: «хорошо, я хочу использовать метод encrypted_password моей переменной экземпляра User .»
В Ruby метод вызывается, передавая свое имя получателю. Вы пишете это так:
my_receiver.my_method
В вашем случае мы передаем метод encrypted_password переменной экземпляра User . Но мы не знаем, как будет названа эта переменная экземпляра, поэтому мы используем слово self , чтобы сказать Руби: «Я говорю о любой переменной экземпляра класса User , которая вызывает метод encrypted_password ".
Например, мы могли бы назвать нашу переменную экземпляра «toto»:
toto = User.new
, чтобы toto.encrypted_password
отображал зашифрованный пароль, и self в этом случае в нашем коде ссылается на to.
Однако, благодаря Ruby, , если вы не дадите какой-либо получатель при вызове метода , Ruby будет предполагать, что вы передадите его self .
Ссылка: Руководство программиста Pragmatic
Так что в вашем примере вам даже не нужно ставить «себя». в качестве префикса. Вы могли бы написать это так:
class User < ActiveRecord::Base
def encrypt_password
encrypted_password = encrypt(password)
end
end
Надеюсь, это поможет прояснить эту интересную тему.