ruby - File-private methods

こ雲淡風輕ζ 提交于 2019-12-23 16:02:43

问题


In ruby, is there a way to define a method that is visible by every class in the file (or in the module), but not by files that require the file ?

Related, but not quite the same: can we redefine a method (for instance a method from a class of the standard library) such that this redefinition is visible only in the current file ? All other files should view the original definition.


回答1:


No and no.

The only visibilities in Ruby are public, protected, and private. There is no concept of file-level visibility. You could maybe "cheat" and and do something like this:

# In some file foobar.rb

class Foo
  def to_bar
    Bar.new.file_private
  end
end

class Bar
  def file_private
    raise unless caller[0].split(':')[0] == __FILE__
  end
end
# In IRB or some other file

Foo.new.to_bar  #=> nil
Bar.new.file_private  #=> RuntimeError

But this is a bad idea. A file of the same name in a different directory might work. It also isn't true visibility, but rather enforces it in the method itself.

Really, though, you should mostly have your classes each in their own file. It makes organization better. Further, you should not depend on public/protected/private. You can always just use send to call a private method, but the above breaks that expectation. If user of your code really wants to do something with your code, there's next to nothing from letting them do it, that's the nature of dynamic languages. If you don't document a method, most users will never even know it's there anyway :P.

As for your second question, there is no way to have two methods of the same name in the same class with different visibility, the second method will always overwrite the original. You could do something similar to what I've done above, and run different code depending on the condition instead of raising, but as above I don't really think this is a good idea.




回答2:


  1. Define a new method in Object class(like an attribute). If you do not want to mess up the Object class, you can use another name, and Foo should inherit that class.

    class Object
      @@file_only_methods = []
    
      def file_only(method_name)
        method_name = method_name.to_sym
        new_method_name = "file_only_#{method_name}".to_sym
        self.send(:alias_method, new_method_name, method_name)
        self.send(:undef_method, method_name)
        self.send(:private, new_method_name)
        @@file_only_methods << method_name
      end
    
    
      def method_missing(method_name, *arg, &block)
        if @@file_only_methods.include? method_name
          if __FILE__ == $0
            self.send("file_only_#{method_name}".to_sym,*arg,&block)
          else
            raise "Method #{method_name} is called outside the definition file."
          end
        else
          raise "Method #{method_name} does not exist."
        end
      end
    end
    
    class Foo
      def bar
        puts 'bar method'
      end
      file_only :bar
    end
    
    Foo.new.bar
    #output:bar method
    Foo.new.x
    #output:no method
    

    In file2.rb,

    require_relative 'file1'
    Foo.new.bar
    #output: Method bar is called outside the definition file.
    


来源:https://stackoverflow.com/questions/10535319/ruby-file-private-methods

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!