Variabili d'istanza non visibili

Ciao a tutti, vorrei fare un po’ di chiarezza sulle variabili d’istanza
in ruby/rails: ho notato un comportamento diverso di queste variabili
usando un modello in rails, ed una classe in ruby (provata con irb).

Con entrambe: ho notato che creando una variabile d’istanza non nel
costruttore essa non è visibile negli altri metodi, viene ignorata
(facendo ad esempio @var = “valore”). Perchè? Il bello è dichiararle con
@, se non si possono dichiarare nei metodi avrebbero fatto meglio a
farle dichiarare a livello di classe.

Con una classe in ruby: se dichiaro la variabile d’istanza nel
costruttore essa è visibile e modificabile negli altri metodi

Con un modello: dichiarandole nel costruttore alcune sono visibili,
alcune no

def initialize(session)
@session = session
@user = nil
end

def log_in(user)
if user
@user = user

end
end

In questo caso @session viene letta e modificata negli altri metodi,
mentre @user no.
O meglio, in log_in, @user viene impostata correttamente (facendo puts
vedo che è impostata a user), ma è come se non fosse d’istanza, perchè
usando @user in altri metodi questo vale ancora nil. Fatemi sapere,
spero che la domanda sia interessante e non una cosa banale. ciao

nicola

Le variabili d’istanza sono accessibili ovunque all’interno
dell’applicazione indipendentemente dal fatto che siano state o meno
dichiarate nell’initializer.

Il tuo codice infatti funziona. Prova ad eseguire questo

class LoginMock

def initialize(session)
@session = session
@user = nil
end

def log_in(user)
if user
@user = user
end
end

def give_me_the_user
@user
end

end

c = LoginMock.new(“I am a session”)
c.log_in(“weppos”)
puts c.give_me_the_user

Il problema è senza dubbio altrove. Tieni in considerazione che se apri
una
pagina A (login) e B (verifica), anche se hai una variabile d’istanza in
A
non sarà visibile in B perché sono due esecuzioni differenti.
Per trasportarti il valore devi ricorrere all’uso di una sessione o di
un
cookie.

– Simone

2009/8/25 Nicola N. [email protected]

end
end


Simone C.

Site & Blog: http://www.simonecarletti.com
Email: [email protected]
LinkedIn: http://linkedin.com/in/weppos
Nick: weppos | Skype: weppos

Il giorno 25 agosto 2009 10.18, Nicola N.[email protected] ha
scritto:
scusa, non ho capito.

Con entrambe: ho notato che creando una variabile d’istanza non nel
costruttore essa non è visibile negli altri metodi, viene ignorata

class SenzaCostruttore
def settala
@miavar = 3
end

def dimmela
@miavar
end
end

a = SenzaCostruttore.new
#SenzaCostruttore:0x4043b6c
a.dimmela
nil
a.settala
3
a.dimmela
3
a
#<SenzaCostruttore:0x4011180 @miavar=3>

class ColCostruttore
def initialize
@questa = 4
end

def settala
@miavar = 3
end

def dimmela
puts @questa
@miavar
end
end

a = ColCostruttore.new
#<ColCostruttore:0x4023164 @questa=4>
a.dimmela
nil
a.settala
3
a.dimmela
3
a
#<ColCostruttore:0x4023164 @miavar=3, @questa=4>

non è che, in rails, stai tentando di accedere a una variabile
d’istanza di un model da dentro un controller, o altro?

pietro

per pietro:

Ok, ho fatto un’altra prova e ho visto che le variabili posso
dichiararle anche in altri metodi, e sono comunque visibili… ChissÃ
come avevo provato prima

per simone:

forse ho intuito dove sta il problema. l’istanza della classe viene
creata nell’Application, in questo modo

before_filter :initialize_session
def initialize_session
@user_session ||= UserSession.new(session)
end

before_filter :fetch_logged_in_user
def fetch_logged_in_user
if @user_session
@current_user = @user_session.get_logged_user
end

return nil

end

La classe che deve contenere la variabile è UserSession
Poi uso @user_session dove voglio (in teoria), per impostare la
sessione. Forse il problema è dato dal before_filter. Io vorrei che
venisse creata un’istanza di UserSession una tantum per gestire le
sessioni dell’utente corrente.

