Ruby: перехват доступа к переменной внутри класса - PullRequest
0 голосов
/ 24 января 2010

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

Только сейчас, если я получу доступ к любому значению верхнего уровня 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 ~ 

Ответы [ 3 ]

1 голос
/ 24 января 2010

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

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

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

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

0 голосов
/ 24 января 2010
#!/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 голосов
/ 24 января 2010

В 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...