hanami 2.0.0.beta3 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/hanami.gemspec +9 -8
  4. data/lib/hanami/app.rb +50 -39
  5. data/lib/hanami/assets/app_config.rb +61 -0
  6. data/lib/hanami/assets/{configuration.rb → config.rb} +9 -10
  7. data/lib/hanami/{configuration → config}/actions/content_security_policy.rb +3 -3
  8. data/lib/hanami/config/actions/cookies.rb +57 -0
  9. data/lib/hanami/config/actions/sessions.rb +83 -0
  10. data/lib/hanami/config/actions.rb +164 -0
  11. data/lib/hanami/config/logger.rb +176 -0
  12. data/lib/hanami/config/null_config.rb +14 -0
  13. data/lib/hanami/{configuration → config}/router.rb +8 -9
  14. data/lib/hanami/{configuration → config}/views.rb +16 -20
  15. data/lib/hanami/config.rb +396 -0
  16. data/lib/hanami/constants.rb +4 -0
  17. data/lib/hanami/errors.rb +20 -0
  18. data/lib/hanami/extensions/action/slice_configured_action.rb +10 -6
  19. data/lib/hanami/extensions/action.rb +59 -7
  20. data/lib/hanami/extensions/view/context.rb +3 -4
  21. data/lib/hanami/extensions/view/slice_configured_view.rb +4 -4
  22. data/lib/hanami/extensions/view.rb +7 -5
  23. data/lib/hanami/providers/inflector.rb +6 -2
  24. data/lib/hanami/providers/logger.rb +9 -3
  25. data/lib/hanami/providers/rack.rb +12 -2
  26. data/lib/hanami/providers/routes.rb +14 -6
  27. data/lib/hanami/routes.rb +36 -1
  28. data/lib/hanami/settings/env_store.rb +4 -4
  29. data/lib/hanami/settings.rb +169 -21
  30. data/lib/hanami/slice/router.rb +38 -16
  31. data/lib/hanami/slice/routing/middleware/stack.rb +108 -40
  32. data/lib/hanami/slice/routing/resolver.rb +10 -17
  33. data/lib/hanami/slice/view_name_inferrer.rb +1 -1
  34. data/lib/hanami/slice.rb +605 -51
  35. data/lib/hanami/slice_configurable.rb +1 -1
  36. data/lib/hanami/slice_registrar.rb +25 -14
  37. data/lib/hanami/version.rb +2 -3
  38. data/lib/hanami/web/rack_logger.rb +14 -4
  39. data/lib/hanami.rb +122 -24
  40. data/spec/integration/action/csrf_protection_spec.rb +1 -1
  41. data/spec/integration/container/application_routes_helper_spec.rb +3 -1
  42. data/spec/integration/container/prepare_container_spec.rb +2 -0
  43. data/spec/integration/container/provider_lifecycle_spec.rb +61 -0
  44. data/spec/integration/container/standard_providers/rack_provider_spec.rb +44 -0
  45. data/spec/integration/container/{standard_bootable_components_spec.rb → standard_providers_spec.rb} +3 -3
  46. data/spec/integration/rack_app/body_parser_spec.rb +111 -0
  47. data/spec/integration/rack_app/middleware_spec.rb +455 -3
  48. data/spec/integration/rack_app/non_booted_rack_app_spec.rb +2 -1
  49. data/spec/integration/rack_app/rack_app_spec.rb +39 -11
  50. data/spec/integration/settings/access_in_slice_class_body_spec.rb +82 -0
  51. data/spec/integration/settings/access_to_constants_spec.rb +23 -146
  52. data/spec/integration/{slices/slice_settings_spec.rb → settings/slice_registration_spec.rb} +5 -1
  53. data/spec/integration/settings/using_types_spec.rb +4 -11
  54. data/spec/integration/setup_spec.rb +4 -4
  55. data/spec/integration/slices/external_slice_spec.rb +2 -1
  56. data/spec/integration/slices/slice_configuration_spec.rb +3 -1
  57. data/spec/integration/slices/slice_loading_spec.rb +4 -4
  58. data/spec/integration/slices/slice_routing_spec.rb +4 -3
  59. data/spec/integration/slices_spec.rb +100 -0
  60. data/spec/isolation/hanami/boot/success_spec.rb +1 -1
  61. data/spec/support/app_integration.rb +10 -15
  62. data/spec/unit/hanami/{configuration → config}/actions/content_security_policy_spec.rb +16 -16
  63. data/spec/unit/hanami/{configuration → config}/actions/cookies_spec.rb +6 -6
  64. data/spec/unit/hanami/{configuration → config}/actions/csrf_protection_spec.rb +12 -12
  65. data/spec/unit/hanami/config/actions/default_values_spec.rb +54 -0
  66. data/spec/unit/hanami/{configuration → config}/actions/sessions_spec.rb +6 -8
  67. data/spec/unit/hanami/{configuration → config}/actions_spec.rb +8 -20
  68. data/spec/unit/hanami/{configuration → config}/base_url_spec.rb +2 -2
  69. data/spec/unit/hanami/{configuration → config}/inflector_spec.rb +2 -2
  70. data/spec/unit/hanami/{configuration → config}/logger_spec.rb +42 -59
  71. data/spec/unit/hanami/{configuration → config}/router_spec.rb +7 -8
  72. data/spec/unit/hanami/{configuration → config}/slices_spec.rb +2 -2
  73. data/spec/unit/hanami/{configuration → config}/views_spec.rb +13 -24
  74. data/spec/unit/hanami/settings_spec.rb +65 -10
  75. data/spec/unit/hanami/slice_configurable_spec.rb +21 -2
  76. data/spec/unit/hanami/slice_spec.rb +32 -0
  77. data/spec/unit/hanami/version_spec.rb +1 -1
  78. data/spec/unit/hanami/web/rack_logger_spec.rb +13 -2
  79. metadata +100 -76
  80. data/lib/hanami/assets/app_configuration.rb +0 -69
  81. data/lib/hanami/configuration/actions/cookies.rb +0 -29
  82. data/lib/hanami/configuration/actions/sessions.rb +0 -46
  83. data/lib/hanami/configuration/actions.rb +0 -101
  84. data/lib/hanami/configuration/logger.rb +0 -87
  85. data/lib/hanami/configuration/null_configuration.rb +0 -14
  86. data/lib/hanami/configuration/sessions.rb +0 -50
  87. data/lib/hanami/configuration.rb +0 -234
  88. data/lib/hanami/providers/settings.rb +0 -98
  89. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +0 -52
  90. data/spec/unit/hanami/configuration_spec.rb +0 -43