Un filtro in before_filter lavora nel contesto dell’istanza. Quindi ogni
filtro ha visibilità delle istanze create dai filtri precedenti ed ogni
azione ha visibilità delle istanze create da un qualsiasi filtro.

class MyController

before_filter :load_user

def action
# qui @user == :me
end

protected

def load_user
  @user = :me
end

end

I tuoi due filtri, dunque, sembrano corretti, fermo restando che non mi
quadra quel return nil al fine del secondo filtro.
Controlla solo che i filtri vengano eseguiti nell’ordine che ti aspetti,
altrimenti le variabili non le avrai a disposizione. Invece che un
filtro,
potresti usare un metodo e sfruttare il lazy loading per avere la
variabile
a disposizione esattamente dove e quando ti serve.

class ApplicationController

protected

def current_session
  @user_session ||= UserSession.new(session)
end

def current_user
  # evita if quando non servono e soprattutto
  # evita di usare if come "patch" per nil exception
  # può un UserSession.new mai tornare nil?
  # e se lo torna, è veramente corretto?
  @current_user = current_session.get_logged_user
end

end

class MyController < ApplicationController

def alpha
# qui non chiamo mai user session quindi risparmio risorse e filtri
end

def beta
@stuff = current_user.do_stuff
end

end

Se infatti guardi il funzionamento di Authlogic, a scopo didattico,
vedrai
che funziona esattamente in questo modo.
Hai un metodo, che può essere usato anche come before_filter, che imposta
una variabile current_user che puoi richiamare nell’azione o nel
template
corrente.

– Simone

2009/8/25 Nicola N. [email protected]

before_filter :initialize_session
return nil
Ml mailing list
[email protected]
http://lists.ruby-it.org/mailman/listinfo/ml


Simone C.

Site & Blog: http://www.simonecarletti.com
Email: [email protected]
LinkedIn: http://linkedin.com/in/weppos
Nick: weppos | Skype: weppos

Ciao Nicola,

non sono proprio sicuro cosa vuoi fare, comunque ho qualche chiarimenti:

  • Per i modelli Rails, #initialize non vienne chiamato quando
    l’oggetto è creato dalla db, quindi la variabile vienne mai
    innizzialisato
  • Sovrascrivere #initialize per sottoclasse non è molto
    consigliato. In quel caso, chiama almeno #super
  • Per i controller: I variabili assegnato nel :before_filter sono
    visibile sia nel controller, sia nel view - ma solo per la richiesta
    attuale
    Non saranno li per la prossima richiesta - ricordi che HTTP è un
    protocollo stateless. Questo vale per tutti i variabili della
    runtime! (O almeno si assume sempre che la runtime è “vuota” al inizio
    della richiesta)
  • Dati per la sessione si può mettere nel hash “session”. I
    contenuti di quel hash sono sempre disponibile in tutte le richieste
    della stessa sessione. Non è consigliato mettere dei oggi complessi
    nella session-hash
  • Dati nella db sono (ovviamente) sempre disponibile ovunque

Ciao,
Daniel

Il giorno 25/ago/09, alle ore 11:24, Nicola N. ha scritto:

before_filter :initialize_session
return nil
Ml mailing list
[email protected]
http://lists.ruby-it.org/mailman/listinfo/ml


Daniel H.
Software Architect
[email protected]

Net7
Via Marche 8a - 56123 Pisa

vi rinrgazio per le risposte numerose e gli spunti, ho provato a capire
cosa non funzionava senza successo.

  • Ho provato a mettere una variabile di classe in UserSession e a fare
    così per vedere che istanza venisse chiamata:

    @@numero_istanze = 0

    def initialize(session)
    @session = session
    @istanze = @@numero_istanze
    @@numero_istanze += 1
    end

    quando chiamo i vari metodi dell’istanza @user_session il numero
    dell’istanza è sempre lo stesso, ma le variabili che non vengono
    impostate nel costruttore non ci sono

  • ho provato salvando una copia di user con
    Marshal.load(Marshal.dump(user)), pensando che rimanesse un puntatore a
    quando viene letta nel database e quando venisse cancellata il puntatore
    puntasse a nil

Non so se sia un problema della mancanza di stato HTTP, perchè l’istanza
di UserSession che uso è sempre la stessa (credo, perchè non ho ben
chiaro come vengano servite le richieste dal server ai client) quindi
chiamandone un metodo dovrei impostarne una variabile.

Visto tutto questo, preferisco rinunciare a salvare l’utente quando
faccio il login, e leggo dal database i dati che mi servono. poi con
calma mi metterò ad analizzare tutto quanto, per ora resta così.

per daniel:

non ho capito perchè è meglio che non scriva il costruttore: non c’è
nessuna sopraclasse esplicita, quindi secondo me può andare così. mi
sbaglio?

Saluti a tutti e grazie,

nicola

Ciao,

Il giorno 30/ago/09, alle ore 15:41, Nicola N. ha scritto:

quando chiamo i vari metodi dell’istanza @user_session il numero
dell’istanza è sempre lo stesso, ma le variabili che non vengono
impostate nel costruttore non ci sono

Non so cosa vuoi dire con ‘le variabili che non vengono impostate nel
costruttore non ci sono’. Per cosa riguarda ruby, una variabile di una
istanza nasce quando è assegnato un valore. Ci saranno fine al cleanup
dell’oggetto. Non importa se sono impostati nell costruttore o in
qualsiasi altro metodo dell’oggetto. Attenti: Se chiami un metodo
della class (tipo

def self.mett
@var = ‘val’
end

la variabile sarà una variabile dell’istanza dell’oggetto della classe
(tutto chiaro? :wink:

Non so se sia un problema della mancanza di stato HTTP, perchè
l’istanza
di UserSession che uso è sempre la stessa (credo, perchè non ho ben
chiaro come vengano servite le richieste dal server ai client) quindi
chiamandone un metodo dovrei impostarne una variabile.

Non puoi essere sicuro se l’istanza sia sempre la stessa. È possibile
ritenere un oggetto dentro la runtime, anche tra richiesti. MA quando
una applicazione Rails vienne messo in produzione ci saranno più di
una istanza della runtime! Non ci sara nessun modo di scoprire se
esistono altri runtime o quale oggetti hanno. Quindi l’unico modo di
ritenere dati tra richiesti è usare la db o “session”.

per daniel:

non ho capito perchè è meglio che non scriva il costruttore: non c’è
nessuna sopraclasse esplicita, quindi secondo me può andare così. mi
sbaglio?

Ho capito che la tua classes UserSession e una sottoclasse di
ActiveRecord::Base? In quel caso, #initialize funziona come prevista
se chiami

UserSession.new

ma, se chiami qualche metodo come

UserSession.find(…)

Rails crea l’oggetto senza usare #new, quindi non vienne chiamato
neanche #initialize.

Per tutti i “record” (sottoclassi di ActiveRecord::Base) è sempre
meglio usare i callback.

Ciao,
Daniel

http://lists.ruby-it.org/mailman/listinfo/ml

Daniel H.
Software Architect
[email protected]

Net7
Via Marche 8a - 56123 Pisa

ciao daniel,

grazie per avermi risposto di nuovo. Allora, riguardo ai vari punti

  1. la differenza tra variabili d’istanza e di classe mi è chiara, e ho
    capito che in rails una variabile d’istanza può essere dichiarata in
    qualsiasi metodo e da quel momento in poi ci sarà .

  2. la classe UserSession non è sottoclasse di ActiveRecord: avevo
    bisogno di spostare la logica delle sessioni ad un livello più alto per
    poterle usare da dovunque. Se stava soltanto nel controller delle
    sessioni non potevo creare una sessione da un altro controller.
    Ho creato quindi un modello che non ha riferimenti nel database per
    gestire l’hash session.

  3. grazie per la precisazione su come funziona new e find, vale anche
    per modelli non active record?

Con UserSession volevo proprio gestire l’hash session per non gestirlo
altrove: mi sarebbe piaciuto salvare l’utente come variabile d’istanza
quando lo passo a @userSession, ma non funziona. ho associato
all’istanza un ID e quello corrispondeva, ma l’utente non veniva
salvato.
Infine ho rinunciato a procedere così, e dal modello interrogo il
database con il dato in session[:user_id]. Spero di essermi spiegato a
sufficienza, grazie a tutti per la panoramica

nicola