Pregunta

Pregunta: El uso de Ruby es fácil de añadir a medida métodos a las clases existentes, pero ¿Cómo se puede añadir personalizados propiedades ? Aquí está un ejemplo de lo que estoy tratando de hacer:

myarray = Array.new();
myarray.concat([1,2,3]);
myarray._meta_ = Hash.new();      # obviously, this wont work
myarray._meta_['createdby'] = 'dreftymac';
myarray._meta_['lastupdate'] = '1993-12-12';

## desired result
puts myarray._meta_['createdby']; #=> 'dreftymac'
puts myarray.inspect()            #=> [1,2,3]

El objetivo es construir la definición de clase de tal manera que el material que no funciona en el ejemplo anterior trabajo voluntad como se esperaba.

Actualización: (aclarar cuestión) Un aspecto que ha quedado fuera de la pregunta original: es también un objetivo de añadir "valores por defecto" que normalmente se ponen en marcha en el initialize método de la clase.

Actualización: (¿por qué hacer esto) Normalmente, es muy fácil de simplemente crear una clase personalizada que hereda de la matriz (o lo que sea incorporado en la clase que desea emular). Esta cuestión se deriva de algunos "sólo prueba" código y no es un intento de hacer caso omiso de este enfoque general aceptable.

¿Fue útil?

Solución

Hay que recordar que en Ruby, usted no tiene acceso a los atributos (variables de instancia) fuera de esa instancia. Sólo se tiene acceso a los métodos públicos de una instancia.

Se puede usar attr_accessor para crear un método para una clase que actúa como una propiedad que usted describe:

irb(main):001:0> class Array
irb(main):002:1>  attr_accessor :_meta_
irb(main):003:1> end
=> nil
irb(main):004:0> 
irb(main):005:0* x = [1,2,3]
=> [1, 2, 3]
irb(main):006:0> x._meta_ = Hash.new
=> {}
irb(main):007:0> x._meta_[:key] = 'value'
=> "value"
irb(main):008:0> 

Para una manera simple de hacer una inicialización por defecto para un descriptor de acceso, tendremos que básicamente reimplementar attr_accessor nosotros mismos:

class Class
  def attr_accessor_with_default accessor, default_value
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_, {}
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

Pero espera! La salida no es correcta:

{:key=>"value"}
{:foo=>"bar", :key=>"value"}

Hemos creado un cierre en torno al valor por defecto de un hash literal.

Una mejor manera podría ser simplemente utilizar un bloque:

class Class
  def attr_accessor_with_default accessor, &default_value_block
    define_method(accessor) do
      name = "@#{accessor}"
      instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name)
      instance_variable_get(name)
    end

    define_method("#{accessor}=") do |val|
      instance_variable_set("@#{accessor}", val)
    end
  end
end

class Array
    attr_accessor_with_default :_meta_ do Hash.new end
end

x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_

y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_

Ahora el resultado es correcto porque Hash.new se llama cada vez que se recupera el valor por defecto, en lugar de volver a utilizar el mismo hash literal cada vez.

{:key=>"value"}
{:foo=>"bar"}

Otros consejos

No es una propiedad de sólo un getter y setter? Si es así, ¿no podría simplemente hacer:

class Array
  # Define the setter
  def _meta_=(value)
    @_meta_ = value
  end

  # Define the getter
  def _meta_
    @_meta_
  end
end

A continuación, se puede hacer:

x = Array.new
x._meta_
# => nil

x._meta_ = {:name => 'Bob'}

x._meta_
# => {:name => 'Bob'}

¿Eso ayuda?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top