Entiendo cómo implementar un setter (validador) ( def item= ), pero ¿cómo intercepto la operación << en un campo?
class Bla attr_reader :item def initialize @item = [] end # only called for =, +=, -= operations (not <<) def item=(value) puts "Changing value to #{value}" # pretend that there is a conditional here @item = value end # This is wrong: #def item<<(value) # puts "adding value #{value}" # @item << value #end end b = Bla.new b.item = ['one'] # works b.item += ['one'] # works b.item << 'two' # bypasses my setter Probé def item<<(value) , eso no parece funcionar.
Cuando llama a b.item << 'two' , está llamando al método << en el item directamente. Así que tienes algunas opciones aquí:
Implemente << directamente en su clase Bla , luego use b << 'two' :
# in class Bla def <<(value) # do validation here @item << value end Use algún otro nombre de método contenedor con un nombre más agradable como add_item :
# in class Bla def add_item(value) # do validation here @item << value end Use una clase de matriz especial para @item que tiene una definición personalizada para << :
class MyArray < Array def <<(item) # run validation here super end end # in Bla class def initialize @item = MyArray.new endProbablemente elegiría la opción 2, es la más simple y legible.
Aquí hay una sugerencia alternativa:
class Bla # DO NOT DEFINE A READER: # attr_reader :item def initialize @items = [] end def set_items(new_items) puts "Changing value to #{new_items}" # pretend that there is a conditional here @items = new_items end def remove_item(item) # pretend that there is a conditional here @items -= items end def add_item(item) # pretend that there is a conditional here @items += items end end Al no exponer el objeto @items directamente, mantiene el control total de la interfaz sobre cómo se manipula la variable.
(¡A menos que una persona que llama haga algo realmente extraño, como bla.instance_variable_get('@items') !!)
class Pit def <<(dirt) puts 'shovel ' + dirt end end Pit.new << 'sandy loam' # => shovel sandy loam