@@ -56,7 +56,7 @@ module Hanami
56
56
  def slice_for(klass)
57
57
  return unless klass.name
58
58
 
59
- slices = Hanami.app.slices.to_a + [Hanami.app]
59
+ slices = Hanami.app.slices.with_nested + [Hanami.app]
60
60
 
61
61
  slices.detect { |slice| klass.name.include?(slice.namespace.to_s) }
62
62
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "constants"
4
- require_relative "slice"
5
4
 
6
5
  module Hanami
7
6
  # @api private
8
7
  class SliceRegistrar
8
+ VALID_SLICE_NAME_RE = /^[a-z][a-z0-9_]+$/
9
9
  SLICE_DELIMITER = CONTAINER_KEY_DELIMITER
10
10
 
11
11
  attr_reader :parent, :slices
@@ -17,14 +17,16 @@ module Hanami
17
17
  end
18
18
 
19
19
  def register(name, slice_class = nil, &block)
20
+ unless name.to_s =~ VALID_SLICE_NAME_RE
21
+ raise ArgumentError, "slice name #{name.inspect} must be lowercase alphanumeric text and underscores only"
22
+ end
23
+
20
24
  return unless filter_slice_names([name]).any?
21
25
 
22
26
  if slices.key?(name.to_sym)
23
27
  raise SliceLoadError, "Slice '#{name}' is already registered"
24
28
  end
25
29
 
26
- # TODO: raise error unless name meets format (i.e. single level depth only)
27
-
28
30
  slice = slice_class || build_slice(name, &block)
29
31
 
30
32
  configure_slice(name, slice)
@@ -44,8 +46,6 @@ module Hanami
44
46
  end
45
47
 
46
48
  def load_slices
47
- return self unless root
48
-
49
49
  slice_configs = Dir[root.join(CONFIG_DIR, SLICES_DIR, "*#{RB_EXT}")]
50
50
  .map { |file| File.basename(file, RB_EXT) }
51
51
 
@@ -75,6 +75,14 @@ module Hanami
75
75
  slices.values
76
76
  end
77
77
 
78
+ def with_nested
79
+ to_a.flat_map { |slice|
80
+ # Return nested slices first so that their more specific namespaces may be picked up first
81
+ # by SliceConfigurable#slice_for
82
+ slice.slices.with_nested + [slice]
83
+ }
84
+ end
85
+
78
86
  private
79
87
 
80
88
  def root
@@ -85,36 +93,39 @@ module Hanami
85
93
  parent.inflector
86
94
  end
87
95
 
88
- # Runs when a slice file has been found at `config/slices/[slice_name].rb`, or a slice
89
- # directory at `slices/[slice_name]`. Attempts to require the slice class, if defined,
90
- # or generates a new slice class for the given slice name.
96
+ def parent_slice_namespace
97
+ parent.eql?(parent.app) ? Object : parent.namespace
98
+ end
99
+
100
+ # Runs when a slice file has been found at `config/slices/[slice_name].rb`, or a slice directory
101
+ # at `slices/[slice_name]`. Attempts to require the slice class, if defined, before registering
102
+ # the slice. If a slice class is not found, registering the slice will generate the slice class.
91
103
  def load_slice(slice_name)
92
- slice_const_name = inflector.camelize(slice_name)
93
104
  slice_require_path = root.join(CONFIG_DIR, SLICES_DIR, slice_name).to_s
94
-
95
105
  begin
96
106
  require(slice_require_path)
97
107
  rescue LoadError => e
98
108
  raise e unless e.path == slice_require_path
99
109
  end
100
110
 
111
+ slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
101
112
  slice_class =
102
113
  begin
103
- inflector.constantize("#{slice_const_name}::Slice")
114
+ inflector.constantize("#{slice_module_name}#{MODULE_DELIMITER}Slice")
104
115
  rescue NameError => e
105
- raise e unless e.name.to_s == slice_const_name || e.name.to_s == :Slice
116
+ raise e unless e.name.to_s == inflector.camelize(slice_name) || e.name.to_s == :Slice
106
117
  end
107
118
 
108
119
  register(slice_name, slice_class)
109
120
  end
110
121
 
111
122
  def build_slice(slice_name, &block)
123
+ slice_module_name = inflector.camelize("#{parent_slice_namespace.name}#{PATH_DELIMITER}#{slice_name}")
112
124
  slice_module =
113
125
  begin
114
- slice_module_name = inflector.camelize(slice_name.to_s)
115
126
  inflector.constantize(slice_module_name)
116
127
  rescue NameError
117
- Object.const_set(inflector.camelize(slice_module_name), Module.new)
128
+ parent_slice_namespace.const_set(inflector.camelize(slice_name), Module.new)
118
129
  end
119
130
 
120
131
  slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
@@ -6,9 +6,8 @@ module Hanami
6
6
  # @since 0.9.0
7
7
  # @api private
8
8
  module Version
9
- # @since 0.9.0
10
- # @api private
11
- VERSION = "2.0.0.beta3"
9
+ # @api public
10
+ VERSION = "2.0.0.rc1"
12
11
 
13
12
  # @since 0.9.0
14
13
  # @api private
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanami
4
+ # @api private
4
5
  module Web
5
6
  # Rack logger for Hanami apps
7
+ #
8
+ # @api private
9
+ # @since 2.0.0
6
10
  class RackLogger
7
11
  REQUEST_METHOD = "REQUEST_METHOD"
8
12
  private_constant :REQUEST_METHOD
@@ -25,10 +29,14 @@ module Hanami
25
29
  CONTENT_LENGTH = "Content-Length"
26
30
  private_constant :CONTENT_LENGTH
27
31
 
32
+ # @api private
33
+ # @since 2.0.0
28
34
  def initialize(logger)
29
35
  @logger = logger
30
36
  end
31
37
 
38
+ # @api private
39
+ # @since 2.0.0
32
40
  def attach(rack_monitor)
33
41
  rack_monitor.on :stop do |event|
34
42
  log_request event[:env], event[:status], event[:time]
@@ -39,6 +47,8 @@ module Hanami
39
47
  end
