Nuby sanity check--do I have a right to be confused by this error msg?

Hey All,

If I run this script (ruby 1.8.2 (2004-07-29) [i386-mswin32] on
win2k):

# ---------------------------------------
require "net/http"

PROXYSERVER = "proxy.ghc.org"
PROXYPORT = 8080

my_array = ["rama", "lama", "ding"]

def someMethod
   Net::HTTP.Proxy(PROXYSERVER, PROXYPORT).start("www.ruby-doc.org")
do |http|
      my_array += ["dong"]
   end
end

puts my_array

someMethod
# ---------------------------------------

I get this for output:

C:\>ruby httperr.rb
rama
lama
ding
httperr.rb:12:in `someMethod': undefined method `+' for nil:NilClass
(NoMethodEr
ror)
        from httperr.rb:11:in `start'
        from c:/program files/ruby/lib/ruby/1.8/net/http.rb:324:in
`start'
        from httperr.rb:11:in `someMethod'
        from httperr.rb:18

Can anybody tell me why my backtrace seems to implicate http.rb? I
was chasing my tail for quite a bit on that one. (At one point I
figured there was just something wrong w/http.rb on windows & was
regex searching the backtrace for "http.rb:324" to see if I could
disregard the error. Silly me.)

[And I'm still wondering why my_array is not visible inside
someMethod. If I make the var name @my_array all is well, which is
also confusing since I'm not in a class def, so why should I need an
instance var...]

Thanks!

-Roy

Hey All,

If I run this script (ruby 1.8.2 (2004-07-29) [i386-mswin32] on
win2k):

# ---------------------------------------
require "net/http"

PROXYSERVER = "proxy.ghc.org"
PROXYPORT = 8080

my_array = ["rama", "lama", "ding"]

def someMethod
  Net::HTTP.Proxy(PROXYSERVER, PROXYPORT).start("www.ruby-doc.org")
do |http|
     my_array += ["dong"]
  end
end

puts my_array

someMethod
# ---------------------------------------

I get this for output:

C:\>ruby httperr.rb
rama
lama
ding
httperr.rb:12:in `someMethod': undefined method `+' for nil:NilClass
(NoMethodEr
ror)
       from httperr.rb:11:in `start'
       from c:/program files/ruby/lib/ruby/1.8/net/http.rb:324:in
`start'
       from httperr.rb:11:in `someMethod'
       from httperr.rb:18

Can anybody tell me why my backtrace seems to implicate http.rb? I
was chasing my tail for quite a bit on that one. (At one point I
figured there was just something wrong w/http.rb on windows & was
regex searching the backtrace for "http.rb:324" to see if I could
disregard the error. Silly me.)

well - in a sense you ARE inside http.rb. see, the 'start' method takes a
block, which is an anonyous callback, and calls it for you. eg.

   harp:~ > cat a.rb
   def call_the_callback
     yield
   end

   call_the_callback do
     raise 'foobar'
   end

   harp:~ > ruby a.rb
   a.rb:6: foobar (RuntimeError)
           from a.rb:5:in `call_the_callback'
           from a.rb:5

and here we see the same thing:

   - the error starts from line 6 where the raise statement is
   - but this code is actually called from inside 'call_the_callback'

in your case the error is on line 12 in your program, but this code itself has
been passed to, and is being called from, the 'start' method of httpd.rb on
line 324 of that file.

