5

Ho un modello con diversi attributi di data. Mi piacerebbe essere in grado di impostare e ottenere i valori come stringhe. I over-rode uno dei metodi (bill_date) in questo modo:Utilizzo del metodo mancante in Rails

def bill_date_human 
    date = self.bill_date || Date.today 
    date.strftime('%b %d, %Y') 
    end 
    def bill_date_human=(date_string) 
    self.bill_date = Date.strptime(date_string, '%b %d, %Y') 
    end 

Questo esegue grande per le mie esigenze, ma voglio fare la stessa cosa per molti altra data attributi ... come vorrei approfittare di metodo mancante in modo che qualsiasi attributo data possa essere impostato/ottenuto in questo modo?

+1

'method_missing' è l'ultima goccia che dovresti prendere. In realtà, la definizione dei metodi è molto più pulita, porta a una migliore progettazione del codice con una chiara separazione delle preoccupazioni, è molto più facile da capire e anche più veloce. Quindi se puoi definire i tuoi metodi, dovresti sempre farlo. –

+0

Come appreso da KL-7 ci sono approcci migliori rispetto a method_missing, ma considerando che ho 4 diversi attributi di data per questo modello, la definizione manuale di ognuno non è la soluzione. DRY – tybro0103

+0

Bene, il metodo di KL-7 è in realtà il preferito qui. Perché sta proponendo esattamente ciò che volevo dire: definire i metodi. –

risposta

10

Come già si conosce la firma dei metodi desiderati, potrebbe essere preferibile definirli invece di utilizzare method_missing. Si può fare così (dentro di te definizione di classe):

[:bill_date, :registration_date, :some_other_date].each do |attr| 
    define_method("#{attr}_human") do 
    (send(attr) || Date.today).strftime('%b %d, %Y') 
    end 

    define_method("#{attr}_human=") do |date_string| 
    self.send "#{attr}=", Date.strptime(date_string, '%b %d, %Y') 
    end 
end 

Se messa in vendita di tutti gli attributi di data non è un problema di questo approccio è meglio come avete a che fare con i metodi normali, invece di qualche magia all'interno method_missing.

Se si desidera applicare che a tutti gli attributi che hanno nomi che terminano con _date loro è possibile recuperare così (all'interno della vostra definizione di classe):

column_names.grep(/_date$/) 

Ed ecco method_missing soluzione (non testato, anche se il precedente non è testato né):

def method_missing(method_name, *args, &block) 
    # delegate to superclass if you're not handling that method_name 
    return super unless /^(.*)_date(=?)/ =~ method_name 

    # after match we have attribute name in $1 captured group and '' or '=' in $2 
    if $2.blank? 
    (send($1) || Date.today).strftime('%b %d, %Y') 
    else 
    self.send "#{$1}=", Date.strptime(args[0], '%b %d, %Y') 
    end 
end 

Inoltre è bello per ignorare respond_to? metodo e tornare true per i nomi dei metodi, che si maniglia interna method_missing (in 1.9 si dovrebbe sostituire invece respond_to_missing?).

+0

oooo bello! Non avevo ancora incontrato define_method :) Mi piacerebbe ancora vedere il metodo che manca implementazione, ma +1 per una soluzione migliore – tybro0103

+1

Aggiunta la versione 'method_missing', ma non usarla se è possibile definire questi metodi. –

+1

@ KL-7 Nel tuo metodo_missing dovresti tornare direttamente con super quando non gestisci il metodo. In questo momento, la gestione degli attributi di seguito viene sempre eseguita. –

5

Potresti essere interessato al modulo AttributeMethods di ActiveModel (che il record attivo utilizza già per un sacco di cose), che è quasi (ma non del tutto) quello di cui hai bisogno.

In poche parole si dovrebbe essere in grado di fare

class MyModel < ActiveRecord::Base 

    attribute_method_suffix '_human' 

    def attribute_human(attr_name) 
    date = self.send(attr_name) || Date.today 
    date.strftime('%b %d, %Y') 
    end 
end 

Dopo aver fatto questo, my_instance.bill_date_human chiamerebbe attribute_human con attr_name insieme a 'bill_date'. ActiveModel gestirà cose come method_missing, respond_to per te. L'unico svantaggio è che questi metodi umani esisterebbero per tutte le colonne.