Hi,
Thank you answers to my previous question(s) !
Why does this not work?
doc = File::open("my.txt")
class Crap
def junko
@doc = doc
@doc.each {|line| print line}
end
end
myobj=Crap.new
myobj.junko
The segment below does work - I just don't quite understand why the Crap
object can't 'see' the txt file in the above example...
doc = File::open("my.txt")
class Crap
def junko(a)
a.each {|line| print line}
end
end
myobj=Crap.new
myobj.junko(doc)
Thanks in advance
Charles L. Snyder wrote:
Why does this not work?
Because the variable doc is local to the top-level scope and can't be seen inside the methods of Crap. This works:
$doc = File::open("my.txt")
class Crap
def junko
@doc = $doc
@doc.each {|line| print line}
end
end
myobj=Crap.new
myobj.junko
$doc.close
$doc is now a global. Of course, I would not recommend coding this way. For example your original code never closed the file (which is probably not that bad here because at exit open files will be closed.) In addition globals are bad in most cases.
Using File::open with a block, and passing in the file (like your second example) is better:
class Crap
def junko(doc)
@doc = doc
@doc.each {|line| print line}
end
end
File::open("my.txt") do |file|
myobj=Crap.new
myobj.junko(file)
end
That way the file is closed automatically when the block ends.
Ryan
Charles L. Snyder wrote:
Why does this not work?
doc = File::open("my.txt")
class Crap
def junko
@doc = doc
@doc.each {|line| print line}
end
end
myobj=Crap.new
myobj.junko
Please note that class Foo ... end does indeed open a new scope that is totally disconnected to the scope that surrounds it in the source file.
This basically means that you could workaround the issue with this:
File.open("my.txt") do |doc|
Crap = Class.new do
def junko
doc.each { |line| print line }
end
end
end
In Ruby it is /not/ considered good style do conditionally define classes which is probably the reason why the source code you tried to use above is discouraged.
Ryan Leavengood wrote:
Because the variable doc is local to the top-level scope and can't be seen inside the methods of Crap.
That is interesting, though. In most cases, variables defined in the outer scope are available in the inner scope. This applies in the case of begin...end, blocks, and the if/while/etc control flow operators. It seems that the same rule does not apply to method and class definitions. Why? Is there anything else to which it doesn't apply?
Devin
Unlike some other languages, variables that are not in scope in Ruby
are completely inaccessible (unless you resort to certain tricks).
Class, Module and Method definitions introduce new scopes, so outside
variables will be entirely invisible. Conditionals and looping
statements (ie, if..else, while..end, case..when..end) do not
introduce new scopes, so they can access variables that are outside
them, and variables defined inside them will be accessible outside the
statement.
Blocks are a little more complicated; they don't really introduce a
new scope, but any variables defined only inside a block will not
escape the block.
foo = 23
class Bar # new scope! foo is not accessible
baz = 42
def qux # another new scope! no baz for you!
puts baz # yoiks, that'll be a NameError
end
end
And for blocks:
name = "Mark"
5.times{puts name} # works
5.times{|n| n+=1}
puts n # doesn't work, n stays in the block
n = nil # n exists before the block
5.times{|n| n+=1}
puts n # outputs: "6"
Hope this helps, and isn't too confusing Except for blocks, it's
actually a simpler scheme than most other languages; no masking of
variable names in outer scopes.
cheers,
Mark
···
On 6/26/05, Devin Mullins <twifkak@comcast.net> wrote:
Ryan Leavengood wrote:
> Because the variable doc is local to the top-level scope and can't be
> seen inside the methods of Crap.
That is interesting, though. In most cases, variables defined in the
outer scope are available in the inner scope. This applies in the case
of begin...end, blocks, and the if/while/etc control flow operators. It
seems that the same rule does not apply to method and class definitions.
Why? Is there anything else to which it doesn't apply?
Wow, that was a simple and complete summary of scope in Ruby. That helps a lot.
I had known that variables defined inside blocks didn't escape blocks. I didn't know that the same wasn't true for loops and conditionals. Essentially, scope is flat inside a class/module/method, with the odd exception of blocks.
This is good, except it makes blocks a little confusing
Example #1:
irb(main):001:0> a = nil
=> nil
irb(main):002:0> 5.times {|a| }
=> 5
irb(main):003:0> a
=> 4
Example #2:
irb(main):006:0> 5.times {
irb(main):007:1* a ||= 0
irb(main):008:1> a+=1
irb(main):009:1> print a, ' '
irb(main):010:1> }
1 1 1 1 1
Example #3:
irb(main):001:0> a = 0
=> 0
irb(main):002:0> 5.times {
irb(main):003:1* a ||= 0
irb(main):004:1> a+=1
irb(main):005:1> print a, ' '
irb(main):006:1> }
1 2 3 4 5
Not a huge issue. Just something to get used to.
Devin