2011-03-28 10 views
7

¿Cómo obtengo la funcionalidad del método de atributos ActiveRecord? Tengo esta clase:Atributos de ActiveModel

class PurchaseForm 
    include ActiveModel::Validations 
    include ActiveModel::Conversion 
    extend ActiveModel::Naming 

    attr_accessor :name, 
       :surname, 
       :email 

    validates_presence_of :name 

    validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i 

    def initialize(attributes = {}, shop_name) 
    if not attributes.nil? 
     attributes.each do |name, value| 
     send("#{name}=", value) 
    end 
    end 

    def persisted? 
    false 
    end 
end 

Lo que tengo que hacer, tener un método atributos para listar todos los nombres y valores de objeto PurchaseForm?

+0

A la vista de las respuestas existentes hacky, la respuesta correcta parece ser "no hay ningún módulo de rieles para ese". Personalmente evitaría imitar los atributos API. – vemv

Respuesta

6

he conseguido resolver el problema con este código:

class PurchaseForm 
    include ActiveModel::Validations 
    include ActiveModel::Conversion 
    extend ActiveModel::Naming 

    attr_accessor :attributes, 
       :name, 
       :surname, 
       :email 

    validates_presence_of :name 

    validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i 

    def initialize(attributes = {}) 
    @attributes = attributes 
    end 

    def persisted? 
    false 
    end 
end 
+0

¿Por qué agregó: atributos a la llamada attr_accessor? – Craig

+0

Así que puedo llamar a un método de atributos desde la vista ... Soy bastante novato en Rails, así que si tienes una mejor solución, comparte :) – zigomir

+0

Esta solución tiene inconvenientes. Si no pasa todos los atributos a '.new',' .attributes' devolvería hash incorrecto con solo atributos especificados. Y segundo, si intenta cambiar la instancia después de la inicialización, diga '@purchase_form.name = 'Other name'' y luego llame a '@ purchase_form.attributes', el hash contendría old': name' proporcionado a '.new'. –

-3

¿no sería mejor utilizar

include ActiveModel::Serialization 

def attributes 
    JSON.parse(self.to_json) 
end 
9

Aquí es la variante refactorizado:

class PurchaseForm 
    include ActiveModel::Model 

    def self.attributes 
    [:name, :surname, :email] 
    end 

    attr_accessor *self.attributes 

    # your validations 

    def to_hash 
    self.class.attributes.inject({}) do |hash, key| 
     hash.merge({ key => self.send(key) }) 
    end 
    end 
end 

Ahora puede trabajar fácilmente con esta clase:

irb(main):001:0> a = PurchaseForm.new({ name: 'Name' }) 
=> #<PurchaseForm:0x00000002606b50 @name="Name"> 
irb(main):002:0> a.to_hash 
=> {:name=>"Name", :surname=>nil, :email=>nil} 
irb(main):003:0> a.email = '[email protected]' 
=> "[email protected]" 
irb(main):004:0> a 
=> #<PurchaseForm:0x00000002606b50 @name="Name", @email="[email protected]"> 
irb(main):005:0> a.to_hash 
=> {:name=>"Name", :surname=>nil, :email=>"[email protected]"} 

Aún más, si usted quiere hacer este comportamiento reutilizable, considere la extracción de .attributes y métodos en #to_hash módulo separado:

module AttributesHash 
    extend ActiveSupport::Concern 

    class_methods do 
    def attr_accessor(*args) 
     @attributes = args 
     super(*args) 
    end 

    def attributes 
     @attributes 
    end 
    end 

    included do 
    def to_hash 
     self.class.attributes.inject({}) do |hash, key| 
     hash.merge({ key => self.send(key) }) 
     end 
    end 
    end 
end 

Ahora, sólo se incluyen a su modelo y ya está:

class PurchaseForm 
    include ActiveModel::Model 
    include AttributesHash 

    attr_accessor :name, :surname, :email 

    # your validations 
end 
6

#instance_values podría hacer el trabajo:

class PurchaseForm 
    attr_accessor :name, :email 

    def attributes 
    instance_values 
    end 
end 

muestra de salida:

purchase.attributes #=> {"name"=>"John", "email"=>"[email protected]"} 
Cuestiones relacionadas