hanami 2.3.0.beta1 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -1
- data/README.md +1 -3
- data/hanami.gemspec +8 -8
- data/lib/hanami/config/actions/content_security_policy.rb +2 -2
- data/lib/hanami/config/actions.rb +3 -2
- data/lib/hanami/config/router.rb +1 -0
- data/lib/hanami/config.rb +11 -19
- data/lib/hanami/extensions/action.rb +1 -0
- data/lib/hanami/extensions/operation/slice_configured_db_operation.rb +88 -0
- data/lib/hanami/extensions/operation.rb +8 -23
- data/lib/hanami/extensions/view/context.rb +2 -11
- data/lib/hanami/extensions/view/part.rb +3 -0
- data/lib/hanami/extensions/view/scope.rb +3 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +0 -7
- data/lib/hanami/extensions/view.rb +1 -0
- data/lib/hanami/helpers/assets_helper.rb +2 -2
- data/lib/hanami/middleware/content_security_policy_nonce.rb +3 -3
- data/lib/hanami/routes.rb +4 -3
- data/lib/hanami/slice/router.rb +201 -12
- data/lib/hanami/slice.rb +13 -0
- data/lib/hanami/slice_configurable.rb +1 -3
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +25 -8
- data/lib/hanami.rb +15 -1
- data/spec/integration/action/format_config_spec.rb +2 -67
- data/spec/integration/action/slice_configuration_spec.rb +36 -36
- data/spec/integration/logging/request_logging_spec.rb +16 -0
- data/spec/integration/operations/extension_spec.rb +63 -0
- data/spec/integration/rack_app/body_parser_spec.rb +3 -3
- data/spec/integration/rack_app/rack_app_spec.rb +2 -2
- data/spec/integration/router/resource_routes_spec.rb +281 -0
- data/spec/unit/hanami/config/actions_spec.rb +2 -2
- metadata +19 -20
- data/spec/integration/view/context/settings_spec.rb +0 -46
- data/spec/unit/hanami/version_spec.rb +0 -7
data/lib/hanami/slice/router.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "hanami/router"
|
|
4
|
+
require_relative "routing/resolver"
|
|
5
|
+
require_relative "routing/middleware/stack"
|
|
4
6
|
|
|
5
7
|
module Hanami
|
|
6
8
|
class Slice
|
|
@@ -10,27 +12,27 @@ module Hanami
|
|
|
10
12
|
# {Hanami::Slice::ClassMethods#router router}.
|
|
11
13
|
#
|
|
12
14
|
# @api private
|
|
13
|
-
# @since 2.0.0
|
|
14
15
|
class Router < ::Hanami::Router
|
|
15
16
|
# @api private
|
|
16
|
-
|
|
17
|
+
attr_reader :inflector
|
|
18
|
+
|
|
19
|
+
# @api private
|
|
17
20
|
attr_reader :middleware_stack
|
|
18
21
|
|
|
19
22
|
# @api private
|
|
20
|
-
# @since 2.0.0
|
|
21
23
|
attr_reader :path_prefix
|
|
22
24
|
|
|
23
25
|
# @api private
|
|
24
|
-
|
|
25
|
-
def initialize(routes:, middleware_stack: Routing::Middleware::Stack.new, prefix: ::Hanami::Router::DEFAULT_PREFIX, **kwargs, &blk)
|
|
26
|
+
def initialize(routes:, inflector:, middleware_stack: Routing::Middleware::Stack.new, prefix: ::Hanami::Router::DEFAULT_PREFIX, **kwargs, &blk)
|
|
26
27
|
@path_prefix = Hanami::Router::Prefix.new(prefix)
|
|
28
|
+
@inflector = inflector
|
|
27
29
|
@middleware_stack = middleware_stack
|
|
30
|
+
@resource_scope = []
|
|
28
31
|
instance_eval(&blk)
|
|
29
32
|
super(**kwargs, &routes)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
# @api private
|
|
33
|
-
# @since 2.0.0
|
|
34
36
|
def freeze
|
|
35
37
|
return self if frozen?
|
|
36
38
|
|
|
@@ -39,7 +41,11 @@ module Hanami
|
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# @api private
|
|
42
|
-
|
|
44
|
+
def to_rack_app
|
|
45
|
+
middleware_stack.to_rack_app(self)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
43
49
|
def use(*args, **kwargs, &blk)
|
|
44
50
|
middleware_stack.use(*args, **kwargs.merge(path_prefix: path_prefix.to_s), &blk)
|
|
45
51
|
end
|
|
@@ -65,21 +71,204 @@ module Hanami
|
|
|
65
71
|
#
|
|
66
72
|
# @api public
|
|
67
73
|
# @since 2.0.0
|
|
68
|
-
def slice(slice_name, at:, &blk)
|
|
74
|
+
def slice(slice_name, at:, as: nil, &blk)
|
|
69
75
|
blk ||= @resolver.find_slice(slice_name).routes
|
|
70
76
|
|
|
71
77
|
prev_resolver = @resolver
|
|
72
78
|
@resolver = @resolver.to_slice(slice_name)
|
|
73
79
|
|
|
74
|
-
scope(at, &blk)
|
|
80
|
+
scope(at, as:, &blk)
|
|
75
81
|
ensure
|
|
76
82
|
@resolver = prev_resolver
|
|
77
83
|
end
|
|
78
84
|
|
|
85
|
+
# Generates RESTful routes for a plural resource.
|
|
86
|
+
#
|
|
87
|
+
# @param name [Symbol] the resource name (plural)
|
|
88
|
+
# @param options [Hash] options for customizing the routes
|
|
89
|
+
# @option options [Array<Symbol>] :only Limit to specific actions
|
|
90
|
+
# @option options [Array<Symbol>] :except Exclude specific actions
|
|
91
|
+
# @option options [String] :to the action key namespace, e.g. "namespace.action"
|
|
92
|
+
# @option options [String] :path the URL path
|
|
93
|
+
# @option options [String, Symbol] :as the route name prefix
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# resources :users
|
|
97
|
+
# # Generates:
|
|
98
|
+
# # GET /users users.index
|
|
99
|
+
# # GET /users/new users.new
|
|
100
|
+
# # POST /users users.create
|
|
101
|
+
# # GET /users/:id users.show
|
|
102
|
+
# # GET /users/:id/edit users.edit
|
|
103
|
+
# # PATCH /users/:id users.update
|
|
104
|
+
# # DELETE /users/:id users.destroy
|
|
105
|
+
#
|
|
106
|
+
# @api public
|
|
107
|
+
# @since 2.3.0
|
|
108
|
+
def resources(name, **options, &block)
|
|
109
|
+
build_resource(name, :plural, options, &block)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Generates RESTful routes for a singular resource.
|
|
113
|
+
#
|
|
114
|
+
# @param name [Symbol] the resource name (singular)
|
|
115
|
+
# @param options [Hash] options for customizing the routes
|
|
116
|
+
# @option options [Array<Symbol>] :only limit to specific actions
|
|
117
|
+
# @option options [Array<Symbol>] :except exclude specific actions
|
|
118
|
+
# @option options [String] :to the action key namespace, e.g. "namespace.action"
|
|
119
|
+
# @option options [String] :path the URL path
|
|
120
|
+
# @option options [String, Symbol] :as the route name prefix
|
|
121
|
+
#
|
|
122
|
+
# @example
|
|
123
|
+
# resource :profile
|
|
124
|
+
# # Generates (singular, no index):
|
|
125
|
+
# # GET /profile/new profile.new
|
|
126
|
+
# # POST /profile profile.create
|
|
127
|
+
# # GET /profile profile.show
|
|
128
|
+
# # GET /profile/edit profile.edit
|
|
129
|
+
# # PATCH /profile profile.update
|
|
130
|
+
# # DELETE /profile profile.destroy
|
|
131
|
+
#
|
|
132
|
+
# @api public
|
|
133
|
+
# @since 2.3.0
|
|
134
|
+
def resource(name, **options, &block)
|
|
135
|
+
build_resource(name, :singular, options, &block)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def build_resource(name, type, options, &block)
|
|
141
|
+
resource_builder = ResourceBuilder.new(
|
|
142
|
+
name: name,
|
|
143
|
+
type: type,
|
|
144
|
+
resource_scope: @resource_scope,
|
|
145
|
+
options: options,
|
|
146
|
+
inflector: inflector
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
resource_builder.add_routes(self)
|
|
150
|
+
resource_builder.scope(self, &block) if block
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Builds RESTful routes for a resource
|
|
154
|
+
#
|
|
79
155
|
# @api private
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
156
|
+
class ResourceBuilder
|
|
157
|
+
ROUTE_OPTIONS = {
|
|
158
|
+
index: {method: :get},
|
|
159
|
+
new: {method: :get, path_suffix: "/new", name_prefix: "new"},
|
|
160
|
+
create: {method: :post},
|
|
161
|
+
show: {method: :get, path_suffix: "/:id"},
|
|
162
|
+
edit: {method: :get, path_suffix: "/:id/edit", name_prefix: "edit"},
|
|
163
|
+
update: {method: :patch, path_suffix: "/:id"},
|
|
164
|
+
destroy: {method: :delete, path_suffix: "/:id"}
|
|
165
|
+
}.freeze
|
|
166
|
+
|
|
167
|
+
PLURAL_ACTIONS = %i[index new create show edit update destroy].freeze
|
|
168
|
+
SINGULAR_ACTIONS = (PLURAL_ACTIONS - %i[index]).freeze
|
|
169
|
+
|
|
170
|
+
def initialize(name:, type:, resource_scope:, options:, inflector:)
|
|
171
|
+
@name = name
|
|
172
|
+
@type = type
|
|
173
|
+
@resource_scope = resource_scope
|
|
174
|
+
@options = options
|
|
175
|
+
@inflector = inflector
|
|
176
|
+
|
|
177
|
+
@path = options[:path] || name.to_s
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def scope(router, &block)
|
|
181
|
+
@resource_scope.push(@name)
|
|
182
|
+
router.scope(scope_path, as: scope_name, &block)
|
|
183
|
+
ensure
|
|
184
|
+
@resource_scope.pop
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def add_routes(router)
|
|
188
|
+
actions.each do |action|
|
|
189
|
+
add_route(router, action, ROUTE_OPTIONS.fetch(action))
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
def plural?
|
|
196
|
+
@type == :plural
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def singular?
|
|
200
|
+
!plural?
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def scope_path
|
|
204
|
+
if plural?
|
|
205
|
+
"#{@path}/:#{@inflector.singularize(@path.to_s)}_id"
|
|
206
|
+
else
|
|
207
|
+
@path
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def scope_name
|
|
212
|
+
@inflector.singularize(@name)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def actions
|
|
216
|
+
default_actions = plural? ? PLURAL_ACTIONS : SINGULAR_ACTIONS
|
|
217
|
+
if @options[:only]
|
|
218
|
+
Array(@options[:only]) & default_actions
|
|
219
|
+
elsif @options[:except]
|
|
220
|
+
default_actions - Array(@options[:except])
|
|
221
|
+
else
|
|
222
|
+
default_actions
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def add_route(router, action, route_options)
|
|
227
|
+
path = "/#{@path}#{route_suffix(route_options[:path_suffix])}"
|
|
228
|
+
to = "#{key_path_base}#{CONTAINER_KEY_DELIMITER}#{action}"
|
|
229
|
+
as = route_name(action, route_options[:name_prefix])
|
|
230
|
+
|
|
231
|
+
router.public_send(route_options[:method], path, to:, as:)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def route_suffix(suffix)
|
|
235
|
+
return suffix.sub(LEADING_ID_REGEX, "") if suffix && singular?
|
|
236
|
+
suffix
|
|
237
|
+
end
|
|
238
|
+
LEADING_ID_REGEX = %r{\A/:id}
|
|
239
|
+
|
|
240
|
+
def key_path_base
|
|
241
|
+
@key_path_base ||=
|
|
242
|
+
if @options[:to]
|
|
243
|
+
@options[:to]
|
|
244
|
+
else
|
|
245
|
+
@name.to_s.then { |name|
|
|
246
|
+
next name unless @resource_scope.any?
|
|
247
|
+
|
|
248
|
+
prefix = @resource_scope.join(CONTAINER_KEY_DELIMITER)
|
|
249
|
+
"#{prefix}#{CONTAINER_KEY_DELIMITER}#{name}"
|
|
250
|
+
}
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def route_name(action, prefix)
|
|
255
|
+
name = route_name_base
|
|
256
|
+
name = @inflector.pluralize(name) if plural? && PLURALIZED_NAME_ACTIONS.include?(action)
|
|
257
|
+
|
|
258
|
+
[prefix, name]
|
|
259
|
+
end
|
|
260
|
+
PLURALIZED_NAME_ACTIONS = %i[index create].freeze
|
|
261
|
+
|
|
262
|
+
def route_name_base
|
|
263
|
+
@route_name_base ||=
|
|
264
|
+
if @options[:as]
|
|
265
|
+
@options[:as].to_s
|
|
266
|
+
elsif plural?
|
|
267
|
+
@inflector.singularize(@name.to_s)
|
|
268
|
+
else
|
|
269
|
+
@name.to_s
|
|
270
|
+
end
|
|
271
|
+
end
|
|
83
272
|
end
|
|
84
273
|
end
|
|
85
274
|
end
|
data/lib/hanami/slice.rb
CHANGED
|
@@ -223,6 +223,18 @@ module Hanami
|
|
|
223
223
|
app? ? root.join(APP_DIR) : root
|
|
224
224
|
end
|
|
225
225
|
|
|
226
|
+
# Returns the slice's root component directory, as a path relative to the app's root.
|
|
227
|
+
#
|
|
228
|
+
# @return [Pathname]
|
|
229
|
+
#
|
|
230
|
+
# @see #source_path
|
|
231
|
+
#
|
|
232
|
+
# @api public
|
|
233
|
+
# @since 2.3.0
|
|
234
|
+
def relative_source_path
|
|
235
|
+
source_path.relative_path_from(app.root)
|
|
236
|
+
end
|
|
237
|
+
|
|
226
238
|
# Returns the slice's configured inflector.
|
|
227
239
|
#
|
|
228
240
|
# Unless explicitly re-configured for the slice, this will be the app's inflector.
|
|
@@ -1032,6 +1044,7 @@ module Hanami
|
|
|
1032
1044
|
|
|
1033
1045
|
Slice::Router.new(
|
|
1034
1046
|
inspector: inspector,
|
|
1047
|
+
inflector: inflector,
|
|
1035
1048
|
routes: routes,
|
|
1036
1049
|
resolver: config.router.resolver.new(slice: self),
|
|
1037
1050
|
**error_handlers,
|
|
@@ -43,7 +43,7 @@ module Hanami
|
|
|
43
43
|
return unless slice
|
|
44
44
|
|
|
45
45
|
unless subclass.configured_for_slice?(slice)
|
|
46
|
-
subclass.configure_for_slice(slice)
|
|
46
|
+
subclass.configure_for_slice(slice) if subclass.respond_to?(:configure_for_slice)
|
|
47
47
|
subclass.configured_for_slices << slice
|
|
48
48
|
end
|
|
49
49
|
end
|
|
@@ -63,8 +63,6 @@ module Hanami
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def configure_for_slice(slice); end
|
|
67
|
-
|
|
68
66
|
def configured_for_slice?(slice)
|
|
69
67
|
configured_for_slices.include?(slice)
|
|
70
68
|
end
|
data/lib/hanami/version.rb
CHANGED
|
@@ -106,18 +106,30 @@ module Hanami
|
|
|
106
106
|
#
|
|
107
107
|
# @since 2.1.0
|
|
108
108
|
# @api private
|
|
109
|
-
def info(message = nil, **payload)
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
def info(message = nil, **payload, &blk)
|
|
110
|
+
logger.info do
|
|
111
|
+
if blk
|
|
112
|
+
JSON.generate(blk.call)
|
|
113
|
+
else
|
|
114
|
+
payload[:message] = message if message
|
|
115
|
+
JSON.generate(payload)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
# @see info
|
|
115
121
|
#
|
|
116
122
|
# @since 2.1.0
|
|
117
123
|
# @api private
|
|
118
|
-
def error(message = nil, **payload)
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
def error(message = nil, **payload, &blk)
|
|
125
|
+
logger.error do
|
|
126
|
+
if blk
|
|
127
|
+
JSON.generate(blk.call)
|
|
128
|
+
else
|
|
129
|
+
payload[:message] = message if message
|
|
130
|
+
JSON.generate(payload)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
121
133
|
end
|
|
122
134
|
end
|
|
123
135
|
|
|
@@ -145,7 +157,10 @@ module Hanami
|
|
|
145
157
|
# @since 2.0.0
|
|
146
158
|
def log_request(env, status, elapsed)
|
|
147
159
|
logger.tagged(:rack) do
|
|
148
|
-
|
|
160
|
+
|
|
161
|
+
logger.info do
|
|
162
|
+
data(env, status: status, elapsed: elapsed)
|
|
163
|
+
end
|
|
149
164
|
end
|
|
150
165
|
end
|
|
151
166
|
|
|
@@ -153,7 +168,9 @@ module Hanami
|
|
|
153
168
|
# @since 2.0.0
|
|
154
169
|
def log_exception(env, exception, status, elapsed)
|
|
155
170
|
logger.tagged(:rack) do
|
|
156
|
-
logger.error(exception
|
|
171
|
+
logger.error(exception) do
|
|
172
|
+
data(env, status: status, elapsed: elapsed)
|
|
173
|
+
end
|
|
157
174
|
end
|
|
158
175
|
end
|
|
159
176
|
|
data/lib/hanami.rb
CHANGED
|
@@ -19,10 +19,24 @@ module Hanami
|
|
|
19
19
|
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
|
20
20
|
loader.inflector.inflect "db" => "DB"
|
|
21
21
|
loader.inflector.inflect "db_logging" => "DBLogging"
|
|
22
|
+
loader.inflector.inflect "slice_configured_db_operation" => "SliceConfiguredDBOperation"
|
|
22
23
|
loader.inflector.inflect "sql_adapter" => "SQLAdapter"
|
|
24
|
+
|
|
25
|
+
gem_lib = loader.dirs.first
|
|
23
26
|
loader.ignore(
|
|
24
|
-
"#{
|
|
27
|
+
"#{gem_lib}/hanami/{constants,boot,errors,extensions/router/errors,prepare,rake_tasks,setup}.rb",
|
|
28
|
+
# Ignore conditionally-loaded classes dependent on gems that may not be included in the
|
|
29
|
+
# user's Gemfile
|
|
30
|
+
"#{gem_lib}/hanami/config/{assets,router,views}.rb",
|
|
31
|
+
"#{gem_lib}/hanami/slice/router.rb",
|
|
32
|
+
"#{gem_lib}/hanami/slice/routing/resolver.rb",
|
|
33
|
+
"#{gem_lib}/hanami/slice/routing/middleware/stack.rb",
|
|
34
|
+
"#{gem_lib}/hanami/extensions/**/*"
|
|
25
35
|
)
|
|
36
|
+
|
|
37
|
+
unless Hanami.bundled?("hanami-router")
|
|
38
|
+
loader.ignore("#{gem_lib}/hanami/routes.rb")
|
|
39
|
+
end
|
|
26
40
|
end
|
|
27
41
|
end
|
|
28
42
|
|
|
@@ -20,7 +20,7 @@ RSpec.describe "App action / Format config", :app_integration do
|
|
|
20
20
|
class App < Hanami::App
|
|
21
21
|
config.logger.stream = StringIO.new
|
|
22
22
|
|
|
23
|
-
config.actions.
|
|
23
|
+
config.actions.formats.accept :json
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
RUBY
|
|
@@ -68,71 +68,6 @@ RSpec.describe "App action / Format config", :app_integration do
|
|
|
68
68
|
expect(last_response.body).to eql("jane-john-jade-joe")
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
specify "adds a body parser middleware configured to parse any custom content type for the accepted formats" do
|
|
72
|
-
write "config/app.rb", <<~RUBY
|
|
73
|
-
require "hanami"
|
|
74
|
-
|
|
75
|
-
module TestApp
|
|
76
|
-
class App < Hanami::App
|
|
77
|
-
config.logger.stream = StringIO.new
|
|
78
|
-
|
|
79
|
-
config.actions.formats.add :json, ["application/json+scim", "application/json"]
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
RUBY
|
|
83
|
-
|
|
84
|
-
write "config/routes.rb", <<~RUBY
|
|
85
|
-
module TestApp
|
|
86
|
-
class Routes < Hanami::Routes
|
|
87
|
-
post "/users", to: "users.create"
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
RUBY
|
|
91
|
-
|
|
92
|
-
write "app/action.rb", <<~RUBY
|
|
93
|
-
# auto_register: false
|
|
94
|
-
|
|
95
|
-
module TestApp
|
|
96
|
-
class Action < Hanami::Action
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
RUBY
|
|
100
|
-
|
|
101
|
-
write "app/actions/users/create.rb", <<~RUBY
|
|
102
|
-
module TestApp
|
|
103
|
-
module Actions
|
|
104
|
-
module Users
|
|
105
|
-
class Create < TestApp::Action
|
|
106
|
-
def handle(req, res)
|
|
107
|
-
res.body = req.params[:users].join("-")
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
RUBY
|
|
114
|
-
|
|
115
|
-
require "hanami/boot"
|
|
116
|
-
|
|
117
|
-
post(
|
|
118
|
-
"/users",
|
|
119
|
-
JSON.generate("users" => %w[jane john jade joe]),
|
|
120
|
-
"CONTENT_TYPE" => "application/json+scim"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
expect(last_response).to be_successful
|
|
124
|
-
expect(last_response.body).to eql("jane-john-jade-joe")
|
|
125
|
-
|
|
126
|
-
post(
|
|
127
|
-
"/users",
|
|
128
|
-
JSON.generate("users" => %w[jane john jade joe]),
|
|
129
|
-
"CONTENT_TYPE" => "application/json"
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
expect(last_response).to be_successful
|
|
133
|
-
expect(last_response.body).to eql("jane-john-jade-joe")
|
|
134
|
-
end
|
|
135
|
-
|
|
136
71
|
it "does not add a body parser middleware if one is already added" do
|
|
137
72
|
write "config/app.rb", <<~RUBY
|
|
138
73
|
require "hanami"
|
|
@@ -141,7 +76,7 @@ RSpec.describe "App action / Format config", :app_integration do
|
|
|
141
76
|
class App < Hanami::App
|
|
142
77
|
config.logger.stream = StringIO.new
|
|
143
78
|
|
|
144
|
-
config.actions.
|
|
79
|
+
config.actions.formats.accept :json
|
|
145
80
|
config.middleware.use :body_parser, [json: "application/json+custom"]
|
|
146
81
|
end
|
|
147
82
|
end
|