RSS
 

Archive for June, 2009

Aggiungere un operazione ad una risorsa in un sistema RESTful

22 Jun

Creare un applicazione RESTful con rails è molto semplice:

C:>rails MyApp

In un sistema REST, l’applicazione è composta da risorse web aventi un insieme vincolato di operazioni. Con lo scaffold generiamo una risorsa e le quattro basilari funzioni per gestirla, denominate CRUD (Create, Read, Update, Delete)

C:>script/generate scaffold assets name:string

Abbiamo creato la base da cui partire per testare questo breve articolo.

Supponiamo di voler aggiungere una nuova operazione alla nostra risorsa risorsa Asset, per esempio vogliamo gestire la copia. Iniziamo creando il nuovo indirizzamento modificando quanto creato dallo scaffold:

#config/routes.rb
map.resources :assets, :member => { :copy => :get }

Testiamo l’esito:
C:>rake routes

verificando che ci sia il nuovo percorso:

copy_asset GET /assets/:id/copy(.:format) {:controller=>"assets", :action=>"copy"}

La copia è un’operazione utile in molti contesti in quanto permette di effettuare l’inserimento di una nuova risorsa partendo da una già esistente.
Traduciamo in codice e andiamo a creare la nuova operazione nel controller:

#app/controllers/assets_controller.rb
 
# GET /assets/copy
# GET /assets/copy.xml
def copy
  @asset = asset.new
  @asset.attributes = Asset.find(params[:id]).attributes
 
  respond_to do |format|
    format.html { render :new }
    format.xml  { render :xml => @asset }
  end
end

Eventualmente si può creare la view, dipende dall’operazione che si deve gestire. In questo esempio si sfrutta la view dell’operazione new in quanto sono molto simili e richiamano entrambe la :create.

Creare o non creare una nuova azione sulla risorsa è pura filosofia, tralasciando la questione vediamo come indirizzare una nuova create, ci riferiamo ad una collezione di risorse:

#config/routes.rb
map.resources :assets, :member => { :copy => :get }, :collection => { :createcp => :put }

ho utilizzato una collezione perchè in questo caso non serve un riferimento ad una risorsa esistente, se avessi usato :member il path per la generazione dell’uri lo avrebbe richiesto.

se testiamo nuovamente le routes dovrebbe aggiungersi questa nuova:

createcp_asset PUT /assets/createcp(.:format) {:controller=>"assets", :action=>"createcp"}

creiamo quindi la nuova view e il form con la uri della nuova azione specificando il metodo (per precisione ho indicato put ma equivale ad utilizzare un post) :

#app/views/assets/copy.html.erb
 
<% form_for(@asset, :url => createcp_assets_path, :method=>:put do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label t(:name) %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit t(:create) %>
  </p>
<% end %>

e la nuova azione nel controller:

#app/controllers/assets_controller.rb
 
# POST /assets/createcp
# POST /assets/createcp.xml
def createcp
  @asset = Asset.new(params[:asset])
 
  #do something
 
  respond_to do |format|
    if @asset.save
      flash[:notice] = I18n.t(:created, :model => I18n.t('model.asset'))
      format.html { redirect_to assets_path }
      format.xml  { render :xml => @asset, :status => :created, :location => @asset }
    else
      format.html { render :action => "copy" }
      format.xml  { render :xml => @asset.errors, :status => :unprocessable_entity }
    end
  end
end

Per richiamare la nuova operazione dalla index:

#app/views/assets/index.html.erb
<%= link_to t(:copy), copy_asset_path(asset) %>

Infine ed eventualmente, queste sono le parole utilizzate dall’esempio per l’internazionalizzazione:

create: "Crea"
copy: "Copia"
created: "L'oggetto {{model}} è stato creato correttamente."
name: "Nome"
model:
  asset: "Allegato"
 
Comments Off

Posted in Ruby on Rails

 

Distruttori di classe #3

19 Jun

Eccoci all’ultimo articolo della serie rivolta ai distruttori di classe e più in generale a come fare per inserire un metodo da richiamare alla fine dell’utilizzo di una classe.

Nel precedente articolo abbiamo visto come fare per generare un evento in fase di distruzione dell’oggetto da parte del garbage collector e che richiami un metodo per svolgere le ultimissime operazioni.

Passiamo alla pratica e dopo i commenti:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
  @@count=0
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc)
    @var1=var1
    @@count +=1
  end
 
  #Metodo di servizio
  def body
    begin
      yield(self)
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end
 
  #Metodo che fa il lavoro sporco
  def work
    puts 'Work'
  end
 
  private
  #Distruttore
  def finalize
    puts "Finalize #{@var1}"
  end
  def self.finalize(id)
    puts "*** Finalize id #{id} nr. #{@@count} ***"
    @@count -=1
    #puts "Var1 #{ObjectSpace._id2ref(id).var1}"
  end
 
