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 Williams

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

···

--
Posted via http://www.ruby-forum.com/.

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'

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

···

2008/2/12, Brett Williams <brettandbecky@gmail.com>:

--
use.inject do |as, often| as.you_can - without end

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, at 8:26 AM, Brett Williams 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.

--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama

Robert Klemme wrote:

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 =)

···

2008/2/12, Brett Williams <brettandbecky@gmail.com>:

--
Posted via http://www.ruby-forum.com/\.

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, 10:11 am, Robert Klemme <shortcut...@googlemail.com> 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.

Brett Williams 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\.

···

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

Brett Williams 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 <blush> 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]

···

On Feb 12, 2008 7:47 PM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

----------------------------------------------------------------
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 Dober wrote:

Brett Williams 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 7:47 PM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

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

Robert Dober wrote:
>> Brett Williams 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

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.

···

On Feb 12, 2008 10:28 PM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

> On Feb 12, 2008 7:47 PM, Joel VanderWerf <vjoel@path.berkeley.edu> wrote: