Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to dynamically inspect the method visibility scope (private/public/protected)?

Tags:

ruby

As mentioned in this answer, in Ruby 2.1 or later, this code:

class SimpleTest
  private

  define_method :foo do 
    42
  end
end

will define foo as a private method of SimpleTest instances. (In Ruby 2.0 and earlier it won't be private.) However, I'm looking to do something a little less trivial. I would like to define a DSL that classes can extend, and would like the methods that the DSL defines internally to respect the private/protected visibility of the calling context. That may not be clear, so here's an example:

module Dsl
  def has_a(name)
    define_method name do
      42
    end
  end
end

class Test
  extend Dsl

  private

  has_a :thing
end

As written, that code will define a public thing method on Test instances. Instead, I would like has_a to be able to reflect on the method visibility where it was called (private in this case), and define thing under that same method visibility.

I'm not familiar with Ruby's C source code, but I took a quick look and found this function which seems like it might do what I want, but I don't think it's accessible from Ruby. (It seems to only be used here.) I also looked up the documentation for define_method (since the first example works as desired) here and it seems like the noex variable declared and set here:

int noex = NOEX_PUBLIC;
const NODE *cref = rb_vm_cref_in_context(mod, mod);

if (cref) {
    noex = (int)cref->nd_visi;
}

could be the value I want, but again I don't know how I would get that in Ruby, or even if it would be able to reflect back on the calling scope (in Test). Assuming I had the visibility, then I could simply call private name (or protected name) after the define_method call inside has_a if it wasn't called in a public context.

Thoughts? Is there any way to do this, or am I out of luck?

like image 764
rmacklin Avatar asked Aug 27 '15 20:08

rmacklin


1 Answers

I think this question has a similar answer to what you are looking for: https://stackoverflow.com/a/28075865/5129208

It looks like the author of that made a custom module to get the behavior you're after.

like image 97
yez Avatar answered Sep 24 '22 01:09

yez