scope question

N

Nick Brown

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

Mat Brown

[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...
 
R

Raul Jara

Nick said:
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...

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

Nick Brown

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: 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?
 
B

Bob Hutchison

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 ;-)

But in that case you're writing a real Sinatra application not a toy :)
Is there a "better" way to do this than @@? Why do constants have
different scopes than regular variables?


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

 
N

Nick Brown

Bob said:
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.

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?
 
B

Brian Candler

Nick said:
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?

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).
How can I declare a variable that has global scope but is not a
constant?

$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.
 
B

Bob Hutchison

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?

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


 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top