Do czego służą zmienne instancji w modelu?

Witam,
jaki jest sens/cel używania zmiennych instancji (poprzedzone @ ) w modelu? Moje pytanie pojawiło się czytając http://www.aidanf.net/rails_user_authentication_tutorial

Kod:

[code]class User < ActiveRecord::Base
attr_protected :id, :salt
attr_accessor :password, :password_confirmation

def password=(pass)
@password=pass
self.salt = User.random_string(10) if !self.salt?
self.hashed_password = User.encrypt(@password, self.salt)
end

def send_new_password
new_pass = User.random_string(10)
self.password = self.password_confirmation = new_pass
self.save
Notifications.deliver_forgot_password(self.email, self.login, new_pass)
end

end[/code]
w metodzie “password=” jest użyta zmienna @password do przypisania wartości, w “send_new_password” jest już odwołanie przez self.password. Dlaczego w “password=” nie przesłać parametru “pass” do metody “encrypt” (3 linijka)?

Dzięki

Rzuć okiem na linki zamieszczone w tym poście: http://rubyonrails.pl/forum/p31472-Wczoraj-14%3A18%3A55#p31472

W tym kodzie jest to trochę dziwnie zrobione, bo użyty jest attr_accessor, który tworzy metodę password=, a później ta metoda i tak jest zdefiniowana.

Wywołanie attr_accessor :foo zachowuje się tak jakbyś stworzył takie metody:

[code]def foo=(value)
@foo = value
end

def foo
@foo
end[/code]
Dlatego to “def password=” jest zrobione jako rozszerzona wersja tej metody, która zapisuje wartość hasła. W środku nie możesz użyć “self.password =”, bo to by skończyło się rekurencją.

Może trochę bardziej przejrzyście by było jakby to było zapisane jakoś tak:

attr_accessor :password_confirmation attr_reader :password
W tym momencie otwarcie mówisz, że dla password tworzysz tylko metodę do czytania, a metoda do pisania będzie zaimplementowana ręcznie.

dzięki za odpowiedzi.
Z pomocą wskazanego linku, googla i 2h eksperymentów doszedłem do tego co napisał Drogus.

Nie widzę nic złego w nadpisaniu settera dla :password, pozwala oba accessory zapisać w jednej linii, a użycie attr_reader sugeruje że dla tego atrybutu w ogóle nie ma i nie powinno być settera.

Z trochę innej beczki: w Ruby nie można odwoływać się do zmiennych instancji inaczej niż przez metody (dotyczy również przypisywania im wartości), efektywnie twór typu publiczna zmienna instancji w ogóle nie istnieje, zgadza się? Chyba tutaj był pies pogrzebany.

Dokładnie tak - do tych zmiennych zawsze odwołujemy się przez metody. Innymi słowy - są one zawsze prywatne (choć istnieją metody na dostęp “bezpośredni”, to nie będę ich tutaj przytaczał).

Jeśli masz na myśli odwoływać z zewnątrz obiektu to jedynie poprzez napisaną metodę (lub instance_variable_set / instance_variable_get co jednak łamie enkapsulację). Z metody obiektu to przez @ oczywiście.

No właśnie - nie pisałem o instance_variable_set/get bo to jest “zastrzeżone” do specjalnych zastosowań (metaprogramowanie) i lepiej, żeby początkujący programiści nawet nie wiedzieli o ich istnieniu :slight_smile:

A co do drugiego - to bym się jednak nie zgodził do końca - często w metodach wew. danego obiektu również korzystam z akcesorów - nierzadko zdarza się, że musimy dodać jakąś funkcjonalność przy zapisie/odczycie takiej zmiennej, a wtedy musimy odszukać jej wszystkie wystąpienia w klasie. Oczywiście nie jest to zazwyczaj trudne, jeśli nie mamy dziedziczenia. Dlatego nawet w wypadku odwołań wewnętrznych, lepiej stosować akcesory - w szczególności w klasach dziedziczących.