如何只允许一个类从另一个类访问一个方法?

时间:2017-10-17 18:55:03

标签: ruby

我想有这样的事情:

class A
  def only_B_can_call_me
    'called_by_B'
  end
end

class B
  def do_stuff(a)
    a.only_B_can_call_me
  end
end

class C
  def do_stuff(a)
    a.only_B_can_call_me # how to forbid it?
  end
end

B.new.do_stuff(A.new) # => 'called_by_B'
C.new.do_stuff(A.new) # => it should not be allowed!!! but how to do it?

执行此操作的一种方法是使only_B_can_call_me成为私有方法并在B中使用a.send(:only_B_can_call_me)。确定,它可以正常工作。但我可能会在C内做同样的事情...所以,我认为这不是一个好方法。有没有其他方法可以做到这一点? (允许仅通过特定类的实例访问方法。)

(我知道最终总是可以使用send从任何地方访问任何方法。但我想在这种情况下让自己远离send。)

3 个答案:

答案 0 :(得分:4)

没有明确的解决方案。如果B可以做到,那么C也可以。与其他语言不同,ruby没有"内部"或"包"可见性修饰符,如果A和B在同一个"包"中,它可以帮助你,但C是外部的。如果该方法是私有的,即使B必须使用send。如果它是公开的,C可以直接调用它。在您的示例中,B不是A的子类,因此protected修饰符不适用。

一种肮脏的方法是检查caller中的only_B_can_call_me。它返回整个callstack。所以你可以检查它是否确实是B或否则拒绝。但这非常脆弱,完全不推荐。

答案 1 :(得分:0)

在@Sergio Tulentsev的帮助下,这是我能做的最好的事情:

class A 
  def only_B_can_call_me(b) 
    return unless b.class == B # here you are asking
    'called_by_B' 
  end 
end

class B
  def do_stuff(a)
    a.only_B_can_call_me(self) # how to forbid it? ask if self is B
  end
end

class C
  def do_stuff(a)
    a.only_B_can_call_me(self) # how to forbid it? ask if self is B
  end
end

其他方式是使用子类:

class A 
  def only_B_can_call_me
    'called_by_B' 
  end 
end

class B < A
  def do_stuff
    self.only_B_can_call_me
  end
end

class C
  def do_stuff
    self.only_B_can_call_me # how to forbid it?, like this C hasn't the method, B does
  end
end



puts(B.new.do_stuff) # => 'called_by_B'
puts(C.new.do_stuff) # => it should not be allowed!!! but how to do it?

答案 2 :(得分:0)

塞尔吉奥的回答是正确的,但如果你真的需要黑客,请看下面的代码:

 class CallerClass
  def self.get (c)
     line = c.first.scan(/^.*:(.*):.*/).first.first.to_i
     file = c.first.scan(/^(.*?):/).first.first
     func = c.first.scan(/:in `(.*)?'/).first.first
     fc = File.readlines(file)
     caller_class = nil

     caller_class = '<main>' if func == '<main>'

     line.downto(0) do |it|
       break if caller_class
       caller_class = fc[it].scan(/^\s*class\s+(.*)\s+/).first.first if fc[it] =~ /^\s*class\s+(.*)\s+/
     end
     caller_class
  end

end

class A
  def only_B_can_call_me
     caller_class = CallerClass.get(caller)
     raise "'#{caller_class}' is not an allowed caller" unless caller_class == 'B'
     'called_by_B'
  end
end

class B
  def do_stuff
    A.new.only_B_can_call_me
  end
end

class C
  def do_stuff
    A.new.only_B_can_call_me
  end
end

B.new.do_stuff #called_by_B
C.new.do_stuff #Raises exception

OBS。这是一个使用正则表达式的ruby代码的脆弱解析,这是一个 HACK 你已被警告!!