end
 
#Istanzio tre oggetti e li utilizzo
['aaa','bbb','ccc'].each do |str|
  E.new(str).body do |o| 
    o.work
    #...e tutti gli altri metodi
  end
end

L’output generato sarà questo:


D:\Marco\Progetti\Ruby\Batch\Test\Destructor>ruby D3.rb
Initializing 20806360 var aaa
Work
Finalize aaa
Initializing 20806220 var bbb
Work
Finalize bbb
Initializing 20806080 var ccc
Work
Finalize ccc
*** Finalize id 20806080 nr. 3 ***
*** Finalize id 20806220 nr. 2 ***
*** Finalize id 20806360 nr. 1 ***

Ci sono un pò di differenze rispetto al precedente articolo:

Questa volta il distruttore è richiamato direttamente nel costruttore perchè ho ipotizzato che servisse ad ogni oggetto, l’alternativa è definirlo come in un esempio precedente, solo per gli oggetti interessati, dopo l’istanziamento:

E.new('xxx').body do |o|
  ObjectSpace.define_finalizer(o, o.class.method(:finalize).to_proc)
  o.work
end

Un altra differenza è che questa volta non si associa una procedura creata “al volo” ma un metodo di classe, nella definizione della classe è infatti presente un nuovo metodo: self.finalize

Ho inserito una variabile di classe, chiamata anche attributo statico, @@count. E’ in comune con tutte le istanze e nell’esempio serve per conteggiare gli oggetti della classe E. Nel distruttore viene mostrato il valore per informarci del numero rimanente di oggetti.

Questa volta non è necessario forzare l’esecuzione del garbage collector in quanto al termine dello script viene fatto in automatico generando l’evento che richiama il costruttore per ogni oggetto.

Ho cercato di semplificare il codice e cosi facendo ho omesso una finezza di ruby che devo però indicarla almeno come nota anche se non c’entra nulla coi distruttori. Se i metodi da richiamare sono molti si può utilizzare questa sintassi lasciando il codice molto leggibile:

E.new('xxx').body do |o|
  ObjectSpace.define_finalizer(o, o.class.method(:finalize).to_proc)
    ['work', 'hardwork', 'ecc'].each do |method_string|
        o.send(method_string)
    end
end

Purtroppo non riesco ad utilizzare un metodo distruttore che sia univoco per istanza. Un altro tentativo poteva essere quello di puntare all’istanza tramite ObjectSpace che tramite il metodo _id2ref restituisce l’oggetto con id uguale a quello passato come parametro:

ObjectSpace._id2ref(id)

Sfortunatamente non funziona dall’interno del distruttore, probabilmente il garbage collector ha già distrutto l’istanza, questo è un peccato e spero esista un altro sistema che però non conosco…

 
Comments Off

Posted in Ruby

 

Distruttori di classe #2

19 Jun

Nell’articolo precedente abbiamo visto come creare un metodo che permetta di rilasciare le risorse al termine dell’utilizzo. Non sempre però è possibile determinare il momento del rilascio, ad esempio quando è legato ad eventi esterni. In questo caso è necessario che il distruttore sia eseguito proprio quando l’oggetto sia in fase di smaltimento, in automatico.

Con un linguaggio che adopera un garbage collector però bisogna fare molta attenzione per il semplice motivo che non si sa quando verrà eseguita la pulizia e solo in questo momento viene scaturito l’evento che richiama il metodo distruttore.

Quando distruggiamo esplicitamente un oggetto da codice, valorizzandolo con nil, stiamo solamente indicando l’intenzione, sarà poi compito del garbage collector analizzare se effettivamente non esiste nessun riferimento a quell’area di memoria, basta un solo indirizzamento anche involontario (i casi più probabili) per impedirne il rilascio. Poi c’è l’incognita di quando tutto ciò verrà fatto.

Riprendiamo l’esempio precedente con qualche aggiunta al codice:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    @var1=var1
  end
 
  #Metodo di servizio
  def body
    begin
      yield
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end
 
  #Metodo che fa il lavoro sporco
  def work
    puts 'Work'
  end
 
  private
  #Distruttore
  def finalize
    puts "End #{@var1}"
  end
 
end
 
#Istanzio il primo oggetto
e1=E.new('Try1')
e1.body do
  e1.work
end
 
#Istanzio il secondo oggetto per il quale ho anche la necessità
#di rilasciare alcune risorse in un momento indeterminato
e2=E.new('Try2')
e2.body do
  ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"})
  e2.work
end
 
puts 'Oggetti prima della pulizia:'
ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}
 
#Distruggo gli oggetti
e1=nil
e2=nil
 
puts 'Richiamo il garbage collector per la pulizia...'
GC.start
sleep(2)
puts 'Oggetti dopo la pulizia:'
ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}

Se eseguiamo questo script otterremo il seguente output:

D:\Marco\Progetti\Ruby\Batch\Test>ruby D2.rb
Initializing 20805980 var Try1
Work
End Try1
Initializing 20805910 var Try2
Work
End Try2
Oggetti prima della pulizia:
Oggetto: 20805910
Oggetto: 20805980
Richiamo il garbage collector per la pulizia...
Finalizing 20805910
Oggetti dopo la pulizia:

La definizione della classe è rimasta identica, questa volta stanzio due oggetti, il primo è identico all’articolo precedente, per il secondo invece ho ipotizzato che dovesse gestire il rilascio di alcune risorse in un momento x, noi non sappiamo quando si verificherà sappiamo solo che quando avverrà dobbiamo fare in modo che vengano deallocate alcune risorse.

Un esempio concreto è quando si scrive un file mediante un processo esterno, dobbiamo riferire di chiuderlo prima di terminare la nostra applicazione, anche se si verifica qualcosa di inatteso.

Se esaminiamo il secondo oggetto:

e2=E.new('Try2')
e2.body do
  ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"})
  e2.work
end

Si nota che abbiamo fatto uso della classe ObjectSpace, contenitore di tutte le classi stanziate, per associare la dimissione dell’oggetto e2 alla proc passata come parametro. Tradotto in parole semplici stiamo dicendo: prima di distruggere l’oggetto esegui quella procedura.

Se riguardiamo l’output si può notare un altro l’utilizzo della classe ObjectSpace:

ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}

E’ possibile esaminare ogni oggetto istanziato e specificando come parametro la classe E possiamo filtrare solo quella che ci interessa (attenzione che senza filtro la lista potrebbe essere molto lunga).

Tornando all’output: gli oggetti trovati nel contenitore sono due e viene mostrato l’id di riferimento. Dopo di che vengono distrutti e viene forzata l’esecuzione del garbage collector con GC.start. In alternativa a questo è anche possibile eseguire il metodo garbage_collect di ObjectSpace

Dopo la pulizia viene generato l’evento che richiama la proc associata all’oggetto, quella che nell’esempio scrive a video: Finalizing 20805910. L’id dell’oggetto è passato come parametro al blocco e noi lo mostriamo. Se andiamo a riesaminare il contenitore degli oggetti questa volta non conterrà nessuna istanza della classe E.

Nel prossimo articolo esaminerò l’utilizzo di metodi distruttori da associare all’evento di distruzione dell’oggetto. Tratterò anche la problematica legata alle variabili di istanza.

 
Comments Off

Posted in Ruby

 

Distruttori di classe #1

19 Jun

