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?
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"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"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
You Don't Have Write Permissions into the /Var/Lib/Gems/1.9.1 Directory
How to Do "Late" String Interpolation in Ruby
Error "Undefinded Method "Load_Defaults" " When Trying to Deploy App on Heroku
Rails: Wkhtmltopdf Runtimeerror (Location of Wkhtmltopdf Unknown)
Sorting an Array by Two Values
Change String in a Date Format to Another Format
How to Require File from 'Gem' Which Are Not Under 'Lib' Directory
Why Require Mongo Gives Me Loaderror: No Such File to Load -- Openssl
Ruby Incorrectly Parses 2 Digit Year
Recommended Two-Way Encryption Gems for Ruby
How to Use Regex for Utf8 in Ruby
Rake Db:Migrate Is Being Aborted Due to Rake Version Difference