Create module variables in Ruby
Ruby natively supports class variables in modules, so you can use class variables directly, and not some proxy or pseudo-class-variables:
module Site
@@name = "StackOverflow"
def self.setName(value)
@@name = value
end
def self.name
@@name
end
end
Site.name # => "StackOverflow"
Site.setName("Test")
Site.name # => "Test"
Instance variables in modules?
Think of the instance variable as something which will exist in any class that includes your module, and things make a bit more sense:
module Stacklike
def stack
@stack ||= []
end
def add_to_stack(obj)
stack.push(obj)
end
def take_from_stack
stack.pop
end
end
class ClownStack
include Stacklike
def size
@stack.length
end
end
cs = ClownStack.new
cs.add_to_stack(1)
puts cs.size
will output "1"
How can I initialize a module's instance variables in Ruby?
Initialize them in the module definition.
module MyModule
# self here is MyModule
@species = "frog"
@color = "red polka-dotted"
@log = []
def self.log(msg)
# self here is still MyModule, so the instance variables are still available
@log << msg
end
def self.show_log
puts @log.map { |m| "A #@color #@species says #{m.inspect}" }
end
end
MyModule.log "I like cheese."
MyModule.log "There's no mop!"
MyModule.show_log #=> A red polka-dotted frog says "I like cheese."
# A red polka-dotted frog says "There's no mop!"
This will set the instance variables when the module is defined. Remember, you can alwasys reopen the module later to add more instance variables and method definitions,
or to redefine existing ones:
# continued from above...
module MyModule
@verb = "shouts"
def self.show_log
puts @log.map { |m| "A #@color #@species #@verb #{m.inspect}" }
end
end
MyModule.log "What's going on?"
MyModule.show_log #=> A red polka-dotted frog shouts "I like cheese."
# A red polka-dotted frog shouts "There's no mop!"
# A red polka-dotted frog shouts "What's going on?"
Shared module variables in ruby
You are confusing namespace and including a module. It is possible to share a class variable if you actually include a shared module, and actually initialize the class variable. And it will not cause an error if you not use an undefined method NestedClass1#bar
but use a defined method NestedClass1#foo
.
module Module1
@@shared = nil
end
class NestedClass1
include Module1
def initialize
@@shared = "hello world"
end
def foo
p @@shared
end
end
class NestedClass2
include Module1
def bar
p @@shared
end
end
NestedClass1.new.foo # => "hello world"
NestedClass2.new.bar # => "hello world"
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.
Module and class variable scope in ruby
Firstly - class variables are evil and should be avoided (because they are also inherited by all subclasses and usually causes more harm than good.
You want to create a class instance variable (not class variable) on a class or module which is including given module. It is easy to do with included
method:
module Foo
@default_settings = {}
module ClassMethods
def foo_settings
@foo_settings
end
end
def self.included(target)
target.instance_variable_set('@foo_settings', @default_settings.dup)
target.extend ClassMethods
end
end
Why can't my Ruby module method access my module class variable?
The first @main_menu_options
is not the same as the @main_menu_options
referenced in the mixin method. The second method operates in the context of whatever called include
.
If you want these both on the same page you'll need to redefine that method to be in the same scope:
module UserInterface
@main_menu_options = {
1 => "Display Task List",
2 => "Add New Task",
3 => "Edit A Task",
4 => "Delete A Task",
"Q" => "Quit"
}
# Module-level method has access to module-level defined variables
def self.main_menu(string = "")
@main_menu_options.each_value { |v| puts v }
if @main_menu_options == nil then puts "NOTHING THERE" end
end
end
This misses the bigger problem. Since those options never change you really should be defining this as a constant:
module UserInterface
MAIN_MENU_OPTIONS = {
1 => "Display Task List",
2 => "Add New Task",
3 => "Edit A Task",
4 => "Delete A Task",
"Q" => "Quit"
}
def main_menu(string = "")
MAIN_MENU_OPTIONS.each_value { |v| puts v }
if MAIN_MENU_OPTIONS == nil then puts "NOTHING THERE" end
end
end
You'll need to fix your if
since that's not really functional code. When writing Ruby try to avoid one-liners like that, skip the then
to avoid clutter.
Set class variable from module
Do not use class variable, use instance_variable
that is attached to the class.
module MyApp
module MyLog
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def logger
@logger ||= Logger.new("#{Rails.root}/log/#{self.name.underscore}.log")
end
end
end
end
Example:
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def logger
puts @logger
@logger ||= name
end
end
end
class B
include A
end
class C
include A
end
B.logger
#
B.logger
# B
C.logger
#
B.logger
# B
C.logger
# C
First time you call the method it is nil
, thus the empty line, second time you call method the value equals to class name, B
, and if called on new class it is again nil
, check also this answer
Ruby class instance variable vs. class variable
How to make module variables that can be accessible in class and in each model instance?
You try to call pluggin_by_name
, which is class singleton method, on the instance (your self
is an instance of Plugin
in pry
console). This should work:
Plugin.pluggin_by_name(name)
Also, this part:
included do
@pluggins = [{name: "dupa"},{name: "hello"}]
end
makes no sense. You want your @pluggins
, as far as I understand, as an instance variable of Plugin
, but in this block self
certainly isn't Plugin
instance but, if I remember correctly, Plugin
class. If you want pluggins
attr reader to work, maybe you should set some kind of default value, like this:
module Plugginable
attr_writer :pluggins
def pluggins
@pluggins ||= [{name: 'dupa'}, {name: 'hello'}]
end
end
Related Topics
Difference Between Has_One and Belongs_To in Rails
Any Success with Sinatra Working Together with Eventmachine Websockets
How to Pass Parameter on 'Vagrant Up' and Have It in the Scope of Vagrantfile
Ruby: How to Iterate Over a Range, But in Set Increments
How to Return Early from a Rake Task
"Which in Ruby": Checking If Program Exists in $Path from Ruby
How to Merge Two Hashes Without Overwritten Duplicate Keys in Ruby
Create Module Variables in Ruby
Installing Cocoapods: No Response
How to Suppress Rails Console/Irb Outputs
Rvm Ruby 1.9.1 Install Can't Locate Zlib But Its Runtime and Dev Library Are There
How to Sort a Ruby Hash by Number Value
How to Get Sinatra to Auto-Reload the File After Each Change