Skip to content

Instantly share code, notes, and snippets.

@nickjacob
Last active December 11, 2015 04:28
Show Gist options
  • Save nickjacob/4544865 to your computer and use it in GitHub Desktop.
Save nickjacob/4544865 to your computer and use it in GitHub Desktop.
I know this has been done before, but here's my metaprogramming solution to wrapping a RESTful API.
###
# API Client for "MyApi"
###
require 'httparty'
require 'yaml'
module MyApi
class Config
attr_accessor :base_url, :page_size, :key
end
def self.configuration
@_config ||= Config.new
if block_given?
@_config = yield(@_config)
else
@_config
end
end
class Resource
include HTTParty
Resource = nil
base_uri "#{ MyApi.configuration.base_url }#{ self::Resource}/"
Methods = [
:post,
:get,
:put,
:delete,
]
def initialize(data)
@_id = data['_id']
@_data = data
end
def self.method_missing(method, *args, &block)
method = method.to_s
extra = {}
if block_given?
extra = yield(extra)
end
if method =~ /^find_by_(.+)$/
opt = {}
opt[$1] = args and args.length == 1 ? args[0] : args
self._where(opt.merge(extra), &block)
elsif method.eql? 'where' and opt = args[0]
self._where(opt.merge(extra), &block)
elsif method.eql? 'find' and id = args[0]
self._by_id(id, opt.merge(extra), &block)
end
end
def method_missing(method, *args, &block)
method = method.to_s
special = nil
if method[-1] =~ /=|\?|!/
special = method[-1]
method = method[0..-1]
end
if @_data.include? method
case special
when nil
@_data[method]
when "="
@_data[method] = args and args[0]
when ("!" and not args.nil?)
@_data[method] = args[0] if args[0].class == @_data[method].class
end
elsif special.eql? '?'
false
end
end
def delete
self.call_api(:delete, @_id)
end
def save
self.call_api(:post, @_id, @_data)
end
def save!
self.call_api(:post, @_id, @_data)
end
def update(opt)
self.call_api(:post, @_id, opt)
end
private
def self._where(opt)
results = self.call_api(:get, "", opt)
results.map { |r| self.class.new(r) } if results && results[0] && results[0]['_id']
end
def self._by_id(id, opt)
result = self.call_api(:get, id, opt)
self.class.new(result) if result and result['_id']
end
def self.call_api(method, path, args)
if self.class::Methods.include? method
self.class.send(
method.to_sym,
"/#{ path }.json",
args.merge(args, { key: MyApi.configuration.key, page_size: MyApi.configuration.page_size })
)
end
end
end
resources = YAML.load_file('./my_api.yml')['resources']
resources.map do |resource, opt|
opt = opt.is_a?(Hash) ? opt : {}
MyApi.const_set(resource, Class.new(MyApi::Resource) { |obj|
obj.const_set("Resource", resource.downcase + "s")
if opt.include? 'methods'
obj::Methods = opt['methods'].map { |x| x.to_sym }
end
})
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment