Running another ruby script with globals intact? - ruby

For reasons that are a little hard to explain, I need to do the following: I have a master.rb file that sets some global like: a = 1. I want to call another file other_file.rb that will run with the globals that were set in the master file. In python I'd use runpy.run_module( 'other_module', globals() ).
Can anyone think of an equivalent in Ruby? I've looked at require, include, and load, but none seem to do quite what I need, specifically they don't pull the globals into the other_file.rb. Note that I am not trying to fork a new process, just hand execution over to "other_module" while maintaining the state of the globals.

a=1 is not a global variable, it is a local variable that gets scoped to the file. If you really nee this behavior, use $a=1 to set global variables.

If you absolutely must, you can use globals, and they're declared with the $ prefix. They are highly discouraged because there is only one global namespace, which makes collisions possible. Generally they are used for interpreter configuration, like $LOAD_PATH.
A better approach is to use a module that has instance variables:
module MyContainer
def self.settings
#settings ||= { }
end
end
MyContainer.settings[:foo] = :bar
This has the advantage of keeping your variables contained in a namespace while not preventing other sub-programs from accessing them.
Keep in mind this will only work within the context of the same Ruby process or children created using fork, so using system or exec will not work. Remember also that forked processes need to use IPC to communicate with their parent.

Related

Setting a global value (and keeping it) within the scope of an eval

I've got a large Rails 5 app (Ruby 2.6.x at present) that makes crucial use of Kernel::eval (please don't tell me to try to refactor this out because eval is dangerous - I didn't write the original code, and this is not in the cards for any time soon).
There are a very wide variety of Ruby expressions (coming out of the db) that can be passed to eval, sometimes of great complexity, making extensive use of classes and resources of the app.
(you might want to jump straight down to BIG EDIT below)
What I want is to be able to set a global value ($global) that will be seen within the scope of the eval execution, but that will not "infect" any of the execution context outside of that. I can't try to interpolate this into the string and pass it down though method params and such, because, as I say, the code being eval'ed is complex and stacks can get very deep, and I want the value to potentially be accessed (though never modified) anywhere within.
I understand about Bindings. I have played around with setting local and instance vars in a binding, and passing this to eval, but inevitably these are not seen inside any method calls within the eval, especially if I'm inside a method of some random class (which I always am). Seems like global is the only possibility. But experimentation shows that a global set inside an eval remains in the code that calls the eval:
2.6.3 :002 > $foo
=> nil
2.6.3 :003 > eval("$foo = 12")
=> 12
2.6.3 :004 > $foo
=> 12
Although I might find some hacky way to deal with this situation, I'm sure you can see where I'd really rather not.
The Binding class offers methods to set local and instance vars dynamically within a Binding object, but nothing for globals (apparently). I've thought about something like this:
...
eval code_string, get_binding()
...
def get_binding
$global = :special_value
binding
end
but I'm really worried, with a Rails app that might be servicing lots of requests at the same time, that these settings of $global will step on each other in unpredictable ways. Related clarifying question: Is a global value in a Rails app global to the entire thing, readable and writable within the scope of all the requests whose servicing may be overlapping in time? (I'm running under Passenger, if that means anything)
So this is a fairly simple and straightforward problem when you understand it, although oddly not addressed in anything I can google about it, and I think I've written enough words. Thanks for any help or ideas to try.
BIG EDIT:
Ok, let me try to refocus this in a different way. I'm getting that the scope of a global can never, no-how, be constrained (duh, right?), but how about this strategy (similar to above):
...
eval code_string, get_binding()
...
def get_binding
luaapg = :special_value ## local used as a pseudo-global
binding
end
So, now I've got this Binding that includes the local var luaapg. I've confirmed that. I eval code_string with this Binding. When I am somewhere inside the execution of code_string, where do I find luaapg - how do I access it? If you look at pretty much every tutorial on this stuff on the web, they show you puts eval("luaapg", get_binding) and voila, the assigned value comes out! But this is too simplistic for real life. When I am in the middle of my code_string, in some method scope of some class, luaapg is not there. I had great hope that this would work, even deep down the stack:
TOPLEVEL_BINDING.local_variable_get(:luaapg)
but it doesn't (I learned about TOPLEVEL_BINDING from here - thanks to that author). So this is the new question: what does it mean to say that I have executed (eval'ed) my code_string in the context of that Binding, which contains a local variable, if I have no way to access that variable, other than with the most simpleminded code? (incidentally I played around with instance vars too - same thing). I'm still hoping there's some magic incantation...
I think you've put your finger on it in the name of the type of variable - it's global - common to all the code in the executing program. I'm not sure exactly how Passenger works but I suspect it runs several copies of your program, so it won't be common between the copies.
To get reliable shared information I think you're going to have to use your database or some sort of information cache like memcached. You choose how you save/name it there.

Make Dynamic Variable Available to Multiple Chef Recipes

