I am new to Stackoverflow and I am having some trouble on a coding challenge. I need to be able to output the president's age, name, citizenship, along with a catchphrase. I keep receiving the error,
stack level too deep
exercise.rb:12
Can anyone explain, what is wrong? What does the stack level too deep mean?
module Presidential
attr_accessor :name, :age, :citizenship
def initialize(name, age)
#name, #age, #citizenship = name, age, self.class.citizenship
end
end
class FrancePresident
include Presidential
def citizenship
"#{self.citizenship}, #{catchphrase}"
end
def name
"#{name}, #{catchphrase}"
end
def age
"#{age}, #{catchphrase}"
end
def catchphrase
"bien sur"
end
def self.citizenship
"La France"
end
end
Spec:
describe FrancePresident do
describe "catchphrase" do
it "sounds just right" do
expect( FrancePresident.citizenship ).to eq("La France")
sarcozy = FrancePresident.new("Nicolas Sarkozy", 59)
expect( sarcozy.citizenship ).to eq("La France, bien sur")
expect( sarcozy.age ).to eq("59, bien sur")
expect( sarcozy.name ).to eq("Nicolas Sarkozy, bien sur")
end
end
describe "inheritance" do
it "should not inherit from President" do
expect( FrancePresident.superclass.to_s ).not_to eq('President')
end
end
end
Basically Ruby keeps a stack of things waiting for something, and it is getting too large:
class FrancePresident
def citizenship
self.citizenship
end
end
sarkozy = FrancePresident.new
sarkozy.citizenship
When the citizenship method is called, Ruby executes it. The execution is: call the citizenship method. Ruby does this about 10000 times before giving up.
Maybe you can add line numbers to your question, this would make it easier to get the exact line nr. 12 from your error.. but what i can see so far is following:
def citizenship
"#{self.citizenship}, #{catchphrase}"
end
You are calling the method "citizenship" on your instance method "citizenship"..this will end in an stack level to deep, because you call the same method inside the method itself.. I can see that you have a class method "citizenship". You must edit your instance method citizenship like following:
def citizenship
"#{FrancePresident.citizenship}, #{catchphrase}"
end
or
def citizenship
"#{self.class.citizenship}, #{catchphrase}"
end
and your instance method "citizenship" will be save. Within the scope of your instance method "citizenship" the "self" is the instance and not the class. Outside of the instance "self" is the class.
if you fix this like I said you will get your next errors on the "name" and "age" instance method...because of the same raison..
I was getting the same error, the solution lies in NOT accessing the name and age variables from the Presidential module, but from the FrancePresident class itself.
class FrancePresident
attr_accessor :name, :age
def initialize(name, age)
#name = name
#age = age
end
Then you call the instance variable #
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
The program below is an attempt to take in an American president, and French President's age, and name. The catch is that the French president says "bein sur" afterward calling his name, age and citizenship (not my idea). I'm having trouble with the French president's catchphrase. Here's my code
class President
attr_accessor :name, :age
def initialize(name, age)
#name = name
#age = age
end
end
class FrancePresident < President
def self.citizenship
"La France"
end
def initialize(name, age)
super(name, age)
end
def catchphrase
"bien sur"
end
def name
"#{name}, #{catchphrase}"
end
def age
"#{age}, #{catchphrase}"
end
def citizenship
"#{self.class.citizenship}, #{catchphrase}"
end
end
class UnitedStatesPresident < President
def self.citizenship
"The Unites States of America"
end
end
I think I'm referring to the superclass incorrectly because I'm receiving the stack error below.
SystemStackError
stack level too deep
exercise.rb:29
I'm new to Ruby, so any insight will be helpful.
Your name function generates infinite recursion, because it calls itself:
def name
"#{name}, #{catchphrase}" # <-- here, name calls this very function again and again
end
Same goes for age. They should call the instance variables, #name and #age, respectively:
def name
"#{#name}, #{catchphrase}"
end
def age
"#{#age}, #{catchphrase}"
end
Edit
It's probably better still to use super instead of the instance variables, because it makes it clear that you are using the functionality from the base class and adding something to it (Thanks for the tip, tadman!):
def name
"#{super}, #{catchphrase}"
end
def age
"#{super}, #{catchphrase}"
end
Here's the result based on all of your comments. Thanks for all your help!
class President
attr_accessor :name, :age
def initialize(name, age)
#name = name
#age = age
end
end
class FrancePresident < President
def self.citizenship
"La France"
end
def catchphrase
"bien sur"
end
def name
"#{#name}, #{catchphrase}"
end
def age
"#{#age}, #{catchphrase}"
end
def citizenship
"#{self.class.citizenship}, #{catchphrase}"
end
end
class UnitedStatesPresident < President
def self.citizenship
"The Unites States of America"
end
end
Extremely basic, yet I can't figure it out! Noob issues - I have tried several different answers for this, and I am still getting argument errors. Can someone please help enlighten me on the correct answer?
Correct this code, so that the greet function returns the expected value.
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{name}"
end
end
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{#name}"
end
end
You need to access your instance variables by prefixing the variable name with #. Just the same way as when you assigned it.
name is not available in greet. You can either use #name, or add an accessor:
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{#name}"
end
end
or
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{name}"
end
end
I'm wondering if one could define a class in Ruby to have the following sort of usage:
Class Book
def Book
puts self.to_s
end
def initialize(name)
#name = name
end
def to_s
#name.to_s
end
end
Usage:
Book "To Kill a Mocking Bird" #=>To Kill a Mocking Bird
The idea that I want is for this to behave like the following
An instance of the method is created (as a short hand).
The method Book is immediately called after this and performs a block of code.
(The intent of having the method named the same as the class is to have the call back when it is used like a method.)
Is this possible in Ruby?
How about this?
class Book
attr_accessor :name
def initialize(name)
self.name = name
end
def to_s
name
end
end
def Book(name)
Book.new(name)
end
puts Book("To Kill a Mocking Bird")
As a minor point of interest, Ruby's Kernel module uses such a technique (written in C) to implement methods named Array, String, and so on:
Array(12) #=> [12]
String(12) #=> '12'
Integer('0x12') #=> 18
Something like this ?
class Book
def show_me
puts self.to_s
end
def initialize(name)
#name = name
show_me
end
def to_s
#name.to_s
end
end
show_me will be executed but once you create a new book, the book object will be returned at the end.
>> Book.new "To Kill a Mocking Bird"
To Kill a Mocking Bird
=> #<Book:0x74f63f0 #name="To Kill a Mocking Bird">
Is it possible not to assign context to lambda?
For example:
class Rule
def get_rule
return lambda {puts name}
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
Rule.new.get_rule.call() # should say "ruby" but say what object of class Rull, does not have variable name
# or self.instance_eval &Rule.new.get_rule
end
end
My target is -> stored procedure objects without contexts, and assign context before call in specific places. Is it possible?
A bit late to party, but here's an alternate way of doing this by explicitly passing the context to the rule.
class Rule
def get_rule
return lambda{|context| puts context.name}
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
Rule.new.get_rule.call(self)
end
end
Person.new.init_rule
#=> ruby
Yeah, but be careful with it, this one is really easy to abuse. I would personally be apprehensive of code like this.
class Rule
def get_rule
Proc.new { puts name }
end
end
class Person
attr_accessor :name
def init_rule
#name = "ruby"
instance_eval(&Rule.new.get_rule)
end
end
In the spirit of being really late to the party ;-)
I think the pattern that you are using here is the Strategy pattern.
This separates the concerns between the code that changes "rules" and
the part that is re-used "person". The other strength of this pattern is
that you can change the rules at run-time.
How it could look
class Person
attr_accessor :name
def initialize(&rules)
#name = "ruby"
instance_eval(&rules)
end
end
Person.new do
puts #name
end
=> ruby