2010-01-24 5 views
0

J'ai une classe qui déstructure la chaîne entrante dans un tableau imbriqué cascade. Par exemple pour une entrée abcde, il va produire un tableau [[[[a,b],c],d],e]. En ce moment, si j'accède à définir une valeur de niveau supérieur de cascade, la méthode []=(index, value) de ma classe sera invoquée. Mais j'ai également besoin d'attraper l'accès au tableau imbriqué au sein de cascade de niveau arbitraire.Ruby: accès à la variable à l'intérieur de la classe

Voir l'exemple ci-dessous, où l'accès x[0][0] n'appelle évidemment pas une méthode de classe []=. Alors, est-il possible d'attraper cet accès dans une méthode de classe (ou du moins d'une manière différente)?

class MyClass 

    attr_accessor :cascade 

    def initialize string  
    build_cascade string.split(//) 
    end 

    def build_cascade array 
    if array.length > 2 
     array[0] = array[0..1] 
     array.delete_at(1) 
     build_cascade array 
    else 
     @cascade = array 
    end 
    end 

    def []=(index, value) 
    puts 'You\'ve just tried to set \''+value.to_s+'\' for \''+index.to_s+'\' of @cascade!' 
    end 

    def [](index) 
    @cascade[index] 
    end 

end 

x = MyClass.new('abcdefghigk') 
puts x.inspect 

x[0] = 5 # => You've just tried to set '5' for '0' of @cascade! 
x[0][0] = 10 #= > ~ no output ~ 
+0

D'ailleurs, qu'est-ce que vous êtes en train de faire? Il pourrait y avoir un moyen plus simple tout à fait. –

Répondre

1

Le problème est que vous appelez [] = sur le sous-tableau contenu dans votre tableau principal. En d'autres termes, vous appelez [] sur votre classe, que vous implémentez pour renvoyer cet élément de tableau, et ensuite [] = sur un tableau générique, auquel vous n'avez pas bloqué l'accès en écriture.

Vous pouvez implémenter la structure pour que votre classe crée ses sous-matrices en utilisant d'autres instances de MyClass, ou vous pouvez remplacer la méthode [] = de Array pour en restreindre l'accès. Il est également intéressant de noter que, selon la façon dont cela serait utilisé, les méthodes d'écrasement sur une classe comme Array n'est généralement pas une bonne idée, alors vous pourriez vouloir quelque chose comme ma première suggestion.

0

Dans Ruby, vous pouvez patcher des objets, ajouter de nouvelles méthodes, redéfinir librement les anciennes. Donc, vous pouvez juste patcher tous les tableaux que vous créez afin qu'ils vous disent quand ils sont accessibles.

class A 
    def patch_array(arr) 
     class << arr 
      alias old_access_method []= 
      def []= (i, v) 
       @cascade_object.detect_access(self) 
       old_access_method(i,v) 
      end 
     end 

     s = self 
     arr.instance_eval { 
      @cascade_object = s 
     } 

    end 

    def detect_access(arr) 
     p 'access detected!' 
    end 
end 


a = A.new 
arr = [1, 2] 
a.patch_array(arr) 
arr[1] = 3 # prints 'access detected!' 
p arr 


new_arr = [1,4] 
new_arr[1] = 5 #prints nothing 
p new_arr 
0
#!/usr/bin/ruby1.8 

require 'forwardable' 

class MyClass 

    extend Forwardable 

    attr_accessor :cascade 

    def initialize string 
    @cascade = decorate(build_cascade string.split(//)) 
    end 

    private 

    def build_cascade array 
    if array.length <= 2 
     array 
    else 
     build_cascade([array[0..1]] + array[2..-1]) 
    end 
    end 

    def decorate(array) 
    return array unless array.is_a?(Array) 
    class << array 
     alias old_array_assign []= 
     def []=(index, value) 
     puts "#{self}[#{index}] = #{value}" 
     old_array_assign(index, value) 
     end 
    end 
    array.each do |e| 
     decorate(e) 
    end 
    array 
    end 

    def_delegators :@cascade, :[], :[]= 

end 

x = MyClass.new('abcdefghigk') 
p x.cascade 
# => [[[[[[[[[["a", "b"], "c"], "d"], "e"], "f"], "g"], "h"], "i"], "g"], "k"] 
x[0][1] = 5    # => abcdefghig[1] = 5 
x[0][0][1] = 10   # => abcdefghi[1] = 10 
x[0][0][0][1] = 100  # => abcdefgh[1] = 100 
p x.cascade 
# => [[[[[[[[[["a", "b"], "c"], "d"], "e"], "f"], "g"], 100], 10], 5], "k"] 
+0

Merci, Wayne. C'est un très bel exemple! Mais que se passe-t-il si je change «puts» # {self} [# {index}] = # {value} "' pour l'affectation réelle, c'est-à-dire 'self [index] = value'? Il donne '... niveau de pile trop profond ...' error :-(Comment éviter cela? – gmile

+0

Vous appelez old_array_assign pour faire l'affectation réelle.J'ai modifié l'exemple pour montrer comment.la réponse de vava montre également comment appeler une méthode aliasée (renommée). –

Questions connexes