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
Related
I am studying the adapter pattern implementation in ruby. I want to access an instance variable within the adapter module definition. Take a look at the following code:
module Adapter
module Dog
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: woof!"
end
end
module Cat
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: meow!"
end
end
end
class Animal
attr_accessor :name
def initialize(name)
#name = name
end
def speak
self.adapter.speak
end
def adapter
return #adapter if #adapter
self.adapter = :dog
#adapter
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
To test it out I did the following:
animal = Animal.new("catdog")
animal.adapter = :cat
animal.speak
I want it to return the following:
catdog says: meow!
Instead it says:
Adapter::Cat says: meow!
Any tips on how I can get access to the Animal#name instance method from the adapter module? I think the issue is that my adapter methods are class-level methods.
Thanks!
You need to use your Module as a mixin and provide a way to keep track of which module is active, the methods don't seem to be overwritten by reincluding or reextending so I took the extend and remove methods I found here.
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
module Cat
def speak
puts "#{name} says: meow!"
end
end
def extend mod
#ancestors ||= {}
return if #ancestors[mod]
mod_clone = mod.clone
#ancestors[mod] = mod_clone
super mod_clone
end
def remove mod
mod_clone = #ancestors[mod]
mod_clone.instance_methods.each {|m| mod_clone.module_eval {remove_method m } }
#ancestors[mod] = nil
end
end
class Animal
include Adapter
attr_accessor :name, :adapter
def initialize(name)
#name = name
#adapter = Adapter::Dog
extend Adapter::Dog
end
def adapter=(adapter)
remove #adapter
extend Adapter::const_get(adapter.capitalize)
#adapter = Adapter.const_get(adapter.capitalize)
end
end
animal = Animal.new("catdog")
animal.speak # catdog says: woof!
animal.adapter = :cat
animal.speak # catdog says: meow!
animal.adapter = :dog
animal.speak # catdog says: woof!
This is because name inside of the module context refers to something entirely different than the name you're expecting. The Animal class and the Cat module do not share data, they have no relationship. Coincidentally you're calling Module#name which happens to return Adapter::Cat as that's the name of the module.
In order to get around this you need to do one of two things. Either make your module a mix-in (remove self, then include it as necessary) or share the necessary data by passing it in as an argument to speak.
The first method looks like this:
module Adapter
module Dog
def self.speak(name)
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def speak
self.adapter.speak(#name)
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
That doesn't seem as simple as it could be as they basically live in two different worlds. A more Ruby-esque way is this:
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
extend(#adapter)
end
end
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
Not sure why this is giving me an error of
"`greet': undefined local variable or method `name' for # (NameError) from `'
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{name}"
return other_name
end
end
The problem is that name is a local variable only available in the constructor. To reference it as an instance variable in the greet method, use #{#name} instead of #{name}.
Please explain me why i should define attr_list before attr? I can not understand why i should do that?
class Question
def self.attr_list
[:id, :name]
end
attr *self.attr_list
end
class Question
attr *self.attr_list
def self.attr_list
[:id, :name]
end
end
NoMethodError: undefined method `attr_list' for Question:Class
Unlike a def, a class is executed line by line immediately after you hit return to run your program:
class Dog
x = 10
puts x
end
--output:--
10
...
class Dog
puts x
x=10
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `x'
In this line:
...
class Dog
def self.greet
puts 'hello'
end
greet
end
--output:--
hello
...
class Dog
greet
def self.greet
puts 'hello'
end
end
--output:--
1.rb:2:in `<class:Dog>': undefined local variable or method `greet'
Similarly, in this line:
attr *self.attr_list
you call self.attr_list(), yet the def comes after that line, so the method doesn't exist yet.
With a def, you can write this:
def do_math()
10/0
end
and you won't get an error until you call the method.
But by the time you create an instance of a class, all the code inside the class has already executed, creating the methods and constants that are defined inside the class.
By the way, you don't ever need to use attr because ruby 1.8.7+ has attr_accessor(reader and writer), attr_reader, and attr_writer.
I am trying to define a class with methods, and a class lacking those methods, and then allowing an object of the latter class to 'learn' the methods from an instance of the former class.
This is my attempt (Ruby 1.9.2) - it breaks (at the line commented "BREAKS!")when I try to change the value of 'self' in the lambda binding.
If you can work out how to solve this - I'd be fascinated to find out.
class Skill
attr_accessor :name
attr_accessor :technique
def initialize(name, &technique_proc)
#name = name
#technique = lambda(&proc)
end
end
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def method_missing(m, *args)
"#{#name} the #{self.class}: I don't know how to #{m}"
end
def learn_skill(skill)
puts "#{#name} the #{self.class} is learning skill: #{skill.name}"
actual_self = self
eval "self = #{actual_self}", skill.technique.binding ####### BREAKS!
define_singleton_method skill.name.to_sym, skill.technique
end
def teach_skill(skill_name)
skill = nil
if self.respond_to?(skill_name)
puts "#{#name} the #{self.class} is teaching skill: #{skill_name}"
skill_method = self.method(skill_name.to_sym)
skill_proc = skill_method.to_proc
skill_lambda = lambda(&skill_proc)
skill = Skill.new(skill_name, &skill_lambda)
end
skill
end
end
class Teacher < Person
def speak(sentence)
"#{#name} the #{self.class} is now saying \"#{sentence}\"!"
end
def jump(number_of_feet)
"#{name} the #{self.class} is now jumping #{number_of_feet} high!"
end
end
miss_mollyflop = Teacher.new("Miss Mollyflop")
little_billey = Person.new("Little Billy")
puts miss_mollyflop.speak("Good morning, children!")
puts little_billey.speak("Good morning, Miss Mollyflop!")
speak_skill = miss_mollyflop.teach_skill("speak")
little_billey.learn_skill(speak_skill)
puts little_billey.speak("Good morning, Miss Mollyflop!")
The output of this is:
Miss Mollyflop the Teacher is now saying "Good morning, children!"!
Little Billy the Person: I don't know how to speak
Miss Mollyflop the Teacher is teaching skill: speak
Little Billy the Person is learning skill: speak
test.rb:27:in `eval': (eval):1: Can't change the value of self (SyntaxError)
self = #<Person:0x1482270>
^
(eval):1: syntax error, unexpected $end
self = #<Person:0x1482270>
^
from test.rb:27:in `learn_skill'
from test.rb:64:in `<main>'
If you want to copy across methods from one class to another it's possible, but if the method modifies state it will modify state on the original object not on the object the method is subsequently bound to (this is because the method isn't really copied across instead a Proc wrapper of the method is bound to the new object as a method):
a = Object.new
def a.hello
puts "hello world from a"
end
b = Object.new
b.define_singleton_method(:hello, &a.method(:hello))
b.hello #=> "hello world from a"
Using Object2module: gem install object2module
require 'object2module'
o = Object.new
def o.speak
puts "hello from o"
end
m = Object.new
m.gen_extend o
m.speak #=> "hello from o"