40
48
  end
41
49
 
50
+ # @api private
51
+ # @since 2.0.0
42
52
  def log_request(env, status, elapsed)
43
53
  data = {
44
54
  verb: env[REQUEST_METHOD],
@@ -47,16 +57,16 @@ module Hanami
47
57
  ip: env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR],
48
58
  path: env[SCRIPT_NAME] + env[PATH_INFO].to_s,
49
59
  length: extract_content_length(env),
50
- params: env[ROUTER_PARAMS],
51
- time: Time.now,
60
+ params: env[ROUTER_PARAMS]
52
61
  }
53
62
 
54
63
  logger.info(data)
55
64
  end
56
65
 
66
+ # @api private
67
+ # @since 2.0.0
57
68
  def log_exception(exception)
58
- logger.error exception.message
59
- logger.error exception.backtrace.join("\n")
69
+ logger.error(exception)
60
70
  end
61
71
 
62
72
  private
data/lib/hanami.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "zeitwerk"
4
+ require_relative "hanami/constants"
5
+
3
6
  # A complete web framework for Ruby
4
7
  #
5
8
  # @since 0.1.0
@@ -9,11 +12,21 @@ module Hanami
9
12
  @_mutex = Mutex.new
10
13
  @_bundled = {}
11
14
 
15
+ # @api private
16
+ # @since 2.0.0
17
+ def self.loader
18
+ @loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
19
+ loader.ignore(
20
+ "#{loader.dirs.first}/hanami/{constants,boot,errors,prepare,rake_tasks,setup}.rb"
21
+ )
22
+ end
23
+ end
24
+
12
25
  # Finds and loads the Hanami app file (`config/app.rb`).
13
26
  #
14
27
  # Raises an exception if the app file cannot be found.
15
28
  #
16
- # @return [Hanami::App] the loaded app class
29
+ # @return [app] the loaded app class
17
30
  #
18
31
  # @api public
19
32
  # @since 2.0.0
@@ -34,32 +47,18 @@ module Hanami
34
47
  end
35
48
  end
36
49
 
37
- # Finds and returns the absolute path for the Hanami app file (`config/app.rb`).
50
+ # Returns the Hamami app class.
38
51
  #
39
- # Searches within the given directory, then searches upwards through parent directories until the
40
- # app file can be found.
52
+ # To ensure your Hanami app is loaded, run {.setup} (or `require "hanami/setup"`) first.
41
53
  #
42
- # @param dir [String] The directory from which to start searching. Defaults to the current
43
- # directory.
54
+ # @return [Hanami::App] the app class
44
55
  #
45
- # @return [String, nil] the app file path, or nil if not found.
56
+ # @raise [AppLoadError] if the app has not been loaded
57
+ #
58
+ # @see .setup
46
59
  #
47
60
  # @api public
48
61
  # @since 2.0.0
49
- def self.app_path(dir = Dir.pwd)
50
- dir = Pathname(dir).expand_path
51
- path = dir.join(APP_PATH)
52
-
53
- if path.file?
54
- path.to_s
55
- elsif !dir.root?
56
- app_path(dir.parent)
57
- end
58
- end
59
-
60
- APP_PATH = "config/app.rb"
61
- private_constant :APP_PATH
62
-
63
62
  def self.app
64
63
  @_mutex.synchronize do
65
64
  unless defined?(@_app)
@@ -72,10 +71,18 @@ module Hanami
72
71
  end
73
72
  end
74
73
 
74
+ # Returns true if the Hanami app class has been loaded.
75
+ #
76
+ # @return [Boolean]
77
+ #
78
+ # @api public
79
+ # @since 2.0.0
75
80
  def self.app?
76
81
  instance_variable_defined?(:@_app)
77
82
  end
78
83
 
84
+ # @api private
85
+ # @since 2.0.0
79
86
  def self.app=(klass)
80
87
  @_mutex.synchronize do
81
88
  if instance_variable_defined?(:@_app)
@@ -86,47 +93,138 @@ module Hanami
86
93
  end
87
94
  end
88
95
 
