Getting Ruby’s “require” to work in the Poignant Guide to Ruby

I’m working my way thur “Why’s (Poignant) Guide to Ruby”, chapter 4 “Floating Little Leaves of Code” and ran it a problem doing the “Making the Swap” sub-section example (part of 3. Chaining Delusions Together” – (on a Win XP box, with version 1.8.2 ) – with respect to where the Hash of code words is used in anther program using the require function/method (message).

Update : a copy of Why’s (Poignant) Guide to Ruby, see a the bottom of the post for why..

Although the wordlist.rb loaded (confirmed with more testing), when I went to use the variable defined in it (“code_words”) , I was getting “undefined local variable or method `code_words’ for main:Object (NameError)”. WTF?!!

To simplify, the wordlist.rb was :

1
2
3
4
5
6
code_words = {
'starmonkeys' => 'Phil and Pete, those prickly chancellors of the New Reich',
'catapult' => 'chucky go-go', 'firebomb' => 'Heat-Assisted Living',
'Nigeria' => "Ny and Jerry's Dry Cleaning (with Donuts)",
'Put the kabosh on' => 'Put the cable box on'
}

and, to make it an even simpler example, the calling file (SimReq.rb) could have been:

1
2
3
4
# SimReq.rb to test require loading
require 'wordlist'
code_words.each {|key, value| print "Key:", key, " for Value:", value,
"\n" }

After a bit of head scratching and hair pulling, I got around the problem by making code_words global with “$” on “code_words” in both files. (i.e. ”

1
$code_words

“). Which works but was un satisfing. Global variables are evil, and messy for any non trivial example.

So was it a scoping problem? Was there a better, or best practice, to do something like this trivial example or is this Chunky Bacon payback for actually doing the example or some other PEBKAC issue?

The anwser was YES.

Posting on the comp.lang.ruby groups (once again exploding my ignorance so you don’t have to) resolved my problem and increased my understanding (oh happy day), to wit :

a) I should have been using the fixed version of the Poignant Guide @ http://qa.poignantguide.net/, not the usual url location. (and why isn’t the version @ http://poignantguide.net/ruby/ upto date or redirecting? Health concerns about the colour blue, apparently. So it was only recently caught and is in the process). The fixed version used suggetion b), as well as the

1
load

method, rather than

1
require

. Is there a differance?

b) Using a Constant is a better practices and in this case the simplest thing that works. So

1
code_words

becomes

1
Code_words

in both files. (convention is the constants should be all upcase, so it should be

1
CODE_WORDS

rather.

c) Making a Class is a best practice, especially if you are doing something more than trivial. to quote:

Local variables used in a file you ‘require’ do not appear in the requirer’s scope. Usually the best thing is to wrap what you need in a class or module:

1
2
3
4
5
6
 class CodeWords
def initialize
{ 'a' => 'b',
'c' => 'd' }
end
end

then in the requiring file:

1
2
 require 'codewords'
code_words = CodeWords.new

Also, in the Ruby’s Docs for the load method it notes :

In no circumstance will any local variables in the loaded file be propagated to the loading environment.

.

I just wish it said that under require, or “see load method, wrap=true”. …sigh…Many thanks to David and Dominik, and of course to _why .. and now back to the brain busting.

Update: As of August 2009 the hacker / coder / and artist whytheluckystiff (a.k.a. _why) web sites and code disappeared.  

meanwhile we have the web archive and Scibd’s, Ruby inside has collected a set of mirror and links to copies of code and projects at A Cup Full Of Why: 32 Why The Lucky Stiff Links.

John Resig has written a eulogy to _why that best expresses the concerns of myself and many other’s : wherever you are : be well, and thank you for the code and chunky bacon!