scope question

Discussion in 'Ruby' started by Nick Brown, Feb 27, 2010.

  1. Nick Brown

    Nick Brown Guest

    Consider the following Sinatra app:

    require 'sinatra'
    vara = "foo"
    VARB = "bar"
    helpers do
    def geta
    vara
    end
    def getb
    VARB
    end
    end
    get '/a' do geta end
    get '/b' do getb end

    When I run that and browse to /b, it displays "bar". When I browse to
    /a, however, it produces the error: "undefined local variable or method
    `vara'".

    Why do these two variables differ in scope?

    How can I declare a variable that has global scope but is not a
    constant?

    Thanks in advance...
     
    Nick Brown, Feb 27, 2010
    #1
    1. Advertisements

  2. Nick Brown

    Mat Brown Guest

    [Note: parts of this message were removed to make it a legal post.]

    Method definitions are not closures, so they don't have access to external
    local scope. Use $vara to declare a global, if you really think that's a
    good idea.


    Consider the following Sinatra app:

    require 'sinatra'
    vara = "foo"
    VARB = "bar"
    helpers do
    def geta
    vara
    end
    def getb
    VARB
    end
    end
    get '/a' do geta end
    get '/b' do getb end

    When I run that and browse to /b, it displays "bar". When I browse to
    /a, however, it produces the error: "undefined local variable or method
    `vara'".

    Why do these two variables differ in scope?

    How can I declare a variable that has global scope but is not a
    constant?

    Thanks in advance...
     
    Mat Brown, Feb 27, 2010
    #2
    1. Advertisements

  3. Nick Brown

    Raul Jara Guest

    Important disclaimer: I'm kind of new at this, so I'm just taking a stab
    at it...

    One way you could do that is to declare a class variable for the class
    Object. Because every object inherits from Object, everything in ruby
    should have access to that same class variable. E.g.,

    class Object
    @@var = "Hello"
    end

    puts @@var
    class Silly
    def Silly.hi
    puts @@var
    end
    end

    Silly.hi
    @@var = "goodbye"

    Silly.hi


    But it's generally considered good form to not use global variables in
    the first place. There's almost always a way to avoid using them. The
    danger of using them is that if you use a global variable with your
    code, and someone else uses a global variable of the same name in their
    code, the two bits of code will both break if they are ever run
    together.
     
    Raul Jara, Feb 27, 2010
    #3
  4. Nick Brown

    Nick Brown Guest

    But it's generally considered good form to not use global variables in
    Raul: I believe it would also be bad form to hit the database
    unnecessarily with every pageview. If It's bad form to cache data in a
    variable that is visible to every page, then I don't want to be good ;-)

    Is there a "better" way to do this than @@? Why do constants have
    different scopes than regular variables?
     
    Nick Brown, Feb 28, 2010
    #4
  5. But in that case you're writing a real Sinatra application not a toy :)

    You can write your example as:

    module Play
    class Application < Sinatra::Base

    vara = "foo"
    VARB = "bar"

    helpers do
    def geta
    vara
    end
    def getb
    VARB
    end
    end

    get '/a' do geta end
    get '/b' do getb end

    end
    end


    This should make thing a lot clearer as to what's going on, especially
    regarding scopes since it now looks more like actual Ruby code.

    The constant VARB is really Play::Application::VARB in this case. If
    you want VARB global then you refer to it as ::VARB (everywhere)

    In normal Ruby code what and where is vara in this case?

    If you use @@vara it'll work, since vara will become a class variable
    of Play::Application

    The trouble with that is that there's only one @@vara and it'll be
    used for all requests by all users. I'm also thinking that you
    probably don't really want that. If you don't then you'll have to keep
    vara someplace else, and look for it in each request.

    Cheers,
    Bob

     
    Bob Hutchison, Feb 28, 2010
    #5
  6. Nick Brown

    Nick Brown Guest

    OK, I'm with you so far. But here's what I want to do: there is one
    value (such as: number of users currently online) that is shown on every
    page. Every page view reads this number and displays it. This value is
    updated whenever someone logs in or out.

    What is the best way to handle that?
     
    Nick Brown, Feb 28, 2010
    #6
  7. Which two variables?

    vara is a local variable. Its scope extends to the end of the block
    where it's defined. However a method definition using 'def ... end' or a
    class definition using 'class ... end' block starts a fresh empty scope
    inside it.

    VARB is not a variable at all, it's a constant. (Constants start with
    capital letters in Ruby).
    $varc is a true global variable.

    But usually you don't want these. Better is an instance variable of a
    class (remembering that a class is just an object)

    class Foo
    @vard = "hello"
    def self.vard
    @vard
    end
    def self.vard=(v)
    @vard=v
    end
    end

    puts Foo.vard
    Foo.vard = "world"
    puts Foo.vard

    Inside an instance method of foo you can use either 'Foo.vard' or
    'self.class.vard' to get at this value.
     
    Brian Candler, Feb 28, 2010
    #7
  8. That's quite a question.

    Well, you could do it as a class variable of Play::Application but
    that's probably not the best idea. Play::Application instances are
    created by Sinatra to handle requests, and that's probably all they
    are should be used for. If you did take this approach then you would
    need to write accessor methods to increment/decrement the counter as
    people login/logout -- that is, your authentication system would have
    to call these accessors. This doesn't sound very good to me (doesn't
    smell so good eather.)

    You'll probably be better off looking for some object in your system
    that would know about these things, something where this kind of
    responsibility is in line with its responsibilities. There is likely
    something in your authentication system that would be the right place
    for this. If so, then where you have @@vara in Play::Application you
    ask the authentication system to tell you how many are currently
    logged in. Maybe something in your user management system?

    Cheers,
    Bob


     
    Bob Hutchison, Feb 28, 2010
    #8
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.