module Sawyer class Relation class Map # Tracks the available next actions for a resource, and # issues requests for them. def initialize @map = {} end # Adds a Relation to the map. # # rel - A Relation. # # Returns nothing. def <<(rel) @map[rel.name] = rel if rel end # Gets the raw Relation by its name. # # key - The Symbol name of the Relation. # # Returns a Relation. def [](key) @map[key.to_sym] end # Gets the number of mapped Relations. # # Returns an Integer. def size @map.size end # Gets a list of the Relation names. # # Returns an Array of Symbols in no specific order. def keys @map.keys end def to_hash pairs = @map.map do |k, v| [(k.to_s + "_url").to_sym, v.href] end Hash[pairs] end alias :to_h :to_hash def inspect hash = to_hash hash.respond_to?(:pretty_inspect) ? hash.pretty_inspect : hash.inspect end end attr_reader :agent, :name, :href_template, :method, :available_methods # Public: Builds an index of Relations from the value of a `_links` # property in a resource. :get is the default method. Any links with # multiple specified methods will get multiple relations created. # # index - The Hash mapping Relation names to the Hash Relation # options. # rels - A Relation::Map to store the Relations. # # Returns a Relation::Map def self.from_links(agent, index, rels = Map.new) if index.is_a?(Array) raise ArgumentError, "Links must be a hash of rel => {_href => '...'}: #{index.inspect}" end index.each do |name, options| rels << from_link(agent, name, options) end if index rels end # Public: Builds a single Relation from the given options. These are # usually taken from a `_links` property in a resource. # # agent - The Sawyer::Agent that made the request. # name - The Symbol name of the Relation. # options - A Hash containing the other Relation properties. # :href - The String URL of the next action's location. # :method - The optional String HTTP method. # # Returns a Relation. def self.from_link(agent, name, options) case options when Hash new agent, name, options[:href], options[:method] when String new agent, name, options end end # A Relation represents an available next action for a resource. # # agent - The Sawyer::Agent that made the request. # name - The Symbol name of the relation. # href - The String URL of the location of the next action. # method - The Symbol HTTP method. Default: :get def initialize(agent, name, href, method = nil) @agent = agent @name = name.to_sym @href = href @href_template = Addressable::Template.new(href.to_s) methods = nil if method.is_a? String if method.size.zero? method = nil else method.downcase! methods = method.split(',').map! do |m| m.strip! m.to_sym end method = methods.first end end @method = (method || :get).to_sym @available_methods = Set.new methods || [@method] end # Public: Makes an API request with the curent Relation using HEAD. # # data - The Optional Hash or Resource body to be sent. :get or :head # requests can have no body, so this can be the options Hash # instead. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def head(options = nil) options ||= {} options[:method] = :head call options end # Public: Makes an API request with the curent Relation using GET. # # data - The Optional Hash or Resource body to be sent. :get or :head # requests can have no body, so this can be the options Hash # instead. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def get(options = nil) options ||= {} options[:method] = :get call options end # Public: Makes an API request with the curent Relation using POST. # # data - The Optional Hash or Resource body to be sent. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def post(data = nil, options = nil) options ||= {} options[:method] = :post call data, options end # Public: Makes an API request with the curent Relation using PUT. # # data - The Optional Hash or Resource body to be sent. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def put(data = nil, options = nil) options ||= {} options[:method] = :put call data, options end # Public: Makes an API request with the curent Relation using PATCH. # # data - The Optional Hash or Resource body to be sent. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def patch(data = nil, options = nil) options ||= {} options[:method] = :patch call data, options end # Public: Makes an API request with the curent Relation using DELETE. # # data - The Optional Hash or Resource body to be sent. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def delete(data = nil, options = nil) options ||= {} options[:method] = :delete call data, options end # Public: Makes an API request with the curent Relation using OPTIONS. # # data - The Optional Hash or Resource body to be sent. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Returns a Sawyer::Response. def options(data = nil, opt = nil) opt ||= {} opt[:method] = :options call data, opt end def href(options = nil) return @href if @href_template.nil? @href_template.expand(options || {}).to_s end # Public: Makes an API request with the curent Relation. # # data - The Optional Hash or Resource body to be sent. :get or :head # requests can have no body, so this can be the options Hash # instead. # options - Hash of option to configure the API request. # :headers - Hash of API headers to set. # :query - Hash of URL query params to set. # :method - Symbol HTTP method. # # Raises ArgumentError if the :method value is not in @available_methods. # Returns a Sawyer::Response. def call(data = nil, options = nil) m = options && options[:method] if m && !@agent.allow_undefined_methods? && !@available_methods.include?(m == :head ? :get : m) raise ArgumentError, "method #{m.inspect} is not available: #{@available_methods.to_a.inspect}" end @agent.call m || @method, @href_template, data, options end def inspect %(#<#{self.class}: #{@name}: #{@method} #{@href_template}>) end end end