96
+ # Finds and returns the absolute path for the Hanami app file (`config/app.rb`).
97
+ #
98
+ # Searches within the given directory, then searches upwards through parent directories until the
99
+ # app file can be found.
100
+ #
101
+ # @param dir [String] The directory from which to start searching. Defaults to the current
102
+ # directory.
103
+ #
104
+ # @return [String, nil] the app file path, or nil if not found.
105
+ #
106
+ # @api public
107
+ # @since 2.0.0
108
+ def self.app_path(dir = Dir.pwd)
109
+ dir = Pathname(dir).expand_path
110
+ path = dir.join(APP_PATH)
111
+
112
+ if path.file?
113
+ path.to_s
114
+ elsif !dir.root?
115
+ app_path(dir.parent)
116
+ end
117
+ end
118
+
119
+ # Returns the Hanami app environment as loaded from the `HANAMI_ENV` environment variable.
120
+ #
121
+ # @example
122
+ # Hanami.env # => :development
123
+ #
124
+ # @return [Symbol] the environment name
125
+ #
126
+ # @api public
127
+ # @since 2.0.0
89
128
  def self.env
90
129
  ENV.fetch("HANAMI_ENV") { ENV.fetch("RACK_ENV", "development") }.to_sym
91
130
  end
92
131
 
132
+ # Returns true if {.env} matches any of the given names
133
+ #
134
+ # @example
135
+ # Hanami.env # => :development
136
+ # Hanami.env?(:development, :test) # => true
137
+ #
138
+ # @param names [Array<Symbol>] the environment names to check
139
+ #
140
+ # @return [Boolean]
141
+ #
142
+ # @api public
143
+ # @since 2.0.0
93
144
  def self.env?(*names)
94
145
  names.map(&:to_sym).include?(env)
95
146
  end
96
147
 
148
+ # Returns the app's logger.
149
+ #
150
+ # Direct global access to the logger via this method is not recommended. Instead, consider
151
+ # accessing the logger via the app or slice container, in most cases as an dependency using the
152
+ # `Deps` mixin.
153
+ #
154
+ # @example
155
+ # # app/my_component.rb
156
+ #
157
+ # module MyApp
158
+ # class MyComponent
159
+ # include Deps["logger"]
160
+ #
161
+ # def some_method
162
+ # logger.info("hello")
163
+ # end
164
+ # end
165
+ # end
166
+ #
167
+ # @return [Dry::Logger::Dispatcher]
168
+ #
169
+ # @api public
170
+ # @since 1.0.0
97
171
  def self.logger
98
172
  app[:logger]
99
173
  end
100
174
 
175
+ # Prepares the Hanami app.
176
+ #
177
+ # @see App::ClassMethods#prepare
178
+ #
179
+ # @api public
180
+ # @since 2.0.0
101
181
  def self.prepare
102
182
  app.prepare
103
183
  end
104
184
 
185
+ # Boots the Hanami app.
186
+ #
187
+ # @see App::ClassMethods#boot
188
+ #
189
+ # @api public
190
+ # @since 2.0.0
105
191
  def self.boot
106
192
  app.boot
107
193
  end
108
194
 
195
+ # Shuts down the Hanami app.
196
+ #
197
+ # @see App::ClassMethods#shutdown
198
+ #
199
+ # @api public
200
+ # @since 2.0.0
109
201
  def self.shutdown
110
202
  app.shutdown
111
203
  end
112
204
 
205
+ # @api private
206
+ # @since 2.0.0
113
207
  def self.bundled?(gem_name)
114
208
  @_mutex.synchronize do
115
209
  @_bundled[gem_name] ||= begin
116
210
  gem(gem_name)
117
- true
118
211
  rescue Gem::LoadError
119
212
  false
120
213
  end
121
214
  end
122
215
  end
123
216
 
217
+ # Returns an array of bundler group names to be eagerly loaded by hanami-cli and other CLI
218
+ # extensions.
219
+ #
220
+ # @api private
221
+ # @since 2.0.0
124
222
  def self.bundler_groups
125
223
  [:plugins]
126
224
  end
127
225
 
128
- require_relative "hanami/version"
226
+ loader.setup
227
+
129
228
  require_relative "hanami/errors"
130
229
  require_relative "hanami/extensions"
131
- require_relative "hanami/app"
132
230
  end
@@ -35,7 +35,7 @@ RSpec.describe "App action / CSRF protection", :app_integration do
35
35
  context "CSRF protection explicitly disabled" do
