jsonrpc-middleware 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.aiexclude +5 -0
  3. data/.claude/commands/document.md +105 -0
  4. data/.claude/docs/yard.md +602 -0
  5. data/.claude/settings.local.json +2 -1
  6. data/.env.example +5 -0
  7. data/.tool-versions +1 -1
  8. data/CHANGELOG.md +38 -2
  9. data/CLAUDE.md +114 -0
  10. data/README.md +42 -102
  11. data/Rakefile +59 -1
  12. data/examples/README.md +38 -0
  13. data/examples/procedures.rb +6 -1
  14. data/examples/rack/Gemfile.lock +6 -6
  15. data/examples/rack/README.md +26 -1
  16. data/examples/rack/app.rb +1 -1
  17. data/examples/rack-echo/Gemfile.lock +6 -6
  18. data/examples/rack-echo/README.md +23 -1
  19. data/examples/rack-single-file/README.md +37 -0
  20. data/examples/rack-single-file/config.ru +54 -0
  21. data/examples/rails/.gitignore +21 -0
  22. data/examples/rails/Gemfile +15 -0
  23. data/examples/rails/Gemfile.lock +261 -0
  24. data/examples/rails/README.md +32 -0
  25. data/examples/rails/Rakefile +8 -0
  26. data/examples/rails/app/controllers/application_controller.rb +4 -0
  27. data/examples/rails/app/controllers/jsonrpc_controller.rb +44 -0
  28. data/examples/rails/bin/dev +4 -0
  29. data/examples/rails/bin/rails +6 -0
  30. data/examples/rails/bin/rake +6 -0
  31. data/examples/rails/bin/setup +28 -0
  32. data/examples/rails/config/application.rb +47 -0
  33. data/examples/rails/config/boot.rb +5 -0
  34. data/examples/rails/config/credentials.yml.enc +1 -0
  35. data/examples/rails/config/environment.rb +7 -0
  36. data/examples/rails/config/environments/development.rb +42 -0
  37. data/examples/rails/config/environments/production.rb +60 -0
  38. data/examples/rails/config/environments/test.rb +44 -0
  39. data/examples/rails/config/initializers/cors.rb +18 -0
  40. data/examples/rails/config/initializers/filter_parameter_logging.rb +10 -0
  41. data/examples/rails/config/initializers/inflections.rb +18 -0
  42. data/examples/rails/config/initializers/jsonrpc.rb +62 -0
  43. data/examples/rails/config/locales/en.yml +31 -0
  44. data/examples/rails/config/puma.rb +40 -0
  45. data/examples/rails/config/routes.rb +14 -0
  46. data/examples/rails/config.ru +8 -0
  47. data/examples/rails/public/robots.txt +1 -0
  48. data/examples/rails-single-file/README.md +23 -0
  49. data/examples/rails-single-file/config.ru +71 -0
  50. data/examples/rails-single-file-routing/README.md +53 -0
  51. data/examples/rails-single-file-routing/config.ru +62 -0
  52. data/examples/sinatra-classic/Gemfile +9 -0
  53. data/examples/sinatra-classic/Gemfile.lock +95 -0
  54. data/examples/sinatra-classic/README.md +32 -0
  55. data/examples/sinatra-classic/app.rb +52 -0
  56. data/examples/sinatra-classic/config.ru +6 -0
  57. data/examples/sinatra-modular/Gemfile +9 -0
  58. data/examples/sinatra-modular/Gemfile.lock +95 -0
  59. data/examples/sinatra-modular/README.md +32 -0
  60. data/examples/sinatra-modular/app.rb +57 -0
  61. data/examples/sinatra-modular/config.ru +6 -0
  62. data/lib/jsonrpc/batch_request.rb +67 -2
  63. data/lib/jsonrpc/batch_response.rb +56 -0
  64. data/lib/jsonrpc/configuration.rb +156 -14
  65. data/lib/jsonrpc/error.rb +83 -2
  66. data/lib/jsonrpc/errors/internal_error.rb +14 -2
  67. data/lib/jsonrpc/errors/invalid_params_error.rb +13 -1
  68. data/lib/jsonrpc/errors/invalid_request_error.rb +8 -0
  69. data/lib/jsonrpc/errors/method_not_found_error.rb +8 -0
  70. data/lib/jsonrpc/errors/parse_error.rb +8 -0
  71. data/lib/jsonrpc/helpers.rb +212 -21
  72. data/lib/jsonrpc/middleware.rb +211 -5
  73. data/lib/jsonrpc/notification.rb +58 -0
  74. data/lib/jsonrpc/parser.rb +30 -0
  75. data/lib/jsonrpc/railtie/method_constraint.rb +35 -0
  76. data/lib/jsonrpc/railtie.rb +58 -0
  77. data/lib/jsonrpc/request.rb +68 -1
  78. data/lib/jsonrpc/response.rb +76 -0
  79. data/lib/jsonrpc/validator.rb +67 -6
  80. data/lib/jsonrpc/version.rb +11 -1
  81. data/lib/jsonrpc.rb +56 -1
  82. metadata +53 -2
