Ruby equivalent to source command

Hello all,

I am trying to make a “source” command for Ruby (a la sh or tcl), but I
think lexical scoping is tripping me up. I’ve done a lot of reading
about bindings, and it seems I just can’t do what I’m trying to do.

def source(filename, bind = TOPLEVEL_BINDING)
code = nil
File.open(filename) { |f| code = f.read }
eval(code, bind)
end

This works as I would like, with the exception of local variables
present in the sourced file. For example, if I had something like this
in the sourced file:

x = “value set in sourced file”

then I get an error : undefined local variable or method ‘x’

unless I extend the scope of x by setting it before sourcing the file.

Is there any way to accomplish what I want? I could work around this by
sticking to instance variables or gulp global variables for this
application, but before I throw in the towel I wanted to throw this out
to people here.

Thanks,

Brett W.

P.S.: Things have changed since I was a pretty regular poster back in
2002-2003… :wink:

2008/2/12, Brett W. [email protected]:

This works as I would like, with the exception of local variables
present in the sourced file. For example, if I had something like this
in the sourced file:

x = “value set in sourced file”

then I get an error : undefined local variable or method ‘x’

Can you show more code? For me it works:

17:05:12 ~
$ echo “x=123; puts x” | ruby -e ‘eval(ARGF.read, TOPLEVEL_BINDING)’
123
17:05:18 ~
$ echo “x=123; puts x” | ruby -e ‘eval(ARGF.read, binding)’
123
17:05:25 ~
$

Or are you seeing this:

17:05:25 ~
$ echo “x=123” | ruby -e ‘eval(ARGF.read, binding); puts x’
-e:1: undefined local variable or method `x’ for main:Object (NameError)
17:06:31 ~
$

unless I extend the scope of x by setting it before sourcing the file.

Like

17:06:31 ~
$ echo “x=123” | ruby -e ‘x=1; eval(ARGF.read, binding); puts x’
123
17:07:32 ~
$

This is because local variables are detected at compile time. That’s
why Ruby thinks “x” in my bit above is a method because there is no
assignment.

Is there any way to accomplish what I want? I could work around this by
sticking to instance variables or gulp global variables for this
application, but before I throw in the towel I wanted to throw this out
to people here.

A global is probably much more appropriate here. If the code you
source somehow generates configuration settings then maybe you can set
a global Hash that will receive values. It depends on the larger
context of what you want to achieve.

P.S.: Things have changed since I was a pretty regular poster back in
2002-2003… :wink:

Does this have to do with your email address? :wink: If yes, congrats
and greetings to Becky!

Cheers

robert

Robert K. wrote:

2008/2/12, Brett W. [email protected]:

This works as I would like, with the exception of local variables
present in the sourced file. For example, if I had something like this
in the sourced file:

x = “value set in sourced file”

then I get an error : undefined local variable or method ‘x’

[snip]

Or are you seeing this:

17:05:25 ~
$ echo “x=123” | ruby -e ‘eval(ARGF.read, binding); puts x’
-e:1: undefined local variable or method `x’ for main:Object (NameError)
17:06:31 ~
$

unless I extend the scope of x by setting it before sourcing the file.

Like

17:06:31 ~
$ echo “x=123” | ruby -e ‘x=1; eval(ARGF.read, binding); puts x’
123
17:07:32 ~
$

Precisely.

This is because local variables are detected at compile time. That’s
why Ruby thinks “x” in my bit above is a method because there is no
assignment.

Aye, that’s the problem.

Is there any way to accomplish what I want? I could work around this by
sticking to instance variables or gulp global variables for this
application, but before I throw in the towel I wanted to throw this out
to people here.

A global is probably much more appropriate here. If the code you
source somehow generates configuration settings then maybe you can set
a global Hash that will receive values. It depends on the larger
context of what you want to achieve.

I figured this was the case. I’ll go with globals for my context.

