2009-06-11 5 views
14

¿Hay una forma más 'SECA' de hacer lo siguiente en ruby?Inicialización de objeto DRY'er en Ruby

#!/usr/bin/env ruby 

class Volume 
    attr_accessor :name, :size, :type, :owner, :date_created, :date_modified, :iscsi_target, :iscsi_portal 

    SYSTEM = 0 
    DATA = 1 

    def initialize(args={:type => SYSTEM}) 
     @name = args[:name] 
     @size = args[:size] 
     @type = args[:type] 
     @owner = args[:owner] 
     @iscsi_target = args[:iscsi_target] 
     @iscsi_portal = args[:iscsi_portal] 
    end 

    def inspect 
     return {:name => @name, 
       :size => @size, 
       :type => @type, 
       :owner => @owner, 
       :date_created => @date_created, 
       :date_modified => @date_modified, 
       :iscsi_target => @iscsi_target, 
       :iscsi_portal => @iscsi_portal } 
    end 

    def to_json 
     self.inspect.to_json 
    end 

final

+0

¿Sería un OpenStruct la funcionalidad deseada? –

+0

Esto debería estar en codereview.stackexchange.com –

Respuesta

12

Siempre que vea una larga lista de cosas por el estilo, por lo general se puede rodar todo para arriba en una matriz singular:

class Volume 
    ATTRIBUTES = [ 
    :name, :size, :type, :owner, :date_created, :date_modified, 
    :iscsi_target, :iscsi_portal 
    ].freeze 

    ATTRIBUTES.each do |attr| 
    attr_accessor attr 
    end 

    SYSTEM = 0 
    DATA = 1 

    DEFAULTS = { 
    :type => SYSTEM 
    }.freeze 

    def initialize(args = nil) 
    # EDIT 
    # args = args ? DEFAULTS : DEFAULTS.merge(args) # Original 
    args = args ? DEFAULTS.merge(args) : DEFAULTS 

    ATTRIBUTES.each do |attr| 
     if (args.key?(attr)) 
     instance_variable_set("@#{attr}", args[attr]) 
     end 
    end 
    end 

    def inspect 
    ATTRIBUTES.inject({ }) do |h, attr| 
     h[attr] = instance_variable_get("@#{attr}") 
     h 
    end 
    end 

    def to_json 
    self.inspect.to_json 
    end 
end 

La manipulación de las variables de instancia es bastante sencillo después de eso .

+0

¡Impresionante! Tuve que cambiar una línea en la función de inicialización args = args? DEFAULTS: DEFAULTS.merge (args) se convierte en args = (args == nil)? DEFAULTS: DEFAULTS.merge (args) De lo contrario, devolverá DEFAULTS y no configurará nada. –

+0

El único inconveniente aquí es que si paso un hash con un valor predeterminado, como 'Hash.new (4)' o 'Hash.new {| h, k | h [k] = k.to_s} ', nunca se usaría (y lo haría en el código original). Para evitar esto, ¿dejaría caer el args.key? if cláusula, y usa el leve klunky 'args = args? args.merge (DEFAULTS) .merge (args): DEFAULTS' que permitirá que args anule los DEFAULTS pero mantenga su comportamiento predeterminado. – rampion

+0

Ah, parece que obtuve mi ternario al revés en el control de args. ¡Eso es lo que obtengo por publicar un código no probado! Publica una solución para evitar confusiones. – tadman

9
class Volume 

    FIELDS = %w(name size type owner iscsi_target iscsi_portal date_create date_modified) 
    SYSTEM = 0 
    DATA = 1 
    attr_accessor *FIELDS 

    def initialize(args= { :type => SYSTEM }) 
    args.each_pair do | key, value | 
     self.send("#{key}=", value) if self.respond_to?("#{key}=") 
    end 
    end 

    def inspect 
    FIELDS.inject({}) do | hash, field | 
     hash.merge(field.to_sym => self.send(field)) 
    end.inspect 
    end 

end 
+0

-1 inspeccionar no devuelve los valores de los atributos en el hash – rampion

+0

tiene razón. Lo arreglé. el código anterior debería funcionar ahora. Gracias. –

1

Riffing fuera de tadman 's answer

me habría #inspect devolver una cadena (como la mayoría #inspect métodos), y tal vez el factor a cabo su conversión a un método de comprobación aleatoria, un método #to_hash lugar.

El disparate args.merge(DEFAULTS).merge(args) permite al args anulación del DEFAULTS, pero mantiene un comportamiento por defecto para args (por ejemplo, si args == Hash.new(3) o args == Hash.new { |h,k| h[k] = h.to_s.length }

class Volume 
    ATTRIBUTES = %w{ 
    name size type owner date_created date_modified 
    iscsi_target iscsi_portal 
    }.map! { |s| s.to_sym }.freeze 

    attr_accessor *ATTRIBUTES 

    SYSTEM = 0 
    DATA = 1 

    DEFAULTS = { :type => SYSTEM }.freeze 

    def initialize(args = nil) 
    args = args ? args.merge(DEFAULTS).merge(args) : DEFAULTS 

    ATTRIBUTES.each do |attr| 
     instance_variable_set("@#{attr}", args[attr]) 
    end 
    end 

    def to_hash 
    Hash[ *ATTRIBUTES.map { |attr| [ attr, instance_variable_get("@#{attr}") ] }.flatten ] 
    end 

    def inspect 
    to_hash.inspect 
    end 

    def to_json 
    self.to_hash.to_json 
    end 
end 
1

Aquí hay un giro ligeramente diferente en la respuesta por Tadman:

class Volume 
    ATTRIBUTES = [ 
    :name, :size, :type, :owner, :date_created, :date_modified, 
    :iscsi_target, :iscsi_portal 
    ].freeze 

    ATTRIBUTES.each do |attr| 
    attr_accessor attr 
    end 

    SYSTEM = 0 
    DATA = 1 

    DEFAULTS = { 
    :type => SYSTEM 
    }.freeze 

    def initialize(&block) 
    ATTRIBUTES.each do |attr| 
     self.__send__ "#{attr}=", DEFAULTS[attr] 
    end 
    yield(self) 
    end 

    def inspect 
    ATTRIBUTES.inject({}) { |h,attr| h[attr] = self.__send__ attr; h } 
    end 
    def to_json 
    self.inspect.to_json 
    end 
end 

Esto permite hacer esto:

vol = Volume.new do |v| 
    v.name = 'myVolume' 
end 

Me gusta porque tiene la ventaja de dar errores de inmediato si alguien ha cometido un error tipográfico en un atributo.

Además, a diferencia de su respuesta, inicializa los valores predeterminados cuando no se suministran.

o si termina haciendo que sea mucho y realmente necesitan SECO:

module Attributable 
    @@ATTRIBUTES = [] 
    @@DEFAULTS = {} 

    def initialize(&block) 
    @@ATTRIBUTES.each do |attr| 
     self.class.__send__ :attr_accessor, attr 
     self.__send__ "#{attr}=", @@DEFAULTS[attr] 
    end 
    yield(self) 
    end 
end 

class Volume 
    include Attributable 
    @@ATTRIBUTES = [ :name, :size, :type, :owner ] 
    @@DEFAULTS = { :type => 0 } 
end 

no podía encontrar la manera de hacerlo con constantes así que lo hice con variables de clase en su lugar.

Cuestiones relacionadas