data/lib/jsonrpc/error.rb CHANGED
@@ -3,44 +3,92 @@
3
3
  module JSONRPC
4
4
  # A JSON-RPC 2.0 Error object
5
5
  #
6
+ # @api public
7
+ #
6
8
  # When a rpc call encounters an error, the Response Object must contain an Error object
7
- # with specific properties according to the JSON-RPC 2.0.
9
+ # with specific properties according to the JSON-RPC 2.0 specification.
8
10
  #
9
11
  # @example Create an error
10
12
  # error = JSONRPC::Error.new(
13
+ # "Invalid Request",
11
14
  # code: -32600,
12
- # message: "Invalid Request",
13
15
  # data: { detail: "Additional information about the error" }
14
16
  # )
15
17
  #
16
18
  class Error < StandardError
17
19
  # The request identifier (optional for notifications)
20
+ #
21
+ # @api public
22
+ #
23
+ # @example Get request ID
24
+ # error.request_id # => "1"
25
+ #
26
+ # @example Set request ID
27
+ # error.request_id = "2"
28
+ #
18
29
  # @return [String, Integer, nil]
19
30
  #
20
31
  attr_accessor :request_id
21
32
 
22
33
  # Error code indicating the error type
34
+ #
35
+ # @api public
36
+ #
37
+ # @example Get error code
38
+ # error.code # => -32600
39
+ #
40
+ # @example Set error code
41
+ # error.code = -32601
42
+ #
23
43
  # @return [Integer]
24
44
  #
25
45
  attr_accessor :code
26
46
 
27
47
  # Short description of the error
48
+ #
49
+ # @api public
50
+ #
51
+ # @example Get error message
52
+ # error.message # => "Invalid Request"
53
+ #
54
+ # @example Set error message
55
+ # error.message = "Method not found"
56
+ #
28
57
  # @return [String]
29
58
  #
30
59
  attr_accessor :message
31
60
 
32
61
  # Additional information about the error (optional)
62
+ #
63
+ # @api public
64
+ #
65
+ # @example Get error data
66
+ # error.data # => { "detail" => "Additional info" }
67
+ #
68
+ # @example Set error data
69
+ # error.data = { "field" => "invalid" }
70
+ #
33
71
  # @return [Hash, Array, String, Number, Boolean, nil]
34
72
  #
35
73
  attr_accessor :data
36
74
 
37
75
  # Creates a new JSON-RPC 2.0 Error object
38
76
  #
77
+ # @api public
78
+ #
79
+ # @example Create an error with code and message
80
+ # error = JSONRPC::Error.new("Invalid Request", code: -32600)
81
+ #
82
+ # @example Create an error with additional data
83
+ # error = JSONRPC::Error.new("Invalid params", code: -32602, data: { "field" => "missing" })
84
+ #
39
85
  # @param message [String] short description of the error
40
86
  # @param code [Integer] a number indicating the error type
41
87
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
42
88
  # @param request_id [String, Integer, nil] the request identifier
89
+ #
43
90
  # @raise [ArgumentError] if code is not an Integer
