Cant write string to attr_accessor in ruby - ruby

Please tell me what am I doing wrong here. I want to be able to capitalize any string that is being added later in the code through the method titles .
class Book
attr_accessor :book_title
def initialize
#book_title = String.new
end
def titles
book_title.capitalize
end
end
#book = Book.new
puts #book.titles = "steve jobs"
Because when I execute it, I get undefined method `titles=' for #<Book:0x007fbd25813d98 #book_title=""> (NoMethodError)

It's a little unclear as to what you're trying to accomplish. You're adding an attr_accessor for book_title, not for titles (which isn't a variable at all). You need to call
puts #book.book_titles = "Steve Jobs"
in order to set (and print) the title.
If you're trying to pass a title to titles and have that method capitalize the title and set #book_title to that, you need to declare it as an assignment method using =, pass in the parameter title, and actually set #book_title to that. Something like this
def titles= title
#book_title = title.capitalize
end
Currently your titles method only returns the capitalized local variable book_title, which doesn't exist (it needs the # to reference the instance variable).

It's saying "undefined method titles=" because you haven't defined a method named titles=. Instead, attr_accessor (one of Ruby's many misleading names) defines two methods, in your case named book_title and book_title=. Then you add titles. None of these is named titles= (the equal sign is significant).
Your titles method (by the way, you should figure out if it's singular or plural) is redundant with book_title, which will lead to confusion. As someone seeing this code for the first time -- or once again, after a break -- how am I to know which method to call?
You need to decide whether to capitalize the string on the way in (during the setter) or on the way out (during the getter). No matter what you do, I recommend that you stop using attr_accessor for a bit, and just explicitly define a getter and a setter (def title and def title=) until it's clear in your mind what they do. The attr_accessor macro is just a shorthand; you should learn longhand first.

Related

Accessing subclass variables in a superclass in Ruby

So, I've been studying Ruby for over two weeks now and I'm having some issues understanding the whole OOP thing.
In this lesson, in the exercises in the end, we are asked to create a superclass of vehicle and add non-specific behavior to it, and then create a MyCar and a MyTruck subclasses that inherit from it and also have a constant that separates them. So I did like so:
class Vehicle
attr_accessor :color, :curr_speed
attr_reader :year, :model
def initialize(y, c, m)
#year = y
#color = c
#model = m
end
#SOME OTHER METHODS
def to_s
"My #{VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
end
end
class MyCar < Vehicle
VEHICLE_TYPE = 'car'
end
class MyTruck < Vehicle
VEHICLE_TYPE = 'truck'
end
I also redefined the to_s method so that it would return a string that would say what kind of vehicle it is along with the other info. Now, the exercise does not ask us to do that -- in fact, they define a to_s method for MyCar and another to MyTruck that starts with "My car..." or "My truck..." but I feel like this goes against the DRY principle.
Thing is, it seems that Ruby does not accept a subclass variable passed in a superclass method. If I use #{VEHICLE_TYPE} it throws a uninitialized constant Vehicle::VEHICLE_TYPE (NameError), and if I use #{self.VEHICLE_TYPE} it throws a undefined method 'VEHICLE_TYPE' for Vehicle:Class (NoMethodError).
Am I correct in my assumption that Ruby does not accept a subclass variable in a superclass? And how could I go about to fix this issue in a similar fashion? Because I thought about simply adding another parameter to initizalize for type, store in a superclass instance variable and be done with it, but I don't think it would serve the purpose of the exercise.
And bear in mind that I'm a newbie!
Thanks in advance!
That's right. The superclass can't use the shortcut notation of VEHICLE_TYPE to access the constant you've defined.
When you want to access a constant outside of the class it is defined in, you can access it using a qualified name like:
Car::VEHICLE_TYPE
You were getting pretty close on your self.VEHICLE_TYPE attempt. But to provide a generic replacement for something like Car::, you'd need to use self.class::.
So your to_s method would look like:
def to_s
"My #{self.class::VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
end
This will work as long as each instantiable class defines that constant.

Ruby test about classes, and changing a variable, has me stuck. (I assume VERY simple)

I don't understand what to do, or how to solve this.
#This is the test
require 'book'
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
end
I've tried things like this:
class Book
def title string
#title = string.to_s.capitalize
end
end
#book = Book.new
puts #book.title("inferno")
This works, but fails the test since the test wants:
#book.title = "inferno"
But everything I've tried with the above line of code fails and I get an error message stating in some way:
"undefined method 'title=' for #<Book" etc
I don't understand how I can just try to change the value of a variable within a class with an "=" in the open like that. I really don't understand what's going on. I think I'm simply too uneducated at the moment to solve this.
I've seen someone do this online
class Book
def title
#title
end
def title=(title)
#title = titlieze(title)
end
end
But I have no idea what's going on. Why is there an argument following an "="?
Why is there a 'title' method, then a 'title='?
What is #title = titlieze(title) doing?
Looking at this code, I can't even reverse engineer it and understand what's happening.
If anyone can give me some insights into any of this, I would be extremely grateful.
It's so simple. title= looks like an assignment overloading but it's just a method. A "setter method" if you want. It's what happen when you use attr_writer.
attr_writer :title
is a method that define a title= method like this
def title=(value)
#title = value
end
The nice thing about TDD and BDD is that – at least assuming you are using a decent testing framework and your tests are well-written – your tests will tell you what to do next. You just need to listen to your tests.
At every step, just do the simplest thing possible to change the message.
So, let's do that. Let's simply run the test, and listen to what it tells us:
lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- book (LoadError)
Okay, it tells us that it can't find a file named book. What's the simplest thing possible to change the message? Well, just create a file named book.rb:
touch book.rb
Now run the test again:
book_spec.rb:5:in `': uninitialized constant Book (NameError)
Now, what is the simplest possible thing we can do to change the message? We initialize a constant named Book:
Book = Object.new
Here's the new error we get now:
undefined method `new' for #<Object:0x007fbe9c8e4a60>
Okay, so there is no method new? Then we create one. The simplest possible thing is to create a method that does nothing.
Book = Object.new
def Book.new; end
undefined method `title=' for nil:NilClass
Hmm … it tries to call a method that doesn't exist on the thing that we returned from new. Calling new and then calling methods on the thing that is returned from new, that sounds like the test wants us to create class, right? I mean, that's what you do with a class: you call new and then you continue working with the thing that you got back from new.
Okay, so let's try what happens when we change the whole thing to a class:
class Book; end
undefined method `title=' for #<Book:0x007f9aa99cd010>
That wasn't too surprising, we didn't actually fix the error. However! We didn't introduce any new errors either, but we simplified our code. That's still some kind of progress. Now, let's actually create our method:
class Book
def title=; end
end
wrong number of arguments (given 1, expected 0)
That's much better. We managed to change the message. It attempts to pass an argument, but the method doesn't take a parameter. Let's change that:
class Book
def title=(_) end
end
undefined method `title' for #<Book:0x007fd10102be60>
We have seen that before, haven't we? And we know how to fix it:
class Book
def title=(_) end
def title; end
end
expected: "Inferno"
got: nil
Alright, so the test expected our title method to return "Inferno", but it returned nil instead. Let's fix that:
class Book
def title=(_) end
def title; 'Inferno' end
end
Hooray! The test passes! Our Book class works perfectly! It passes the test, the code is short and easy to read, and it does only the absolute minimal thing it needs to, there is no extraneous fluff.
Okay, well, no, obviously, it doesn't. In a "real" project, the test wouldn't pass yet, there would be another test that tries the same thing but with a different book title. Imagine, the test looked more like this:
require 'book'
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
it 'should leave the first letter capitalized if it is already' do
#book.title = 'Hannibal'
expect(#book.title).to eq('Hannibal')
end
end
end
expected: "Hannibal"
got: "Inferno"
This is more helpful: the test tells us that it expected the second test to return 'Hannibal' instead of the same thing we returned for the first test.
Now, the simplest thing we can do would be to change the code to return 'Hannibal' instead, but that would be stupid, since it would break the test we have already gotten to pass. Instead, we need to think about what the test is telling us "between the lines". It doesn't want us to literally return 'Hannibal', it wants us to return back what it gave us in the title= method. So, we need to store that somehow.
class Book
def title=(title)
#title = title
end
def title
#title
end
end
expected: "Inferno"
got: "inferno"
Well, we managed to break our first test anyway … but I expected that! After all, I didn't actually do anything to make sure that the name is capitalized. But it is a "useful" breakage, because it actually tells us what we need to do: we need to capitalize the name somehow.
Now we need to make a choice: we can either store the name capitalized, then we can simply just return it later, or we can store it as it is and return it capitalized later. Both of these approaches have their advantages and disadvantages. The first approach has the advantage that the data is normalized, i.e. that it is stored in a standardized format. There is no way that we can accidentally return the wrong format since we didn't store it in the first place. But, we lose the exact format that the user entered, which means that we can never "go back". For example, if we later want to change the rules about capitalization, we can't do that easily, because we don't know what the original input was.
In a real system, there would probably be other tests or other concerns that would tell us which of the two is the right choice. Right now, I will simply demonstrate both.
class Book
def title=(title)
#title = title.capitalize
end
def title
#title
end
end
Hooray! The test passes! Now comes the next step when doing BDD: with the confidence that we have a passing testsuite that will catch any mistakes we make, we will refactor our code. In this particular case, there is really only one thing we can do: we can replace the trivial title method with an attribute reader method by using the Module#attr_reader method, which will generate a method for us that does pretty much the same thing as we did manually:
class Book
attr_reader :title
def title=(title)
#title = title.capitalize
end
end
The test still passes, our program is complete.
Here's the other alternative:
class Book
def title=(title)
#title = title
end
def title
#title.capitalize
end
end
Again, the test passes, and again, the last remaining step is to refactor. This time, it's the title= method that is so trivial that we can let Module#attr_writer generate it for us:
class Book
attr_writer :title
def title
#title.capitalize
end
end
If we really want to, we can factor out how we want our titles to be transformed into a separate helper method. This has two advantages: if we need to perform this transformation in different places in the code, the knowledge about how to do so is not duplicated in multiple places, it is instead centralized in one place. Note, however, that at the moment, this violates the YAGNI (You Ain't Gonna Need It) principle of not trying to predict the future. However, there is a second benefit, and that is that it allows us to attach a name to this transformation. So, we could do something like this:
class Book
attr_reader :title
def title=(title)
#title = titleize(title)
end
private def titleize(title)
title.capitalize
end
end
or
class Book
attr_writer :title
def title
titleize(#title)
end
private def titleize(title)
title.capitalize
end
end

Ruby: Automatically set instance variable as method argument?

Are there any plans to implement ruby behavior similar to the CoffeeScript feature of specifying an instance variable name in a method argument list?
Like
class User
def initialize(#name, age)
# #name is set implicitly, but #age isn't.
# the local variable "age" will be set, just like it currently works.
end
end
I'm aware of this question: in Ruby can I automatically populate instance variables somehow in the initialize method? , but all the solutions (including my own) don't seem to fit the ruby simplicity philosophy.
And, would there be any downsides for having this behavior?
UPDATE
One of the reasons for this is the DRY (don't repeat yourself) philosophy of the ruby community. I often find myself needing to repeat the name of an argument variable because I want it to be assigned to the instance variable of the same name.
def initialize(name)
# not DRY
#name = name
end
One downside I can think of is that it may look as though a method is doing nothing if it has no body. If you're scanning quickly, this may look like a no-op. But I think given time, we can adapt.
Another downside: if you're setting other instance variables in the body, and you try to be readable by putting all the assignments at the beginning, it can take more cognitive "power" to see that there assignments also happening in the argument list. But I don't think this is any harder than, say, seeing a constant or method call and having to jump to its definition.
# notice: instance var assignments are happening in 2 places!
def initialize(#name)
#errors = []
end
After some pondering, I wondered if it's possible to actually get the argument names from a ruby method. If so, I could use a special argument prefix like "iv_" to indicate which args should be set as instance variables.
And it is possible: How to get argument names using reflection.
Yes! So I can maybe write a module to handle this for me. Then I got stuck because if I call the module's helper method, it doesn't know the values of the arguments because they're local to the caller. Ah, but ruby has Binding objects.
Here's the module (ruby 1.9 only):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:#instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:#instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "##{ivar_name} = #{pname}", binding
end
end
nil
end
end
And here's the usage:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:#name, #name]
p [:#age, #age]
end
end
user = User.new("Methuselah", 969)
p user
Output:
[:iv_name, "Methuselah"]
[:age, 969]
[:#name, "Methuselah"]
[:#age, nil]
#<User:0x00000101089448 #name="Methuselah">
It doesn't let you have an empty method body, but it is DRY. I'm sure it can be enhanced further by merely specifying which methods should have this behavior (implemented via alias_method), rather than calling slurp_args in each method - the specification would have to be after all the methods are defined though.
Note that the module and helper method name could probably be improved. I just used the first thing that came to mind.
Well, actually...
class User
define_method(:initialize) { |#name| }
end
User.new(:name).instance_variable_get :#name
# => :name
Works in 1.8.7, but not in 1.9.3. Now, just where did I learn about this...
I think you answered your own question, it does not fit the ruby simplicity philosophy. It would add additional complexity for how parameters are handled in methods and moves the logic for managing variables up into the method parameters. I can see the argument that it makes the code less readable a toss up, but it does strike me as not very verbose.
Some scenarios the # param would have to contend with:
def initialize( first, last, #scope, #opts = {} )
def search( #query, condition )
def ratchet( #*arg )
Should all of these scenarios be valid? Just the initialize? The #*arg seems particularly dicey in my mind. All these rules and exclusions make the Ruby language more complicated. For the benefit of auto instance variables, I do not think it would be worth it.

Should a ruby method modify a class's instance method?

I am writing a class in Ruby where I have instance variables (i.e. #person_summary_info, #name, #dob, #favorite_food) for the class.
To parse a piece of text, I have a public method that I call from outside the class (let's call it interpret).
This method calls some private class methods such as get_name that use #person_summary_info to extract the respective piece of information (in this case, the name of the person). Should those private methods:
a) use the instance #person_summary_info, or get that information through a parameter passed to them (i.e. get_name vs get_name(person_summary_info))
b) modify the instance variable directly and return nothing, or modify nothing outside the scope of the function, and return the result (i.e. inside get_name, set #name = 'John', or return 'John')?
What is the best practice here?
Thanks!
I have included my best representation of your question in code at the bottom of my answer, but I'd like to present my solution as I understand your dilemma first...
Do this if your name attribute is meant to be publicly accessible:
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
self.name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.name
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.name
# => "Fred"
Do this if your name attribute should not be (easily) publicly accessible: (For what it's worth, pretty much anything can be accessed one way or another in Ruby. One of the many things that make it awesome!)
class Person
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
#name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.instance_variable_get("#name")
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.instance_variable_get("#name")
# => "Fred"
And, as mentioned above, here's my best translation of your question into code:
class Person
def initialize
#person_summary_info = { name: "foo" }
#name = "bar"
#dob = "baz"
#favorite_food = "beer"
end
def interpret(text_to_parse)
# Some kind of parsing?
get_name_1
# OR
get_name_2(#person_summary_info)
# OR
get_name_3
# OR
#name = get_name_4
end
private
def get_name_1
#person_summary_info[:name]
end
def get_name_2(person_summary_info)
person_summary_info[:name]
end
def get_name_3
#name = 'John'
end
def get_name_4
'John'
end
end
Hopefully, you can see why there's some confusion in the comments about what you are asking exactly. If nothing else, maybe seeing this will help you to form your question more clearly so we can help!
Finally, you should avoid writing your own getters/setters in Ruby unless you need to hook in some custom code to the getting/setting processes -- use the class-level attr_reader/attr_writer/attr_accessor macros to create them for you.
If interpret() is not meant to change the state of a particular instance of Person, then consider naming the method something like get_name_from_string(string) and possibly making it static, since it doesnt do anything to the state of the instance.
If you want interpret() to change the state of a particular instance of Person, then consider changing the name of the method, prefixing it with set and include the attribute name being set (set_name_from_string()). If several attributes are being set, then perhaps set_from_string() and include a code comment stating what instance variables are being modified. Internally the method could call get/set_name() as described below.
Typically, getter/setter methods are public and should be quite simple, doing what their name suggests:
- getName() returns the instance variable #name
- setName(name) sets or overwrites the instance variable #name with the value passed in and returns nothing
In Java, this is a type of POJO, specifically Java Beans (excluding the part about needing to be serializable) Its very common programming practice in several different languages to have public setter/getter methods for the instance variables and to also have a default constructor (one that takes no arguments) and another constructor allowing you to set the instance variables upon instantiation of the Object.
using #instance directly from another class is a good way how to get into troubles. Each class should have it's own variables and anything you would like to process or return back should be assigned/returned directly.. that means that way
#instance = my_class.get_name(person_summary_info)
and not
my_class.get_name
Just try to imagine how to test that code using #instance variables and chance to reuse that piece of code..
just my 2c

Why isn't self always needed in ruby / rails / activerecord?

In testing a getter/setter pair in a rails model, I've found a good example of behavior I've always thought was odd and inconsistent.
In this example I'm dealing with class Folder < ActiveRecord::Base.
Folder belongs_to :parent, :class_name => 'Folder'
On the getter method, if I use:
def parent_name
parent.name
end
...or...
def parent_name
self.parent.name
end
...the result is exactly the same, I get the name of the parent folder. However, in the getter method if I use...
def parent_name=(name)
parent = self.class.find_by_name(name)
end
... parent becomes nil, but if I use...
def parent_name=(name)
self.parent = self.class.find_by_name(name)
end
...then then it works.
So, my question is, why do you need to declare self.method sometimes and why can you just use a local variable?
It seems the need for / use of self in ActiveRecord is inconsistent, and I'd like to understand this better so I don't feel like I'm always guessing whether I need to declare self or not. When should you / should you not use self in ActiveRecord models?
This is because attributes/associations are actually methods(getters/setters) and not local variables. When you state "parent = value" Ruby assumes you want to assign the value to the local variable parent.
Somewhere up the stack there's a setter method "def parent=" and to call that you must use "self.parent = " to tell ruby that you actually want to call a setter and not just set a local variable.
When it comes to getters Ruby looks to see if there's a local variable first and if can't find it then it tries to find a method with the same name which is why your getter method works without "self".
In other words it's not the fault of Rails, but it's how Ruby works inherently.

Resources