hanami 2.3.0.beta2 → 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 +64 -1
- data/README.md +1 -3
- data/hanami.gemspec +3 -3
- 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 +8 -16
- 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 -1
- data/lib/hanami/extensions/view/part.rb +3 -0
- data/lib/hanami/extensions/view/scope.rb +3 -0
- 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 +1 -0
- data/lib/hanami/slice/router.rb +201 -12
- data/lib/hanami/slice.rb +1 -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 +0 -68
- 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 +1 -2
- data/spec/integration/router/resource_routes_spec.rb +281 -0
- metadata +14 -11
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
|
@@ -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
|
|
|
@@ -68,74 +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.register :json, "application/json",
|
|
80
|
-
accept_types: ["application/json", "application/json+scim"],
|
|
81
|
-
content_types: ["application/json", "application/json+scim"]
|
|
82
|
-
config.actions.formats.accept :json
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
RUBY
|
|
86
|
-
|
|
87
|
-
write "config/routes.rb", <<~RUBY
|
|
88
|
-
module TestApp
|
|
89
|
-
class Routes < Hanami::Routes
|
|
90
|
-
post "/users", to: "users.create"
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
RUBY
|
|
94
|
-
|
|
95
|
-
write "app/action.rb", <<~RUBY
|
|
96
|
-
# auto_register: false
|
|
97
|
-
|
|
98
|
-
module TestApp
|
|
99
|
-
class Action < Hanami::Action
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
RUBY
|
|
103
|
-
|
|
104
|
-
write "app/actions/users/create.rb", <<~RUBY
|
|
105
|
-
module TestApp
|
|
106
|
-
module Actions
|
|
107
|
-
module Users
|
|
108
|
-
class Create < TestApp::Action
|
|
109
|
-
def handle(req, res)
|
|
110
|
-
res.body = req.params[:users].join("-")
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
RUBY
|
|
117
|
-
|
|
118
|
-
require "hanami/boot"
|
|
119
|
-
|
|
120
|
-
post(
|
|
121
|
-
"/users",
|
|
122
|
-
JSON.generate("users" => %w[jane john jade joe]),
|
|
123
|
-
"CONTENT_TYPE" => "application/json+scim"
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
expect(last_response).to be_successful
|
|
127
|
-
expect(last_response.body).to eql("jane-john-jade-joe")
|
|
128
|
-
|
|
129
|
-
post(
|
|
130
|
-
"/users",
|
|
131
|
-
JSON.generate("users" => %w[jane john jade joe]),
|
|
132
|
-
"CONTENT_TYPE" => "application/json"
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
expect(last_response).to be_successful
|
|
136
|
-
expect(last_response.body).to eql("jane-john-jade-joe")
|
|
137
|
-
end
|
|
138
|
-
|
|
139
71
|
it "does not add a body parser middleware if one is already added" do
|
|
140
72
|
write "config/app.rb", <<~RUBY
|
|
141
73
|
require "hanami"
|
|
@@ -11,10 +11,13 @@ RSpec.describe "Logging / Request logging", :app_integration do
|
|
|
11
11
|
|
|
12
12
|
let(:logger_stream) { StringIO.new }
|
|
13
13
|
|
|
14
|
+
let(:logger_level) { nil }
|
|
15
|
+
|
|
14
16
|
let(:root) { make_tmp_directory }
|
|
15
17
|
|
|
16
18
|
def configure_logger
|
|
17
19
|
Hanami.app.config.logger.stream = logger_stream
|
|
20
|
+
Hanami.app.config.logger.level = logger_level if logger_level
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def logs
|
|
@@ -84,6 +87,19 @@ RSpec.describe "Logging / Request logging", :app_integration do
|
|
|
84
87
|
)
|
|
85
88
|
end
|
|
86
89
|
end
|
|
90
|
+
|
|
91
|
+
context "log level error" do
|
|
92
|
+
let(:logger_level) { :error }
|
|
93
|
+
before do
|
|
94
|
+
expect_any_instance_of(Hanami::Web::RackLogger).to_not receive(:data)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "does not log info" do
|
|
98
|
+
get "/"
|
|
99
|
+
|
|
100
|
+
expect(logs.split("\n").length).to eq 0
|
|
101
|
+
end
|
|
102
|
+
end
|
|
87
103
|
end
|
|
88
104
|
|
|
89
105
|
describe "slice router" do
|
|
@@ -56,4 +56,67 @@ RSpec.describe "Operation / Extensions", :app_integration do
|
|
|
56
56
|
expect(main.rom).to be Main::Slice["db.rom"]
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
|
+
|
|
60
|
+
context "hanami-db bundled, but no db configured" do
|
|
61
|
+
it "does not extend the operation class" do
|
|
62
|
+
with_tmp_directory(Dir.mktmpdir) do
|
|
63
|
+
write "config/app.rb", <<~RUBY
|
|
64
|
+
require "hanami"
|
|
65
|
+
|
|
66
|
+
module TestApp
|
|
67
|
+
class App < Hanami::App
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
RUBY
|
|
71
|
+
|
|
72
|
+
write "app/operation.rb", <<~RUBY
|
|
73
|
+
module TestApp
|
|
74
|
+
class Operation < Dry::Operation
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
RUBY
|
|
78
|
+
|
|
79
|
+
require "hanami/prepare"
|
|
80
|
+
|
|
81
|
+
operation = TestApp::Operation.new
|
|
82
|
+
|
|
83
|
+
expect(operation.rom).to be nil
|
|
84
|
+
expect { operation.transaction }.to raise_error Hanami::ComponentLoadError, "A configured db for TestApp::App is required to run transactions."
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "hanami-db not bundled" do
|
|
90
|
+
before do
|
|
91
|
+
allow(Hanami).to receive(:bundled?).and_call_original
|
|
92
|
+
allow(Hanami).to receive(:bundled?).with("hanami-db").and_return false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "does not extend the operation class" do
|
|
96
|
+
with_tmp_directory(Dir.mktmpdir) do
|
|
97
|
+
write "config/app.rb", <<~RUBY
|
|
98
|
+
require "hanami"
|
|
99
|
+
|
|
100
|
+
module TestApp
|
|
101
|
+
class App < Hanami::App
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
RUBY
|
|
105
|
+
|
|
106
|
+
write "app/operation.rb", <<~RUBY
|
|
107
|
+
module TestApp
|
|
108
|
+
class Operation < Dry::Operation
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
RUBY
|
|
112
|
+
|
|
113
|
+
require "hanami/prepare"
|
|
114
|
+
|
|
115
|
+
operation = TestApp::Operation.new
|
|
116
|
+
|
|
117
|
+
expect { operation.rom }.to raise_error NoMethodError
|
|
118
|
+
expect { operation.transaction }.to raise_error NoMethodError
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
59
122
|
end
|
|
@@ -12,14 +12,13 @@ RSpec.describe "Hanami web app", :app_integration do
|
|
|
12
12
|
with_tmp_directory(Dir.mktmpdir, &example)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
specify "
|
|
15
|
+
specify "Default body parser" do
|
|
16
16
|
write "config/app.rb", <<~RUBY
|
|
17
17
|
require "hanami"
|
|
18
18
|
|
|
19
19
|
module TestApp
|
|
20
20
|
class App < Hanami::App
|
|
21
21
|
config.actions.format :json
|
|
22
|
-
config.middleware.use :body_parser, :json
|
|
23
22
|
config.logger.stream = StringIO.new
|
|
24
23
|
end
|
|
25
24
|
end
|