Rails - self vs. @

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

17
задан Community 23 May 2017 в 12:16
поделиться

2 ответа

Средства доступа для 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 (но методы доступа являются удобным способом сделать это).

20
ответ дан 30 November 2019 в 13:26
поделиться

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

Я объяснил в этом посте , что как только вы создаете (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

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

2
ответ дан 30 November 2019 в 13:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: