Accessing Variables from Included Files in Ruby

Accessing variables from included files in Ruby

You can't access a local outside of the scope it was defined in — the file in this case. If you want variables that cross file boundaries, make them anything but locals. $foo, Foo and @foo will all work.

If you just really don't want to put any sort of decoration on the symbol (because you don't like the way it reads, maybe), a common hack is just to define it as a method: def foo() "bar" end.

Accessing a variable declared in another rb file

The best way to export data from one file and make use of it in another is either a class or a module.

An example is:

# price.rb
module InstancePrices
PRICES = {
'us-east-1' => {'t1.micro' => 0.02, ... },
...
}
end

In another file you can require this. Using load is incorrect.

require 'price'

InstancePrices::PRICES['us-east-1']

You can even shorten this by using include:

require 'price'

include InstancePrices
PRICES['us-east-1']

What you've done is a bit difficult to use, though. A proper object-oriented design would encapsulate this data within some kind of class and then provide an interface to that. Exposing your data directly is counter to those principles.

For instance, you'd want a method InstancePrices.price_for('t1.micro', 'us-east-1') that would return the proper pricing. By separating the internal structure used to store the data from the interface you avoid creating huge dependencies within your application.

How can I access a variable defined in a Ruby file I required in IRB?

While it is true that you cannot access local variables defined in required files, you can access constants, and you can access anything stored in an object that you have access to in both contexts. So, there are a few ways to share information, depending on your goals.

The most common solution is probably to define a module and put your shared value in there. Since modules are constants, you'll be able to access it in the requiring context.

# in welcome.rb
module Messages
WELCOME = "hi there"
end

# in irb
puts Messages::WELCOME # prints out "hi there"

You could also put the value inside a class, to much the same effect. Alternatively, you could just define it as a constant in the file. Since the default context is an object of class Object, referred to as main, you could also define a method, instance variable, or class variable on main. All of these approaches end up being essentially different ways of making "global variables," more or less, and may not be optimal for most purposes. On the other hand, for small projects with very well defined scopes, they may be fine.

# in welcome.rb
WELCOME = "hi constant"
@welcome = "hi instance var"
@@welcome = "hi class var"
def welcome
"hi method"
end

# in irb
# These all print out what you would expect.
puts WELCOME
puts @welcome
puts @@welcome
puts welcome

How to access variable from outside class in another file

Those are not global variables. They are called "instance variables" and to access them you need to create instances of your casinos and players. Looks like this.

player = Player.new
player.money # => 0
player.money += 10
player.money # => 10

In your Casino class you don't call parent initializers (a simple oversight, I think), so it doesn't initialize @name and @money.

And Roulette doesn't do anything at all to obtain a wallet. So it stays at default value nil.

access scope of 'required' files to get variables

You use require to load a library into your Ruby program. It will return true if successful.

So you have a file example.rb:

require 'library.rb'

# Some code

x = CONSTANTFROMREQUIREFILE

puts x # "Hello World"

method_from_required_file # "I'm a method from a required file."

and a file library.rb:

CONSTANTFROMREQUIREFILE = "Hello World"

def method_from_required_file
puts "I'm a method from a required file."
end

As you can see you access the constant and the method as you would access a constant and a method from the same file.

You can read more about require here: What is the difference between include and require in Ruby? and here: Kernal Module in Ruby

How to share variables across my .rb files?

  1. Constants (which include modules and classes) are added to the shared global environment:

    phrogz$ cat constants1.rb 
    TEST_VARIABLE = "test"

    phrogz$ cat constants2.rb
    require_relative 'constants1'
    p TEST_VARIABLE

    phrogz$ ruby constants2.rb
    "test"
  2. Instance variables declared in main are all part of the same main:

    phrogz$ cat instance1.rb 
    @test_variable = "test"

    phrogz$ cat instance2.rb
    require_relative 'instance1'
    p @test_variable

    phrogz$ ruby instance2.rb
    "test"
  3. Global variables are also all part of the same environment (tested in 1.8.6, 1.8.7, and 1.9.2):

    phrogz$ cat global1.rb 
    $test_variable = "test"

    phrogz$ cat global2.rb
    require_relative 'global1'
    p $test_variable, RUBY_DESCRIPTION

    phrogz$ ruby global2.rb
    "test"
    "ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.7.0]"

Ruby shared variable between files

The module is just a chunk of methods and variables. It doesn't provide you a storage that you can use between your Importer class and the SiteController. It's just provides you an ability to define equal methods in several classes which are not inherited from each other. It's something like Mixin pattern (take a look at this description: https://en.wikipedia.org/wiki/Mixin).

To "share variable between files" you should use something similar to Redis or Memcached. The regular SQL DB can provide this functionality too, but it won't be so effective in this case =)

Take a look at this guide to understand how it should be working: https://www.sitepoint.com/introduction-to-using-redis-with-rails/

So here's a little improvement of yours example to make it clear (even more better solution would be to implement counters in redis or update it's value on every 10th step or so to decrease load on redis. But it's a complication)):

module ProgressBar
attr_accessor :curr, :max

def increment_bar
@curr += 1
save_progress # <- Save progress on each step
end

def progress
@curr / @max
end

def save_progress(process_id)
$redis.set("progress_#{process_id}", progress)
end

def load_progress(process_id)
$redis.get("progress_#{process_id}") || 0
end
end

class Importer
include ::ProgressBar

def calculate_max_rows
# some logic will go here...
set_max_rows(l)
end
end

class SpecificImporter < Importer
def import
# custom logic...
increment_bar
end
end

class SiteController < ApplicationController
include ProgressBar
def update_progress
# some logic here...
status = load_progress(params[:process_id]) # <- Load stored progress
end
end

Modules and Accessing Variables from Modules (Ruby Language)

When writing a module the convention is to declare constants like this:

module Week
FIRST_DAY = 'Sunday'
end

Note that they're in ALL_CAPS. Anything that begins with a capital letter is treated as a constant. Lower-case names of that sort are treated as local variables.

Generally it's bad form to access the constants of another module, it limits your ability to refactor how those are stored. Instead define a public accessor method:

module Week
def first_day
FIRST_DAY
end
end

Now you can call that externally:

Week.first_day

Note you can also change how that's implemented:

module Week
DAYS = %w[
Sunday
Monday
Tuesday
...
Saturday
]

def first_day
DAYS.first
end

extend self # Makes methods callable like Week.first_day
end

The nice thing about that is the first_day method does exactly the same thing, no other code has to change. This makes refactoring significantly easier. Imagine if you had to track down and replace all those instances to Week::FIRST_DAY.

There's some other things to note here. The first is that any time you call include on a module then you get the methods and constants loaded in locally. The second thing is when you define a mix-in module, be careful with your names to avoid potential conflict with the target class.

Since you've mixed it in, you don't need the namespace prefix, just calling first_day should do it.

Load file to rails console with access to variables defined in this file

When you load a file local variables go out of scope after the file is loaded that is why a and b will be unavailable in the console that loads it.

Since you are treating a and b as constants how about just capitalizing them like so

A = 1
B = 2
puts A+B

Now in you console you should be able to do the following

load 'myfile.rb'
A #=> 1

Alternately you could make the variables in myfile.rb global ($a, $b)



Related Topics



Leave a reply



Submit