Ruby:捕获对类内变量的访问

时间:2010-01-24 01:22:19

标签: ruby arrays methods

我有一个类,它将传入的字符串解构为嵌套数组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 个答案:

答案 0 :(得分:1)

问题是你在主数组中包含的子数组上调用[] =。

换句话说,你在你的类上调用[],你实现它来返回那个数组元素,然后在一个通用数组上调用[] =你没有阻止写入访问权限。

你可以实现这个结构让你的类通过使用MyClass的其他实例来创建它的子数组,或者你可以覆盖Array的[] =方法来限制访问。

还值得注意的是,取决于如何使用它,在像Array这样的类上覆盖方法通常不是一个好主意,所以你可能想要像我的第一个建议那样。

答案 1 :(得分: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

答案 2 :(得分: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"]