0

I need a line of code that can does something like this:

instance_eval(IO.read(File.expand_path('../../policyfiles/base.rb',   __FILE__)))

But for multiple files... this does not work:

instance_eval(IO.read(File.expand_path('../../policyfiles/base.rb', '../../policyfiles/app.rb',  __FILE__)))

Any idea on how to do this?

TyMac
  • 783
  • 2
  • 9
  • 32
  • This almost seems to work: ['../../policyfiles/base.rb', '../../policyfiles/app.rb'].each { |file| instance_eval(IO.read(File.expand_path(file, __FILE__))) } ..however each file contains an array called run_list and the second file's array only seems to overwrite the first... – TyMac Jul 10 '17 at 20:54

1 Answers1

1

If you have a list and want to do things to each element, use each.

policy_files = ['../../policyfiles/base.rb', '../../policyfiles/app.rb']
policy_files.each { |file|
    instance_eval(IO.read(File.expand_path(file,   __FILE__)))
}

However a bare instance_eval should be avoided. It will load the contents of those files as if they were in the current scope. Any global variables will blow over your own and each other. For example...

$ cat policy.rb
foo = 42

def bar
    99
end

$ cat test.rb
foo = 23
instance_eval(IO.read("policy.rb"))

puts foo
puts bar

$ ruby ~/tmp/test.rb
42
99

Because instance_eval runs the content of test2.rb inside test.rb's scope, that includes foo = 42. Surprise!

Instead use require_relative. This does the work of loading a relative file, but it won't mix up globals. You'll only get the methods.

policy_files.each { |file|
    require_relative(file)
}

Now when we do the same thing, but with require_relative, your globals are safe.

$ cat policy.rb
foo = 42

def bar
    99
end

$ cat test.rb
foo = 23
require_relative("policy.rb")

puts foo
puts bar

$ ruby ~/tmp/test.rb
23
99

Now globals in the policy file can't muck up the caller.

require_relative has the additional advantage that it will only load a given file once. That means any code can safely load these files without considering whether something else has already loaded them.


Note that it seems odd that the user of the policies has to load the base policy. I can't say for sure without knowing what these files do, but I suspect app.rb should handle loading base.rb.

Also note that using relative paths might not be the best thing to do. If you reorganize your code files, they'll all break. Instead, consider adding the top level library directory to $LOAD_PATH.

lib_dir = ...whatever you need to figure this out...
$LOAD_PATH.unshift(lib_dir)

Then, assuming policyfiles/ is at the top of the library tree, you can require("policyfiles/app.rb") from any file in the project no matter its location.

Schwern
  • 153,029
  • 25
  • 195
  • 336