summaryrefslogtreecommitdiff
path: root/lib/zero
diff options
context:
space:
mode:
Diffstat (limited to 'lib/zero')
-rw-r--r--lib/zero/request.rb53
-rw-r--r--lib/zero/request/accept.rb76
-rw-r--r--lib/zero/request/parameter.rb64
3 files changed, 193 insertions, 0 deletions
diff --git a/lib/zero/request.rb b/lib/zero/request.rb
new file mode 100644
index 0000000..48b59d3
--- /dev/null
+++ b/lib/zero/request.rb
@@ -0,0 +1,53 @@
+require_relative 'request/accept'
+require_relative 'request/parameter'
+
+module Zero
+ # This class wraps around a rack environment for easier access to all data.
+ class Request
+ CONST_CONTENT_TYPE = 'CONTENT_TYPE'
+ CONST_HTTP_ACCEPT = 'HTTP_ACCEPT'
+ CONST_PATH_INFO = 'PATH_INFO'
+ CONST_REQUEST_METHOD = 'REQUEST_METHOD'
+
+ # create a new request object
+ def initialize(env)
+ @env = env
+ @method = @env[CONST_REQUEST_METHOD].downcase.to_sym
+ end
+
+ # get the requested path
+ def path
+ @path ||= @env[CONST_PATH_INFO]
+ end
+
+ # returns a set of get and post variables
+ def params
+ @params ||= Request::Parameter.new(@env)
+ end
+
+ # return the content type of the request
+ # TODO change into its own object?
+ def content_type
+ @env[CONST_CONTENT_TYPE] if @env.has_key?(CONST_CONTENT_TYPE)
+ end
+
+ # get the media types
+ # @return Accept on Accept object managing all types and their order
+ def media_types
+ @accept ||= Request::Accept.new(@env[CONST_HTTP_ACCEPT])
+ end
+
+ # is the method 'GET'?
+ def get?; @method == :get; end
+ # is the method 'POST'?
+ def post?; @method == :post; end
+ # is the method 'PUT'?
+ def put?; @method == :put; end
+ # is the method 'DELETE'?
+ def delete?; @method == :delete; end
+ # is the method 'HEAD'?
+ def head?; @method == :head; end
+ # is the method 'PATCH'?
+ def head?; @method == :patch; end
+ end
+end
diff --git a/lib/zero/request/accept.rb b/lib/zero/request/accept.rb
new file mode 100644
index 0000000..f81490e
--- /dev/null
+++ b/lib/zero/request/accept.rb
@@ -0,0 +1,76 @@
+module Zero
+ class Request
+ # encapsulates the accept header to easier work with
+ # this is partly copied from sinatra
+ class Accept
+ MEDIA_TYPE_SEPERATOR = ','
+ MEDIA_PARAM_SEPERATOR = ';'
+ MEDIA_QUALITY_REGEX = /q=[01]\./
+
+ def self.media_map(map)
+ @@map = map
+ end
+
+ def self.map
+ @@map ||= {}
+ end
+
+ # create a new accept object
+ def initialize(accept_string)
+ @types = parse_media_types(accept_string)
+ end
+
+ # return the preferred type
+ # @return String the preferred media type
+ def preferred
+ @types.first
+ end
+
+ # iterate over all media types
+ def each
+ @types.each {|type| yield type}
+ end
+
+ private
+
+ # converts the accept string to a useable array
+ # @param accept_string the string containing media ranges and options
+ def parse_media_types(accept_string = '*/*')
+ accept_string.
+ gsub(/\s/, '').
+ split(MEDIA_TYPE_SEPERATOR).
+ map do |accept_range|
+ extract_order(*accept_range.split(MEDIA_PARAM_SEPERATOR))
+ end.
+ sort_by(&:last).
+ map(&:first)
+ end
+
+ # extract the order of the type
+ # @param media_type the type itself
+ # @param params further options to the type
+ # @return Array the media type and quality in that order
+ def extract_order(media_type, *params)
+ params.each do |param|
+ if param.match(MEDIA_QUALITY_REGEX)
+ return [map_type(media_type), 10 - param[4..-1].to_i]
+ end
+ end
+ [map_type(media_type), 0]
+ end
+
+ # map media types to the type given in the map
+ # @param type [String] the media type
+ # @return the media type of the mapping or the original
+ def map_type(type)
+ return map[type] if map.has_key?(type)
+ type
+ end
+
+ # a small wrapper to the class method
+ def map
+ self.class.map
+ end
+ end
+ end
+end
diff --git a/lib/zero/request/parameter.rb b/lib/zero/request/parameter.rb
new file mode 100644
index 0000000..592c620
--- /dev/null
+++ b/lib/zero/request/parameter.rb
@@ -0,0 +1,64 @@
+# TODO should that go into the main zero file?
+require 'set'
+
+module Zero
+ class Request
+ # represents all parameter set in a session
+ #
+ # This class holds all parameters available in the rack environment, split
+ # on query and payload parameters.
+ class Parameter
+ ENV_KEY_QUERY = 'QUERY_STRING'
+ ENV_KEY_PAYLOAD = 'rack.input'
+ ENV_KEY_CONTENT_TYPE = 'CONTENT_TYPE'
+ PAYLOAD_CONTENT_TYPES = [
+ 'application/x-www-form-urlencoded',
+ 'multipart/form-data'
+ ].to_set
+
+ # get the query parameters
+ attr_reader :query
+ alias_method(:get, :query)
+
+ # get the payload or form data parameters
+ attr_reader :payload
+ alias_method(:post, :payload)
+
+ # creates a new parameter instance
+ #
+ # This should never be called directly, as it will be generated for you.
+ # This instance gives you the options to get query parameters (mostly
+ # called GET parameters) and payload parameters (or POST parameters).
+ # @param environment [Hash] the rack environment
+ def initialize(environment)
+ @query = extract_query_params(environment)
+ @payload = extract_payload_params(environment)
+ end
+
+ private
+
+ # extracts the key value pairs from the environment
+ # @return Hash all key value pairs from query string
+ def extract_query_params(environment)
+ return {} if environment[ENV_KEY_QUERY].length == 0
+ parse_string(environment[ENV_KEY_QUERY])
+ end
+
+ # extracts the key value pairs from the body
+ # @return Hash all key value pairs from the payload
+ def extract_payload_params(environment)
+ return {} unless PAYLOAD_CONTENT_TYPES.include?(environment[ENV_KEY_CONTENT_TYPE])
+ parse_string(environment[ENV_KEY_PAYLOAD].read)
+ end
+
+ # parse the query string like input to a hash
+ # @param query [String] the query string
+ # @return [Hash] the key/valuie pairs
+ def parse_string(query)
+ params = query.split('&')
+ params.map! {|part| part.split('=') }
+ Hash[params]
+ end
+ end
+ end
+end