91
+ #
44
92
  # @raise [ArgumentError] if message is not a String
45
93
  #
46
94
  def initialize(message, code:, data: nil, request_id: nil)
@@ -57,6 +105,11 @@ module JSONRPC
57
105
 
58
106
  # Converts the error to a JSON-compatible Hash
59
107
  #
108
+ # @api public
109
+ #
110
+ # @example Convert error to hash
111
+ # error.to_h # => { code: -32600, message: "Invalid Request" }
112
+ #
60
113
  # @return [Hash] the error as a JSON-compatible Hash
61
114
  #
62
115
  def to_h
@@ -65,10 +118,28 @@ module JSONRPC
65
118
  hash
66
119
  end
67
120
 
121
+ # Converts the error to JSON
122
+ #
123
+ # @api public
124
+ #
125
+ # @example Convert error to JSON
126
+ # error.to_json # => '{"code":-32600,"message":"Invalid Request"}'
127
+ #
128
+ # @return [String] the error as a JSON string
129
+ #
68
130
  def to_json(*)
69
131
  to_h.to_json(*)
70
132
  end
71
133
 
134
+ # Converts the error to a complete JSON-RPC response
135
+ #
136
+ # @api public
137
+ #
138
+ # @example Convert error to response
139
+ # error.to_response # => { jsonrpc: "2.0", error: { code: -32600, message: "Invalid Request" }, id: nil }
140
+ #
141
+ # @return [Hash] a complete JSON-RPC response with this error
142
+ #
72
143
  def to_response
73
144
  Response.new(id: request_id, error: self).to_h
74
145
  end
@@ -77,18 +148,28 @@ module JSONRPC
77
148
 
78
149
  # Validates that the code is a valid Integer
79
150
  #
151
+ # @api private
152
+ #
80
153
  # @param code [Integer] the error code
154
+ #
81
155
  # @raise [ArgumentError] if code is not an Integer
82
156
  #
157
+ # @return [void]
158
+ #
83
159
  def validate_code(code)
84
160
  raise ArgumentError, 'Error code must be an Integer' unless code.is_a?(Integer)
85
161
  end
86
162
 
87
163
  # Validates that the message is a String
88
164
  #
165
+ # @api private
166
+ #
89
167
  # @param message [String] the error message
168
+ #
90
169
  # @raise [ArgumentError] if message is not a String
91
170
  #
171
+ # @return [void]
172
+ #
92
173
  def validate_message(message)
93
174
  raise ArgumentError, 'Error message must be a String' unless message.is_a?(String)
94
175
  end
@@ -6,16 +6,28 @@ module JSONRPC
6
6
  # Raised when there was an internal JSON-RPC error.
7
7
  #
8
8
  # @example Create an internal error
9
- # error = JSONRPC::Errors::InternalError.new(data: { details: "Unexpected server error" })
9
+ # error = JSONRPC::Errors::InternalError.new(data: { details: 'Unexpected server error' })
10
10
  #
11
11
  class InternalError < Error
12
12
  # Creates a new Internal Error with code -32603
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an internal error
17
+ # error = JSONRPC::InternalError.new
18
+ #
19
+ # @example Create an internal error with exception details
20
+ # error = JSONRPC::InternalError.new(data: { exception: "NoMethodError" }, request_id: 1)
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
17
25
  #
18
- def initialize(message = 'Internal JSON-RPC error.', data: nil, request_id: nil)
26
+ def initialize(
27
+ message = 'Internal JSON-RPC error.',
28
+ data: nil,
29
+ request_id: nil
30
+ )
19
31
  super(
20
32
  message,
21
33
  code: -32_603,
@@ -11,11 +11,23 @@ module JSONRPC
11
11
  class InvalidParamsError < Error
12
12
  # Creates a new Invalid Params Error with code -32602
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an invalid params error
17
+ # error = JSONRPC::InvalidParamsError.new
18
+ #
19
+ # @example Create an invalid params error with validation details
20
+ # error = JSONRPC::InvalidParamsError.new(data: { errors: ["param a is required"] })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
17
25
  #
18
- def initialize(message = 'Invalid method parameter(s).', data: nil, request_id: nil)
26
+ def initialize(
27
+ message = 'Invalid method parameter(s).',
28
+ data: nil,
29
+ request_id: nil
30
+ )
19
31
  super(
20
32
  message,
21
33
  code: -32_602,
@@ -11,6 +11,14 @@ module JSONRPC
11
11
  class InvalidRequestError < Error
12
12
  # Creates a new Invalid Request Error with code -32600
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an invalid request error
17
+ # error = JSONRPC::InvalidRequestError.new
18
+ #
19
+ # @example Create an invalid request error with custom data
20
+ # error = JSONRPC::InvalidRequestError.new(data: { field: "missing jsonrpc" })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
@@ -11,6 +11,14 @@ module JSONRPC
11
11
  class MethodNotFoundError < Error
12
12
  # Creates a new Method Not Found Error with code -32601
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create a method not found error
17
+ # error = JSONRPC::MethodNotFoundError.new
18
+ #
19
+ # @example Create a method not found error with method name
20
+ # error = JSONRPC::MethodNotFoundError.new(data: { method: "unknown_method" })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
@@ -12,6 +12,14 @@ module JSONRPC
12
12
  class ParseError < Error
13
13
  # Creates a new Parse Error with code -32700
14
14
  #
15
+ # @api public
16
+ #
17
+ # @example Create a parse error with default message
18
+ # error = JSONRPC::ParseError.new
19
+ #
20
+ # @example Create a parse error with custom data
21
+ # error = JSONRPC::ParseError.new(data: { detail: "Unexpected end of input" })
22
+ #
15
23
  # @param message [String] short description of the error
16
24
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
17
25
  #
@@ -3,32 +3,140 @@
3
3
  module JSONRPC
4
4
  # Framework-agnostic helpers for JSON-RPC
5
5
  module Helpers
6
+ # Extends the including class with ClassMethods when module is included
7
+ #
8
+ # @api public
9
+ #
10
+ # @example Include helpers in a class
11
+ # class MyController
12
+ # include JSONRPC::Helpers
13
+ # end
14
+ #
15
+ # @param base [Class] The class including this module
16
+ #
17
+ # @return [void]
18
+ #
6
19
  def self.included(base)
7
20
  base.extend(ClassMethods)
8
21
  end
9
22
 
10
23
  # Class methods for registering JSON-RPC procedure handlers
11
24
  module ClassMethods
25
+ # Registers a JSON-RPC procedure with the given method name
26
+ #
27
+ # @api public
28
+ #
29
+ # @example Register a procedure
30
+ # jsonrpc_method('add') do
31
+ # params do
32
+ # required(:a).value(:integer)
33
+ # required(:b).value(:integer)
34
+ # end
35
+ # end
36
+ #
37
+ # @param method_name [String, Symbol] the name of the method
38
+ #
39
+ # @yield Block containing the procedure definition
40
+ #
41
+ # @return [Configuration::Procedure] the registered procedure
42
+ #
12
43
  def jsonrpc_method(method_name, &)
13
44
  Configuration.instance.procedure(method_name, &)
14
45
  end
15
46
  end
16
47
 
17
- def jsonrpc_batch? = @env.key?('jsonrpc.batch')
18
- def jsonrpc_notification? = @env.key?('jsonrpc.notification')
19
- def jsonrpc_request? = @env.key?('jsonrpc.request')
48
+ # Checks if the current request is a batch request
49
+ #
50
+ # @api public
51
+ #
52
+ # @example Check if request is batch
53
+ # jsonrpc_batch? # => true/false
54
+ #
55
+ # @return [Boolean] true if current request is a batch
56
+ #
57
+ def jsonrpc_batch? = env.key?('jsonrpc.batch')
20
58
 
21
- # Get the current JSON-RPC request object
22
- def jsonrpc_batch = @env['jsonrpc.batch']
23
- def jsonrpc_request = @env['jsonrpc.request']
24
- def jsonrpc_notification = @env['jsonrpc.notification']
59
+ # Checks if the current request is a notification
60
+ #
61
+ # @api public
62
+ #
63
+ # @example Check if request is notification
64
+ # jsonrpc_notification? # => true/false
65
+ #
66
+ # @return [Boolean] true if current request is a notification
67
+ #
68
+ def jsonrpc_notification? = env.key?('jsonrpc.notification')
25
69
 
26
- # Create a JSON-RPC response
70
+ # Checks if the current request is a regular request
71
+ #
72
+ # @api public
73
+ #
74
+ # @example Check if request is regular request
75
+ # jsonrpc_request? # => true/false
76
+ #
77
+ # @return [Boolean] true if current request is a regular request
78
+ #
79
+ def jsonrpc_request? = env.key?('jsonrpc.request')
80
+
81
+ # Gets the current JSON-RPC batch request object
82
+ #
83
+ # @api public
84
+ #
85
+ # @example Get current batch
86
+ # batch = jsonrpc_batch
87
+ #
88
+ # @return [BatchRequest, nil] the current batch request or nil
89
+ #
90
+ def jsonrpc_batch = env['jsonrpc.batch']
91
+
92
+ # Gets the current JSON-RPC request object
93
+ #
94
+ # @api public
95
+ #
96
+ # @example Get current request
97
+ # request = jsonrpc_request
98
+ #
99
+ # @return [Request, nil] the current request or nil
100
+ #
101
+ def jsonrpc_request = env['jsonrpc.request']
102
+
103
+ # Gets the current JSON-RPC notification object
104
+ #
105
+ # @api public
106
+ #
107
+ # @example Get current notification
108
+ # notification = jsonrpc_notification
109
+ #
110
+ # @return [Notification, nil] the current notification or nil
111
+ #
112
+ def jsonrpc_notification = env['jsonrpc.notification']
113
+
114
+ # Creates a JSON-RPC response
115
+ #
116
+ # @api public
117
+ #
118
+ # @example Create a successful response
119
+ # jsonrpc_response(42) # => [200, headers, body]
120
+ #
121
+ # @param result [Object] the result to return
122
+ #
123
+ # @return [Array] Rack response tuple
124
+ #
27
125
  def jsonrpc_response(result)
28
126
  [200, { 'content-type' => 'application/json' }, [Response.new(id: jsonrpc_request.id, result: result).to_json]]
29
127
  end
30
128
 
31
- # Create a JSON-RPC response
129
+ # Creates a JSON-RPC batch response
130
+ #
131
+ # @api public
132
+ #
133
+ # @example Create a batch response
134
+ # jsonrpc_batch_response([response1, response2]) # => [200, headers, body]
135
+ #
136
+ # @param responses [Array] array of responses
137
+ #
138
+ # @return [Array] Rack response tuple
139
+ #
32
140
  def jsonrpc_batch_response(responses)
33
141
  # If batch contained only notifications, responses will be empty or contain only nils
34
142
  return [204, {}, []] if responses.compact.empty?
@@ -36,48 +144,131 @@ module JSONRPC
36
144
  [200, { 'content-type' => 'application/json' }, [responses.to_json]]
37
145
  end
38
146
 
147
+ # Creates a response for a notification (no content)
148
+ #
149
+ # @api public
150
+ #
151
+ # @example Create notification response
152
+ # jsonrpc_notification_response # => [204, {}, []]
153
+ #
154
+ # @return [Array] Rack response tuple with 204 status
155
+ #
39
156
  def jsonrpc_notification_response
40
157
  [204, {}, []]
41
158
  end
42
159
 
43
- # Create a JSON-RPC error response
160
+ # Creates a JSON-RPC error response
161
+ #
162
+ # @api public
163
+ #
164
+ # @example Create error response
165
+ # error = JSONRPC::Error.new(code: -32600, message: "Invalid Request")
166
+ # jsonrpc_error(error) # => JSON string
167
+ #
168
+ # @param error [Error] the error object
169
+ #
170
+ # @return [String] JSON-formatted error response
171
+ #
44
172
  def jsonrpc_error(error)
45
173
  Response.new(id: jsonrpc_request.id, error: error).to_json
46
174
  end
47
175
 
48
- # Get the current JSON-RPC request params object
176
+ # Gets the current JSON-RPC request params object
177
+ #
178
+ # @api public
179
+ #
180
+ # @example Get request parameters
181
+ # params = jsonrpc_params # => [1, 2, 3] or {"a": 1, "b": 2}
182
+ #
183
+ # @return [Array, Hash, nil] the request parameters
184
+ #
49
185
  def jsonrpc_params
50
186
  jsonrpc_request.params
51
187
  end
52
188
 
53
- # Create a Parse error (-32700)
54
- # Used when invalid JSON was received by the server
189
+ # Creates a Parse error (-32700) for invalid JSON
190
+ #
191
+ # @api public
192
+ #
193
+ # @example Create parse error
194
+ # jsonrpc_parse_error # => JSON error response
195
+ #
196
+ # @param data [Object, nil] additional error data
197
+ #
198
+ # @return [String] JSON-formatted error response
199
+ #
55
200
  def jsonrpc_parse_error(data: nil)
56
201
  jsonrpc_error(ParseError.new(data: data))
57
202
  end
58
203
 
59
- # Create an Invalid Request error (-32600)
60
- # Used when the JSON sent is not a valid Request object
204
+ # Creates an Invalid Request error (-32600) for invalid Request objects
205
+ #
206
+ # @api public
207
+ #
208
+ # @example Create invalid request error
209
+ # jsonrpc_invalid_request_error # => JSON error response
210
+ #
211
+ # @param data [Object, nil] additional error data
212
+ #
213
+ # @return [String] JSON-formatted error response
214
+ #
61
215
  def jsonrpc_invalid_request_error(data: nil)
62
216
  jsonrpc_error(InvalidRequestError.new(data: data))
63
217
  end
64
218
 
65
- # Create a Method not found error (-32601)
66
- # Used when the method does not exist / is not available
219
+ # Creates a Method not found error (-32601) for missing methods
220
+ #
221
+ # @api public
222
+ #
223
+ # @example Create method not found error
224
+ # jsonrpc_method_not_found_error # => JSON error response
225
+ #
226
+ # @param data [Object, nil] additional error data
227
+ #
228
+ # @return [String] JSON-formatted error response
229
+ #
67
230
  def jsonrpc_method_not_found_error(data: nil)
68
231
  jsonrpc_error(MethodNotFoundError.new(data: data))
69
232
  end
70
233
 
71
- # Create an Invalid params error (-32602)
72
- # Used when invalid method parameter(s) were received
234
+ # Creates an Invalid params error (-32602) for invalid parameters
235
+ #
236
+ # @api public
237
+ #
238
+ # @example Create invalid params error
239
+ # jsonrpc_invalid_params_error # => JSON error response
240
+ #
241
+ # @param data [Object, nil] additional error data
242
+ #
243
+ # @return [String] JSON-formatted error response
244
+ #
73
245
  def jsonrpc_invalid_params_error(data: nil)
74
246
  jsonrpc_error(InvalidParamsError.new(data: data))
75
247
  end
76
248
 
77
- # Create an Internal error (-32603)
78
- # Used for implementation-defined server errors
249
+ # Creates an Internal error (-32603) for server errors
250
+ #
251
+ # @api public
252
+ #
253
+ # @example Create internal error
254
+ # jsonrpc_internal_error # => JSON error response
255
+ #
256
+ # @param data [Object, nil] additional error data
257
+ #
258
+ # @return [String] JSON-formatted error response
259
+ #
79
260
  def jsonrpc_internal_error(data: nil)
80
261
  jsonrpc_error(InternalError.new(data: data))
81
262
  end
263
+
264
+ # Gets the Rack environment hash from @env or the Rails Request
265
+ #
266
+ # @api private
267
+ #
268
+ # @return [Hash] the Rack environment hash
269
+ #
270
+ def env
271
+ @env ||= request.env
272
+ end
82
273
  end
83
274
  end