hanami-controller 2.2.0.beta1 → 2.2.0.beta2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/hanami/action/csrf_protection.rb +2 -2
- data/lib/hanami/action/params.rb +171 -53
- data/lib/hanami/action/request.rb +1 -4
- data/lib/hanami/action/validatable.rb +117 -28
- data/lib/hanami/action.rb +27 -28
- data/lib/hanami/controller/version.rb +1 -1
- metadata +3 -4
- data/lib/hanami/action/base_params.rb +0 -170
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60b2a7ff4db376b8ef573c5141910b643630febd016d1019d5df4f17a2b29c76
|
4
|
+
data.tar.gz: 3c721bd675ef9cfbb397666dc21080fa8c2c4d2cc771f43dec87eef84511dc1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a1aa20043ecad3e5e79a008f142bc9274f6df4d1ebae9929454263214ecbec0c9449016b376879d296737f5df69e2bfbf5be4da5d5c0bbb128a38fea5d84203
|
7
|
+
data.tar.gz: 543ef54c68b7f0280dce88da5ceb2ca7917a49c045ca7285946c44604023243f16f92b16e57c347c5c1387d475783a6856f8e014d95795285c82f63c2968e959
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
Complete, fast and testable actions for Rack
|
4
4
|
|
5
|
+
## v2.2.0.beta1 - 2024-09-25
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- [Tim Riley, Krzysztof Piotrowski] Add support for using full dry-validation contracts for action param validation, via `Hanami::Action.contract` (#453, #454)
|
10
|
+
|
5
11
|
## v2.2.0.beta1 - 2024-07-16
|
6
12
|
|
7
13
|
### Changed
|
@@ -131,14 +131,14 @@ module Hanami
|
|
131
131
|
return false unless verify_csrf_token?(req, res)
|
132
132
|
|
133
133
|
missing_csrf_token?(req, res) ||
|
134
|
-
!::Rack::Utils.secure_compare(req.session[CSRF_TOKEN], req.params[CSRF_TOKEN])
|
134
|
+
!::Rack::Utils.secure_compare(req.session[CSRF_TOKEN], req.params.raw[CSRF_TOKEN.to_s])
|
135
135
|
end
|
136
136
|
|
137
137
|
# Verify the CSRF token was passed in params.
|
138
138
|
#
|
139
139
|
# @api private
|
140
140
|
def missing_csrf_token?(req, *)
|
141
|
-
Hanami::Utils::Blank.blank?(req.params[CSRF_TOKEN])
|
141
|
+
Hanami::Utils::Blank.blank?(req.params.raw[CSRF_TOKEN.to_s])
|
142
142
|
end
|
143
143
|
|
144
144
|
# Generates a random CSRF Token
|
data/lib/hanami/action/params.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "rack/request"
|
4
|
+
require "hanami/utils/hash"
|
4
5
|
|
5
6
|
module Hanami
|
6
7
|
class Action
|
8
|
+
# Provides access to params included in a Rack request.
|
9
|
+
#
|
10
|
+
# Offers useful access to params via methods like {#[]}, {#get} and {#to_h}.
|
11
|
+
#
|
12
|
+
# These params are available via {Request#params}.
|
13
|
+
#
|
14
|
+
# This class is used by default when {Hanami::Action::Validatable} is not included, or when no
|
15
|
+
# {Validatable::ClassMethods#params params} validation schema is defined.
|
16
|
+
#
|
17
|
+
# @see Hanami::Action::Request#params
|
18
|
+
|
7
19
|
# A set of params requested by the client
|
8
20
|
#
|
9
21
|
# It's able to extract the relevant params from a Rack env of from an Hash.
|
@@ -14,8 +26,23 @@ module Hanami
|
|
14
26
|
# * Default: it returns the given hash as it is. It's useful for testing purposes.
|
15
27
|
#
|
16
28
|
# @since 0.1.0
|
17
|
-
class Params
|
18
|
-
|
29
|
+
class Params
|
30
|
+
# Permits all params and returns them as symbolized keys. Stands in for a
|
31
|
+
# `Dry::Validation::Contract` when neither {Action.params} nor {Action.contract} are called.
|
32
|
+
#
|
33
|
+
# @see {Params#initialize}
|
34
|
+
#
|
35
|
+
# @since 2.2.0
|
36
|
+
# @api private
|
37
|
+
class DefaultContract
|
38
|
+
def self.call(attrs) = Result.new(attrs)
|
39
|
+
|
40
|
+
class Result
|
41
|
+
def initialize(attrs) = @attrs = Utils::Hash.deep_symbolize(attrs)
|
42
|
+
def to_h = @attrs
|
43
|
+
def errors = {}
|
44
|
+
end
|
45
|
+
end
|
19
46
|
|
20
47
|
# Params errors
|
21
48
|
#
|
@@ -107,46 +134,57 @@ module Hanami
|
|
107
134
|
end
|
108
135
|
end
|
109
136
|
|
110
|
-
#
|
137
|
+
# Defines validations for the params, using the `params` schema of a dry-validation contract.
|
138
|
+
#
|
139
|
+
# @param block [Proc] the schema definition
|
111
140
|
#
|
141
|
+
# @see https://dry-rb.org/gems/dry-validation/
|
142
|
+
#
|
143
|
+
# @api public
|
112
144
|
# @since 0.7.0
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
145
|
+
def self.params(&block)
|
146
|
+
unless defined?(Dry::Validation::Contract)
|
147
|
+
message = %(To use `.params`, please add the "hanami-validations" gem to your Gemfile)
|
148
|
+
raise NoMethodError, message
|
117
149
|
end
|
150
|
+
|
151
|
+
@_contract = Class.new(Dry::Validation::Contract) { params(&block || -> {}) }.new
|
152
|
+
end
|
153
|
+
|
154
|
+
class << self
|
155
|
+
# @api private
|
156
|
+
# @since 2.2.0
|
157
|
+
attr_reader :_contract
|
118
158
|
end
|
119
159
|
|
120
|
-
#
|
160
|
+
# @attr_reader env [Hash] the Rack env
|
121
161
|
#
|
122
|
-
# @
|
162
|
+
# @since 0.7.0
|
163
|
+
# @api private
|
164
|
+
attr_reader :env
|
165
|
+
|
166
|
+
# @attr_reader raw [Hash] the raw params from the request
|
123
167
|
#
|
124
168
|
# @since 0.7.0
|
169
|
+
# @api private
|
170
|
+
attr_reader :raw
|
171
|
+
|
172
|
+
# Returns structured error messages
|
125
173
|
#
|
126
|
-
# @
|
174
|
+
# @return [Hash]
|
127
175
|
#
|
128
|
-
# @
|
129
|
-
# class Signup < Hanami::Action
|
130
|
-
# MEGABYTE = 1024 ** 2
|
131
|
-
#
|
132
|
-
# params do
|
133
|
-
# required(:first_name).filled(:str?)
|
134
|
-
# required(:last_name).filled(:str?)
|
135
|
-
# required(:email).filled?(:str?, format?: /\A.+@.+\z/)
|
136
|
-
# required(:password).filled(:str?).confirmation
|
137
|
-
# required(:terms_of_service).filled(:bool?)
|
138
|
-
# required(:age).filled(:int?, included_in?: 18..99)
|
139
|
-
# optional(:avatar).filled(size?: 1..(MEGABYTE * 3))
|
140
|
-
# end
|
176
|
+
# @since 0.7.0
|
141
177
|
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
|
148
|
-
|
149
|
-
|
178
|
+
# @example
|
179
|
+
# params.errors
|
180
|
+
# # => {
|
181
|
+
# :email=>["is missing", "is in invalid format"],
|
182
|
+
# :name=>["is missing"],
|
183
|
+
# :tos=>["is missing"],
|
184
|
+
# :age=>["is missing"],
|
185
|
+
# :address=>["is missing"]
|
186
|
+
# }
|
187
|
+
attr_reader :errors
|
150
188
|
|
151
189
|
# Initialize the params and freeze them.
|
152
190
|
#
|
@@ -156,40 +194,75 @@ module Hanami
|
|
156
194
|
#
|
157
195
|
# @since 0.1.0
|
158
196
|
# @api private
|
159
|
-
def initialize(env)
|
197
|
+
def initialize(env:, contract: nil)
|
160
198
|
@env = env
|
161
|
-
|
162
|
-
|
199
|
+
@raw = _extract_params
|
200
|
+
|
201
|
+
# Fall back to the default contract here, rather than in the `._contract` method itself.
|
202
|
+
# This allows `._contract` to return nil when there is no user-defined contract, which is
|
203
|
+
# important for the backwards compatibility behavior in `Validatable::ClassMethods#params`.
|
204
|
+
contract ||= self.class._contract || DefaultContract
|
205
|
+
validation = contract.call(raw)
|
206
|
+
|
163
207
|
@params = validation.to_h
|
164
|
-
@errors = Errors.new(validation.
|
208
|
+
@errors = Errors.new(validation.errors.to_h)
|
209
|
+
|
165
210
|
freeze
|
166
211
|
end
|
167
212
|
|
168
|
-
# Returns
|
213
|
+
# Returns the value for the given params key.
|
169
214
|
#
|
170
|
-
# @
|
215
|
+
# @param key [Symbol] the key
|
216
|
+
#
|
217
|
+
# @return [Object,nil] the associated value, if found
|
171
218
|
#
|
172
|
-
# @since 0.
|
173
|
-
|
174
|
-
|
219
|
+
# @since 0.7.0
|
220
|
+
# @api public
|
221
|
+
def [](key)
|
222
|
+
@params[key]
|
175
223
|
end
|
176
224
|
|
177
|
-
# Returns
|
225
|
+
# Returns an value associated with the given params key.
|
178
226
|
#
|
179
|
-
#
|
227
|
+
# You can access nested attributes by listing all the keys in the path. This uses the same key
|
228
|
+
# path semantics as `Hash#dig`.
|
180
229
|
#
|
181
|
-
# @
|
230
|
+
# @param keys [Array<Symbol,Integer>] the key
|
231
|
+
#
|
232
|
+
# @return [Object,NilClass] return the associated value, if found
|
182
233
|
#
|
183
234
|
# @example
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
|
235
|
+
# require "hanami/controller"
|
236
|
+
#
|
237
|
+
# module Deliveries
|
238
|
+
# class Create < Hanami::Action
|
239
|
+
# def handle(req, *)
|
240
|
+
# req.params.get(:customer_name) # => "Luca"
|
241
|
+
# req.params.get(:uknown) # => nil
|
242
|
+
#
|
243
|
+
# req.params.get(:address, :city) # => "Rome"
|
244
|
+
# req.params.get(:address, :unknown) # => nil
|
245
|
+
#
|
246
|
+
# req.params.get(:tags, 0) # => "foo"
|
247
|
+
# req.params.get(:tags, 1) # => "bar"
|
248
|
+
# req.params.get(:tags, 999) # => nil
|
249
|
+
#
|
250
|
+
# req.params.get(nil) # => nil
|
251
|
+
# end
|
252
|
+
# end
|
253
|
+
# end
|
254
|
+
#
|
255
|
+
# @since 0.7.0
|
256
|
+
# @api public
|
257
|
+
def get(*keys)
|
258
|
+
@params.dig(*keys)
|
259
|
+
end
|
260
|
+
|
261
|
+
# This is for compatibility with Hanami::Helpers::FormHelper::Values
|
262
|
+
#
|
263
|
+
# @api private
|
264
|
+
# @since 0.8.0
|
265
|
+
alias_method :dig, :get
|
193
266
|
|
194
267
|
# Returns flat collection of full error messages
|
195
268
|
#
|
@@ -234,6 +307,21 @@ module Hanami
|
|
234
307
|
errors.empty?
|
235
308
|
end
|
236
309
|
|
310
|
+
# Iterates over the params.
|
311
|
+
#
|
312
|
+
# Calls the given block with each param key-value pair; returns the full hash of params.
|
313
|
+
#
|
314
|
+
# @yieldparam key [Symbol]
|
315
|
+
# @yieldparam value [Object]
|
316
|
+
#
|
317
|
+
# @return [to_h]
|
318
|
+
#
|
319
|
+
# @since 0.7.1
|
320
|
+
# @api public
|
321
|
+
def each(&blk)
|
322
|
+
to_h.each(&blk)
|
323
|
+
end
|
324
|
+
|
237
325
|
# Serialize validated params to Hash
|
238
326
|
#
|
239
327
|
# @return [::Hash]
|
@@ -252,6 +340,36 @@ module Hanami
|
|
252
340
|
def deconstruct_keys(*)
|
253
341
|
to_hash
|
254
342
|
end
|
343
|
+
|
344
|
+
private
|
345
|
+
|
346
|
+
# @since 0.7.0
|
347
|
+
# @api private
|
348
|
+
def _extract_params
|
349
|
+
result = {}
|
350
|
+
|
351
|
+
if env.key?(Action::RACK_INPUT)
|
352
|
+
result.merge! ::Rack::Request.new(env).params
|
353
|
+
result.merge! _router_params
|
354
|
+
else
|
355
|
+
result.merge! _router_params(env)
|
356
|
+
env[Action::REQUEST_METHOD] ||= Action::DEFAULT_REQUEST_METHOD
|
357
|
+
end
|
358
|
+
|
359
|
+
result
|
360
|
+
end
|
361
|
+
|
362
|
+
# @since 0.7.0
|
363
|
+
# @api private
|
364
|
+
def _router_params(fallback = {})
|
365
|
+
env.fetch(ROUTER_PARAMS) do
|
366
|
+
if session = fallback.delete(Action::RACK_SESSION)
|
367
|
+
fallback[Action::RACK_SESSION] = Utils::Hash.deep_symbolize(session)
|
368
|
+
end
|
369
|
+
|
370
|
+
fallback
|
371
|
+
end
|
372
|
+
end
|
255
373
|
end
|
256
374
|
end
|
257
375
|
end
|
@@ -18,10 +18,7 @@ module Hanami
|
|
18
18
|
class Request < ::Rack::Request
|
19
19
|
# Returns the request's params.
|
20
20
|
#
|
21
|
-
#
|
22
|
-
# {BaseParams}.
|
23
|
-
#
|
24
|
-
# @return [BaseParams,Params]
|
21
|
+
# @return [Params]
|
25
22
|
#
|
26
23
|
# @since 2.0.0
|
27
24
|
# @api public
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "params"
|
4
|
-
|
5
3
|
module Hanami
|
6
4
|
class Action
|
7
5
|
# Support for validating params when calling actions.
|
@@ -28,34 +26,32 @@ module Hanami
|
|
28
26
|
# @since 0.1.0
|
29
27
|
# @api private
|
30
28
|
module ClassMethods
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# This feature isn't mandatory, but higly recommended for security
|
34
|
-
# reasons.
|
29
|
+
# Defines a validation schema for the params passed to {Hanami::Action#call}.
|
35
30
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
31
|
+
# This feature isn't mandatory, but is highly recommended for secure handling of params:
|
32
|
+
# because params come from an untrusted source, it's good practice to filter these to only
|
33
|
+
# the keys and types required for your action's use case.
|
39
34
|
#
|
40
|
-
#
|
41
|
-
#
|
35
|
+
# The given block is evaluated inside a `params` schema of a `Dry::Validation::Contract`
|
36
|
+
# class. This constrains the validation to simple structure and type rules only. If you want
|
37
|
+
# to use all the features of dry-validation contracts, use {#contract} instead.
|
42
38
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# Hanami::Action::Params.
|
39
|
+
# The resulting contract becomes part of a dedicated params class for the action, inheriting
|
40
|
+
# from {Hanami::Action::Params}.
|
46
41
|
#
|
47
|
-
#
|
48
|
-
# Hanami::Action::Params.
|
42
|
+
# Instead of defining the params validation schema inline, you can alternatively provide a
|
43
|
+
# concrete params class, which should inherit from {Hanami::Action::Params}.
|
49
44
|
#
|
50
45
|
# @param klass [Class,nil] a Hanami::Action::Params subclass
|
51
|
-
# @param
|
46
|
+
# @param block [Proc] the params schema definition
|
52
47
|
#
|
53
48
|
# @return void
|
54
49
|
#
|
50
|
+
# @see #contract
|
55
51
|
# @see Hanami::Action::Params
|
56
|
-
# @see https://
|
52
|
+
# @see https://dry-rb.org/gems/dry-validation/
|
57
53
|
#
|
58
|
-
# @example
|
54
|
+
# @example Inline definition
|
59
55
|
# require "hanami/controller"
|
60
56
|
#
|
61
57
|
# class Signup < Hanami::Action
|
@@ -78,9 +74,11 @@ module Hanami
|
|
78
74
|
# require "hanami/controller"
|
79
75
|
#
|
80
76
|
# class SignupParams < Hanami::Action::Params
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
77
|
+
# params do
|
78
|
+
# required(:first_name)
|
79
|
+
# required(:last_name)
|
80
|
+
# required(:email)
|
81
|
+
# end
|
84
82
|
# end
|
85
83
|
#
|
86
84
|
# class Signup < Hanami::Action
|
@@ -95,15 +93,106 @@ module Hanami
|
|
95
93
|
# end
|
96
94
|
# end
|
97
95
|
#
|
96
|
+
# @api public
|
98
97
|
# @since 0.3.0
|
98
|
+
def params(klass = nil, &block)
|
99
|
+
contract_class =
|
100
|
+
if klass.nil?
|
101
|
+
Class.new(Dry::Validation::Contract) { params(&block) }
|
102
|
+
elsif klass < Params
|
103
|
+
# Handle subclasses of Hanami::Action::Params.
|
104
|
+
klass._contract.class
|
105
|
+
else
|
106
|
+
klass
|
107
|
+
end
|
108
|
+
|
109
|
+
config.contract_class = contract_class
|
110
|
+
end
|
111
|
+
|
112
|
+
# Defines a validation contract for the params passed to {Hanami::Action#call}.
|
113
|
+
#
|
114
|
+
# This feature isn't mandatory, but is highly recommended for secure handling of params:
|
115
|
+
# because params come from an untrusted source, it's good practice to filter these to only
|
116
|
+
# the keys and types required for your action's use case.
|
117
|
+
#
|
118
|
+
# The given block is evaluated inside a `Dry::Validation::Contract` class. This allows you
|
119
|
+
# to use all features of dry-validation contracts
|
120
|
+
#
|
121
|
+
# The resulting contract becomes part of a dedicated params class for the action, inheriting
|
122
|
+
# from {Hanami::Action::Params}.
|
123
|
+
#
|
124
|
+
# Instead of defining the params validation contract inline, you can alternatively provide a
|
125
|
+
# concrete params class, which should inherit from {Hanami::Action::Params}.
|
126
|
+
#
|
127
|
+
# @param klass [Class,nil] a Hanami::Action::Params subclass
|
128
|
+
# @param block [Proc] the params schema definition
|
129
|
+
#
|
130
|
+
# @return void
|
131
|
+
#
|
132
|
+
# @see #params
|
133
|
+
# @see Hanami::Action::Params
|
134
|
+
# @see https://dry-rb.org/gems/dry-validation/
|
135
|
+
#
|
136
|
+
# @example Inline definition
|
137
|
+
# require "hanami/controller"
|
138
|
+
#
|
139
|
+
# class Signup < Hanami::Action
|
140
|
+
# contract do
|
141
|
+
# params do
|
142
|
+
# required(:first_name)
|
143
|
+
# required(:last_name)
|
144
|
+
# required(:email)
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# rule(:email) do
|
148
|
+
# # custom rule logic here
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# def handle(req, *)
|
153
|
+
# puts req.params.class # => Signup::Params
|
154
|
+
# puts req.params.class.superclass # => Hanami::Action::Params
|
155
|
+
#
|
156
|
+
# puts req.params[:first_name] # => "Luca"
|
157
|
+
# puts req.params[:admin] # => nil
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# @example Concrete class
|
162
|
+
# require "hanami/controller"
|
163
|
+
#
|
164
|
+
# class SignupParams < Hanami::Action::Params
|
165
|
+
# contract do
|
166
|
+
# params do
|
167
|
+
# required(:first_name)
|
168
|
+
# required(:last_name)
|
169
|
+
# required(:email)
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# rule(:email) do
|
173
|
+
# # custom rule logic here
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
# class Signup < Hanami::Action
|
179
|
+
# params SignupParams
|
180
|
+
#
|
181
|
+
# def handle(req, *)
|
182
|
+
# puts req.params.class # => SignupParams
|
183
|
+
# puts req.params.class.superclass # => Hanami::Action::Params
|
184
|
+
#
|
185
|
+
# req.params[:first_name] # => "Luca"
|
186
|
+
# req.params[:admin] # => nil
|
187
|
+
# end
|
188
|
+
# end
|
189
|
+
#
|
99
190
|
# @api public
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
klass.class_eval { params(&blk) }
|
104
|
-
end
|
191
|
+
# @since 2.2.0
|
192
|
+
def contract(klass = nil, &block)
|
193
|
+
contract_class = klass || Class.new(Dry::Validation::Contract, &block)
|
105
194
|
|
106
|
-
|
195
|
+
config.contract_class = contract_class
|
107
196
|
end
|
108
197
|
end
|
109
198
|
end
|
data/lib/hanami/action.rb
CHANGED
@@ -39,7 +39,7 @@ module Hanami
|
|
39
39
|
loader.ignore(
|
40
40
|
"#{root}/hanami-controller.rb",
|
41
41
|
"#{root}/hanami/controller/version.rb",
|
42
|
-
"#{root}/hanami/action/{constants,errors,
|
42
|
+
"#{root}/hanami/action/{constants,errors,validatable}.rb"
|
43
43
|
)
|
44
44
|
loader.inflector.inflect("csrf_protection" => "CSRFProtection")
|
45
45
|
end
|
@@ -72,6 +72,7 @@ module Hanami
|
|
72
72
|
setting :public_directory, default: Config::DEFAULT_PUBLIC_DIRECTORY
|
73
73
|
setting :before_callbacks, default: Utils::Callbacks::Chain.new, mutable: true
|
74
74
|
setting :after_callbacks, default: Utils::Callbacks::Chain.new, mutable: true
|
75
|
+
setting :contract_class
|
75
76
|
|
76
77
|
# @!scope class
|
77
78
|
|
@@ -105,37 +106,30 @@ module Hanami
|
|
105
106
|
include Validatable if defined?(Validatable)
|
106
107
|
end
|
107
108
|
end
|
108
|
-
|
109
|
-
if instance_variable_defined?(:@params_class)
|
110
|
-
subclass.instance_variable_set(:@params_class, @params_class)
|
111
|
-
end
|
112
109
|
end
|
113
110
|
|
114
|
-
#
|
115
|
-
#
|
116
|
-
# Returns the class which has been provided to define the
|
117
|
-
# params. By default this will be Hanami::Action::Params.
|
111
|
+
# Placeholder for the `.params` method. Raises an error when the hanami-validations gem is not
|
112
|
+
# installed.
|
118
113
|
#
|
119
|
-
# @
|
120
|
-
# Hanami::Action::Params
|
114
|
+
# @raise [NoMethodError]
|
121
115
|
#
|
122
|
-
# @api
|
123
|
-
# @since 0.
|
124
|
-
def self.
|
125
|
-
|
116
|
+
# @api public
|
117
|
+
# @since 2.0.0
|
118
|
+
def self.params(_klass = nil)
|
119
|
+
message = %(To use `.params`, please add the "hanami-validations" gem to your Gemfile)
|
120
|
+
raise NoMethodError, message
|
126
121
|
end
|
127
122
|
|
128
|
-
# Placeholder
|
129
|
-
#
|
130
|
-
# Raises a developer friendly error to include `hanami/validations`.
|
123
|
+
# Placeholder for the `.contract` method. Raises an error when the hanami-validations gem is not
|
124
|
+
# installed.
|
131
125
|
#
|
132
126
|
# @raise [NoMethodError]
|
133
127
|
#
|
134
128
|
# @api public
|
135
|
-
# @since 2.
|
136
|
-
def self.
|
137
|
-
|
138
|
-
|
129
|
+
# @since 2.2.0
|
130
|
+
def self.contract
|
131
|
+
message = %(To use `.contract`, please add the "hanami-validations" gem to your Gemfile)
|
132
|
+
raise NoMethodError, message
|
139
133
|
end
|
140
134
|
|
141
135
|
# @overload self.append_before(*callbacks, &block)
|
@@ -287,12 +281,21 @@ module Hanami
|
|
287
281
|
config.handle_exception(...)
|
288
282
|
end
|
289
283
|
|
284
|
+
# @since 2.0.0
|
285
|
+
# @api private
|
286
|
+
private attr_reader :config
|
287
|
+
|
288
|
+
# @since 2.2.0
|
289
|
+
# @api private
|
290
|
+
private attr_reader :contract
|
291
|
+
|
290
292
|
# Returns a new action
|
291
293
|
#
|
292
294
|
# @since 2.0.0
|
293
295
|
# @api public
|
294
|
-
def initialize(config: self.class.config)
|
296
|
+
def initialize(config: self.class.config, contract: nil)
|
295
297
|
@config = config
|
298
|
+
@contract = contract || config.contract_class&.new # TODO: tests showing this overridden by a dep
|
296
299
|
freeze
|
297
300
|
end
|
298
301
|
|
@@ -305,7 +308,7 @@ module Hanami
|
|
305
308
|
response = nil
|
306
309
|
|
307
310
|
halted = catch :halt do
|
308
|
-
params
|
311
|
+
params = Params.new(env: env, contract: contract)
|
309
312
|
request = build_request(
|
310
313
|
env: env,
|
311
314
|
params: params,
|
@@ -401,10 +404,6 @@ module Hanami
|
|
401
404
|
|
402
405
|
private
|
403
406
|
|
404
|
-
# @since 2.0.0
|
405
|
-
# @api private
|
406
|
-
attr_reader :config
|
407
|
-
|
408
407
|
# @since 2.0.0
|
409
408
|
# @api private
|
410
409
|
def enforce_accepted_mime_types(request)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.0.
|
4
|
+
version: 2.2.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -175,7 +175,6 @@ files:
|
|
175
175
|
- hanami-controller.gemspec
|
176
176
|
- lib/hanami-controller.rb
|
177
177
|
- lib/hanami/action.rb
|
178
|
-
- lib/hanami/action/base_params.rb
|
179
178
|
- lib/hanami/action/cache.rb
|
180
179
|
- lib/hanami/action/cache/cache_control.rb
|
181
180
|
- lib/hanami/action/cache/conditional_get.rb
|
@@ -222,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
221
|
- !ruby/object:Gem::Version
|
223
222
|
version: '0'
|
224
223
|
requirements: []
|
225
|
-
rubygems_version: 3.5.
|
224
|
+
rubygems_version: 3.5.16
|
226
225
|
signing_key:
|
227
226
|
specification_version: 4
|
228
227
|
summary: Complete, fast and testable actions for Rack and Hanami
|
@@ -1,170 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rack/request"
|
4
|
-
require "hanami/utils/hash"
|
5
|
-
|
6
|
-
module Hanami
|
7
|
-
class Action
|
8
|
-
# Provides access to params included in a Rack request.
|
9
|
-
#
|
10
|
-
# Offers useful access to params via methods like {#[]}, {#get} and {#to_h}.
|
11
|
-
#
|
12
|
-
# These params are available via {Request#params}.
|
13
|
-
#
|
14
|
-
# This class is used by default when {Hanami::Action::Validatable} is not included, or when no
|
15
|
-
# {Validatable::ClassMethods#params params} validation schema is defined.
|
16
|
-
#
|
17
|
-
# @see Hanami::Action::Request#params
|
18
|
-
#
|
19
|
-
# @api private
|
20
|
-
# @since 0.7.0
|
21
|
-
class BaseParams
|
22
|
-
# @attr_reader env [Hash] the Rack env
|
23
|
-
#
|
24
|
-
# @since 0.7.0
|
25
|
-
# @api private
|
26
|
-
attr_reader :env
|
27
|
-
|
28
|
-
# @attr_reader raw [Hash] the raw params from the request
|
29
|
-
#
|
30
|
-
# @since 0.7.0
|
31
|
-
# @api private
|
32
|
-
attr_reader :raw
|
33
|
-
|
34
|
-
# Returns a new frozen params object for the Rack env.
|
35
|
-
#
|
36
|
-
# @param env [Hash] a Rack env or an hash of params.
|
37
|
-
#
|
38
|
-
# @since 0.7.0
|
39
|
-
# @api private
|
40
|
-
def initialize(env)
|
41
|
-
@env = env
|
42
|
-
@raw = _extract_params
|
43
|
-
@params = Utils::Hash.deep_symbolize(@raw)
|
44
|
-
freeze
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns the value for the given params key.
|
48
|
-
#
|
49
|
-
# @param key [Symbol] the key
|
50
|
-
#
|
51
|
-
# @return [Object,nil] the associated value, if found
|
52
|
-
#
|
53
|
-
# @since 0.7.0
|
54
|
-
# @api public
|
55
|
-
def [](key)
|
56
|
-
@params[key]
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns an value associated with the given params key.
|
60
|
-
#
|
61
|
-
# You can access nested attributes by listing all the keys in the path. This uses the same key
|
62
|
-
# path semantics as `Hash#dig`.
|
63
|
-
#
|
64
|
-
# @param keys [Array<Symbol,Integer>] the key
|
65
|
-
#
|
66
|
-
# @return [Object,NilClass] return the associated value, if found
|
67
|
-
#
|
68
|
-
# @example
|
69
|
-
# require "hanami/controller"
|
70
|
-
#
|
71
|
-
# module Deliveries
|
72
|
-
# class Create < Hanami::Action
|
73
|
-
# def handle(req, *)
|
74
|
-
# req.params.get(:customer_name) # => "Luca"
|
75
|
-
# req.params.get(:uknown) # => nil
|
76
|
-
#
|
77
|
-
# req.params.get(:address, :city) # => "Rome"
|
78
|
-
# req.params.get(:address, :unknown) # => nil
|
79
|
-
#
|
80
|
-
# req.params.get(:tags, 0) # => "foo"
|
81
|
-
# req.params.get(:tags, 1) # => "bar"
|
82
|
-
# req.params.get(:tags, 999) # => nil
|
83
|
-
#
|
84
|
-
# req.params.get(nil) # => nil
|
85
|
-
# end
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
# @since 0.7.0
|
90
|
-
# @api public
|
91
|
-
def get(*keys)
|
92
|
-
@params.dig(*keys)
|
93
|
-
end
|
94
|
-
|
95
|
-
# This is for compatibility with Hanami::Helpers::FormHelper::Values
|
96
|
-
#
|
97
|
-
# @api private
|
98
|
-
# @since 0.8.0
|
99
|
-
alias_method :dig, :get
|
100
|
-
|
101
|
-
# Returns true at all times, providing a common interface with {Params}.
|
102
|
-
#
|
103
|
-
# @return [TrueClass] always returns true
|
104
|
-
#
|
105
|
-
# @see Hanami::Action::Params#valid?
|
106
|
-
#
|
107
|
-
# @api public
|
108
|
-
# @since 0.7.0
|
109
|
-
def valid?
|
110
|
-
true
|
111
|
-
end
|
112
|
-
|
113
|
-
# Returns a hash of the parsed request params.
|
114
|
-
#
|
115
|
-
# @return [Hash]
|
116
|
-
#
|
117
|
-
# @since 0.7.0
|
118
|
-
# @api public
|
119
|
-
def to_h
|
120
|
-
@params
|
121
|
-
end
|
122
|
-
alias_method :to_hash, :to_h
|
123
|
-
|
124
|
-
# Iterates over the params.
|
125
|
-
#
|
126
|
-
# Calls the given block with each param key-value pair; returns the full hash of params.
|
127
|
-
#
|
128
|
-
# @yieldparam key [Symbol]
|
129
|
-
# @yieldparam value [Object]
|
130
|
-
#
|
131
|
-
# @return [to_h]
|
132
|
-
#
|
133
|
-
# @since 0.7.1
|
134
|
-
# @api public
|
135
|
-
def each(&blk)
|
136
|
-
to_h.each(&blk)
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
# @since 0.7.0
|
142
|
-
# @api private
|
143
|
-
def _extract_params
|
144
|
-
result = {}
|
145
|
-
|
146
|
-
if env.key?(Action::RACK_INPUT)
|
147
|
-
result.merge! ::Rack::Request.new(env).params
|
148
|
-
result.merge! _router_params
|
149
|
-
else
|
150
|
-
result.merge! _router_params(env)
|
151
|
-
env[Action::REQUEST_METHOD] ||= Action::DEFAULT_REQUEST_METHOD
|
152
|
-
end
|
153
|
-
|
154
|
-
result
|
155
|
-
end
|
156
|
-
|
157
|
-
# @since 0.7.0
|
158
|
-
# @api private
|
159
|
-
def _router_params(fallback = {})
|
160
|
-
env.fetch(ROUTER_PARAMS) do
|
161
|
-
if session = fallback.delete(Action::RACK_SESSION)
|
162
|
-
fallback[Action::RACK_SESSION] = Utils::Hash.deep_symbolize(session)
|
163
|
-
end
|
164
|
-
|
165
|
-
fallback
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|