From 24eba118e6a5cdef01a6f73a32dcfc7f57184ac1 Mon Sep 17 00:00:00 2001 From: Gibheer Date: Thu, 8 Nov 2012 21:31:24 +0100 Subject: [PATCH] replacement for rack request This should replace Rack::Request in the hole lib. It seperates everything worth into its own classes, like parameters and the accept header till now. More will follow --- lib/zero/request.rb | 53 ++++++++++++++++++++++++ lib/zero/request/accept.rb | 76 +++++++++++++++++++++++++++++++++++ lib/zero/request/parameter.rb | 64 +++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 lib/zero/request.rb create mode 100644 lib/zero/request/accept.rb create mode 100644 lib/zero/request/parameter.rb 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