lib/api.rb in servicy-0.0.3 vs lib/api.rb in servicy-0.0.5
- old
+ new
@@ -1,91 +1,109 @@
-# TODO: Investigate this kind of thing using Delegator to see if I can get
-# a speed boost. Each exported method would have to be dynamically defined
-# in the API class to do contract checking and then pass that on to the
-# delegated object. I don't, however, know if that's actually faster than
-# using method_missing. Investigate
module Servicy
class API
- def self.create(klass, method_descriptions)
- @klass = klass
- method_descriptions = { class: method_descriptions[:class].inject({}) { |h, info| h[info[:method].to_sym] = info; h },
- instance: method_descriptions[:instance].inject({}) { |h, info| h[info[:method].to_sym] = info; h }
+ def self.create(klass, mds)
+ method_descriptions = { class: mds[:class].inject({}) { |h, info| h[info[:method].to_sym] = info; h },
+ instance: mds[:instance].inject({}) { |h, info| h[info[:method].to_sym] = info; h }
}
# We inherit from API here so that in the future we can add functionality
# for setting up servers, service discovery, etc.
# This may change to Client at some point... Who
# knows.
magic_class = Class.new(Servicy::API) do
- @@base_class = klass
- @@method_descriptions = method_descriptions
-
def initialize(*args)
- @instance = @@base_class.new(*args)
+ @instance = self.class.const_get('BASE_CLASS').new(*args)
end
- def method_descriptions
- @@method_descriptions
+ def self.set_remote(client)
+ @remote = client
end
+ def self.remote?
+ @remote
+ end
+ def self.client
+ @remote
+ end
+ # TODO: I'm thinking that I might be able to do this with
+ # #define_method's instead. That will spead things up aroud 2-5x.
+
# The magic happens in the two method_missing methods.
# Meta-programming-averse; gaze upon my works, ye haughty, and despair!
def method_missing(method, *args, &block)
# Verify the contract
- data = method_descriptions[:instance][method]
- raise NoMethodError.new("#{@klass.to_s} does not expose an API endpoint called, #{method.to_s}") unless data
+ data = self.class.const_get('METHOD_DESCRIPTIONS')[:instance][method]
+ raise NoMethodError.new("#{self.class.const_get('BASE_CLASS')} does not expose an API endpoint called, #{method.to_s}") unless data
data[:contract].valid_args?(*args)
# Dispatch the result
- result = @instance.send(method, *args, &block)
+ result = nil
+ if self.class.remote?
+ params = data[:contract].params.each_with_index.inject({}) do |h, (param, index)|
+ h[param.to_s] = args[index]
+ h
+ end
+ result = self.class.client.transport.remote_request(method, params)
+ else
+ result = @instance.send(method, *args, &block)
+ end
# Check the result
data[:contract].valid_return?(*args, result)
# And we are good to go
result
end
# Make sure that we can respond to the things we actually do respond to.
def respond_to?(method)
- method_descriptions[:instance].include?(method) || super
+ self.class.const_get('METHOD_DESCRIPTIONS')[:instance].include?(method) || super
end
def self.method_missing(method, *args, &block)
# Verify the contract
- data = @@method_descriptions[:class][method]
- raise NoMethodError.new("#{@klass.to_s} does not expose an API endpoint called, #{method.to_s}") unless data
+ data = self.const_get('METHOD_DESCRIPTIONS')[:class][method]
+ raise NoMethodError.new("#{self.const_get('BASE_CLASS')} does not expose an API endpoint called, #{method.to_s}") unless data
data[:contract].valid_args?(*args)
# Dispatch the result
- result = @@base_class.send(method, *args, &block)
+ result = nil
+ if remote?
+ params = data[:contract].params.each_with_index.inject({}) do |h, (param, index)|
+ h[param.to_s] = args[index]
+ h
+ end
+ result = client.transport.remote_request(method, params)
+ else
+ result = self.const_get('BASE_CLASS').send(method, *args, &block)
+ end
# Check the result
data[:contract].valid_return?(*args, result)
# And we are good to go
result
end
def self.respond_to?(method)
- @@method_descriptions[:class].include?(method) || super
+ const_get('METHOD_DESCRIPTIONS')[:class].include?(method) || super
end
# Send back either all the documentation, or just for a particular
# method.
def self.docs(method=nil)
if method.nil?
- return @@method_descriptions.map do |(type, methods)|
+ return const_get('METHOD_DESCRIPTIONS').map do |(type, methods)|
methods.values.map { |v| v[:docs] }
end.flatten
else
search_in = [:class, :instance]
if method.is_a?(String)
search_in = method[0] == '.' ? [:class] : (method[0] == '#' ? [:instance] : search_in)
end
search_in.each do |type|
- @@method_descriptions[type].each do |(name,stuff)|
+ const_get('METHOD_DESCRIPTIONS')[type].each do |(name,stuff)|
return stuff[:docs] if method.to_s =~ /(\.|#)?#{name.to_s}$/
end
end
return nil
end
@@ -93,10 +111,12 @@
end
magic_class.extend(ExtraMethods)
klass.constants(false).each do |const|
magic_class.const_set(const, klass.const_get(const))
end
+ magic_class.const_set('BASE_CLASS', klass)
+ magic_class.const_set('METHOD_DESCRIPTIONS', method_descriptions)
# Give it a good name
class_name = "#{klass.to_s}API"
Object.const_set(class_name, magic_class)
end
@@ -114,13 +134,8 @@
version = self.const_get(:VERSION) rescue '1.0.0'
port = self.const_get(:PORT) rescue 1234
heartbeat_port = self.const_get(:HEARTBEAT_PORT) rescue port
{ name: name, host: 'localhost', port: port, version: version, heartbeat_port: heartbeat_port }
- end
-
- # Sets the remote configuration options
- def set_remote_configuration(config)
- @remote_config = config
end
end
end