36
36
  let(:app_hook) {
37
37
  proc do
38
- config.sessions = :cookie, {secret: "abc123"}
38
+ config.actions.sessions = :cookie, {secret: "abc123"}
39
39
  config.actions.csrf_protection = false
40
40
  end
41
41
  }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "stringio"
4
+
3
5
  RSpec.describe "App routes helper", :app_integration do
4
6
  specify "Routing to actions based on their container identifiers" do
5
7
  with_tmp_directory(Dir.mktmpdir) do
@@ -8,7 +10,7 @@ RSpec.describe "App routes helper", :app_integration do
8
10
 
9
11
  module TestApp
10
12
  class App < Hanami::App
11
- config.logger.stream = File.new("/dev/null", "w")
13
+ config.logger.stream = StringIO.new
12
14
  end
13
15
  end
14
16
  RUBY
@@ -23,6 +23,8 @@ RSpec.describe "Container / prepare_container", :app_integration do
23
23
 
24
24
  # The prepare_container block is called when the slice is prepared
25
25
  slice.prepare
26
+
27
+ autoloaders_teardown!
26
28
  end
27
29
 
28
30
  describe "in app", :in_prepare_container do
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Container / Provider lifecycle", :app_integration do
4
+ let!(:slice) {
5
+ module TestApp
6
+ class App < Hanami::App
7
+ register_provider :connection do
8
+ prepare do
9
+ module ::TestApp
10
+ class Connection
11
+ def initialize
12
+ @connected = true
13
+ end
14
+
15
+ def disconnect
16
+ @connected = false
17
+ end
18
+
19
+ def connected?
20
+ @connected
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ start do
27
+ register("connection", TestApp::Connection.new)
28
+ end
29
+
30
+ stop do
31
+ container["connection"].disconnect
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ TestApp::App
38
+ }
39
+
40
+ before do
41
+ require "hanami/setup"
42
+ end
43
+
44
+ specify "individual providers can be prepared, started and stopped" do
45
+ expect { TestApp::Connection }.to raise_error NameError
46
+
47
+ slice.prepare :connection
48
+
49
+ expect(TestApp::Connection).to be
50
+ expect(slice.container.registered?("connection")).to be false
51
+
52
+ slice.start :connection
53
+
54
+ expect(slice.container.registered?("connection")).to be true
55
+ expect(slice["connection"]).to be_connected
56
+
57
+ slice.stop :connection
58
+
59
+ expect(slice["connection"]).not_to be_connected
60
+ end
61
+ end
@@ -0,0 +1,44 @@
1
+ RSpec.describe "Container / Standard providers / Rack", :app_integration do
2
+ specify "Rack provider is loaded when rack is bundled" do
3
+ with_tmp_directory(Dir.mktmpdir) do
4
+ write "config/app.rb", <<~RUBY
5
+ require "hanami"
6
+
7
+ module TestApp
8
+ class App < Hanami::App
9
+ end
10
+ end
11
+ RUBY
12
+
13
+ write "slices/main/.keep", ""
14
+
15
+ require "hanami/prepare"
16
+
17
+ expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
18
+ expect(Main::Slice["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
19
+ end
20
+ end
21
+
22
+ specify "Rack provider is not loaded when rack is not bundled" do
23
+ allow(Hanami).to receive(:bundled?).and_call_original
24
+ allow(Hanami).to receive(:bundled?).with("rack").and_return false
25
+
26
+ with_tmp_directory(Dir.mktmpdir) do
27
+ write "config/app.rb", <<~RUBY
28
+ require "hanami"
29
+
30
+ module TestApp
31
+ class App < Hanami::App
32
+ end
33
+ end
34
+ RUBY
35
+
36
+ write "slices/main/.keep", ""
37
+
38
+ require "hanami/prepare"
39
+
40
+ expect(Hanami.app.key?("rack.monitor")).to be false
41
+ expect(Main::Slice.key?("rack.monitor")).to be false
42
+ end
43
+ end
44
+ end
@@ -1,4 +1,4 @@
1
- RSpec.describe "Container / Standard bootable components", :app_integration do
1
+ RSpec.describe "Container / Standard providers", :app_integration do
2
2
  specify "Standard components are available on booted container" do
3
3
  with_tmp_directory(Dir.mktmpdir) do
4
4
  write "config/app.rb", <<~RUBY
@@ -15,7 +15,7 @@ RSpec.describe "Container / Standard bootable components", :app_integration do
15
15
 
16
16
  expect(Hanami.app.key?(:settings)).to be false
17
17
  expect(Hanami.app["inflector"]).to eql Hanami.app.inflector
18
- expect(Hanami.app["logger"]).to be_a_kind_of(Hanami::Logger)
18
+ expect(Hanami.app["logger"]).to be_a_kind_of(Dry::Logger::Dispatcher)
19
19
  expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
20
20
  end
21
21
  end
@@ -36,7 +36,7 @@ RSpec.describe "Container / Standard bootable components", :app_integration do
36
36
 
37
37
  expect(Hanami.app.key?(:settings)).to be false
38
38
  expect(Hanami.app["inflector"]).to eql Hanami.app.inflector
39
- expect(Hanami.app["logger"]).to be_a_kind_of(Hanami::Logger)
39
+ expect(Hanami.app["logger"]).to be_a_kind_of(Dry::Logger::Dispatcher)
40
40
  expect(Hanami.app["rack.monitor"]).to be_a_kind_of(Dry::Monitor::Rack::Middleware)
41
41
  end
42
42
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Hanami web app", :app_integration do
7
+ include Rack::Test::Methods
8
+
9
+ let(:app) { Hanami.app }
10
+
11
+ around do |example|
12
+ with_tmp_directory(Dir.mktmpdir, &example)
13
+ end
14
+
15
+ specify "Setting middlewares in the config" do
16
+ write "config/app.rb", <<~RUBY
17
+ require "hanami"
18
+
19
+ module TestApp
20
+ class App < Hanami::App
21
+ config.middleware.use :body_parser, :json
22
+ config.logger.stream = StringIO.new
23
+ end
24
+ end
25
+ RUBY
26
+
27
+ write "config/routes.rb", <<~RUBY
28
+ module TestApp
29
+ class Routes < Hanami::Routes
30
+ post "/users", to: "users.create"
31
+ end
32
+ end
33
+ RUBY
34
+
35
+ write "app/actions/users/create.rb", <<~RUBY
36
+ module TestApp
37
+ module Actions
38
+ module Users
39
+ class Create < Hanami::Action
40
+ accept :json
41
+
42
+ def handle(req, res)
43
+ res.body = req.params[:users].join("-")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ RUBY
50
+
51
+ require "hanami/boot"
52
+
53
+ post(
54
+ "/users",
55
+ JSON.dump("users" => %w[jane john jade joe]),
56
+ "CONTENT_TYPE" => "application/json"
57
+ )
58
+
59
+ expect(last_response).to be_successful
60
+ expect(last_response.body).to eql("jane-john-jade-joe")
61
+ end
62
+
63
+ specify "Configuring custom mime-types and body parser" do
64
+ write "config/app.rb", <<~RUBY
65
+ require "hanami"
66
+
67
+ module TestApp
68
+ class App < Hanami::App
69
+ config.actions.formats["application/json+scim"] = :json
70
+ config.middleware.use :body_parser, [json: "application/json+scim"]
71
+ config.logger.stream = StringIO.new
72
+ end
73
+ end
74
+ RUBY
75
+
76
+ write "config/routes.rb", <<~RUBY
77
+ module TestApp
78
+ class Routes < Hanami::Routes
79
+ post "/users", to: "users.create"
80
+ end
81
+ end
82
+ RUBY
83
+
84
+ write "app/actions/users/create.rb", <<~RUBY
85
+ module TestApp
86
+ module Actions
87
+ module Users
88
+ class Create < Hanami::Action
89
+ accept :json
90
+
91
+ def handle(req, res)
92
+ res.body = req.params[:users].join("-")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ RUBY
99
+
100
+ require "hanami/boot"
101
+
102
+ post(
103
+ "/users",
104
+ JSON.dump("users" => %w[jane john jade joe]),
105
+ "CONTENT_TYPE" => "application/json+scim"
106
+ )
107
+
108
+ expect(last_response).to be_successful
109
+ expect(last_response.body).to eql("jane-john-jade-joe")
110
+ end
111
+ end