2012-07-19 13 views
14

Tengo un modelo con algunos atributos y un atributo virtual. Este atributo virtual se usa para crear una casilla de verificación en el formulario de creación.Escriba un atributo virtual del modelo ActiveRecord

class Thing < ActiveRecord::Base 
    attr_accessor :foo 
    attr_accessible :foo 
end 

Puesto que el campo es una casilla de verificación en la forma, el atributo foo recibirá '0' o '1' como valor. Me gustaría que sea un valor lógico debido al código siguiente:

class Thing < ActiveRecord::Base 
    attr_accessor :foo 
    attr_accessible :foo 

    before_validation :set_default_bar 

    private 

    def set_default_bar 
    self.bar = 'Hello' if foo 
    end 
end 

El problema aquí es que la condición se cumplirá incluso cuando foo es '0'. Me gustaría utilizar el mecanismo de conversión de tipo ActiveRecord pero el único que he encontrado para hacerlo es la siguiente:

class Thing < ActiveRecord::Base 
    attr_reader :foo 
    attr_accessible :foo 

    before_validation :set_default_bar 

    def foo=(value) 
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 


    private 

    def set_default_bar 
    self.bar = 'Hello' if foo 
    end 
end 

Pero me siento sucia hacerlo de esa manera. ¿Hay una mejor alternativa sin volver a escribir el método de conversión?

Gracias

Respuesta

14

Su solución de la publicación original parece ser la mejor solución para mí.

class Thing < ActiveRecord::Base 
    attr_reader :foo 
    def foo=(value) 
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 
end 

Si quieres limpiar un poco las cosas, siempre se puede crear un método auxiliar que define su método de escritor foo= para usted, utilizando value_to_boolean.

que probablemente crear un módulo con un método llamado bool_attr_accessor por lo que podría simplificar su modelo a tener este aspecto:

class Thing < ActiveRecord::Base 
    bool_attr_accessor :foo 
end 

Parece que ActiveModel debería proporcionar algo como esto para nosotros, por lo que los atributos virtuales acto más como atributos "reales" (ActiveRecord-persisted). Este tipo de conversión es esencial siempre que tenga un atributo virtual booleano que se envía desde un formulario.

Tal vez deberíamos enviar un parche a los rieles ...

0

¿Por qué usted no hace esto:

def foo=(value) 
    @foo = value 
    @bar = 'Hello' if value == "1" 
end 
+0

Porque los valores podrían cambiar a ''true'' y'' false'' y todavía debería funcionar. Rails lo hace bien, pero en el lugar equivocado en mi humilde opinión. – Happynoff

+0

Acerca de la configuración de @bar en 'foo =' Creo que no es el lugar para hacerlo, principio de responsabilidad única y todo. Y, en mi caso de uso real, tiene sentido colocarlo antes de la validación;) – Happynoff

+0

Tienes razón. Usted tiene esta solución: https://gist.github.com/2000623 pero no creo que sea una solución mejor. Básicamente es lo mismo que los rieles. Ver la fuente: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb – Dougui

1

Mira validates_acceptance_of código (haga clic en Mostrar fuente). Lo implementaron comparando con "0".

lo estoy usando en formularios de inscripción de esta manera:

class User < ActiveRecord::Base 
    validates_acceptance_of :terms_of_service 
    attr_accessible :terms_of_service 
end 

Si realmente quieren echar de cadena, etc se puede utilizar este:

def foo=(value) 
    self.foo=(value == true || value==1 || value =~ (/(true|t|yes|y|1)$/i)) ? true:false 
end 

O añadir método encasillado para la clase String y úselo en el modelo:

class String 
def to_bool 
    return true if self == true || self =~ (/(true|t|yes|y|1)$/i) 
    return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i) 
    raise ArgumentError.new("invalid value for Boolean: \"#{self}\"") 
end 
end 
+0

Sí, tal vez, pero esta forma de hacerlo es un poco sucia. Prefiero usar un booleano real. ¿Qué pasaría si en alguna otra parte del código escribo 'Thing.new (foo: true)'? – Happynoff

+0

encontré el String # to_bool limpio y útil. Gracias. – linojon

2

En los carriles 5 puede utilizar attribute método. Este método define un atributo con un tipo en este modelo. Anulará el tipo de atributos existentes si es necesario.

class Thing < ActiveRecord::Base 
    attribute :foo, :boolean 
end 

Precaución: hay un comportamiento incorrecto de esta función attribute en los carriles 5.0.0 en modelos cargados desde el PP. Por lo tanto, use los rieles 5.0.1 o superior.

Cuestiones relacionadas