Search code examples
rubyscopesigils

What scope does an "at" sigil (@) give within Ruby functions?


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?


Solution

  • 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.