I'm new to ruby rails, and I'm struggling to understand what the purpose of instance variables are.
What is the point of making a variable into an instance variable?
Basically why would I do this:
class User
def initialize(var1)
#name = var1
end
end
over this:
class User
def initialize(var1)
name = var1
end
end
And in what types of situations would I want to use an instance variable over just a regular variable?
The practical use for this is that if you need a variable to persist across function calls, make it an instance variable.
after you define the instance variable, you can call the value of this on another function and you will have the value.
In general an instance variable is local and persisted inside an instance of an object, whereas a local variable is only local and persisted inside a function/object/block scope.
in your example if you have another method on your user class, you can use the value of #name on that other method, but coulnd't use var1 outside of initialize method.
class User
def initialize(var1)
#name = var1
end
def greetings
"hello #{#name}"
end
end
class People
def age
#age
end
def age= age
#age = age
end
end
def show people
age = people.age || '22'
p "Hello, my age is #{age}"
end
people = People.new
show people
=> 'Hello, my age is 22'
age
=> NameError: undefined local variable or method 'age' for main:Object
user.age = 21
greet people
=> 'Hello, my age is 21'
#age
=> nil
Notice that both variable types are local to a specific context though. #age is defined within the people instance, so when you call people.age you are calling the age function in the age instance, in which #age is defined. age is only defined in the show function, so when you call p "Hello, my age is #{age}" you are able to get a value for age because you are within the scope in which it is defined.
Because your instance variable will be accessible to your view code, including within forms and passing data to paths. That's probably the most common, practical use case for them. In your example, I could do the following:
<p>User: <%= "#{#name}" %></p>
And, instead of seeing "#name" in my corresponding paragraph in the rendered H.T.M.L, you'd see the value of that instance variable - whereas the inverse:
<p>User: <%= "#{name}" %></p>
...will probably result in an ActionView error, since "name" is undefined, according to it.
Related
I recently started learning ruby and i am confused between instance variable and local variable and class variable. so , i recently written code which will find the largest palindrome in 1000 prime numbers.
code is:
def prime_large(number)
arr_prime = []
Prime.each(number) do |x|
new_arr_prime = arr_prime.push(x.to_s)
updated = new_arr_prime.select { |y| y.reverse == y }
end
p updated.max
end
p prime_large(1000)
error i got is:
undefined local variable or method `updated' for main:Object (NameError)
I know that updated is a local variable of prime so i can't access it outside it but i changed the code by replacing it with #updated as below:
def prime_large(number)
arr_prime = []
Prime.each(number) do |x|
new_arr_prime = arr_prime.push(x.to_s)
#updated = new_arr_prime.select { |y| y.reverse == y }
end
p #updated.max
end
p prime_large(1000)
after changing it , i got the output:
"929"
"929"
in my code , without creating a class how my instance variable ( #updated) is working . i am confused between local and instance variable . can anyone please explain me the differences and how they work ?
In your first example you created a local variable updated, that is only accessible within the scope of the block it is defined in. Meaning, it is available only within Prime.each(number) do end block.
In your second example you created instance variable #updated.
without creating a class how my instance variable ( #updated) is
working
It is because in Ruby everything occurs in the context of some object. Even though you did not created a class, you are being in the top-level context, in the context of object main.
Thus, any instance variables defined within top-level are instance variables of this object main.
So going back to your issue, to overcome it you'll want to just define the updated local variable outside the Prime.each(number) do end block:
def prime_large(number)
arr_prime = []
updated = nil # initialize local varialbe
Prime.each(number) do |x|
new_arr_prime = arr_prime.push(x.to_s)
updated = new_arr_prime.select { |y| y.reverse == y } # assign new value
end
p updated.max
end
p prime_large(1000)
To test it you can open irb or pry and check it yourself:
self # the object in the context of which you are currently
#=> main
self.class # main is an instance of class Object, any methods defined
# within main are added to Object class as instance methods
#=> Object
instance_variables # list of it's instance variables, none yet created
#=> []
#variable = 1 # create and instance variable
#=> 1
instance_variables # now created variable occurs in the list of current object's instance variables
#=> [:#variable]
def hello; :hello end # define method
#=> :hello
self.class.public_instance_methods(false) # list instance methods defined in Object
#=> [:hello]
What you now want to read about is lexical scopes in Ruby.
A controller method includes:
#organization = Organization.find(params[:description])
check_expiration_date(#organization)
if #organization.update_attributes(subscription: true,
actioncode: session[:actioncode_id],
subs_exp_date: expiration_date)
...
private
def check_expiration_date(org)
expiration_date = Time.zone.now + 366.days
end
On the development server this generates the error below, referring to the line subs_exp_date: expiration_date:
undefined local variable or method `expiration_date' for #<OrganizationsController:0x007f36cd01d2a8>
Any ideas what might be causing this error? My idea was that check_expiration_date is executed first and thus sets the variable expiration_date.
It should be like this:
if #organization.update_attributes(subscription: true,
actioncode: session[:actioncode_id],
subs_exp_date: check_expiration_date(#organization))
If you see your code, expiration_date is a variable inside method check_expiration_date which limits its scope only to that method. Hence you cannot use variable expiration_date outside check_expiration_date.
Other way can be defining expiration date as an instance variable. Here:
def check_expiration_date(org)
#expiration_date = Time.zone.now + 366.days
end
and then
if #organization.update_attributes(... subs_exp_date: #expiration_date)
Key here is:
Contrast the scope of a local variable with that of an instance
variable: the instance variable is bound to the specific instance of
the class. By binding itself to the entire object, an instance
variable makes itself available to every method of the object.
It's because you set local variable expiration_date, it is visible only in current scope defined by method body. If you want to make this variable 'visible' also after method execution, you can set it instance variable:
#expiration_date = Time.zone.now + 366.days
and use it:
subs_exp_date: #expiration_date
For more info about Ruby variable scopes, you can go here:
http://www.techotopia.com/index.php/Ruby_Variable_Scope
or here:
Difference between various variables scopes in ruby
Solution 1:
Change this code:
check_expiration_date(#organization)
to:
expiration_date = check_expiration_date(#organization)
Solution 2:
Change:
def check_expiration_date(org)
expiration_date = Time.zone.now + 366.days
end
to:
def check_expiration_date(org)
#expiration_date = Time.zone.now + 366.days
end
and then:
if #organization.update_attributes(subscription: true,
actioncode: session[:actioncode_id],
subs_exp_date: #expiration_date)
Reason: You're trying to access expiration_date, which is a local variable whose scope is limited to check_expiration_date method and hence ruby is looking for a method instead of a variable. So, you need to make it an instance variable or create a local variable from the returning object of check_expiration_date method to get it working.
You are calling a local variable instead of instance variable. Its about variable scope. There are 2 options to get proper value for subs_exp_date
Convert local variable to instance variable
#expiration_date = Time.zone.now + 366.days
Another option to call method directly
subs_exp_date: check_expiration_date(#organization)
I am very new to programming and am trying to learn Ruby. I can't quite get my head around methods at the moment. I uderstand that:
Methods allow me to execute a chunk of code without having to rewrite it, such a method looks like:
example_method
Arguments allow me to pass values into the code within the method that go in the place of the placeholders defined in the method. This way I can execute a set of code with different inputs. Methods with arguments look like:
example_method( x , y )
But I am confused about what an instantiation of a method on an object is actually doing. For example:
object.example_method( x, y )
What does this mean? Why is the method attached to an object with the period notation? Do we do this so we can reference Instance / Class variables of the object within our method? Is there any other reason to do this?
For the example if:
def example_method(x , y)
x * y
end
Will object.exaple_method(a , b) be the same as example_method(a , b) ?
Thanks for any help, sorry if I am not being clear.
Ruby is an Object Oriented language. This means that objects not only have members (=state) but also methods (=behavior). When you are calling a method on an object (is which case the object is called the caller) the method that runs is the method which corresponds to this object's type behavior.
When you are calling a method with no caller, self is implicitly the caller. From irb, self is considered main, or global scope.
Example:
def my_method(a)
"#{a} from main"
end
class A
def my_method(a)
"#{a} from A"
end
end
class B
def my_method(a)
"#{a} from B"
end
end
a = A.new
b = B.new
my_method(1)
# => "1 from main"
a.my_method(1)
# => "1 from A"
b.my_method(1)
# => "1 from B"
If I assume correctly what you're asking it's a class method vs an instance method
def Object
def self.example_method(x, y)
#this is a class method
x * y
end
def example_method(x,y)
#this is an instance method
x * y
end
end
So now we've defined them here's how we call them
#This called the class method
Object.example_method(3,3) => 9
#To call the instance method we have to have an instance
object = Object.new
object.example_method(3,3) => 9
#result.instance_variable_get("#{#most}_max_count".to_sym)
#most is a single-digit string, for instance i. This code bit gives the error
`i_max_count' is not allowed as an instance variable name
(What I am trying to access is #result.i_max_count)
edit:
What I want to do is to set the value of #result.i_max_count to something.
Class for #result is pretty much empty:
class Result < ActiveRecord::Base
attr_accessor :least, :most
end
The parameter of instance_variable_get should be valid instance variable name: #i_max_count for example.
So you can slightly change your code:
#result.instance_variable_get("##{#most}_max_count".to_sym)
But question has tag "ruby-on-rails" so probably you are working with Hash. This way you can try that (thx for #FrederickCheung):
#result["#{#most}_max_count"]
or even this way:
#result.instance_variable_get("#attributes")["#{#most}_max_count"]
Or using eval:
eval("#rezult.#{#most}_max_count")
The instance variable names always have to start with an # sigil . The below snippet shows usage
Eg
class Ankit
def initialize(name)
#name = name
end
end
UPDATED:
1.9.3p392 :033 > a = Ankit.new("ankit")
=> #<Ankit:0x007fb3c39c79e8 #name="ankit">
1.9.3p392 :034 > a.instance_variable_get("#name")
=> "ankit"
In your case check 2 things
Is #i_max_count set?
If yes than use it obj.instance_variable_get("#i_max_count")
I want to define a class method that has access to a local variable. So this would be different for each instance of the class. I know you can make a class method dynamic with lambda like when you use it with named_scope. But can this be done for values that are specific to an instance?
In detail it is the has_attached_file method for the paperclip plugin in rails. I want to pass a lambda for the styles hash so that the image styles can be based off of attributes of the object stored in the DB. Is this possible?
Disclaimer: First, the question (Can you pass self to lambda?) and the problem you're trying to solve (dynamic styles with paperclip) don't fully match up. I won't answer the original question because it's not entirely related to your problem, and rampion took a valiant stab at it.
I'll instead answer your paperclip question.
In detail it is the has_attached_file method for the paperclip plugin in rails. I want to pass a lambda for the styles hash so that the image styles can be based off of attributes of the object stored in the DB. Is this possible?
Yes, it is possible. In paperclip, the :styles option can take a Proc. When the attachment is initialized, if a Proc was used, the attachment itself is passed to the Proc. The attachment has a reference to the associated ActiveRecord object, so you can use that to determine your dynamic styles.
For example, your has_attached_file declaration might look something like this (assuming a User and avatar scenario where the user can customize the size of their avatar):
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => lambda { |attachment|
user = attachment.instance
dimensions = "#{user.avatar_width}x#{user.avatar_height}#"
{ :custom => dimensions }
}
end
Ok, you're being unclear.
Local variables in ruby begin with a lowercase letter (like foo, bar, or steve), and are lexically scoped (like C variables). They have nothing to do with "an instance of a class"
Instance variables in ruby begin with an # sigil (like #foo, #bar, or #carl), and are in scope whenever the current value of self is the object they are stored in.
If you want a method that can access the instance variables of an object directly, that's called an instance method. For example, battle_cry and initialize are both instance methods:
class Character
def initialize(name)
#name=name
end
def battle_cry
#name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
A class method, by contrast, is a method for a Class object, and doesn't have access to any of the instance variables of that object. In the above example,
default is a class method.
If you want a (class or instance) method that triggers a change in or gets a value from the current scope, ruby uses a type of callback called a block.
class Character
ATTACKS = [ "Ho!", "Haha!", "Guard!", "Turn!", "Parry!", "Dodge!", "Spin!", "Ha", "THRUST!" ]
def attack
ATTACKS.inject(0) { |dmg, word| dmg + yield(word) }
end
end
person = Character.default
puts person.battle_cry
num_attacks = 0;
damage = person.attack do |saying|
puts saying
num_attacks += 1
rand(3)
end
puts "#{damage} points of damage done in #{num_attacks} attacks"
In the above example, attack uses the yield keyword to call the block passed
to it. When we call attack, then, the local variable num_attacks is still
in scope in the block we pass it (delimited here by do ... end), so we can
increment it. attack is able to pass values into the block, here
they are captured into the saying variable. The block also passes values
back to the method, which show up as the return value of yield.
The word lambda in ruby usually means the lambda keyword, which is used
to make blocks into freestanding, function like objects (which themselves are usually
referred to as lambdas, procs, or Procs).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
So I think what you're asking is whether you can pass a Proc in place of a Hash
for an argument to a method. And the answer is "it depends". If the method only
ever uses the #[] method, then yes:
class Character
attr_accessor :stats
def set_stats(stats)
#stats = stats
end
end
frank = Character.new("Victor Frankenstein")
frank.set_stats({ :str => 7, :dex => 14, :con => 9, :int => 19, :wis => 7, :cha => 11 })
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
However, it might use some other Hash specific methods, or call the same key multiple times,
which can produce weird results:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
In which case, you may be better off caching the requests in an intermediate hash. This is fairly easy,
since a Hash can have an initializer block. So if we change the above to:
monster.set_stats(Hash.new do |stats_hash, stat_name|
stats_hash[stat_name] = rand(20)
end)
monster.stats[:dex] #=> 3
monster.stats[:dex] #=> 3
The results are cached in the hash
To see more about Hash block initializers, see ri Hash::new:
-------------------------------------------------------------- Hash::new
Hash.new => hash
Hash.new(obj) => aHash
Hash.new {|hash, key| block } => aHash
------------------------------------------------------------------------
Returns a new, empty hash. If this hash is subsequently accessed
by a key that doesn't correspond to a hash entry, the value
returned depends on the style of new used to create the hash. In
the first form, the access returns nil. If obj is specified, this
single object will be used for all default values. If a block is
specified, it will be called with the hash object and the key, and
should return the default value. It is the block's responsibility
to store the value in the hash if required.
h = Hash.new("Go Fish")
h["a"] = 100
h["b"] = 200
h["a"] #=> 100
h["c"] #=> "Go Fish"
# The following alters the single default object
h["c"].upcase! #=> "GO FISH"
h["d"] #=> "GO FISH"
h.keys #=> ["a", "b"]
# While this creates a new default object each time
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
h["c"] #=> "Go Fish: c"
h["c"].upcase! #=> "GO FISH: C"
h["d"] #=> "Go Fish: d"
h.keys #=> ["c", "d"]