Reputation: 6263
I am trying to understand code from github repo. It's the main module of the gem to setup the client.
module Github
# more code
class << self
def included(base)
base.extend ClassMethods # what would this be for?
end
def new(options = {}, &block)
Client.new(options, &block)
end
def method_missing(method_name, *args, &block)
if new.respond_to?(method_name)
new.send(method_name, *args, &block)
elsif configuration.respond_to?(method_name)
Github.configuration.send(method_name, *args, &block)
else
super
end
end
def respond_to?(method_name, include_private = false)
new.respond_to?(method_name, include_private) ||
configuration.respond_to?(method_name) ||
super(method_name, include_private)
end
end
module ClassMethods
def require_all(prefix, *libs)
libs.each do |lib|
require "#{File.join(prefix, lib)}"
end
end
# more methods ...
end
extend ClassMethods
require_all LIBDIR,
'authorization',
'validations',
'normalizer',
'parameter_filter',
'api',
'client',
'pagination',
'request',
'response',
'response_wrapper',
'error',
'mime_type',
'page_links',
'paged_request',
'page_iterator',
'params_hash'
end
class << self
and module ClassMethods
used, and then extended instead of being included in the class << self
part?def included(base)
. This seems to add the class methods into a specific object. Why is it like this? It could relate to the functionality of the class, but I do not understand it.Upvotes: 27
Views: 12745
Reputation: 4775
module MyModule
class << self
def included(base)
base.extend ClassMethods # what would this be for?
end
<...>
end
<...>
end
This is actually a pretty common practice in Ruby. Basically, what it's saying is: when some object performs include MyModule
, make it also extend MyModule::ClassMethods
. Such a feat is useful if you want a mixin that adds some methods not just to the instances of a class, but to the class itself.
A short example:
module M
# A normal instance method
def mul
@x * @y
end
module ClassMethods
# A class method
def factory(x)
new(x, 2 * x)
end
end
def self.included(base)
base.extend ClassMethods
end
end
class P
include M
def initialize(x, y)
@x = x
@y = y
end
def sum
@x + @y
end
end
p1 = P.new(5, 15)
puts "#{p1.sum} #{p1.mul}" # <= 20 75
# Calling the class method from the module here!
p2 = P.factory(10)
puts "#{p2.sum} #{p2.mul}" # <= 30 200
Upvotes: 34
Reputation: 6263
Looking more at the repo there is another class Github::API
. This class seems to require functionality of the Github::ClassMethods
module.
module Github
# Core class responsible for api interface operations
class API
extend Github::ClassMethods
So it makes sense that it's own own module. It gives the ability to only import those methods. If the methods from class << self
were included, they would become available which is probably not wanted.
It may have been better to have the module in it's own class or named something else. But I guess that is just personal choice.
Upvotes: 4