a stacktrace is a nesting of all method calls you are in - starting from
innermost:

   harp:~ > cat a.rb
   def c
     raise 'here'
   end
   def b
     c
   end
   def a
     b
   end

   a()

   harp:~ > ruby a.rb
   a.rb:2:in `c': here (RuntimeError)
           from a.rb:5:in `b'
           from a.rb:8:in `a'
           from a.rb:11

what is causing this to be even a bit more confusing is that the argument to
some of the methods in your program are bits of code themselves - callbacks,
blocks, whatever you want to call them...

[And I'm still wondering why my_array is not visible inside
someMethod. If I make the var name @my_array all is well, which is
also confusing since I'm not in a class def, so why should I need an
instance var...]

in ruby you are ALWAYS in a class def. it's as if you start every script with

   class Object
     ...

this makes ruby seems imperative in that you can say things like

   puts 'foobar'

but you are not calling some global puts, you are calling Object#puts (or
maybe it's Kernel#puts i forget). the point is that ruby is organized so that
you are inside an object which has a whole suite of useful methods in it, so
many you can forget you are in an object. this is how ruby maintains itself
to be completely object oriented without making jump through hoops like java
does when you want to just print something to the terminal - in java you must
do everything from within a class so you have to write a class just to write
'foobar' to the screen.

so, the reason your program runs with @array is that you've made an instance
variable of class Object but, for ruby, this is pretty close to making a
global variable since everything is inside Object already. in general i'd say
'don't do that'. if you don't make the variable in instance variable of class
Object then it's just scoping preventing you from seeing the variable, it's as
if you'd written this in c

void()foobar{ x = 42; }

main(){ int x; foobar(); }

it's just not going to work since x is not in the scope of foobar. likewise
my_array is not in the scopy of the start method. you have some choices such
as

   make the varible in scope for the start method:

     harp:~ > cat a.rb
     require "net/http"

     def someMethod
       my_array = ["rama", "lama", "ding"]
       Net::HTTP::start("www.ruby-doc.org") do |http|
         my_array += ["dong"]
       end
       puts my_array
     end

     someMethod

     harp:~ > ruby a.rb
     rama
     lama
     ding
     dong

   or, if you want to use it outside someMethod but don't want to pollute Object
   with all sorts of instance (and sort of global) variables then make a class,
   after all classes a bundles of state and methods that affect that state or
   other objects, eg:

     harp:~ > cat a.rb
     require "net/http"

     class MyClass
       attr :array
       def initialize
         @array = ["rama", "lama", "ding"]
       end
       def some_method
         Net::HTTP::start("www.ruby-doc.org") do |http|
           @array += ["dong"]
         end
       end
     end

     mc = MyClass::new
     mc.some_method
     p mc.array

     harp:~ > ruby a.rb
     ["rama", "lama", "ding", "dong"]

hope that helps.

kind regards.

-a

···

On Fri, 5 Nov 2004, Roy Pardee wrote:
--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
When you do something, you should burn yourself completely, like a good
bonfire, leaving no trace of yourself. --Shunryu Suzuki

===============================================================================

Huge, huge help--thanks for taking the time!

I was just using the block form of Net::HTTP::start for convenience--I
wasn't trying to do anything callback-y, and so it never occurred to
me that the code in my block would be sort of imported into http.rb &
executed from there. I'll keep that in mind here on out.

And the bit about always being in a class def is definitely
enlightening as well. I was intending my script to be of the
quick-n-dirty variety, and figured that it wouldn't be worth the
trouble of putting my code in a class, instantiating it & calling the
main method, etc. I guess I should get into that habit...

Thanks!

-Roy

Ara.T.Howard@noaa.gov wrote in message news:<Pine.LNX.4.60.0411051636110.29995@harp.ngdc.noaa.gov>...

···

well - in a sense you ARE inside http.rb. see, the 'start' method takes a
block, which is an anonyous callback, and calls it for you. eg.

   harp:~ > cat a.rb
   def call_the_callback
     yield
   end

   call_the_callback do
     raise 'foobar'
   end

   harp:~ > ruby a.rb
   a.rb:6: foobar (RuntimeError)
           from a.rb:5:in `call_the_callback'
           from a.rb:5
           from a.rb:5

and here we see the same thing:

   - the error starts from line 6 where the raise statement is
   - but this code is actually called from inside 'call_the_callback'

in your case the error is on line 12 in your program, but this code itself has
been passed to, and is being called from, the 'start' method of httpd.rb on
line 324 of that file.

a stacktrace is a nesting of all method calls you are in - starting from
innermost:

   harp:~ > cat a.rb
   def c
     raise 'here'
   end
   def b
     c
   end
   def a
     b
   end

   a()

   harp:~ > ruby a.rb
   a.rb:2:in `c': here (RuntimeError)
           from a.rb:5:in `b'
           from a.rb:8:in `a'
           from a.rb:11

what is causing this to be even a bit more confusing is that the argument to
some of the methods in your program are bits of code themselves - callbacks,
blocks, whatever you want to call them...

> [And I'm still wondering why my_array is not visible inside
> someMethod. If I make the var name @my_array all is well, which is
> also confusing since I'm not in a class def, so why should I need an
> instance var...]

in ruby you are ALWAYS in a class def. it's as if you start every script with

   class Object
     ...
     ...
     ...

this makes ruby seems imperative in that you can say things like

   puts 'foobar'

but you are not calling some global puts, you are calling Object#puts (or
maybe it's Kernel#puts i forget). the point is that ruby is organized so that
you are inside an object which has a whole suite of useful methods in it, so
many you can forget you are in an object. this is how ruby maintains itself
to be completely object oriented without making jump through hoops like java
does when you want to just print something to the terminal - in java you must
do everything from within a class so you have to write a class just to write
'foobar' to the screen.

so, the reason your program runs with @array is that you've made an instance
variable of class Object but, for ruby, this is pretty close to making a
global variable since everything is inside Object already. in general i'd say
'don't do that'. if you don't make the variable in instance variable of class
Object then it's just scoping preventing you from seeing the variable, it's as
if you'd written this in c

void()foobar{ x = 42; }

main(){ int x; foobar(); }

it's just not going to work since x is not in the scope of foobar. likewise
my_array is not in the scopy of the start method. you have some choices such
as

   make the varible in scope for the start method:

     harp:~ > cat a.rb
     require "net/http"

     def someMethod
       my_array = ["rama", "lama", "ding"]
       Net::HTTP::start("www.ruby-doc.org") do |http|
         my_array += ["dong"]
       end
       puts my_array
     end

     someMethod

     harp:~ > ruby a.rb
     rama
     lama
     ding
     dong

   or, if you want to use it outside someMethod but don't want to pollute Object
   with all sorts of instance (and sort of global) variables then make a class,
   after all classes a bundles of state and methods that affect that state or
   other objects, eg:

     harp:~ > cat a.rb
     require "net/http"

     class MyClass
       attr :array
       def initialize
         @array = ["rama", "lama", "ding"]
       end
       def some_method
         Net::HTTP::start("www.ruby-doc.org") do |http|
           @array += ["dong"]
         end
       end
     end

     mc = MyClass::new
     mc.some_method
     p mc.array

     harp:~ > ruby a.rb
     ["rama", "lama", "ding", "dong"]

hope that helps.

kind regards.

-a