5

Tengo un modelo con varios atributos de fecha. Me gustaría poder establecer y obtener los valores como cadenas. Yo sobre-Rode uno de los métodos (bill_date) de esta manera:Falta el método de uso en 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 

Esto funciona muy bien para mis necesidades, pero quiero hacer lo mismo para varios otros atributos de la fecha ... ¿cómo iba a aprovechar método que falta para que cualquier atributo de fecha se puede establecer/obtener así?

+1

'method_missing' es la última gota que debe tomar. En realidad, definir los métodos es mucho más limpio, conduce a un mejor diseño del código con una clara separación de las preocupaciones, es mucho más fácil de entender y también más rápido. Entonces, si puedes definir tus métodos, siempre debes hacerlo. –

+0

Como aprendí de KL-7, hay mejores enfoques que la falta de método, pero teniendo en cuenta que tengo 4 atributos de fecha diferentes para este modelo, la definición manual de cada uno no es la solución. DRY – tybro0103

+0

Bueno, el método de KL-7 es realmente el preferido aquí. Porque él está proponiendo exactamente lo que también quise decir: definir los métodos. –

Respuesta

10

Como ya sabe la firma de los métodos deseados, podría ser mejor definirlos en lugar de usar method_missing. Puede hacerlo así (dentro de ti definición de clase):

[: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 

Si una lista de todos los atributos de fecha no es un problema de este enfoque es mejor, ya que se trata de métodos regulares en lugar de un poco de magia en el interior method_missing.

Si desea aplicar eso a todos los atributos que tienen nombres que terminan con _date puede recuperarlos como que (dentro de su definición de clase):

column_names.grep(/_date$/) 

Y aquí es method_missing solución (no probado, aunque el anterior uno no se prueba tampoco):

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 

Además es bueno para anular respond_to? método y volver true de nombres de métodos, que se maneja dentro method_missing (en 1.9 debe reemplazar respond_to_missing? en su lugar).

+0

oooo agradable! No me había encontrado con define_method antes :) Todavía me gustaría ver que falta el método de implementación, pero +1 para una mejor solución – tybro0103

+1

Se agregó la versión 'method_missing', pero no la use si puede definir estos métodos. –

+1

@ KL-7 En su 'method_missing' debe devolver directamente con super cuando no se maneja el método. En este momento, el manejo de atributos a continuación siempre se ejecuta. –

5

Puede que esté interesado en el módulo AttributeMethods de ActiveModel (cuyo registro activo ya se usa para muchas cosas), que es casi (pero no del todo) lo que necesita.

En pocas palabras usted debe ser capaz de hacer

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 

Una vez hecho esto, my_instance.bill_date_human llamarían attribute_human con attr_name conjunto a 'bill_date'. ActiveModel manejará cosas como method_missing, respond_to para usted. El único inconveniente es que estos _ métodos humanos existirían para todas las columnas.

Cuestiones relacionadas