I am quite new to Ruby and always assumed these two notations were an identical way to interpolate instance variables until I noticed the difference in the example code below for the 'vendor' parameter.
class Component
def initialize(name, version)
#vendor = "vendor"
#name = name
#version = version
puts "#{#vendor}-##{name}-##{version}"
end
end
ConfiguredComponent.new("param1", "param2")
=> this works
class Component
def initialize(name, version)
#vendor = "vendor"
#name = name
#version = version
puts "##{vendor}-##{name}-##{version}"
end
end
ConfiguredComponent.new("param1", "param2")
=> using ##{vendor} notation does't work => :in 'initialize': undefined local variable or method `vendor' for # (NameError)
class Component
def initialize(name, version)
#vendor = "vendor"
#name = name
#version = version
puts "#{#vendor}-#{#name}-#{#version}"
end
end
Component.new("param1", "param2")
=> this also works
It's the #{(expression)} that's significant.
If the expression is #{name} then that's substituting the variable name which in all your examples comes from the parameters input into the method.
If the expression is #{#name} then that's substituting the variable #name which is defined in the fourth line of your methods.
##{name} is not a special construct. It's just the string # followed by the contents of the variable name.
The reason it didn't work in your second example is that you simply haven't defined a variable vendor.
Here are all the examples I could think of :
#a = "hello"
strings =[
"#a world",
"#{a} world",
"#a world",
"#{#a} world",
"##a world",
"##aworld", # ! Will not output anything (no space after "#a", and #aworld isn't defined ) !
"##{#a} world"
]
puts strings
It outputs :
#a world
#{a} world
#a world
hello world
hello world
#hello world
But :
puts "##{a} world"
throws an exception :
undefined local variable or method `a' for main:Object (NameError)
a needs to be a defined variable or a method :
class Test
attr_accessor :a
def greetings
puts "#{a} world"
end
end
t = Test.new
t.a = "hello"
t.greetings # hello world
Related
I just started coding bootcamp and I am getting this error in my lab. I have tried setting the value with "" but to no avail I am still getting this error
"undefined method `breed=' for # (NoMethodError)
So after defining correctly with "end" I am still getting this error.
I currently have:
class Dog
def name=(fido)
#name= fido
end
def name
#name
end
def breed=(beagle)
#breed= beagle
end
def breed
#breed
end
end
fido = Dog.new
fido.name = fido
fido.breed = beagle
Some explanations. With
fido = Dog.new
fido.name = fido
puts "fido.name=#{fido.name} fido.name.class=#{fido.name.class}"
fido.breed = self.beagle
you are using the local variable fido just created, and sending the method beagle to self, the default receiver when there is no explicit receiver, which in this case (outside any class) is the special object main provided by the Ruby interpreter :
$ ruby -w fido_op.rb
fido.name=#<Dog:0x007ffdc2a5d620> fido.name.class=Dog
fido_op.rb:22:in `<main>': undefined method `beagle' for main:Object (NoMethodError)
The class Dog could be simplified :
class Dog
attr_reader :name
attr_accessor :breed
def initialize(name)
#name = name
end
end
fido = Dog.new('fido')
puts "fido.name=#{fido.name} fido.name.class=#{fido.name.class}"
fido.breed = 'beagle'
puts "fido.breed=#{fido.breed}"
Execution :
$ ruby -w fido.rb
fido.name=fido fido.name.class=String
fido.breed=beagle
In Ruby, why can't I set the value of variables directly within a module's self.included method?
For example, the following code outputs NilClass (indicating that the #sound variable has NOT been set):
module Animal
def self.included(klass)
attr_accessor :sound
#sound = "Woof!" # <-- Variable assignment
end
def speak
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => NilClass
However, if I set the value of #sound within the module's speak method (instead of inside of self.included), then the variable is set correctly. For example, the following code outputs String:
module Animal
def self.included(klass)
attr_accessor :sound
end
def speak
#sound = "Woof!" # <-- Variable assignment
puts #sound.class
end
end
class Dog
include Animal
end
dog = Dog.new
dog.speak # => String
I would have expected both code samples above to output String.
In first example the receiver in self.included block is Dog class, not it's instance, meaning you are defining class instance variable #sound, not instance variable.
You can check it by running
Dog.instance_variable_get(:#sound) # with first example
speak method returns NilClass because dog does not have #sound instance variable defined.
In second example you are defining an instance variable #sound, thus it works as you expect.
How do I pass the parameter name in the following case..the name is being is evaluated before being passed to class_eval
class Foo
end
Foo.class_eval %Q{
def hello(name)
p "hello #{name}"
end
}
Sorry about not giving the entire scenario...
I just wanted to add a instance method dynamically to a class and that method should be able to take arguments...
the above code would not compile complaining that the name is not defined as local variable when executing in irb..
Thanks
The other answers are the "right" answer, but you could also just skip interpolating inside the p call:
Foo.class_eval %Q{
def hello(name)
p "hello \#{name}"
end
}
I thought you wanted to change the actual parameter name (possibly useful for completion or when using Pry on dynamic methods), here assuming it's in a global, but could also be passed into a method doing the class_eval:
Foo.class_eval %Q{
def hello(#{$argname})
p "hello \#{$argname}"
end
}
Really simple:
Foo.class_eval do
def hello(name)
p "hello #{name}"
end
end
Try passing a block to class_eval instead of an array (from this link):
class Foo
end
Foo.class_eval {
def hello(name)
p "hello #{name}"
end
}
You then can call the instance method hello in the usual fashion:
boo = Foo.new
boo.hello("you")
which produces:
>> boo.hello("you")
"hello you"
=> nil
class Foo
end
Foo.class_eval do
define_method :hello do |name|
p "hello #{name}"
end
end
Foo.new.hello("coool") # => "hello coool"
So suppose I have this (not working):
class User
description = "I am User class variable"
def print
puts description
end
end
So, how should I use the var description, how to pass this into a method as a default parameter, or used in the method directly? Thanks..
In your case, the description is only local variable. You can change this scope using special characters #, ##, $:
a = 5
defined? a
=> "local-variable"
#a = 5
defined? #a
=> "instance-variable"
##a = 5
defined? ##a
=> "class variable"
$a = 5
defined? $a
=> "global-variable"
For your purpose, I think it might be using by this way
class User
def initialize(description)
#description = description
end
def print
puts #description
end
end
obj = User.new("I am User")
obj.print
# => I am User
You can access the class-scope using define_method.
class User
description = "I am User class variable"
define_method :print do
puts description
end
end
> User.new.print
I am User class variable
=> nil
I don't think it's good idea, though :)
To define a class variable, use an ##:
class User
##description = "I am a User class variable"
def print
puts ##description
end
end
Instance variables must be prefixed with an # and are not accessible from outside.
Class variables must be prefixed with an ##.
The first time you use this variable anywhere in code, it will be initialized.
If you want to access the value from outside:
attr_reader :description
http://rubylearning.com/satishtalim/tutorial.html
Say that we have this class:
class Hello
def hello
#hello
end
def hello=(hello)
#hello = hello
end
def other_method
hello = 'hi!'
...
end
end
In this case,
h = Hello.new
h.other_method
puts h.hello
will write 'hi!', due to the interpretation of hello = as self.hello =. Is there a way to avoid this behaviour, giving to the hello = declaration a method-local scope (as, for example , var hello = would do with javascript code)?
EDIT I'm sorry, I was sure I had a problem for this reason! But now I have two other questions!
why hello = is not interpreted as self.hello =??? it is declared as an instance method...
why, even if I write
...
def other_method
self.hello = 'hi!'
end
end
h = Hello.new
h.other_method
puts h.hello
it returns nil??? now it should be an explicit assignment!
Thank you for the patience, I'm a bit confused! :-/
Actually this will not print hi! because hello = 'hi!' in other_method assigns to a local variable and is not interpreted as self.hello=.
If this is not the behaviour that you're seeing then there must be something else specific to your code that you haven't included in the question.
Answers to the follow up questions:
Within the body of a method identifier = value assigns a local variable (no method lookup is performed (so it's doesn't matter that there's a hello= method. You need the explicit self. to invoke the method.
When you write puts h.hello it prints 'hi!' which is the return value of h.hello but then returns nil which is the return value of puts.
It works for me:
$ irb
irb(main):001:0> class Hello
irb(main):002:1> def hello
irb(main):003:2> #hello
irb(main):004:2> end
irb(main):005:1> def hello=(hello)
irb(main):006:2> #hello = hello
irb(main):007:2> end
irb(main):008:1> def other_method
irb(main):009:2> self.hello = 'hi!'
irb(main):010:2> end
irb(main):011:1> end
=> nil
irb(main):012:0> h = Hello.new
=> #<Hello:0xb746b7cc>
irb(main):013:0> h.other_method
=> "hi!"
irb(main):014:0> puts h.hello
hi!
=> nil