lib/whos_got_dirt/request.rb
module WhosGotDirt
# Accepts MQL parameters and return URLs to request.
#
# @example Create a new class for transforming parameters to URLs.
# class MyAPIRequest < WhosGotDirt::Request
# @base_url = 'https://api.example.com'
#
# def to_s
# "#{base_url}/endpoint?#{to_query(input)}"
# end
# end
#
# @example Use the class in requesting a URL.
# url = MyAPIRequest.new(name: 'John Smith').to_s
# response = Faraday.get(url)
# #=> "https://api.example.com/endpoint?name=John+Smith"
class Request
class << self
# @!attribute [r] base_url
# @return [String] the base URL to be used in the request
attr_reader :base_url
# Transforms a query string from a hash to a string.
#
# @param [Hash] params query string parameters
# @return [String] a query string
def to_query(params)
params.map do |key,value|
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
end * '&'
end
end
# @!attribute input
# @return [Hash] the MQL parameters
attr_accessor :input
# @!attribute :output
# @return [Hash] the API-specific parameters
attr_reader :output
# Sets the MQL parameters.
#
# @param [Hash] input the MQL parameters
def initialize(input = {})
@input = ActiveSupport::HashWithIndifferentAccess.new(input)
@output = {}
end
# Returns the base URL to be used in the request.
#
# @return [String] the base URL to be used in the request
def base_url
self.class.base_url
end
# Helper method to map a parameter that supports the MQL equality operator.
#
# @param [String] target the API-specific parameter name
# @param [String] source the request parameter name
# @param [Hash] opts options
# @option opts [String] :input substitute MQL parameters
# @option opts [String] :transform a transformation to apply to the value
# @option opts [String] :default the default value
# @option opts [Set,Array] :valid a list of valid values
# @return [Hash] the API-specific parameters
def equal(target, source, opts = {})
params = parameters(opts)
if opts.key?(:valid)
if opts[:valid].include?(params[source])
output[target] = transform(params[source], opts)
end
else
if params[source]
output[target] = transform(params[source], opts)
elsif opts[:default]
output[target] = opts[:default]
end
end
output
end
# Helper method to map a parameter that supports the MQL `~=` operator.
#
# @param [String] target the API-specific parameter name
# @param [String] source the request parameter name
# @param [Hash] opts options
# @option opts [String] :input substitute MQL parameters
# @option opts [String] :transform a transformation to apply to the value
# @return [Hash] the API-specific parameters
def match(target, source, opts = {})
params = parameters(opts)
if params[source]
equal(target, source, opts)
elsif params["#{source}~="]
output[target] = transform(params["#{source}~="], opts)
end
output
end
# Helper method to map a parameter that supports the MQL `|=` operator.
#
# @param [String] target the API-specific parameter name
# @param [String] source the request parameter name
# @param [Hash] opts options
# @option opts [String] :input substitute MQL parameters
# @option opts [String] :transform a transformation to apply to the value
# @return [Hash] the API-specific parameters
def one_of(target, source, opts = {})
params = parameters(opts)
if params[source]
equal(target, source, opts)
elsif params["#{source}|="]
output[target] = params["#{source}|="].map{|v| transform(v, opts)}.join(or_operator)
end
output
end
# Helper method to map a parameter that supports MQL `AND`-like constraints.
#
# @param [String] target the API-specific parameter name
# @param [String] source the request parameter name
# @param [Hash] opts options
# @option opts [String] :input substitute MQL parameters
# @option opts [String] :transform a transformation to apply to the value
# @option opts [String] :transform a transformation to apply to the value
# @return [Hash] the API-specific parameters
def all_of(target, source, opts = {})
params = parameters(opts)
if params[source]
equal(target, source, opts)
else
values = []
params.each do |key,value|
if key[/:([a-z_]+)$/, 1] == source
values << value
end
end
if values.empty?
if opts.key?(:backup)
send(opts[:backup], target, source, opts)
end
else
output[target] = values.map{|v| transform(v, opts)}.join(and_operator)
end
end
output
end
# Helper method to map a date parameter that supports comparisons.
#
# @param [String] target the API-specific parameter name
# @param [String] source the request parameter name
# @param [Hash] opts options
# @option opts [String] :input substitute MQL parameters
# @return [Hash] the API-specific parameters
def date_range(target, source, opts = {})
params = parameters(opts)
# @note OpenCorporates date range format.
if params[source]
output[target] = "#{params[source]}:#{params[source]}"
elsif params["#{source}>="] || params["#{source}>"] || params["#{source}<="] || params["#{source}<"]
output[target] = "#{params["#{source}>="] || params["#{source}>"]}:#{params["#{source}<="] || params["#{source}<"]}"
end
output
end
# @abstract Subclass and override {#to_s} to return the URL from which to
# `GET` the results
def to_s
raise NotImplementedError
end
# @abstract Subclass and override {#and_operator} to return the "AND"
# operator's serialization
def and_operator
raise NotImplementedError
end
# @abstract Subclass and override {#or_operator} to return the "OR"
# operator's serialization
def or_operator
raise NotImplementedError
end
private
def to_query(params)
self.class.to_query(params)
end
def parameters(opts)
if opts.key?(:input)
opts[:input]
else
input
end
end
def transform(value, opts)
if opts.key?(:transform)
opts[:transform].call(value)
else
value
end
end
end
end