Accessing local variables in the calling context

In my code, i have several methods using the following pattern

def my_method only: [], ignore: []
  something('textvalue') if (only.empty? && ignore.empty?) || (only.any? && only.include?('textvalue')) || (ignore.any? && !ignore.include?('textvalue'))
end

In other word, i can choose to filter the results either by specifying only or ignore, depending on which one is more convenient in the context.

I would like a way to declare a helper want that access the onlyand ignore local parameters without having to specify them each time, the result would ideally look like this:

def my_method only: [], ignore: []
  something('textvalue') if want('textvalue')
end

This helper would be used in several different methods, classes, etc. It would somehow check the local variables at the calling point, validate that only and ignore exists, and then check if the parameter is wanted or not.

Is it possible to access the calling stack and look at the local variables there?

Upvotes: 1

Views: 386

Answers (3)

SRack
SRack

Reputation: 12203

In circumstances such as this, couldn't you just pass the args to want?

def my_method only: [], ignore: []
  something('textvalue') if want?('textvalue', only, ignore)
end

def want?(string, only, ignore)
  (only.empty? && ignore.empty?) || (only.any? && only.include?(string)) || (ignore.any? && !ignore.include?(string))
end

Doesn't seem to be any need for it to be more complex than that?

That is a lot simpler to manage and for others to deal with going forward - which I'd consider far more important than avoiding passing a couple of args to a method.

Upvotes: 2

andriy-baran
andriy-baran

Reputation: 739

You can use ruby method definition to do this

def my_method val, only: [], ignore: [], want: ->(val) { ((only.empty? && ignore.empty?) || (only.any? && only.include?(val))) }
 something(val) if want.call(val)
end

my_method 'value', only: ['value2']
=> false

Upvotes: 2

SteveTurczyn
SteveTurczyn

Reputation: 36860

There is a gem binding_of_caller which can do this. Install the gem and then do

require 'binding_of_caller'

def my_method only: [], ignore: []
  something('textvalue') if want('textvalue')
end

def want(value)
  only = binding.of_caller(1).local_variable_get(:only)
  ignore = binding.of_caller(1).local_variable_get(:ignore)
  (only.empty? && ignore.empty?) || (only.any? && only.include?(value)) || (ignore.any? && !ignore.include?(value))
end

But this is a bad thing to do as it creates very high coupling. It's really not good design. If you want to do this for experimentation and playing around, fair enough, but don't do it in production.

Upvotes: 4

Related Questions