P.S.: Things have changed since I was a pretty regular poster back in
2002-2003… :wink:

Does this have to do with your email address? :wink: If yes, congrats
and greetings to Becky!

Indeed no. Becky predates even my Ruby (which I started using as my
primary language in 2001). I’m just very happy to see this list
mirrored on ruby-forum which makes it much more convenient for me
personally. The email address is one not used for much – it functions
mainly as a spam magnet =)

Brett W. wrote:

I figured this was the case. I’ll go with globals for my context.

The horror, the horror.

Here’s an alternative: http://redshift.sourceforge.net/script.

On Feb 12, 10:11 am, Robert K. [email protected] wrote:

A global is probably much more appropriate here. If the code you
source somehow generates configuration settings then maybe you can set
a global Hash that will receive values. It depends on the larger
context of what you want to achieve.

In cases like this, I’ve found it helpful to define a method that
returns the configuration hash:

$ echo “def config; {:a => 2}; end” | ruby -e ‘eval(ARGF.read,
binding); puts config[:a]’

On Feb 12, 2008 7:47 PM, Joel VanderWerf [email protected]
wrote:

Brett W. wrote:

I figured this was the case. I’ll go with globals for my context.

The horror, the horror.
I agree, i agree :wink:

Here’s an alternative: http://redshift.sourceforge.net/script.
It does not seem to handle OP’s problem though, which are locals or am I
wrong?
My idea would be to use Smalltalk constants, does anybody save Rick
know what that is? :wink:
But of course constants are not enough so I will define setters too :slight_smile:

But I am not sure if that works let me see:
Yup seems to work

file: test1.rb
a=“sourced a”
b=“sourced b”

file: main.rb
a = “Main a”
eval File.open(“test1.rb”){|f| f.read } << %{
local_variables.each do |lvar|
this = class << self; self end
this.send :define_method, lvar do eval lvar end
this.send :define_method, lvar + “=” do |new_val| lvar = new_val
end
end
}
p [:now_a, a]
p [:now_b, b]
b = “changed”
p [:changed_b, b]


Now it depends very much on the use case if you can live with this.
Any pitfalls I have overseen?

HTH
Robert


vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Robert D. wrote:

On Feb 12, 2008 7:47 PM, Joel VanderWerf [email protected] wrote:

Brett W. wrote:

I figured this was the case. I’ll go with globals for my context.
The horror, the horror.
I agree, i agree :wink:
Here’s an alternative: http://redshift.sourceforge.net/script.
It does not seem to handle OP’s problem though, which are locals or am I wrong?

Sorry, I should have made that clear. It uses module_eval, so you don’t
get locals, but using module constants and/or methods is better than
globals :slight_smile:

I like your hack. One possible pitfall is if the file has END.
Another is that local vars can become methods that globally shadow
Kernel/Object methods. For example, add this line to test1.rb:

open = false

Still, I might add this to the script lib as an option to capture locals
in the module.

On Feb 12, 2008, at 8:26 AM, Brett W. wrote:

Is there any way to accomplish what I want? I could work around
this by
sticking to instance variables or gulp global variables for this
application, but before I throw in the towel I wanted to throw this
out
to people here.

slightly different, but check this out

http://codeforpeople.com/lib/ruby/configuration/configuration-0.0.3/README

gem install configuration

a @ http://codeforpeople.com/

On Feb 12, 2008 10:28 PM, Joel VanderWerf [email protected]
wrote:

get locals, but using module constants and/or methods is better than
globals :slight_smile:

I like your hack. One possible pitfall is if the file has END.
Another is that local vars can become methods that globally shadow
Kernel/Object methods. For example, add this line to test1.rb:

open = false
very good OP watch out

Still, I might add this to the script lib as an option to capture locals
in the module.
If you do so just substract the Kernel methods (and potentially
Objects instance methods) from the locals array to avoid shadowing.

I knew I had forgotten something important, thx Joel.
R.