Running this code ...
class List
attr_reader :all_tasks
def initialize
#all_tasks = []
end
def add(task)
all_tasks.push(task)
end
def show
all_tasks
end
end
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
if __FILE__ == $PROGRAM_NAME
my_list = List.new
my_list.add(Task.new("FIRST"))
puts my_list.all_tasks
my_list.add(Task.new("SECOND"))
my_list.add(Task.new("THIRD"))
puts "Next line is my_list.show execution:"
puts my_list.show
end
... I get the following Terminal output:
#<Task:0x007fb24b08fed0>
Next line is my_list.show execution:
#<Task:0x007fb24b08fed0>
#<Task:0x007fb24b08fdb8>
#<Task:0x007fb24b08fd68>
My question is, why am I not seeing the values of the array stored in my_list? (If I understand programming vernacular correctly, the output of Terminal is the object itself, not the values stored in the object. Am I understanding that right?)
#<Task:0x007fb24b08fed0>
means an instance of class Task, or in other words, a Task object.
It has id 0x007fb24b08fed0.
This is the standard Ruby representation for objects that don't answer to to_s method.
You could just define Task#to_s, which is called by puts :
class Task
def to_s
#description
end
end
The output becomes :
FIRST
Next line is my_list.show execution:
FIRST
SECOND
THIRD
Note : If you call Task#inspect, you see the class, the object id and the defined instance variables.
puts task.inspect
#<Task:0x00000002657af0 #description="FIRST">
It is doing exactly what you are telling it to do, which is show. Your show method returns the array of tasks, hence an array of objects. Modify your show method as such
def show
all_tasks.each do |task|
puts task.description
end
end
This will return each task description.
Just change the last line from puts my_list.show to my_list.show, as the puts is already in the show method.
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'm not able to get the instance values of parent class to the child class, My code is like this.
class TimeLine
attr_accessor :tweets
def initialize(tweets=[])
#tweets = tweets
end
def print
puts tweets.join("\n")
end
end
class AuthenticateTimeLine < TimeLine
def print
authenticate!
super
end
def authenticate!
puts "authenticated!"
end
end
TimeLine.new([1,2,3,4,5])
authenticate_timeline = AuthenticateTimeLine.new
authenticate_timeline.print
When I call super on the child class, I'm getting empty array.
It's because you initialize it with empty array, you don't pass any argument to AuthenticateTimeLine.new, so default [] is taken (compare your TimeLine#initialize method). If you passed your array as argument, it would work:
authenticate_timeline = AuthenticatateTimeLine.new([1,2,3,4,5])
authenticate_timeline.print
# 'Works' now!
I have a class that stores blocks, and I would like to run a block later. How can I run it?
class StoreBlocks
##blockarray = []
def initialize
#storedblock = yield
##blockarray << self
end
attr_reader :storedblock
def self.getblock
##blockarray
end
end
StoreBlocks.new do
# cool codey stuff
end
# do other things
How would I run StoreBlocks.getblock[0].storedblock?
= yield would assign the return value of passed block, not the block itself. You would need to change your class to something like this:
class StoreBlocks
attr_accessor :storedblock
##blockarray
def initialize &block
#storedblock = block
##blockarray << self
end
def self.getblock
##blockarray
end
end
& means that block passed to initialize will be converted to Proc and assigned to argument named block, and than you can store it in instance variable. You could than call it like:
StoreBlocks.getblock[0].storedblock.call()
yield will imediately call the block passed to initalize. This means that #storeblock will contain the result of calling yield not the block itself. FYI, you are not putting #storedblock into ##blockarray
If you want to store a block you can do something like this -
class BlockStore
def initialize
#blocks ||= []
end
def add_block(&block)
#blocks << block
end
def run_all_blocks
#blocks.each do |block|
block.call
end
end
end
block_store = BlockStore.new
block_store.add_block do
puts 'Hello!'
end
block_store.add_block do
puts 'World!'
end
puts 'About to run all blocks'
block_store.run_all_blocks
This gives you an output like the below. I've output 'About to run all blocks' before running the blocks to show that they are stored and then run later -
➜ ruby blocks.rb
About to run all blocks
Hello!
World!
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]
As a programming exercise, I've written a Ruby snippet that creates a class, instantiates two objects from that class, monkeypatches one object, and relies on method_missing to monkeypatch the other one.
Here's the deal. This works as intended:
class Monkey
def chatter
puts "I am a chattering monkey!"
end
def method_missing(m)
puts "No #{m}, so I'll make one..."
def screech
puts "This is the new screech."
end
end
end
m1 = Monkey.new
m2 = Monkey.new
m1.chatter
m2.chatter
def m1.screech
puts "Aaaaaargh!"
end
m1.screech
m2.screech
m2.screech
m1.screech
m2.screech
You'll notice that I have a parameter for method_missing. I did this because I was hoping to use define_method to dynamically create missing methods with the appropriate name. However, it doesn't work. In fact, even using define_method with a static name like so:
def method_missing(m)
puts "No #{m}, so I'll make one..."
define_method(:screech) do
puts "This is the new screech."
end
end
Ends with the following result:
ArgumentError: wrong number of arguments (2 for 1)
method method_missing in untitled document at line 9
method method_missing in untitled document at line 9
at top level in untitled document at line 26
Program exited.
What makes the error message more bewildering is that I only have one argument for method_missing...
define_method is a (private) method of the object Class. You are calling it from an instance. There is no instance method called define_method, so it recurses to your method_missing, this time with :define_method (the name of the missing method), and :screech (the sole argument you passed to define_method).
Try this instead (to define the new method on all Monkey objects):
def method_missing(m)
puts "No #{m}, so I'll make one..."
self.class.send(:define_method, :screech) do
puts "This is the new screech."
end
end
Or this (to define it only on the object it is called upon, using the object's "eigenclass"):
def method_missing(m)
puts "No #{m}, so I'll make one..."
class << self
define_method(:screech) do
puts "This is the new screech."
end
end
end
self.class.define_method(:screech) doesn't work,because define_method is private method
you can do that
class << self
public :define_method
end
def method_missing(m)
puts "No #{m}, so I'll make one..."
Monkey.define_method(:screech) do
puts "This is the new screech."
end
def method_missing(m)
self.class.class_exec do
define_method(:screech) {puts "This is the new screech."}
end
end
screech method will be available for all Monkey objects.