aboutsummaryrefslogtreecommitdiff
path: root/lib/zero/request.rb
blob: f7c17af8dde971cd72fdf0dc8c658f7cb7a344f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require_relative 'request/accept'
require_relative 'request/client'
require_relative 'request/parameter'
require_relative 'request/server'

module Zero
  # This class wraps around a rack environment for easier access to all data.
  class Request
    class << self
      # replace #new with a function to reuse an already defined request
      alias_method :__new__, :new

      # reuse an already defined request in the environment or create a new one
      # @param environment [Hash] a rack compatible request environment
      def new(environment)
        return environment['zero.request'] if environment.has_key?('zero.request')
        __new__(environment)
      end
    end

    # create a new request object
    def initialize(env)
      @env = env
      @env['zero.request'] = self
    end

    # get the environment
    # @return [Hash] the environment hash
    attr_reader :env

    # get the requested path
    # @return [String] returns the requested path
    def path
      @path ||= @env[CONST_PATH_INFO]
    end

    # return all information about the client
    # @return [Client] an information object about the client
    def client
      @client ||= Request::Client.new(@env)
    end

    # get the information on the server
    # @return [Server] information on the running server
    def server
      @server ||= Request::Server.new(@env)
    end

    # get an object representing the parameters of the request
    # @return [Parameter] object having all parameters
    def params
      @params ||= Request::Parameter.new(@env)
    end

    # return the content type of the request
    # TODO change into its own object?
    # @return [String] returns the content type of the request
    def content_type
      @env[CONST_CONTENT_TYPE]
    end

    # get the media types
    # @return [Accept] on Accept object managing all types and their order
    def accept
      @accept ||= Request::Accept.new(@env)
    end

    # get the method of the request
    # @return [Symbol] the symbol representation of the method
    def method
      return @method if @method
      @method = extract_method
    end
    # is the method 'GET'?
    # @return [Boolean] true if this is a get request
    def get?;  method == :get;  end
    # is the method 'POST'?
    # @return [Boolean] true if this is a post request
    def post?; method == :post; end
    # is the method 'PUT'?
    # @return [Boolean] true if this is a put request
    def put?;  method == :put;  end
    # is the method 'DELETE'?
    # @return [Boolean] true if this is a delete request
    def delete?; method == :delete; end
    # is the method 'HEAD'?
    # @return [Boolean] true if this is a head request
    def head?; method == :head; end
    # is the method 'PATCH'?
    # @return [Boolean] true if this is a patch request
    def patch?; method == :patch; end

    private

    # constant for the content type key
    # @api private
    CONST_CONTENT_TYPE   = 'CONTENT_TYPE'
    # constant for the http accept key
    # @api private
    CONST_HTTP_ACCEPT    = 'HTTP_ACCEPT'
    # constant for the path info key
    # @api private
    CONST_PATH_INFO      = 'PATH_INFO'
    # constant for the request method key
    # @api private
    CONST_REQUEST_METHOD = 'REQUEST_METHOD'
    # regex to match for valid http methods
    CONST_VALID_METHODS  = /\A(get|post|put|delete|head|link|unlink|patch)\Z/
    # constant for post
    CONST_POST           = 'post'
    # constant for the method keyword
    CONST_METHOD         = '_method'

    # this function tries to figure out what method the request used
    #
    # The problem with extracting the request method is, that every client out
    # there can speak all methods (get, post, put, whatever) but not browsers.
    # When sending a form, they can only send get or post forms, but not put
    # or delete, which makes them a pain to use. So instead an override was
    # introduced in rails and other frameworks to support `_method` in the post
    # payload.
    # So this function does essentially the same, so please do not remove it
    # until browsers learned how to do it.
    # @return [Symbol] the extracted method
    def extract_method
      method = @env[CONST_REQUEST_METHOD].downcase
      return method.to_sym unless method == CONST_POST
      if params.payload.has_key?(CONST_METHOD)
        method = params.payload[CONST_METHOD].downcase
        return method.to_sym if CONST_VALID_METHODS.match(method)
      end
      CONST_POST.to_sym
    end
  end
end