I am new to Ruby and I got a question: is the nil checking necessary in following code? would you please explain a little bit? Thank you in advance!!if you think this is too easy to answer, would you please tell me the document( or link) that I need check to solve my doubts?
this is the original question:
within the say_hi method, the author checks if the instance variable #names is nil. Why is this check done? Is the check really needed in the MegaGreeter class as it is written? Why or Why not?
class MegaGreeter
attr_accessor :names
# Create the object
def initialize(names = "World")
#names = names
end
# Say hi to everybody
def say_hi
if #names.nil?
puts "..."
elsif #names.respond_to?("each")
# #names is a list of some kind, iterate!
#names.each do |name|
puts "Hello #{name}!"
end
else
puts "Hello #{#names}!"
end
end
# Say bye to everybody
def say_bye
if #names.nil?
puts "..."
elsif #names.respond_to?("join")
# Join the list elements with commas
puts "Goodbye #{#names.join(", ")}. Come back soon!"
else
puts "Goodbye #{#names}. Come back soon!"
end
end
end
if __FILE__ == $0
mg = MegaGreeter.new
mg.say_hi
mg.say_bye
# Change name to be "Zeke"
mg.names = "Zeke"
mg.say_hi
mg.say_bye
# Change the name to an array of names
mg.names = ["Albert", "Brenda", "Charles",
"Dave", "Engelbert"]
mg.say_hi
mg.say_bye
# Change to nil
mg.names = nil
mg.say_hi
mg.say_bye
end
The way the initializer is set up, #names should never be nil because of the default value of 'World' being assigned if nothing is provided.
The problem is the public attr_accessor in the MegaGreeter class, which you can read about this method here. It creates a setter method on the #names instance variable, meaning it can be changed to anything, including nil.
Related
When I run the command I get
all the song names and then the instances following it. How do I only
get the names?
class Song
##all = []
attr_accessor :name
def initialize(name)
#name = name
##all << self
end
def self.all
##all
end
def self.print_all_song_names
##all.each do |song|
puts song.name
end
end
end
hotline_bling = Song.new("Hotline Bling")
thriller = Song.new("Thriller")
ninety_nine_problems = Song.new("99 Problems")
thriller = Song.new("Thriller")
puts Song.print_all_song_names
Which outputs:
Hotline Bling
Thriller
99 Problems
Thriller
#<Song:0x00000000058ced30>
#<Song:0x00000000058cecb8>
#<Song:0x00000000058cebc8>
#<Song:0x00000000058ceb50>
The issue with your code is your calling puts here and there.
The code already calls puts in print_all_song_names, and after you call puts Song.print_all_song_names which roughly means call the method and print the value returned.
each returns a receiver, which means print_all_song_names returns the value of ##all class variable. Which gets printed again.
To fix it, just don’t call puts in the last line; Song.print_all_song_names already prints out everything needed.
Song.print_all_song_names # voilà
I am very new to Ruby. Started just few days back. I am learning from the website called The Odin Project and there they got this problem where I have to capitalize a word. I know how to capitalize but the method I am using doesn't seem to return the capitalized string.
I Tried the same code in Repl.it. There it seems to return what I expected, but I can't get the same result in my local machine. I cant understand the problem. Can someone explain it to me..
Here's the code :
class Book
attr_accessor :title
def title= str
str.capitalize!
end #title
end #book
#book = Book.new
#book.title="inferno"
For capitalize vs capitalize!, the latter modifies the string in place, and returns nil if no substitution was made. The former, however, returns a new string with the replacements made. However, there's a weird thing using a setter like this in ruby, that I've yet to fully understand: ruby returns the value you passed in to the setter, not the last line executed as normal
class YourBook
attr_accessor :title
def title= str
str.capitalize!
end #title
end #book
new_title = "inferno"
#book = YourBook.new
puts #book.title = new_title # => Inferno # side note, this is because you modified the original string as seen down below
puts #book.title # => # was never set
puts new_title # => Inferno # the string in the original variable was mutated, which is why the setter returned the "correct" version
class MyBook
attr_reader :title
def title=(str)
#title = str.capitalize
end
def set_title(val) # even with the odd return value from `title=`, I still recommend that instead of using `set_` methods
#title = val.capitalize
end
end
new_title = "inferno"
#book = MyBook.new
puts #book.title = new_title # => inferno # side note, not sure why it does this, but ruby returned the string you entered, not the string you set
puts #book.title # => Inferno # was set correctly
puts new_title # => inferno # the original string was not mutated
new_title = "inferno two"
puts #book.set_title(new_title) # => Inferno two # as expected
puts #book.title # => Inferno two # also as expected
puts new_title # => inferno two # again, didn't modify the original
Looking the code and trying to understand what you want, maybe I can clear your mind.
Your code is doing fine, the capitalize method is working properly. Like you said during the comments if you write puts inside the method it returns what you expect.
#mudasobwa it is changing the string to capitalize if i use a puts to
print the string inside the method it prints as expected. but i am not
getting the expected return.
Now checking the call of this method.
#book.title="inferno"
Here we can see "inferno" passing an argument for title method. Then this method is returning the value, but nothing is getting it.
If you wanna check what's being returned just use puts(#book.title = "inferno").
class Book
attr_accessor :title
def title= str
str.capitalize!
end #title
end #book
#book = Book.new
puts(#book.title="inferno")
This will depend on what exactly you want to achieve. I'm trying to guess you want to capitalize the whole word there you have to use upcase method.
EDIT
I have rewritten your code to reflect your TDD needs. I'm initializing the object the same way as your teaching TDD. You need only attr_accessor which generates both setters and getters for title.
class Book
attr_accessor :title
def name_capitalize
# only first letter capitalized
title.capitalize
end #title
end #book
#book = Book.new
#book.title="inferno" # stores the book title in the `#book.title`
# to print the book's name and first letter in capital
puts #book.name_capitalize # uses the stored `title` variable to return the capitalized name
I want to create a special settings class Settings. The class should be able to handle cases when a user types something like Settings.new.method_1.method_2.method_3 and it's translated to something like:
result = nil
if ConfigurationSettings['method_1'].present?
result = ConfigurationSettings['method_1']
if result['method_2'].present?
result = result['method_2']
...
end
return result
Of course, I'll make it more flexible later so it can have more than 2/3 "methods".
I guess this is the issue you are facing:
class Settings
def abc
puts "abc"
end
def xyz
puts "xyz"
end
end
s = Settings.new
s.abc
#abc
# => nil
s.xyz
#xyz
# => nil
s.abc.xyz
#abc
#NoMethodError: undefined method `xyz' for nil:NilClass
The issue here is s.abc is returning nil and xyz is called over nil. What you are trying to achieve is called Method Chaining. Now, xyz needs an Settings object. Simplest thing to do here is:
class Settings2
def abc
puts "abc"
self
end
def xyz
puts "xyz"
self
end
end
s2 = Settings2.new
s2.abc.xyz
#abc
#xyz
method_missing is available for your use and can be used to help you solve this problem. Coupling this with method chaining and you're good to go. For example:
class Settings
def method_missing(meth)
puts "Missing #{meth}"
self
end
def test
puts "Test"
self
end
end
a = Settings.new
a.test
a.test.b
a.b.test
The trouble with the other answers is all the methods return "self" so if you want to access a nested value...
final_value = Settings.new.method_1.method_2.method_3
You're just going to get the whole settings hash instead.
Try this instead...
class Settings
class SubSettings
def initialize(sub_setting)
#sub_setting = sub_setting
end
def method_missing(method, *arguments, &block)
if #sub_setting[method].is_a?(Hash)
SubSettings.new #sub_setting[method]
else
#sub_setting[method]
end
end
def answer
#sub_setting
end
end
def initialize
#settings = ConfigurationSettings
end
def method_missing(method, *arguments, &block)
SubSettings.new #settings[method]
end
end
ConfigurationSettings = {level1a: {level2a: {level3a: "hello", level3b: "goodbye"}, level2b: {level3b: "howdy"}}}
result = Settings.new.level1a.level2a.level3b
p result
=> "goodbye"
What this does is take the initial method and takes the associated sub-hash of the ConfigurationSettings hash and stored it into a new object of class SubSettings. It applies the next method and if the result is another sub-hash it iterates to create another SubSettings, etc. It only returns the actual result when it no longer sees hashes.
My problem is probably quite easy, but I couldn't find an answer anywhere.
When create a class, for example:
class Book
#author = "blabla"
#title = "blabla"
#number_of_pages"
I want to create a method to print out my variables. And here I'm getting a problem when I try:
def Print
puts #author, #title, #number_of_pages
end
I am getting nothing.
When I try:
def Print
puts "#author, #title, #number_of_pages"
end
I get straight: "#author, #title, #number_of_pages"
How can I make the Print method print out the variables' values?
You should move your variable initializations to initialize:
class Book
def initialize
#author = "blabla"
#title = "blabla"
#number_of_pages = 42 # You had a typo here...
end
end
The way you have it in your question, the variables are class instance variables (which you can Google if you're curious about, but it's not really relevant here).
Initialized as (normal) instance variables, your first version of Print() works if you're just looking to dump the state -- it prints each parameter on its own line.
To make your second version of Print() work, you need to wrap your variables in #{} to get them interpolated:
def print # It's better not to capitalize your method names
puts "#{#author}, #{#title}, #{#number_of_pages}"
end
In addition to the allready exellent answer of Darshan, here is the way you would do it optimally
class Book
attr_accessor :author, :title, :number_of_pages
#so that you can easily read and change the values afterward
def initialize author, title, number_of_pages = nil
#so that you don't really need to provide the number of pages
#author = author
#title = title
#number_of_pages = number_of_pages
end
def print
puts "#{#author}, #{#title}, #{#number_of_pages}"
end
end
my_book = Book.new("blabla", "blabla", 42)
my_book.title = "this is a better title"
my_book.print
#=>blabla, this is a better title, 42
I think Darshan Computing has already solved your problem very well. But here I would like to give you alternative ways of achieving that.
I assume that you'd like to print out all the instance variables you have in the class. The method instance_variables could return an array of all your instance_variables in symbols. And then you can iterate them do whatever you want. Please be careful: instance_variable_get is pretty convenient but not the best practice.
class Book
attr_reader :author, :title, :number_of_pages
def initialize(author, title, number_of_pages)
#author = author
#title = title
#number_of_pages = number_of_pages
end
def print_iv(&block)
self.instance_variables.each do |iv|
name = iv
value = send(iv.to_s.gsub(/^#/, ''))
# value = instance_variable_get(iv) # Not recommended, because instance_variable_get is really powerful, which doesn't actually need attr_reader
block.call(name, value) if block_given?
end
end
end
rb = Book.new("Dave Thomas", "Programming Ruby - The Pragmatic Programmers' Guide", 864)
# rb.instance_variables #=> [:#author, :#title, :#number_of_pages]
rb.print_iv do |name, value|
puts "#{name} = #{value}"
end
#=> #author = Dave Thomas
#=> #title = Programming Ruby - The Pragmatic Programmers' Guide
#=> #number_of_pages = 864
# You can also try instance_eval to run block in object context (current class set to that object)
# rb.instance_eval do
# puts author
# puts title
# puts number_of_pages
# end
How do I check if a method is defined on some class directly, not by inheritance or by inclusion/extension? I want something like 'foo?' in the following:
class A
def a; end
end
module B
def b; end
end
class C < A
include B
def c; end
end
C.foo?(:a) #=> false
C.foo?(:b) #=> false
C.foo?(:c) #=> true
Use this:
C.instance_methods(false).include?(:a)
C.instance_methods(false).include?(:b)
C.instance_methods(false).include?(:c)
The method instance_methods return an Array of methods that an instance of this class would have. Passing false as first parameter returns only methods of this class, not methods of super classes.
So C.instance_methods(false) returns the list of methods defined by C.
Then you just have to check if that method is in the returned Array (this is what the include? calls do).
See docs
For objects you can use Object.respond_to?.
Returns true if obj responds to the given method.
For classes take a look at Module.instance_methods
Returns an array containing the names of the public and protected instance methods in the receiver.
Not exactly an answer to the question, but if you're reading this question, you might be interested in this, which uses .instance_methods(false)
class Object
# This is more or less how Ruby does method lookup internally
def who_responds_to?(method, klass_ancestors = nil)
if klass_ancestors.nil?
return who_responds_to?(method, self.class.ancestors)
end
if klass_ancestors.empty?
return nil
end
if klass_ancestors[0].instance_methods(false).include?(method)
return klass_ancestors[0]
end
klass_ancestors.shift
who_responds_to?(method, klass_ancestors)
end
end
For example
class Person
end
module Drummer
def drum
end
end
module Snowboarder
def jump
end
end
module Engineer
def code
end
end
class Bob < Person
include Drummer
include Snowboarder
include Engineer
def name
end
end
puts "who responds to name"
puts bob.who_responds_to?(:name)
puts "\n"
puts "who responds to code"
puts bob.who_responds_to?(:code)
puts "\n"
puts "who responds to jump"
puts bob.who_responds_to?(:jump)
puts "\n"
puts "who responds to drum"
puts bob.who_responds_to?(:drum)
puts "\n"
puts "who responds to dance"
puts bob.who_responds_to?(:dance)
yields
who responds to name
Bob
who responds to code
Engineer
who responds to jump
Snowboarder
who responds to drum
Drummer
who responds to dance
[this line intentionally blank because return value is nil]