lib/weary/resource.rb in weary-0.7.2 vs lib/weary/resource.rb in weary-1.0.0.rc1
- old
+ new
@@ -1,135 +1,159 @@
+require 'addressable/template'
+require 'weary/request'
+
module Weary
+ # A description of a resource made available by an HTTP request. That
+ # description is composed primarily of a url template, the HTTP method to
+ # retrieve the resource and some constraints on the parameters necessary
+ # to complete the request.
class Resource
- attr_reader :name, :via, :with, :requires, :url
- attr_accessor :headers
-
- def initialize(name)
- self.name = name
- self.via = :get
- self.authenticates = false
- self.follows = true
+ UnmetRequirementsError = Class.new(StandardError)
+
+ attr_reader :method
+
+ def initialize(method, uri)
+ @method = method
+ self.url uri
end
-
- # The name of the Resource. Will be a lowercase string, whitespace replaced with underscores.
- def name=(resource_name)
- @name = resource_name.to_s.downcase.strip.gsub(/\s/,'_')
+
+ # An accessor method to set the url to retrieve the resource. Use either
+ # brackets to delimit url variables or prefix them with a colon, like
+ # Sinatra.
+ #
+ # Returns an Addressable::Template
+ def url(uri=nil)
+ @uri = Addressable::Template.new(uri.gsub(/:(\w+)/) { "{#{$1}}" }) unless uri.nil?
+ @uri
end
-
- # The HTTP Method used to fetch the Resource
- def via=(http_verb)
- verb = HTTPVerb.new(http_verb).normalize
- @via = Methods.include?(verb) ? verb : :get
+
+ # An accessor to set optional parameters permitted by the resource.
+ #
+ # Returns an Array of parameters.
+ def optional(*params)
+ @optional = params unless params.empty?
+ @optional ||= []
end
-
- # Optional params. Should be an array. Merges with requires if that is set.
- def with=(params)
- @with = [params].flatten.collect {|x| x.to_sym}
- @with = (requires | @with) if requires
+
+ # An accessor to set optional parameters required in order to access the
+ # resource.
+ #
+ # Returns an Array of parameters.
+ def required(*params)
+ @required = params unless params.empty?
+ @required ||= []
end
-
- # Required params. Should be an array. Merges with `with` or sets `with`.
- def requires=(params)
- @requires = [params].flatten.collect {|x| x.to_sym}
- with ? @with = (with | @requires) : (@with = @requires)
+
+ # An accessor to set default paramers to send to the resource.
+ def defaults(hash=nil)
+ @defaults = hash unless hash.nil?
+ @defaults ||= {}
end
-
- # Sets whether the Resource requires authentication. Always sets to a boolean value.
- def authenticates=(bool)
- @authenticates = bool ? true : false
+
+ # An accessor to set HTTP request headers.
+ def headers(hash=nil)
+ @headers = hash unless hash.nil?
+ @headers ||= {}
end
-
- # Does the Resource require authentication?
- def authenticates?
- @authenticates
+
+ # Set up a Rack::Middleware to be used by the Request (which is
+ # Rack-friendly).
+ def use(middleware, *args, &block)
+ @middlewares ||= []
+ @middlewares << [middleware, args, block]
end
-
- # Sets whether the Resource should follow redirection. Always sets to a boolean value.
- def follows=(bool)
- @follows = bool ? true : false
+
+ # Convenience method to set a User Agent Header
+ def user_agent(agent)
+ headers.update 'User-Agent' => agent
end
-
- # Should the resource follow redirection?
- def follows?
- @follows
+
+ # Tell the Resource to anticipate Basic Authentication. Optionally,
+ # tell the Resource what parameters to use as credentials.
+ #
+ # user - The parameter in which to expect the username (defaults to :username)
+ # pass - The parameter in which to expect the password (defaults to :password)
+ def basic_auth!(user = :username, pass = :password)
+ @authenticates = :basic_auth
+ @credentials = [user, pass]
end
-
- # Set the Resource's URL as a URI
- def url=(uri)
- @url = URI.parse(uri)
+
+ # Tell the Resource to anticipate OAuth. Optionally, tell the Resource
+ # what parameters to use as the consumer key and access token
+ #
+ # key - The parameter in which to expect the consumer key (defaults to
+ # :consumer_key)
+ # token - The parameter in which to expect the user access token (defaults
+ # to :token)
+ def oauth!(key = :consumer_key, token = :token)
+ @authenticates = :oauth
+ @credentials = [key, token]
end
-
- # A hash representation of the Resource
- def to_hash
- {@name.to_sym => { :via => via,
- :with => with,
- :requires => requires,
- :follows => follows?,
- :authenticates => authenticates?,
- :url => url,
- :headers => headers}}
+
+ # Does the Resource anticipate some sort of authentication parameters?
+ def authenticates?
+ !!@authenticates
end
-
- # Take parameters, default params, and credentials and build a Request object for this Resource
- def build!(params={}, defaults=nil, credentials=nil)
- uri = @url
- parameters = setup_parameters(params, defaults)
- request_opts = setup_options(parameters, credentials)
- uri.query = request_opts[:query].to_params if request_opts[:query]
- Request.new(uri.normalize.to_s, @via, request_opts)
+
+ # The keys expected as parameters to the Request.
+ def expected_params
+ defaults.keys.map(&:to_s) | optional.map(&:to_s) | required.map(&:to_s)
end
-
- # Setup the parameters to make the Request with
- def setup_parameters(params={}, defaults=nil)
- params = defaults ? defaults.merge(params) : params
- find_missing_requirements(params)
- remove_unnecessary_params(params)
+
+ # Does the Resource expect this parameter to be used to make the Request?
+ def expects?(param)
+ expected_params.include? param.to_s
end
-
- # Search the given parameters to see if they are missing any required params
- def find_missing_requirements(params)
- if (@requires && !@requires.empty?)
- missing_requirements = @requires - params.keys
- raise ArgumentError, "This resource is missing required parameters: '#{missing_requirements.inspect}'" unless missing_requirements.empty?
- end
+
+ # The parameter keys that must be fulfilled to create the Request.
+ def requirements
+ required.map(&:to_s) | url.keys
end
-
- # Remove params that have not been specified with #with
- def remove_unnecessary_params(params)
- params.delete_if {|k,v| !@with.include?(k) } if (@with && !@with.empty?)
+
+ # Given a hash of Request parameters, do they meet the requirements?
+ def meets_requirements?(params)
+ requirements.reject {|k| params.keys.map(&:to_s).include? k.to_s }.empty?
end
-
- # Setup the options to be passed into the Request
- def setup_options(params={}, credentials=nil)
- options = {}
- prepare_request_body(params, options)
- setup_authentication(options, credentials)
- options[:no_follow] = true if !follows?
- options[:headers] = @headers if !@headers.blank?
- options
- end
-
- # Prepare the Request query or body depending on the HTTP method
- def prepare_request_body(params, options={})
- if (@via == :post || @via == :put)
- options[:body] = params unless params.blank?
- else
- options[:query] = params unless params.blank?
- end
- options
- end
-
- # Prepare authentication credentials for the Request
- def setup_authentication(options, credentials=nil)
- if authenticates?
- raise ArgumentError, "This resource requires authentication and no credentials were given." if credentials.blank?
- if credentials.is_a?(OAuth::AccessToken)
- options[:oauth] = credentials
- else
- options[:basic_auth] = credentials
+
+ # Construct the request from the given parameters.
+ #
+ # Yields the Request
+ #
+ # Returns the Request.
+ # Raises a Weary::Resource::UnmetRequirementsError if the requirements
+ # are not met.
+ def request(params={})
+ params.delete_if {|k,v| v.nil? || v.to_s.empty? }
+ params.update(defaults)
+ raise UnmetRequirementsError, "Required parameters: #{requirements}" \
+ unless meets_requirements? params
+ credentials = pull_credentials params if authenticates?
+ mapping = url.keys.map {|k| [k, params.delete(k) || params.delete(k.to_sym)] }
+ request = Weary::Request.new url.expand(Hash[mapping]), @method do |r|
+ r.headers headers
+ if !expected_params.empty?
+ r.params params.reject {|k,v| !expects? k }
end
+ r.send @authenticates, *credentials if authenticates?
+ if !@middlewares.nil? && !@middlewares.empty?
+ @middlewares.each {|middleware| r.use *middleware }
+ end
end
- options
+ yield request if block_given?
+ request
end
-
+ alias build request
+
+
+ private
+
+ # Private: Separate the credentials for authentication from the other
+ # request parameters.
+ def pull_credentials(params)
+ (@credentials || []).map do |credential|
+ params.delete(credential) || params.delete(credential.to_s)
+ end.compact
+ end
+
+
end
end
\ No newline at end of file