tags:

views:

163

answers:

4

It's been a while since I last did Ruby programming -- looking at somebody else's code, I see the @ sigil in a function (not a method -- external to any class definition), which I understood to be scoped to instance members.

Is the module the implied self in functions?

A: 

I think it scopes it to a module.

p01nd3xt3r
+2  A: 

The assumption that this is "a function, not a method" is wrong. All Ruby code occurs within the context of some object. The "global" context is an Object called main (try ruby -e "puts self" if you don't believe it). Methods defined in the top scope become instance methods of Object. This means that the method will be available anywhere, with self (and thus the context that instance variables belong to) depending on where the method is called

Chuck
Okay, so is @ident within a module-level function another way of saying $ident?
cdleary
No, it sets the @ident instance variable of the module (remember, in Ruby, a Module is an Object too and can have its own ivars). "@ident = something" will set the @ident instance variable of whatever "self" is at the moment.
Chuck
Okay, so globals are truly global (bound to the interpreter), not confined to the scope of any given module?
cdleary
That's right. Globals are global to the whole program.
Chuck
So is it common practice to use @ident in module-level functions to make module-scoped identifiers? I would think that it would be more straightforward to pre-declare them at the start of the module.
cdleary
A: 

@ makes a variable an instance variable. It sounds like it's not a part of an explicitly declared class, so it's going to available within the main object as Chuck pointed out. It's easier illustrated by example:

$ irb
irb(main):001:0> @var = 1
=> 1
irb(main):002:0> class New
irb(main):003:1>   def test
irb(main):004:2>     puts @var
irb(main):005:2>   end
irb(main):006:1> end
=> nil
irb(main):007:0> test = New.new
=> #<New:0xb7ccbc14>
irb(main):008:0> test.test
nil
=> nil
irb(main):009:0> def test2
irb(main):010:1>   puts @var
irb(main):011:1> end
=> nil
irb(main):012:0> test2
1
=> nil
Andrew Cholakian
+3  A: 

In a top-level function (still a method really), you're in a slightly strange place: a global "main" space (see the prompt when you run irb, or try looking at self inside such a function), but also these functions are defined as private methods inside the Object class.

$ cat foo
def foo
  p self
  p self.class
  puts 'foo'
end

foo
Object.foo

$ ruby foo
main
Object
foo
foo:8: private method `foo' called for Object:Class (NoMethodError)
$

You can sneak around this by explicitly declaring these methods public, but I'm not sure I like this! Curiously, if you define a top-level method inside irb, then you can call it via the class method Object#foo without declaring it public.

So this is a kind of an "implied main namespace hacked onto Object (but shhh don't tell anyone)". @foo defined inside a top-level function is available inside Object, as a plain old attribute. Sort of. If your top-level method set @foo and you call it without scoping then it is declared in the eigen-like main namespace, but if you call the class method via Object then @foo appears in the space of Object.

Another example:

public
def set_foo
  @foo = 'foo'
end

def get_foo
  @foo
end

def Object.get_foo_again
  @foo
end

set_foo
p get_foo
p Object.get_foo_again

Object.set_foo
p Object.get_foo_again

gives

"foo"  # @foo set in main
nil    # @foo nil in Object  
"foo"  # @foo in Object

The converse also applies.

Martin Carpenter