Qualche esempio per testare il funzionamento dei distruttori di classe in ruby.

In questo articolo tratterò qualcosa di simile, non proprio distruttori ma metodi utilizzati per terminare il nostro lavoro, senza il diretto scopo di liberare memoria, comunque per comodità chiamerò anche questi distruttori. Se è giusto o no non lo so ma poco importa, tralasciamo la parte filosofica.

Il manuale Programming Ruby di Dave Thomas senza dubbio è ottimo ma non c’è tutto quello che avrei voluto trovare sull’argomento. Molto si trova anche in rete anche se in maniera molto disordinata.
Questi sono miei tentativi, non è detto che non ci sia un modo migliore per fare la stessa cosa, lo scopo è anche cercare di migliorarsi quindi ben vengano eventuali commenti.

Per rendere queste righe appetibili anche ai neofiti ho inserito delle premesse, chi già conosce l’argomento può così saltare la lettura.

Faccio una premessa riguardo la programmazione ad oggetti: per ogni classe esiste un metodo costruttore, utilizzato per inizializzare una classe, ed un metodo distruttore, in genere utilizzato per rilasciare le risorse occupate dalla classe istanziata. In genere sono opzionali e quando vengono specificati viene fatto un override del metodo presente sulla classe base, dalla quale tutte le classi derivano.

Nei linguaggi più moderni come Java, .NET e altri tra cui anche Ruby, il distruttore ha perso la funzionalità che aveva in origine in favore del garbage collector.

Il garbage collector è, per stare fedele alla traduzione, il processo che smaltisce la spazzatura, in altre parole è colui che si occupa di analizzare ogni oggetto istanziato e determinare se è ancora utilizzato, in caso negativo lo si elimina. Una volta era compito del programmatore avere cura di fare pulizia, ora la gestione della memoria è gestita in automatico, oltre che migliorare la comodità questo ha anche impatto sulle prestazioni, migliorandole nella maggior parte dei casi.

Passiamo alla pratica con questo esempio banale:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    @var1=var1
  end
 
  #Metodo di servizio
  def body
    begin
      yield(self)
    rescue StandardError =&gt; err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end
 
  #Metodo che fa il lavoro sporco
  def work
    puts 'Work'
  end
 
  private
  #Distruttore
  def finalize
    puts "End #{@var1}"
  end
end
 
E.new('Try1').body do |o|
  o.work
end

Nell’esempio viene definita la classe E e subito sotto ci sono le istruzione per testarla, si otterrà un output come questo:


D:\Marco\Progetti\Ruby\Batch\Test>ruby E.rb
Initializing 20807530 var Try1
Work
End Try1

Entrando nel dettaglio, viene istanziata la classe passando al metodo costruttore una stringa per l’inizializzazione della classe. Nel metodo costruttore viene mostrato anche l’id dell’oggetto istanziato, un riferimento univoco nello spazio oggetti.

A questo punto abbiamo istanziato la classe passandola come parametro al blocco.

Utilizzando una metafora, immaginate che la definizione di classe sia uno stampino, come quello che utilizzano i bambini al mare. Lo stampino è la definizione della classe, ogni forma di sabbia che creo equivale ad un istanziamento della classe.

Il metodo body possiamo definirlo di servizio poiché non include nessuna istruzione scritta da noi bensì fa da tramite eseguendo ciò che gli passiamo tramite il blocco:

E.new('Try1').body do |o|
  o.work
end

Dentro questo blocco inseriamo ciò che vogliamo fare con questa classe. Al termine viene eseguito il metodo distruttore “finalize”, lo si può vedere esaminando il contenuto del metodo body:

  def body
    begin
      yield(self)
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end

Il blocco viene “esploso” con l’istruzione yield(self), dove il parametro si riferisce all’oggetto del blocco. Se capita un imprevisto viene intercettata l’eccezione, stampato l’errore e comunque eseguito il distruttore.

Per motivi di tempo devo fermarmi quà. Nel prossimo articolo scriverò come eseguire automaticamente il distruttore, tramite la generazione di un evento da parte del garbage collector.

 
Comments Off

Posted in Ruby