Reputation: 102
I am getting this error "rspec undefined method `title=' when I run bundle exec rspec. Keep note that in the spec file its called "title" not "title=" ive searched the entire spec file to see if i could find a typo but with no luck. this is the error it gives me:
Book
title
should capitalize the first letter (FAILED - 1)
Failures:
1) Book title should capitalize the first letter
Failure/Error: @book.title = "inferno"
NoMethodError:
undefined method `title=' for #<Book:0x007fec2b140450>
and this is the part on the spec file where it stops at:
require '08_book_titles'
describe Book do
before do
@book = Book.new
end
describe "title" do
it "should capitalize the first letter" do
@book.title = "inferno"
expect(@book.title).to eq("Inferno")
end
to my understanding after trying things out, it is confusing the title in "@book.title = "inferno"" to be the "title=" (i changed the title to another word and it puts out that word with the equals at the end of it). I am unsure as to why it adds an equal to the end of of the method.
(i commented out all my own code to make sure that the error is coming from the spec file)
Upvotes: 0
Views: 1128
Reputation: 33420
tl;dr:
In the "should capitalize the first letter"
example, you set the book's title, but the class doesn't implement that method.
NoMethodError: undefined method `title=' for #<Book:0x007fec2b140450>
Is self explanatory, but also might be confusing. Any NoMethodError
means the method that's trying to be accessed on the receiver hasn't be defined.
Think in a very small class, Car, with one instance method to return the total of wheels:
class Car
def initialize
@wheels = '4 big wheels'
end
def wheels
@wheels
end
end
yaris = Car.new
p yaris.wheels # "4 big wheels"
p yaris.doors # `<main>': undefined method `doors' for #<Car:0x0000558d18cfbd68 (NoMethodError)
Accessing the wheels method returns what's expected, but trying to access the doors method throws a NoMethodError. There's no method doors defined in the Car class.
That's for both method in the example, known as "getter" methods, they return values you might need in some point on the class implementation. But as there are "getter" methods, also are "setter" methods, they defined with the =
symbol.
In the example class, we define within the initialize method, the @wheels instance variable, and then through the wheels method, we access them. But what about if you want to re-define the wheels - perhaps you crashed the car and now it has just 3 wheels, so, you need a method to set the value of @wheels.
Let's try setting up them:
yaris = Car.new
p yaris.wheels # "4 big wheels"
yaris.wheels = '3 working wheels' # undefined method `wheels=' for #<Car:0x000055755ad933c8 @wheels="4 big wheels"> (NoMethodError)
Now you can't, as there's no wheels=
method, you can't change the value of @wheels that way. So, is isn't, define it:
...
def wheels=(value)
@wheels = value
end
end
yaris = Car.new
p yaris.wheels # "4 working wheels"
yaris.wheels = '3 working wheels'
p yaris.wheels # "3 working wheels"
That's it, you can get and set the wheels from any Car instance now.
Most of the time, you don't see methods like the wheels=
one. As Ruby has the ability to let you define attribute accessors for your classes. You could define your class with an attr_reader :wheels, which would give you the value of @wheels:
class Car
attr_reader :wheels
def initialize
@wheels = '4 big wheels'
end
end
yaris = Car.new
p yaris.wheels # "4 working wheels"
And it works the same way, but, again, you can not define the value of @wheels, as you have just a read attribute, if you want to, then you could add the attr_reader's brother, an attr_writer:
class Car
attr_reader :wheels
attr_writer :wheels
def initialize
@wheels = '4 big wheels'
end
end
Now it's done, you can read and set the @wheels value, but why bothering on writing both reader and writer attributes one by one. You can use the attr_accessor, which implements both wheels
and wheels=
method on your class, on the specific attribute you pass as argument.
class Car
attr_accessor :wheels
...
end
p Car.instance_methods(false) # [:wheels=, :wheels]
Why such a boring answer?, because if you're able to know where do these methods come from, you'll be able to know why they're or they're not where they're supposed to be. No matter what framework, DSL, any other you're using, it's Ruby.
Upvotes: 4