2010-01-24 5 views
0

У меня есть класс, который деконструирует входящую строку во вложенный массив cascade. Например, для ввода abcde он будет производить массив [[[[a,b],c],d],e].Ruby: доступ к переменной внутри класса

Только сейчас, если я получаю доступ к настройке любого значения верхнего уровня cascade, будет вызван метод []=(index, value) моего класса. Но мне также нужно поймать доступ к вложенному массиву в пределах cascade произвольного уровня.

См. Пример ниже, где доступ к x[0][0] явно не вызывает метод класса []=. Итак, можно ли поймать этот доступ в методе класса (или, по крайней мере, по-другому)?

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

Кстати, что это на самом деле вы делаете? Там может быть более простой способ. –

ответ

1

Проблема в том, что вы вызываете [] = на подматрицу, содержащуюся в вашем основном массиве.

Другими словами, вы вызываете [] на свой класс, который вы реализуете, чтобы вернуть этот элемент массива, а затем [] = на общий массив, к которому вы не заблокировали доступ на запись.

Вы можете реализовать структуру, чтобы ваш класс создавал свои подмассивы, используя другие экземпляры MyClass, или вы могли бы перезаписать метод [] = массива для ограничения доступа.

Его также стоит отметить, что в зависимости от того, как это будет использоваться, методы перезаписи в классе, таком как Array, обычно не являются отличной идеей, поэтому вы можете захотеть пойти на что-то вроде моего первого предложения.

0

В Ruby вы можете патчировать объекты, добавлять новые методы, переопределять старые. Таким образом, вы можете просто исправить все массивы, которые вы создаете, чтобы они рассказывали вам, когда к ним обращаются.

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

Спасибо, Уэйн. Это очень хороший пример! Но что, если я изменяю 'puts '# {self} [# {index}] = # {value}" 'для фактического присвоения, т. Е.' Self [index] = value'? Это дает '... уровень стека слишком глубокий ...' error :-(Как избежать этого? – gmile

+0

Вы вызываете old_array_assign для выполнения фактического назначения. Я изменил пример, чтобы показать, как это делается. Ответ vava также показывает, как вызвать псевдоним (переименованный) метод. –