How can I initialize instance variable?
If you want to persist anything between requests you need to store it somewhere:
- Database
- Memory based storage (Redis, Memcached)
- File system
You can also pass state back and forth between the client and server without actually storing it with:
- HTTP cookies
- Query string parameters
Using a class variable is not really going to solve anything. It will only hold the variable as long as the class is held in memory. Every time the class is reloaded it will be reset.
Multi-threading is another huge issue here as Rails servers are commonly multi-threaded and class variables are not thread safe.
Initializing multiple objects at once?
I want to start by saying this is very strange Ruby code and not something you'd typically do. That's not meant as an insult, just to say that Ruby devs tend to follow the same or similar guidelines on structuring objects and this chunk of code feels like it's ported from another language.
The issue you're seeing is due to the fact that within the initialize
method you're not creating any new objects but instead updating the instance variables and pushing self
into a class variable. self
is referencing this instance directly which means the class variable array is filling up with references to the same object. If you're adamant on keeping the code the same then you should instead push a duplicate of your object after you've updated the instance variables.
@@market << self.dup
This creates a duplicate object that has a different memory address and reference.
If you're looking to write more idiomatic code you'd want to use multiple objects and not rely on class variables at all. If you're not interpolating a variable in a string use single quotes instead of double quotes. Keep object methods simple and focused on specific tasks. These are just a few things Ruby developers consider when writing code, but find what works best for you.
Take something like this for instance:
class Market
attr_accessor :id, :name, :symbol, :price, :price_movement_24h, :market_cap
def initialize(data = {})
@id = data['id'].to_s
@name = data['name'].to_s
@symbol = data['symbol'].to_s
@price = data['current_price'].to_s
@price_movement_24h = data['price_change_percentage_24h'].to_s
@market_cap = data['market_cap'].to_s
end
end
class ImportService
def self.from_api(url)
response = JSON.parse(open(url).read) || []
response.map { |data| Market.new(data) }
end
end
You could then call this as such:
@market_data = ImportService.from_api(BASE_URL + 'markets?vs_currency=usd')
Initializing instance variables in Mixins
module Example
def self.included(base)
base.instance_variable_set :@example_ivar, :foo
end
end
Edit: Note that this is setting a class instance variable. Instance variables on the instance can't be created when the module is mixed into the class, since those instances haven't been created yet. You can, though, create an initialize method in the mixin, e.g.:
module Example
def self.included(base)
base.class_exec do
def initialize
@example_ivar = :foo
end
end
end
end
There may be a way to do this while calling the including class's initialize method (anybody?). Not sure. But here's an alternative:
class Foo
include Example
def initialize
@foo = :bar
after_initialize
end
end
module Example
def after_initialize
@example_ivar = :foo
end
end
How to initialise an instance variable dynamically in ruby?
Mocking @sites data:
@sites = [OpenStruct.new(
site_1_location: 'Japan',
site_2_location: 'India',
site_3_location: 'Chile',
site_4_location: 'Singapore'
)]
You can use instance_variable_set
to set the instance variable
@sites.each do |site|
1.upto(4) do |i|
instance_variable_set("@var#{i}", site.send("site_#{i}_location"))
end
end
Now you can access the variables:
@var1 # returns "Japan"
@var2 # returns "India"
@var3 # returns "Chile"
@var4 # returns "Singapore"
ruby Initializing instance variable outside method
It does have a use: Class variables. The normal Ruby class variable implementation, @@
, shares the same variable between a superclass and its subclasses:
class A
@@v = 0
def self.v; @@v; end
def self.v=(val); @@v=val; end
end
class B < A; end
A.v #-> 0
A.v= 3 #-> 3
B.v #->3
B.v= 42 #-> 42
A.v #-> 42
Obviously, this is pretty useless (except for the fact that, unlike class instance variables, class variables are also accessible directly in instance methods, as opposed to through self.class
). But the same example with class instance variables:
class A
@v = 0
def self.v; @v; end
def self.v=(val); @v=val; end
end
class B < A; end
A.v #-> 0
A.v= 3 #-> 3
B.v= 42 #-> 42
A.v #-> 3
Also, class instance variables can harness all of the metaprogramming already written for instance variables, like so:
class Foo
class << self
attr_accessor :v #Uses a class instance variable
end
end
Ruby: Variable initialization within classes
attr_accessor :symbol
do the same as attr_writer :symbol
and attr_reader :symbol
, i.e. it creates both reader (def symbol; @symbol; end
) and writer (def symbol=(value); @symbol = value; end
).
Initialize is a method called every time new instance of the class is being created. It is not the same as new
method as some classes may have its own custom factory methods. You don't need to define your initialize method, only problem is that then symbol
reader would return nil
, as the local variable would not been set.
In ruby everything is a method. In case of objects, object.attr = value
is just a short for object.attr=(value)
where attr=
is just another method. (Similarly <<
operator is defined as a method on Array class, attr_accessor
is a method defined on class "Class").
Class Level Instance Variables in Ruby
In your answer you don't output the class level instance variable. Besides the usual syntax (@foo
), an instance variable can be accessed via a method (instance_variable_get(:@foo)
). You can use this method to read instance variables of other objects, not only self
.
Here's a modified version of your code
require 'active_support/core_ext'
class MyClass
cattr_reader :class_variable
def self.new_instance(cv, cliv, iv)
@@class_variable = cv
@class_level_instance_variable = cliv
self.new(iv)
end
def initialize(iv)
@instance_variable = iv
end
def use
puts "class_var=#{self.class.class_variable.inspect}"
puts "class inst var: #{self.class.instance_variable_get(:@class_level_instance_variable)}"
puts "inst_var=#{@instance_variable.inspect}"
end
end
c = []
c << MyClass.new_instance(1,2,3)
c << MyClass.new_instance(4,5,6)
c << MyClass.new_instance(7,8,9)
c[0].use
c[1].use
c[2].use
# >> class_var=7
# >> class inst var: 8
# >> inst_var=3
# >> class_var=7
# >> class inst var: 8
# >> inst_var=6
# >> class_var=7
# >> class inst var: 8
# >> inst_var=9
See, class inst var is always 8 (just as class var is always 7). This is because you output values after all your modifications are made. And since class level variables are shared, the last modification wins.
c << MyClass.new_instance(7,8,9)
If you were to output from initializer (as was in your first version of code), you'd see different results.
# >> class_var=1
# >> class inst var: 2
# >> inst_var=3
# >> class_var=4
# >> class inst var: 5
# >> inst_var=6
# >> class_var=7
# >> class inst var: 8
# >> inst_var=9
Can you dynamically initialize multiple variables on one line in ruby?
There is a way, using eval
, but you would rather not want to use it (and I would even go that far to say that it might be better not to learn it until well later).
There is simply no case when you would use that instead of plain arrays.
For your example, one should use class Range
and method map
:
(0..3).map{|i| i * 2}
#=> [0, 2, 4, 6]
You can see that this has been done without declaring any variable - even i
is alive just within the block passed to map
. It doesn't exist afterwards.
initializing variables in ActiveRecord's classes
attr_accessor_with_default :used, false
Or if you want to use initialize approach you can define callback after_initialize
def after_initialize
@used = false
end
Using attr_accessor_with_default
with an object literal (e.g. attr_accessor_with_default :used, false
) is unsafe to use with any mutable objects. Specifically, it means that different instances of your class will have the same object as their default value. This is sort of like trying to use a class variable (@@my_var
) where you want an instance variable (@my_var
). If you want to use a mutable object (e.g. a String
, Array
, or Hash
), you must use the block syntax:
attr_accessor_with_default(:used) { Array.new }
Related Topics
Why Is the Splat Used Inside an Array Definition Here
Rails Gem to Break a Paragraph into Series of Sentences
Rails Query Timestamp Between Two Hours
No Such File to Load -- Bundler/Setup (Ruby on Rails)
Ruby Enterprise Edition VS Ruby 1.9
Rails Form Object with Reform-Rails with Collections Not Working or Validating
Rails: Parameterfilter::Compiled_Filter Tries to Dup Symbol
Rails Reload Dynamic Routes on Multiple Instances/Servers
Fast-Stemmer Installation Problems
Triple Single Quote VS Triple Double Quote in Ruby
Rails - Multi Tenant Application with Customization Framework
Validate That String Contains Only Allowed Characters in Ruby
Do I Need to Indent My Code in Ruby