Рельсы - я против @ - PullRequest
       6

Рельсы - я против @

17 голосов
/ 13 июня 2011

Я следую учебному пособию Майкла Хартла по RoR, и оно охватывает основы шифрования паролей.Это модель пользователя в ее нынешнем виде:

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.Так что я все еще в замешательстве, и это не повторная публикация этих вопросов.

Ответы [ 3 ]

20 голосов
/ 13 июня 2011

Средства доступа для 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 голосов
/ 19 февраля 2013

Если вы позволите мне, я бы хотел перефразировать ответ.

Я объяснил в этом посте , что как только вы создадите (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 "Rails создан для вашего столбца таблицы базы данных encrypted_password, вы добавляете к нему префикс" self", чтобы сказать Ruby:" хорошо, я хочу использовать метод encrypted_password моего пользователя переменная экземпляра. "

В Ruby метод вызывается путем передачи его имени получателю.Вы пишете это так:

my_receiver.my_method

В вашем случае мы передаем метод encrypted_password в переменную экземпляра User .Но мы не знаем, как будет названа эта переменная экземпляра, поэтому мы используем слово self , чтобы сказать Руби: «Я говорю о любой переменной экземпляра класса User , котораявызывает метод encrypted_password ".

Например, мы могли бы назвать нашу переменную экземпляра" toto ":

toto = User.new

, чтобы toto.encrypted_password отображал зашифрованный пароль, и self в этом самом случаев нашем коде будет ссылка на.

Однако, благодаря Ruby, , если вы не дадите какой-либо получатель при вызове метода , Ruby будет предполагать, что вы передадите его self .

Ссылка: Руководство программиста Pragmatic

Так что в вашем примере вам даже не нужно ставить "я".в качестве префикса.Вы могли бы написать это так:

class User < ActiveRecord::Base

    def encrypt_password
        encrypted_password = encrypt(password)
    end
end

Надеюсь, это поможет прояснить эту интересную тему.

1 голос
/ 24 февраля 2017

TL; DR -

Всегда пишите self.widget_count = 123, если вы собираетесь сохранить widget_count обратно в базу данных.

(Но, пожалуйста, прочитайте длинные ответы, как причину, по которой стоит знать.)

...