We dynamically compute the name of a directory at run-time from some attributes:
var1 = (node['foo']['bar']).to_s
var2 = (node['foo']['baz']).to_s
app_dir = "/var/#{var1}/#{var2}
Copying this code block to all of the recipes that needs it works. When we have tried to clean this up, it bombs with "No resource, or local variable named 'app_dir'.
We have tried the following:
1) Move the block of code into attributes/default.rb
2) Move the block of code into recipes/default.rb
3) Same as 2 above, but adding require_relative 'default' in the recipes that require the variable
If you're requiring from a separate file (e.g. require_relative) you need to make the variable something that gets exported. You can try making it a constant instead e.g. AppDir =, or a method. Local variables are not loaded with require.
There isn't really a good way to do this in Chef. Recipe and attributes files run in very different contexts so your best bet is either copy-pasta the boiler plate (if it really is as small as your example, do that) or otherwise maybe a global helper method but those are hard to write safely. Ping me on Slack and we can talk about the right kind of helper method for your exact situation (covering all of them isn't really practical for an SO answer).
If you need to share some code between a bunch of different recipes, you can try using a library loaded from a common cookbook dependency.

Static global C-like variables in Ruby

Does Ruby has static global variables?
With this I mean global variables only accessible from the file where they were defined.
Short answer: No.
The long answer is more complicated.
There's only one global namespace in Ruby and any alterations to it from any code will have the effect of changing it for all code. To keep things local you need to scope them to a particular context, typically module or class. For example:
module PrivateStuff
#private_variable = "Private (mostly)"
def self.expose_private_variable
#private_variable
end
end
Note this doesn't prevent others from accessing your private variables using instance_variable_get or techniques like that.
This usually isn't a big deal since global variables are usually a sign of bad design and should be avoided unless there's no alternative, a case that's exceedingly rare.
Unlike compiled languages which enforce very strict rules when it comes to data access, Ruby leaves it up to the programmer to be disciplined and simply not do it in the first place.

How do I work around Ruby's eval and "Already initialized constant"?

I inherited maintenance on an app that uses eval() as a way to evaluate rules written in Ruby code in a rules engine. I know there are a lot of other ways to do it, but the code base so far is pretty big, and changing it to something else would be prohibitive time-wise at this point; so assume I'm stuck using eval() for the moment.
The rules as written typically call up some of the same objects from the database as each other, and the rules writer gave the variables in the rules the same names as each other. This is resulting in pages and pages of "already initialized constant" warnings in the console during development.
I'm wondering a couple things:
First, if feels like those are slowing down the execution of the program in the dev environment, and so I'm wondering if it's a big performance hit in the production environment, specifically, having those warnings pop, not eval() itself, which I know is a hit.
Second, is there any way to "namespace" the execution of each rules so that it's not defining its variables on the same scope as all the other evals in the request to avoid that warning popping all over the place? I know I could rewrite all the rules to use ||= syntax or to check if a name has already been defined, but there's quite a lot of them so I'd rather do it from the code that runs the eval()'s, if possible.
** update with example rule **
A question has a rule about when it's to be displayed to a user. For example, if the user has stated that they live in an apartment, another question might need to be shown to ask what size the apartment building is. So the second question's rule_text might look like:
UserLivesInApartment = Question.find_by_name "UserLivesInApartment"
UserLivesInApartment.answer_for(current_user)
The code that calls the eval ensures there's a current_user variable in scope prior to evaluating.
uh, eval is not perhaps the most golden of standards. You could probably fire up a drb instance and run the stuff in that instead of eval, that way you would have at least some control of what is happening and not pollute your own namespace.
http://segment7.net/projects/ruby/drb/introduction.html
Edit: added another answer for running the code in the same process:
I don't know how your rule code looks, but it might be possible to wrap a module around it:
# create a module
module RuleEngineRun1;end
# run code in module
RuleEngineRun1.module_eval("class Foo;end")
# get results
#....
# cleanup
Object.send(:remove_const, :RuleEngineRun1)
You can also create an anonymous module with Module.new { #block to be module eval'd } if you need to run code in parallel.
In later rubies you can add -W0 to run your code without printing warnings, but doing so makes possible errors go unnoticed:
$ cat foo.rb
FOO = :bar
FOO = :bar
$ ruby foo.rb
foo.rb:2: warning: already initialized constant FOO
$ ruby -W0 foo.rb
You could also run your eval inside a Kernel.silence_warnings block, but might be devastating as well if you actually run into some real problems with the eval'd code, see
Suppress Ruby warnings when running specs

Ruby global variables, legitimate uses

I've never seen global variables used in any Ruby code. I understand that their use is frowned upon across languages but they seem actually useless in Ruby. Can anyone point to properly designed code that uses them?
If I'm right and they're redundant/historical, why do they persist in 1.9?
To be clear, I don't mean variables that Ruby sets up for you like $" and $stdin. I mean uses in one's own code.
The only time I see it in decent code is for a log.
$log = Logger.new('foo.log', 'daily')
A constant would probably do fine, but it somehow feels strange calling methods on a constant.
Environment variables are usually Global variables in Ruby.
So are CLASSPATH in jruby and so on...
Also, you can implement cheap singletons using global variables (although it's not advisable).
So, global variables definitely have a place in Ruby.

Resources