class Request require 'uri' require 'addressable/uri' require 'typhoeus' include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming attr_accessor :host, :port, :path, :format, :params, :body, :http_method, :headers validates_presence_of :host, :path, :format, :http_method def initialize(http_method, path, attributes={}) attributes[:format] ||= "json" attributes[:host] ||= Wordnik.configuration.base_uri attributes[:params] ||= {} # Set default headers, but allow them to be overridden default_headers = { 'Content-Type' => "application/#{attributes[:format].downcase}", } attributes[:headers] = default_headers.merge(attributes[:headers] || {}) self.http_method = http_method.to_sym self.path = path attributes.each do |name, value| send("#{name.to_s.underscore.to_sym}=", value) end end # Construct a base URL def url u = Addressable::URI.new u.host = self.host.sub(/\/$/, '') u.port = self.port if self.port.present? u.path = self.interpreted_path u.scheme = "http" # For some reason this must be set _after_ host, otherwise Addressable gets upset u.to_s end # Iterate over the params hash, injecting any path values into the path string # e.g. /word.{format}/{word}/entries => /word.json/cat/entries def interpreted_path p = self.path self.params.each_pair do |key, value| p = p.gsub("{#{key}}", value.to_s) end # Stick a .{format} placeholder into the path if there isn't # one already or an actual format like json or xml # e.g. /words/blah => /words.{format}/blah unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s } p = p.sub(/^(\/?\w+)/, "\\1.#{format}") end p = p.sub("{format}", self.format) URI.encode(p) end def interpreted_body return unless self.body.present? return self.body.to_json if self.body.is_a?(Hash) self.body end # Iterate over all params, # .. removing the ones that are part of the path itself. # .. stringifying values so Addressable doesn't blow up. # .. obfuscating the API key if needed. def query_string_params(obfuscated=false) qsp = {} self.params.each_pair do |key, value| next if self.path.include? "{#{key}}" next if value.blank? value = "YOUR_API_KEY" if key.to_sym == :api_key && obfuscated qsp[key] = value.to_s end qsp end # Construct a query string from the query-string-type params def query_string(options={}) # We don't want to end up with '?' as our query string # if there aren't really any params return "" if query_string_params.blank? default_options = {:obfuscated => false} options = default_options.merge(options) qs = Addressable::URI.new qs.query_values = self.query_string_params(options[:obfuscated]) qs.to_s end # Returns full request URL with query string included def url_with_query_string(options={}) default_options = {:obfuscated => false} options = default_options.merge(options) [url, query_string(options)].join('') end def make response = case self.http_method.to_sym when :get Typhoeus::Request.get( self.url_with_query_string, :headers => self.headers.stringify_keys ) when :post Typhoeus::Request.post( self.url_with_query_string, :body => self.interpreted_body, :headers => self.headers.stringify_keys ) when :put Typhoeus::Request.put( self.url_with_query_string, :body => self.interpreted_body, :headers => self.headers.stringify_keys ) when :delete Typhoeus::Request.delete( self.url_with_query_string, :body => self.interpreted_body, :headers => self.headers.stringify_keys ) end @response_obj = Response.new(response) end # If the request has been made, return the existing response # If not, make the request and return the response def response @response_obj || self.make end def response_code_pretty return unless @response.present? @response.code.to_s end def response_headers_pretty return unless @response.present? # JSON.pretty_generate(@response.headers).gsub(/\n/, '
').html_safe # <- This was for RestClient @response.headers.gsub(/\n/, '
').html_safe # <- This is for Typhoeus end # It's an ActiveModel thing.. def persisted? false end end