I am taking ruby-kickstart (Josh Cheek) challenges and even though I managed to pass all the test there is one thing I cannot understand.
In the exercise you are being asked to override the << method for an instance variable. Specifically here is what you have to do:
In Ruby on Rails, when a person goes to a URL on your site, your application looks at the url, and maps it to a controller method to handle the request
My boss wanted to be able to specify what CSS class the body of the HTML output should have, based on which controller method was being accessed. It fell to me to provide a method, which, when invoked, would return a String that could handle the request There are a few nuances, though. The String you return must be retained during the object's entire life The method must be able to be called multiple times The String you return should know how to add new classes: each class is separated by a space The only method you need to worry about being invoked is the << method. (plus a few other irrelevant things) EXAMPLE:
controller = ApplicationController.new
controller.body_class
#=> ""
controller.body_class << 'admin'
controller.body_class
#=> "admin"
controller.body_class << 'category'
controller.body_class
#=> "admin category"
controller.body_class << 'page' << 'order'
controller.body_class
#=> "admin category page order"
My working solution:
class ApplicationController
def initialize
@body_class = ""
end
def body_class
def @body_class.<<(str)
puts "self is:"+self
return self if self=~/\b#{Regexp.escape(str)}\b/
if self==""
self.concat(str)
else
self.concat(" ")
self.concat(str)
end
end
return @body_class
end
end
Everything works perfectly fine. But an earlier solution I gave (and it didn't work) was the following
class ApplicationController
attr_accessor :body_class
def initialize
@body_class = ""
end
def @body_class.<<(str)
puts "self is:"+self
return self if self=~/\b#{Regexp.escape(str)}\b/
if self==""
self.concat(str)
else
self.concat(" ")
self.concat(str)
end
end
def body_class #Even this for the way I work it out on my mind is unnecessary
return @body_class
end
end
When someone runs on the second not-working sollution the following
obj = ApplicationController.new
obj.body_class << "Hi"
The << method is not being overridden by the object's singleton. I do not understand why I have to wrap the singleton methods inside the body_class method. (Mind that in the second solution there is an attr_accessor.
Could anyone enlighten me please! Thanks!
To access the correct instance variable. When you attempt to override it outside of method, you're in the class context. This code runs at class load time. No instances have been created yet. (And even if instances did exist at this point,
@body_class
instance variable belongs to classApplicationController
, which is itself an instance of classClass
).You need instance context.
Also I am pretty sure that this problem can be solved without any method patching voodoo. Just provide a different object (conforming to the same API. This is called "duck typing").
Didn't test this code, naturally.