Why does this not work:
class Myclass
include HTTParty
def dosomething
base_uri("some_url")
end
end
The base_uri method is a class method of HTTParty. It works fine if I call it from my class, outside of any instance methods, or from a class method, but when try to call it from an instance method I get "NoMethodError: undefined method `base_uri' for #"
Why? Shouldn't there be some way to refer to the HTTParty class from within my instance method so I can call that HTTParty class method?
I could change it to a class method, but then every instance of my class would have the same value for base_uri.
Why doesn't it work? Because that's not how Ruby works. Similarly, this doesn't work:
class Foo
def self.utility_method; ...; end
def inst_method
utility_method # Error! This instance has no method named "utility_method"
end
end
You could work around this by just doing:
class MyClass
include HTTParty
def dosomething
HTTParty.base_uri("some_url")
end
end
Let's look deeper at how method lookup works with modules. First, some code:
module M
def self.m1; end
def m2; end
end
class Foo
include M
end
p Foo.methods - Object.methods #=> []
p Foo.new.methods - Object.methods #=> [:m2]
class Bar
extend M
end
p Bar.methods - Object.methods #=> [:m2]
p Bar.new.methods - Object.methods #=> []
class Jim; end
j = Jim.new
j.extend M
p j.methods - Object.methods #=> [:m2]
As we see, you can use extend
to cause an object (a class or instance) to use the 'instance' methods of a module for the object itself (instead of instances), but you cannot cause 'class methods' of the module to be inherited by anything. The closest you can get is this idiom:
module M2
module ClassMethods
def m1; end # Define as an instance method of this sub-module!
end
extend ClassMethods # Make all methods on the submodule also my own
def self.included(k)
k.extend(ClassMethods) # When included in a class, extend that class with
end # my special class methods
def m2; end
end
class Foo
include M2
end
p Foo.methods - Object.methods #=> [:m1]
p Foo.new.methods - Object.methods #=> [:m2]
If the HTTParty
module used the above pattern, and so made the base_uri
method available on your MyClass
, then you could do this:
class MyClass
include HTTParty
def dosomething
self.class.base_uri("some_url")
end
end
...but that's more work than just directly referencing the module owning the method.
Finally, because this might help you, here's a diagram I made some years ago. (It's missing some core objects from Ruby 1.9, like BasicObject
, but is otherwise still applicable. Click for a PDF version. Note #3 from the diagram is particularly applicable.)
(source: phrogz.net)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With