Self w metodzie egzemplarza

Cześć

mam dwa modele, Gallery może mieć przypisany katalog obrazków PicsDir.
PicsDir został stworzony żeby odseparować kod odpowiedzialny za operacje na plikach zdjęć.
Kod symulujący powiązanie w uproszczeniu wygląda tak:

gallery.rb

[code]class Gallery < ActiveRecord::Base
attr_accessible :name, :status, :pics_dir_name

def pics_dir
	# tutaj nie trzeba self
	return unless pics_dir_name
	PicsDir.new(pics_dir_name)
end

def pics_dir= pics_dir_obj
    # tutaj trzeba self
	self.pics_dir_name= pics_dir_obj.name
end

end[/code]
pics_dir.rb

[code]class PicsDir
attr_reader :name

def initialize name
@name = name
end
end[/code]
I w zasadzie już nie ma problemu, bo po dopisaniu selfa do metody “pics_dir=” wszystko działa.
Nie działało w postaci:

def pics_dir= pics_dir_obj pics_dir_name= pics_dir_obj.name end
Chciałbym to jednak zrozumieć :slight_smile: Czy ktoś może mi wytłumaczyć dlaczego muszę używać self razem z “pics_dir_name=” (wewnątrz “def pics_dir=”) a nie muszę używać z “pics_dir_name” (wewnątrz “def pics_dir”)?

Dlatego, że pics_dir_name = ...
wygląda dla Rubiego na przypisanie do zmiennej lokalnej pics_dir_name
.
Natomiast w kodzie pics_dir
odwołujesz się do zmiennej lub metody, a ponieważ taka zmienna lokalna nie była zadeklarowana, to odszukuje on metodą pics_dir_name
.

Inna sprawa, że chyba sensowniej w obu przypadkach byłoby użyć @pics_dir_name.

Przy przypisaniu wartości nie możesz pominąć self, tak jak powiedział apohllo. Wiąże się to z tym, że nie byłoby to jednoznaczne dla interpretera co zrobić: wysłać wiadomość do bieżącego obiektu czy ustawić zmienną lokalną. W przypadku wywołania metody nie będącej setterem, sprawa jest łatwiejsza: najpierw sprawdzane jest czy nie ma zmiennej lokalnej, jeśli nie ma, wywoływana wysyłana jest wiadomość do obiektu.

Gwoli czystości kodu: sensownie jest pomijać self. wszędzie gdzie jest to możliwe. Czyli stosować tylko przy wywołaniu setterów, albo w sytuacjach niejasnych, gdzie masz zmienną lokalną o takiej samej nazwie (chociaż jak głupie to jest to już pominę).

Rozwinę też drugą kwestię wspomnianą przez apohllo: raczej można ominąć tu attr_accessor i dobrać się do zmiennych instancji bezpośrednio, przez @zmienna. Dlaczego? Bo do tego są zmienne instancji aby bieżący obiekt z nich mógł korzystać swobodnie. Są 2 wyjątki: a) kiedy chcesz umożliwić ustawianie / pobieranie wartości zmiennej przez inne obiekty (wtedy używasz attr_accessor), b) jeśli pobieranie/ustawianie wartości wiąże się z dodatkowymi czynnościami, typu konwersja typów czy logowanie danych (wtedy piszesz własne gettery/settery).

Dopisując: Jeśli przy przypisaniu nie użyjesz self, to Ruby uzna, tak jak napisał apohllo, że jest to przypisanie zmiennej lokalnej - i jeśli w tym samym scope będziesz chciał użyć pics_dir_name, Ruby zwróci Ci świeżo przypisaną zmienną lokalną, a do “prawdziwej” metody pics_dir_name będziesz musiał odwoływać się również za pomocą self, co rodzi jeszcze większy chaos jeśli ta metoda jest prywatna…

Generalnie zatem należy unikać takiego bałaganu w nazwach.

Teraz wydaje się to oczywiste dzięki.

A tak przy okazji

uświadomiłem sobie, że w modelach ActiveRecord używałem zawsze akcesorów zamiast prostszej notacji z małpą.
Chyba oczekiwałem, że oprócz zwykłego podstawiania wartości dzieje się też jakaś magia :).

W ActiveRecord powinieneś używać akcesorów na atrybutach które są mapowane na kolumny w bazie danych. Inaczej nie będą one zapisane.