I have an object like this
class SomeObject
def initialize &block
# do something
end
end
class AnotherObject < SomeObject
def initalize &block
super
# do something with block
end
end
When super is called in AnotherObject, the block seems to be passed to SomeObject. Is this the right behaviour and is there away round it?
According to rubyspec this is the correct behaviour, even if you pass explicit arguments to super (i.e. super('foo'))
If you don't want to pass that block, you could just pass a block that does nothing, although this isn't quite the same thing (e.g. if the method changes its behaviour based on block_given?)
It appears that
super(&nil)
is a way to pass no block at all to super, although I couldn't find this in ruby spec.
Related
I came across this solution for a Proxy class in the Ruby koans:
class Proxy
attr_accessor :messages
def initialize(target_object)
#object = target_object
#messages = []
end
def method_missing(method_name, *args, &block)
#messages << method_name
#object.send(method_name, *args, &block)
end
end
I can create an object from this proxy class by passing another class as an argument. For instance, the following code will result in "Do something", without having to type thing.method_missing(:do_thing):
class Thing
def do_thing
puts "Doing something."
end
end
thing = Proxy.new(Thing.new)
thing.do_thing
Why is the code in method_missing executed even without having to call said method?
There are methods that are called implicitly (i.e., called even when you don't write it in the code) when a certain event happens or a certain method is called. I call these methods hooks, borrowing the terminology of e-lisp. As far as I know, Ruby has the following hooks:
Ruby hooks
at_exit
set_trace_func
initialize
method_missing
singleton_method_added
singleton_method_removed
singleton_method_undefined
respond_to_missing?
extended
included
method_added
method_removed
method_undefined
const_missing
inherited
initialize_copy
initialize_clone
initialize_dup
prepend
append_features
extend_features
prepend_features
And method_missing is one of them. For this particular one, it is automatically called when Ruby cannot find a defined method. Or in other words, method_missing is the most default method that is called with the least priority, for any method call.
method_missing is one of the amazing aspects of metaprogramming in ruby. With proper use of this method, you can gracefully handle exceptions and whatnot. In your case it is called because the method you are calling on the object doesn't exist obviously.
But one should be careful of its use too. While you are at it do look at responds_to method too.
An example regarding ActiveRecord will make you understand better. When we write:
User.find_by_email_and_age('me#example.com', 20)
There isn't actually a method by that name. This call goes to the method_missing and then this fancyfindmethod is broken down into pieces and you are served what you asked for. I hope that helps.
I have a Pointer class with a single attribute :contents, that points to an object of class MyObject.
class MyObject
def hello; "hello" end
end
class Pointer
attr_reader :contents
def initialize( cont ); #contents = cont end
# perhaps define some more state
end
I want my Pointer to be able to make copies of itself. I know that #dup method is defined by default, while #clone method is expected to be overriden to be able to make deep copies. But here, the copies don't have to be too deep. So, the first dilemma that I have is, should I override #dup method, because I don't really want to copy the additional state of my Pointer, just make a new one pointing to the same MyObject instance? Or should I refrain from overridine #dup, because I am not "supposed to" and override #clone with a method making shallow copies?
I would welcome comments on the above, but let's say that I will choose to override #dup. I could do just this:
class Pointer
def dup; self.class.new( contents ) end
end
But online, I read something like "the dup method will call the initialize copy method". Also, this guy writes about #initialize_clone, #initialize_dup and #initialize_copy in Ruby. That leaves me wondering, is the best practice perhaps like this?
class Pointer
def initialize_copy
# do I don't know what
end
end
Or like this?
class Pointer
def initialize_dup
# do I don't know what
end
end
Or should I just forget about online rants written to confuse beginners and go for overriding #dup without concerns?
Also, I do understand that I can just call #dup without defining any custom #dup, but what if I want to define #dup with different behavior?
Also, the same question apply to #clone - should I try to define #initialize_clone or just #clone?
From my experience, overloading #initialize_copy works just fine (never heard about initialize_dup and initialize_clone).
The original initialize_copy (which initializes every instance variable with the values from the original object) is available through super, so I usually do:
class MyClass
def initialize_copy(orig)
super
# Do custom initialization for self
end
end
Let's say that I have a class called MyClass, basically I want to do something like this:
class MyClass
def initialize(a)
do_stuff a, 4, 11
end
def do_stuff(a,b,c)
# ...
end
end
# rspec below
MyClass.any_instance.should_receive(:do_stuff).with_the_values(10,
anything, anything)
MyClass.new 10
Basically, I want to check that initialize will call on, and pass the correct value to do_stuff.
I think you should test if your class behaves correctly or not instead of watching who passes what to whom.
I guess, the do_stuff method is supposed to produce a side-effect of some sort. If so, then check if this side-effect is the one you expect. This way you're free to change actual implementation of your class without needing to rewrite your specs every time.
I was trying to write my first method_missing override when I kept running into (edited) stack level too deep errors. The main culprit seemed to be trying to utilize an instance attribute. For instance if 'self' was a instance of the User class then checking for something like:
def method_missing(name)
if self.name
# do stuff
end
end
Would seg fault. I spent a long time on this but ended up giving up. There must be something I'm not understanding about accessing it.
Edit
My apologies, Andrew is correct, I am getting Stack Level too deep errors. With this in mind, what is the appropriate (if any) way to access the instances attribute values?
You can potentially rectify this problem by ensuring that self.name actually exists:
def method_missing(name)
if self.respond_to?(:name) && self.name
# do stuff
end
end
Note this may not work if your class inherits from anything Railsy (e.g. ActiveRecord::Base), since it overrides respond_to?.
If you are in a Railsy class, your method missing should call super, lest you lose a lot of the "magic" ActiveRecord methods (including, probably, self.name itself):
def method_missing(name, *args, &block)
if name_is_something_i_should_handle_here
# do your stuff
else
super(name, *args, block) # call parent's method_missing
end
end
Obviously you should replace name_is_something_i_should_handle_here with the appropriate logic.
You may also wish to consider using dynamic method creation instead of method_missing.
I've been working with Ruby for a little under a year, and I still don't fully understand "what makes blocks tick". In particular I'm curious how much control one has over the scope of a block. For example, say I have this code:
class Blob
attr_accessor :viscosity
def configure(&:block)
block.call self
end
end
blob = Blob.new
blob.configure do |b|
b.viscosity 0.5
end
A bit of a contrived example there, obviously.
Now, one thing I've noticed while migrating from Rails 2 to Rails 3 is that a lot of their configuration methods that take blocks no longer take a non-block argument.
For example, in routes.rb, it used to be ActionController::Routing::Routes.draw do |map| ... end, and now it's just ActionController::Routing::Routes.draw do ... end. But the methods that are called inside the block still have the appropriate context, without the need to repeat the name of the block's argument over and over.
In my example above, then, I want to be able to do:
blob.configure do
viscosity 0.5
end
so that I can tell people how easy it is to write a DSL in Ruby. :)
This uses instance_eval to do the magic. See http://apidock.com/ruby/Object/instance_eval/ for some documentation. instance_eval evaluates a block (or a string) in the context of it's receiver.
def configure(&block)
self.instance_eval &block
end
You'd still have to use the accessor method viscosity= in your example block or you'd have to define
def viscosity(value)
#viscosity = value
end
in your class.