hanami 2.0.0.beta2 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/hanami.gemspec +5 -5
  4. data/lib/hanami/app.rb +4 -4
  5. data/lib/hanami/assets/{app_configuration.rb → app_config.rb} +10 -10
  6. data/lib/hanami/assets/{configuration.rb → config.rb} +4 -4
  7. data/lib/hanami/{configuration → config}/actions/content_security_policy.rb +2 -2
  8. data/lib/hanami/{configuration → config}/actions/cookies.rb +6 -5
  9. data/lib/hanami/{configuration → config}/actions/sessions.rb +2 -2
  10. data/lib/hanami/{configuration → config}/actions.rb +11 -12
  11. data/lib/hanami/{configuration → config}/logger.rb +2 -2
  12. data/lib/hanami/config/null_config.rb +14 -0
  13. data/lib/hanami/{configuration → config}/router.rb +8 -8
  14. data/lib/hanami/{configuration → config}/sessions.rb +2 -2
  15. data/lib/hanami/{configuration → config}/views.rb +10 -10
  16. data/lib/hanami/{configuration.rb → config.rb} +30 -30
  17. data/lib/hanami/errors.rb +3 -0
  18. data/lib/hanami/extensions/action/slice_configured_action.rb +1 -1
  19. data/lib/hanami/providers/inflector.rb +0 -2
  20. data/lib/hanami/providers/logger.rb +1 -3
  21. data/lib/hanami/providers/rack.rb +0 -2
  22. data/lib/hanami/providers/routes.rb +0 -2
  23. data/lib/hanami/routes.rb +48 -21
  24. data/lib/hanami/settings/env_store.rb +3 -3
  25. data/lib/hanami/settings.rb +87 -5
  26. data/lib/hanami/slice/routing/middleware/stack.rb +45 -1
  27. data/lib/hanami/slice.rb +52 -37
  28. data/lib/hanami/slice_configurable.rb +1 -1
  29. data/lib/hanami/slice_registrar.rb +6 -0
  30. data/lib/hanami/version.rb +1 -1
  31. data/lib/hanami.rb +0 -1
  32. data/spec/{new_integration → integration}/action/cookies_spec.rb +0 -0
  33. data/spec/{new_integration → integration}/action/csrf_protection_spec.rb +0 -0
  34. data/spec/{new_integration → integration}/action/routes_spec.rb +3 -5
  35. data/spec/{new_integration → integration}/action/sessions_spec.rb +0 -0
  36. data/spec/{new_integration → integration}/action/slice_configuration_spec.rb +0 -0
  37. data/spec/{new_integration → integration}/action/view_integration_spec.rb +0 -0
  38. data/spec/{new_integration → integration}/action/view_rendering/automatic_rendering_spec.rb +0 -0
  39. data/spec/{new_integration → integration}/action/view_rendering/paired_view_inference_spec.rb +0 -0
  40. data/spec/{new_integration → integration}/action/view_rendering_spec.rb +0 -0
  41. data/spec/{new_integration → integration}/code_loading/loading_from_app_spec.rb +0 -0
  42. data/spec/{new_integration → integration}/code_loading/loading_from_lib_spec.rb +0 -0
  43. data/spec/{new_integration → integration}/code_loading/loading_from_slice_spec.rb +0 -0
  44. data/spec/{new_integration → integration}/container/application_routes_helper_spec.rb +1 -3
  45. data/spec/{new_integration → integration}/container/auto_injection_spec.rb +0 -0
  46. data/spec/{new_integration → integration}/container/auto_registration_spec.rb +0 -0
  47. data/spec/{new_integration → integration}/container/autoloader_spec.rb +0 -0
  48. data/spec/{new_integration → integration}/container/imports_spec.rb +0 -0
  49. data/spec/{new_integration → integration}/container/prepare_container_spec.rb +2 -0
  50. data/spec/{new_integration → integration}/container/shutdown_spec.rb +0 -0
  51. data/spec/{new_integration → integration}/container/standard_bootable_components_spec.rb +0 -0
  52. data/spec/{new_integration → integration}/dotenv_loading_spec.rb +0 -0
  53. data/spec/integration/rack_app/body_parser_spec.rb +108 -0
  54. data/spec/{new_integration → integration}/rack_app/middleware_spec.rb +37 -15
  55. data/spec/{new_integration → integration}/rack_app/non_booted_rack_app_spec.rb +3 -5
  56. data/spec/{new_integration → integration}/rack_app/rack_app_spec.rb +28 -48
  57. data/spec/integration/settings/access_in_slice_class_body_spec.rb +82 -0
  58. data/spec/integration/settings/access_to_constants_spec.rb +46 -0
  59. data/spec/{new_integration → integration}/settings/loading_from_env_spec.rb +0 -0
  60. data/spec/{new_integration → integration}/settings/settings_component_loading_spec.rb +0 -0
  61. data/spec/{new_integration/slices/slice_settings_spec.rb → integration/settings/slice_registration_spec.rb} +5 -1
  62. data/spec/{new_integration → integration}/settings/using_types_spec.rb +4 -11
  63. data/spec/{new_integration → integration}/setup_spec.rb +20 -0
  64. data/spec/{new_integration → integration}/slices/external_slice_spec.rb +2 -4
  65. data/spec/{new_integration → integration}/slices/slice_configuration_spec.rb +0 -0
  66. data/spec/{new_integration → integration}/slices/slice_loading_spec.rb +0 -0
  67. data/spec/{new_integration → integration}/slices/slice_routing_spec.rb +5 -13
  68. data/spec/{new_integration → integration}/slices_spec.rb +0 -0
  69. data/spec/{new_integration → integration}/view/context/assets_spec.rb +0 -0
  70. data/spec/{new_integration → integration}/view/context/inflector_spec.rb +0 -0
  71. data/spec/{new_integration → integration}/view/context/request_spec.rb +0 -0
  72. data/spec/{new_integration → integration}/view/context/routes_spec.rb +1 -3
  73. data/spec/{new_integration → integration}/view/context/settings_spec.rb +0 -0
  74. data/spec/{new_integration → integration}/view/inflector_spec.rb +0 -0
  75. data/spec/{new_integration → integration}/view/part_namespace_spec.rb +0 -0
  76. data/spec/{new_integration → integration}/view/path_spec.rb +0 -0
  77. data/spec/{new_integration → integration}/view/slice_configuration_spec.rb +0 -0
  78. data/spec/{new_integration → integration}/view/template_spec.rb +0 -0
  79. data/spec/{new_integration → integration}/view/views_spec.rb +0 -0
  80. data/spec/support/app_integration.rb +18 -15
  81. data/spec/unit/hanami/{configuration → config}/actions/content_security_policy_spec.rb +10 -10
  82. data/spec/unit/hanami/{configuration → config}/actions/cookies_spec.rb +6 -6
  83. data/spec/unit/hanami/{configuration → config}/actions/csrf_protection_spec.rb +12 -12
  84. data/spec/unit/hanami/config/actions/default_values_spec.rb +54 -0
  85. data/spec/unit/hanami/{configuration → config}/actions/sessions_spec.rb +5 -5
  86. data/spec/unit/hanami/{configuration → config}/actions_spec.rb +13 -25
  87. data/spec/unit/hanami/{configuration → config}/base_url_spec.rb +2 -2
  88. data/spec/unit/hanami/{configuration → config}/inflector_spec.rb +2 -2
  89. data/spec/unit/hanami/{configuration → config}/logger_spec.rb +4 -4
  90. data/spec/unit/hanami/config/router_spec.rb +44 -0
  91. data/spec/unit/hanami/{configuration → config}/slices_spec.rb +2 -2
  92. data/spec/unit/hanami/{configuration → config}/views_spec.rb +15 -27
  93. data/spec/unit/hanami/{configuration_spec.rb → config_spec.rb} +3 -3
  94. data/spec/unit/hanami/settings_spec.rb +65 -10
  95. data/spec/unit/hanami/slice_configurable_spec.rb +21 -2
  96. data/spec/unit/hanami/version_spec.rb +1 -1
  97. metadata +162 -294
  98. data/lib/hanami/configuration/null_configuration.rb +0 -14
  99. data/lib/hanami/providers/settings.rb +0 -98
  100. data/lib/hanami/server.rb +0 -29
  101. data/spec/integration/application_middleware_stack_spec.rb +0 -84
  102. data/spec/integration/assets/cdn_spec.rb +0 -48
  103. data/spec/integration/assets/fingerprint_spec.rb +0 -42
  104. data/spec/integration/assets/helpers_spec.rb +0 -50
  105. data/spec/integration/assets/serve_spec.rb +0 -70
  106. data/spec/integration/assets/subresource_integrity_spec.rb +0 -54
  107. data/spec/integration/body_parsers_spec.rb +0 -50
  108. data/spec/integration/cli/assets/precompile_spec.rb +0 -147
  109. data/spec/integration/cli/assets_spec.rb +0 -14
  110. data/spec/integration/cli/console_spec.rb +0 -105
  111. data/spec/integration/cli/db/apply_spec.rb +0 -74
  112. data/spec/integration/cli/db/console_spec.rb +0 -40
  113. data/spec/integration/cli/db/create_spec.rb +0 -50
  114. data/spec/integration/cli/db/drop_spec.rb +0 -54
  115. data/spec/integration/cli/db/migrate_spec.rb +0 -108
  116. data/spec/integration/cli/db/prepare_spec.rb +0 -36
  117. data/spec/integration/cli/db/rollback_spec.rb +0 -96
  118. data/spec/integration/cli/db/version_spec.rb +0 -38
  119. data/spec/integration/cli/db_spec.rb +0 -21
  120. data/spec/integration/cli/destroy/action_spec.rb +0 -143
  121. data/spec/integration/cli/destroy/app_spec.rb +0 -118
  122. data/spec/integration/cli/destroy/mailer_spec.rb +0 -74
  123. data/spec/integration/cli/destroy/migration_spec.rb +0 -70
  124. data/spec/integration/cli/destroy/model_spec.rb +0 -113
  125. data/spec/integration/cli/destroy_spec.rb +0 -18
  126. data/spec/integration/cli/generate/action_spec.rb +0 -469
  127. data/spec/integration/cli/generate/app_spec.rb +0 -215
  128. data/spec/integration/cli/generate/mailer_spec.rb +0 -189
  129. data/spec/integration/cli/generate/migration_spec.rb +0 -72
  130. data/spec/integration/cli/generate/model_spec.rb +0 -290
  131. data/spec/integration/cli/generate/secret_spec.rb +0 -56
  132. data/spec/integration/cli/generate_spec.rb +0 -19
  133. data/spec/integration/cli/new/database_spec.rb +0 -235
  134. data/spec/integration/cli/new/hanami_head_spec.rb +0 -27
  135. data/spec/integration/cli/new/template_spec.rb +0 -118
  136. data/spec/integration/cli/new/test_spec.rb +0 -274
  137. data/spec/integration/cli/new_spec.rb +0 -970
  138. data/spec/integration/cli/plugins_spec.rb +0 -39
  139. data/spec/integration/cli/routes_spec.rb +0 -49
  140. data/spec/integration/cli/server_spec.rb +0 -626
  141. data/spec/integration/cli/version_spec.rb +0 -85
  142. data/spec/integration/early_hints_spec.rb +0 -35
  143. data/spec/integration/handle_exceptions_spec.rb +0 -244
  144. data/spec/integration/head_spec.rb +0 -89
  145. data/spec/integration/http_headers_spec.rb +0 -29
  146. data/spec/integration/mailer_spec.rb +0 -32
  147. data/spec/integration/middleware_spec.rb +0 -81
  148. data/spec/integration/mount_applications_spec.rb +0 -88
  149. data/spec/integration/project_initializers_spec.rb +0 -40
  150. data/spec/integration/rackup_spec.rb +0 -35
  151. data/spec/integration/rake/with_minitest_spec.rb +0 -67
  152. data/spec/integration/rake/with_rspec_spec.rb +0 -69
  153. data/spec/integration/routing_helpers_spec.rb +0 -61
  154. data/spec/integration/security/content_security_policy_spec.rb +0 -46
  155. data/spec/integration/security/csrf_protection_spec.rb +0 -42
  156. data/spec/integration/security/force_ssl_spec.rb +0 -29
  157. data/spec/integration/security/x_content_type_options_spec.rb +0 -46
  158. data/spec/integration/security/x_frame_options_spec.rb +0 -46
  159. data/spec/integration/security/x_xss_protection_spec.rb +0 -46
  160. data/spec/integration/send_file_spec.rb +0 -51
  161. data/spec/integration/sessions_spec.rb +0 -247
  162. data/spec/integration/static_middleware_spec.rb +0 -21
  163. data/spec/integration/streaming_spec.rb +0 -41
  164. data/spec/integration/unsafe_send_file_spec.rb +0 -52
  165. data/spec/new_integration/settings/access_to_constants_spec.rb +0 -169
  166. data/spec/support/fixtures/hanami-plugin/Gemfile +0 -8
  167. data/spec/support/fixtures/hanami-plugin/README.md +0 -35
  168. data/spec/support/fixtures/hanami-plugin/Rakefile +0 -4
  169. data/spec/support/fixtures/hanami-plugin/bin/console +0 -15
  170. data/spec/support/fixtures/hanami-plugin/bin/setup +0 -8
  171. data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +0 -28
  172. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +0 -19
  173. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +0 -7
  174. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +0 -8
  175. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +0 -52
  176. data/spec/unit/hanami/routes_spec.rb +0 -25
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/system/provider/source"
4
-
5
3
  module Hanami
6
4
  module Providers
7
5
  class Routes < Dry::System::Provider::Source
data/lib/hanami/routes.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "hanami/slice/router"
4
+
3
5
  module Hanami
4
6
  # App routes
5
7
  #
@@ -14,38 +16,63 @@ module Hanami
14
16
  #
15
17
  # module MyApp
16
18
  # class Routes < Hanami::Routes
17
- # define do
18
- # root to: "home.show"
19
- # end
19
+ # root to: "home.show"
20
20
  # end
21
21
  # end
22
22
  #
23
- # See {Hanami::Slice::Router} for the syntax allowed within the
24
- # `define` block.
23
+ # See {Hanami::Slice::Router} for the syntax allowed within the `define` block.
25
24
  #
26
25
  # @see Hanami::Slice::Router
27
26
  # @since 2.0.0
28
27
  class Routes
29
- # Defines app routes
30
- #
31
- # @yield DSL syntax to define app routes executed in the context
32
- # of {Hanami::Slice::Router}
33
- #
34
- # @return [Proc]
35
- def self.define(&block)
36
- @_routes = block
37
- end
38
-
39
28
  # @api private
40
29
  def self.routes
41
- @_routes || raise(<<~MSG)
42
- Routes need to be defined before being able to fetch them. E.g.,
43
- define do
44
- slice :main, at: "/" do
45
- root to: "home.show"
30
+ @routes ||= build_routes
31
+ end
32
+
33
+ class << self
34
+ # @api private
35
+ def build_routes(definitions = self.definitions)
36
+ return if definitions.empty?
37
+
38
+ proc do
39
+ definitions.each do |(name, args, kwargs, block)|
40
+ if block
41
+ public_send(name, *args, **kwargs, &block)
42
+ else
43
+ public_send(name, *args, **kwargs)
46
44
  end
47
45
  end
48
- MSG
46
+ end
47
+ end
48
+
49
+ # @api private
50
+ def definitions
51
+ @definitions ||= []
52
+ end
53
+
54
+ private
55
+
56
+ # @api private
57
+ def supported_methods
58
+ @supported_methods ||= Slice::Router.public_instance_methods
59
+ end
60
+
61
+ # @api private
62
+ def respond_to_missing?(name, include_private = false)
63
+ supported_methods.include?(name) || super
64
+ end
65
+
66
+ # Capture all method calls that are supported by the router DSL
67
+ # so that it can be evaluated lazily during configuration/boot
68
+ # process
69
+ #
70
+ # @api private
71
+ def method_missing(name, *args, **kwargs, &block)
72
+ return super unless respond_to?(name)
73
+ definitions << [name, args, kwargs, block]
74
+ self
75
+ end
49
76
  end
50
77
  end
51
78
  end
@@ -12,7 +12,7 @@ module Hanami
12
12
  # @since 2.0.0
13
13
  # @api private
14
14
  class EnvStore
15
- Undefined = Dry::Core::Constants::Undefined
15
+ NO_ARG = Object.new.freeze
16
16
 
17
17
  attr_reader :store, :hanami_env
18
18
 
@@ -21,9 +21,9 @@ module Hanami
21
21
  @hanami_env = hanami_env
22
22
  end
23
23
 
24
- def fetch(name, default_value = Undefined, &block)
24
+ def fetch(name, default_value = NO_ARG, &block)
25
25
  name = name.to_s.upcase
26
- args = default_value == Undefined ? [name] : [name, default_value]
26
+ args = default_value.eql?(NO_ARG) ? [name] : [name, default_value]
27
27
 
28
28
  store.fetch(*args, &block)
29
29
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/core"
3
4
  require "dry/configurable"
4
- require "dry/core/constants"
5
5
 
6
6
  module Hanami
7
7
  # App settings
@@ -33,11 +33,72 @@ module Hanami
33
33
  # needs to do is implementing a `#fetch` method with the same signature as `Hash#fetch`.
34
34
  #
35
35
  # @see Hanami::Settings::DotenvStore
36
+ #
37
+ # @api public
36
38
  # @since 2.0.0
37
39
  class Settings
40
+ class << self
41
+ def inherited(subclass)
42
+ super
43
+
44
+ if Hanami.bundled?("dry-types")
45
+ require "dry/types"
46
+ subclass.const_set(:Types, Dry.Types())
47
+ end
48
+ end
49
+
50
+ # Loads the settings for a slice.
51
+ #
52
+ # Returns nil if no settings class is defined.
53
+ #
54
+ # @return [Settings, nil]
55
+ #
56
+ # @api private
57
+ def load_for_slice(slice)
58
+ return unless settings_defined?(slice)
59
+
60
+ require_slice_settings(slice) unless slice_settings_class?(slice)
61
+
62
+ slice_settings_class(slice).new(slice.config.settings_store)
63
+ end
64
+
65
+ private
66
+
67
+ # Returns true if settings are defined for the slice.
68
+ #
69
+ # Settings are considered defined if a `Settings` class is already defined in the slice
70
+ # namespace, or a `config/settings.rb` exists under the slice root.
71
+ def settings_defined?(slice)
72
+ slice.namespace.const_defined?(SETTINGS_CLASS_NAME) ||
73
+ slice.root.join("#{SETTINGS_PATH}#{RB_EXT}").file?
74
+ end
75
+
76
+ def slice_settings_class?(slice)
77
+ slice.namespace.const_defined?(SETTINGS_CLASS_NAME)
78
+ end
79
+
80
+ def slice_settings_class(slice)
81
+ slice.namespace.const_get(SETTINGS_CLASS_NAME)
82
+ end
83
+
84
+ def require_slice_settings(slice)
85
+ require "hanami/settings"
86
+
87
+ slice_settings_require_path = File.join(slice.root, SETTINGS_PATH)
88
+
89
+ begin
90
+ require slice_settings_require_path
91
+ rescue LoadError => e
92
+ raise e unless e.path == slice_settings_require_path
93
+ end
94
+ end
95
+ end
96
+
38
97
  # Exception for errors in the definition of settings.
39
98
  #
40
99
  # Its message collects all the individual errors that can be raised for each setting.
100
+ #
101
+ # @api public
41
102
  InvalidSettingsError = Class.new(StandardError) do
42
103
  def initialize(errors)
43
104
  @errors = errors
@@ -52,6 +113,9 @@ module Hanami
52
113
  end
53
114
  end
54
115
 
116
+ # @api private
117
+ Undefined = Dry::Core::Constants::Undefined
118
+
55
119
  # @api private
56
120
  EMPTY_STORE = Dry::Core::Constants::EMPTY_HASH
57
121
 
@@ -59,14 +123,32 @@ module Hanami
59
123
 
60
124
  # @api private
61
125
  def initialize(store = EMPTY_STORE)
62
- errors = config._settings.map(&:name).reduce({}) do |errs, name|
63
- public_send("#{name}=", store.fetch(name) { Dry::Core::Constants::Undefined })
64
- errs
126
+ errors = config._settings.map(&:name).each_with_object({}) do |name, errs|
127
+ value = store.fetch(name, Undefined)
128
+
129
+ if value.eql?(Undefined)
130
+ # When a key is missing entirely from the store, _read_ its value from the config instead,
131
+ # which ensures its setting constructor runs (with a `nil` argument given) and raises any
132
+ # necessary errors.
133
+ public_send(name)
134
+ else
135
+ public_send("#{name}=", value)
136
+ end
65
137
  rescue => e # rubocop:disable Style/RescueStandardError
66
- errs.merge(name => e)
138
+ errs[name] = e
67
139
  end
68
140
 
69
141
  raise InvalidSettingsError, errors if errors.any?
142
+
143
+ config.finalize!
144
+ end
145
+
146
+ def inspect
147
+ "#<#{self.class.to_s} [#{config._settings.map(&:name).join(", ")}]>"
148
+ end
149
+
150
+ def inspect_values
151
+ "#<#{self.class.to_s} #{config._settings.map { |setting| "#{setting.name}=#{config[setting.name].inspect}" }.join(" ")}>"
70
152
  end
71
153
 
72
154
  private
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "hanami/middleware"
4
+ require "hanami/errors"
5
+
3
6
  module Hanami
4
7
  class Slice
5
8
  module Routing
@@ -39,11 +42,16 @@ module Hanami
39
42
  # @api private
40
43
  attr_reader :stack
41
44
 
45
+ # @since 2.0.0
46
+ # @api public
47
+ attr_reader :namespaces
48
+
42
49
  # @since 2.0.0
43
50
  # @api private
44
51
  def initialize
45
52
  @prefix = ROOT_PREFIX
46
53
  @stack = Hash.new { |hash, key| hash[key] = [] }
54
+ @namespaces = [Hanami::Middleware]
47
55
  end
48
56
 
49
57
  # @since 2.0.0
@@ -52,11 +60,13 @@ module Hanami
52
60
  super
53
61
  @prefix = source.instance_variable_get(:@prefix).dup
54
62
  @stack = stack.dup
63
+ @namespaces = namespaces.dup
55
64
  end
56
65
 
57
66
  # @since 2.0.0
58
67
  # @api private
59
- def use(middleware, *args, before: nil, after: nil, &blk)
68
+ def use(spec, *args, before: nil, after: nil, &blk)
69
+ middleware = resolve_middleware_class(spec)
60
70
  item = [middleware, args, blk]
61
71
 
62
72
  if before
@@ -141,6 +151,40 @@ module Hanami
141
151
  def index_of(middleware)
142
152
  @stack[@prefix].index { |(m, *)| m.equal?(middleware) }
143
153
  end
154
+
155
+ # @since 2.0.0
156
+ def resolve_middleware_class(spec)
157
+ case spec
158
+ when Symbol then load_middleware_class(spec)
159
+ when Class, Module then spec
160
+ else
161
+ if spec.respond_to?(:call)
162
+ spec
163
+ else
164
+ raise UnsupportedMiddlewareSpecError, spec
165
+ end
166
+ end
167
+ end
168
+
169
+ # @since 2.0.0
170
+ def load_middleware_class(spec)
171
+ begin
172
+ require "hanami/middleware/#{spec}"
173
+ rescue LoadError # rubocop:disable Lint/SuppressedException
174
+ end
175
+
176
+ class_name = Hanami::Utils::String.classify(spec.to_s)
177
+ namespace = namespaces.detect { |ns| ns.const_defined?(class_name) }
178
+
179
+ if namespace
180
+ namespace.const_get(class_name)
181
+ else
182
+ raise(
183
+ UnsupportedMiddlewareSpecError,
184
+ "Failed to find corresponding middleware class for `#{spec}` in #{namespaces.join(', ')}"
185
+ )
186
+ end
187
+ end
144
188
  end
145
189
  end
146
190
  end
data/lib/hanami/slice.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/system/container"
4
3
  require "zeitwerk"
4
+ require "dry/system"
5
+
6
+ require_relative "../hanami"
5
7
  require_relative "constants"
6
8
  require_relative "errors"
9
+ require_relative "settings"
7
10
  require_relative "slice_name"
8
11
  require_relative "slice_registrar"
9
- require_relative "providers/settings"
10
12
 
11
13
  module Hanami
12
14
  # A slice represents any distinct area of concern within an Hanami app.
@@ -17,12 +19,11 @@ module Hanami
17
19
  # Each slice corresponds a single module namespace and a single root directory of source
18
20
  # files for loading as components into its container.
19
21
  #
20
- # Each slice has its own configuration, and may optionally have its own settings,
21
- # routes, as well as other nested slices.
22
+ # Each slice has its own config, and may optionally have its own settings, routes, as well as
23
+ # other nested slices.
22
24
  #
23
- # Slices expect an Hanami app to be defined (which itself is a slice). They will
24
- # initialize their configuration as a copy of the app's, and will also configure
25
- # certain components
25
+ # Slices expect an Hanami app to be defined (which itself is a slice). They will initialize their
26
+ # config as a copy of the app's, and will also configure certain components
26
27
  #
27
28
  # Slices must be _prepared_ and optionally _booted_ before they can be used (see
28
29
  # {ClassMethods.prepare} and {ClassMethods.boot}). A prepared slice will lazily load its
@@ -56,15 +57,14 @@ module Hanami
56
57
  Hanami.app
57
58
  end
58
59
 
59
- # A slice's configuration is copied from the app configuration at time of first access. The
60
- # app should have its configuration completed before slices are loaded.
61
- def configuration
62
- @configuration ||= app.configuration.dup.tap do |config|
60
+ # A slice's config is copied from the app config at time of first access. The app should have
61
+ # its config completed before slices are loaded.
62
+ def config
63
+ @config ||= app.config.dup.tap do |slice_config|
63
64
  # Remove specific values from app that will not apply to this slice
64
- config.root = nil
65
+ slice_config.root = nil
65
66
  end
66
67
  end
67
- alias_method :config, :configuration
68
68
 
69
69
  def slice_name
70
70
  @slice_name ||= SliceName.new(self, inflector: method(:inflector))
@@ -75,11 +75,22 @@ module Hanami
75
75
  end
76
76
 
77
77
  def root
78
- configuration.root
78
+ # Provide a best guess for a root when it is not yet configured.
79
+ #
80
+ # This is particularly useful for user-defined slice classes that access `settings` inside
81
+ # the class body (since the root needed to find the settings file). In this case,
82
+ # `configuration.root` may be nil when `settings` is called, since the root is configured by
83
+ # `SliceRegistrar#configure_slice` _after_ the class is loaded.
84
+ #
85
+ # In common cases, this best guess will be correct since most Hanami slices will be expected
86
+ # to live in the app SLICES_DIR. For advanced cases, the correct slice root should be
87
+ # explicitly configured at the beginning of the slice class body, before any calls to
88
+ # `settings`.
89
+ config.root || app.root.join(SLICES_DIR, slice_name.to_s)
79
90
  end
80
91
 
81
92
  def inflector
82
- configuration.inflector
93
+ config.inflector
83
94
  end
84
95
 
85
96
  def prepare(provider_name = nil)
@@ -180,6 +191,12 @@ module Hanami
180
191
  end
181
192
  end
182
193
 
194
+ def settings
195
+ return @settings if instance_variable_defined?(:@settings)
196
+
197
+ @settings = Settings.load_for_slice(self)
198
+ end
199
+
183
200
  def routes
184
201
  @routes ||= load_routes
185
202
  end
@@ -209,7 +226,7 @@ module Hanami
209
226
  def prepare_slice
210
227
  return self if prepared?
211
228
 
212
- configuration.finalize!
229
+ config.finalize!
213
230
 
214
231
  ensure_slice_name
215
232
  ensure_slice_consts
@@ -222,8 +239,6 @@ module Hanami
222
239
 
223
240
  prepare_autoloader
224
241
 
225
- ensure_prepared
226
-
227
242
  # Load child slices last, ensuring their parent is fully prepared beforehand
228
243
  # (useful e.g. for slices that may wish to access constants defined in the
229
244
  # parent's autoloaded directories)
@@ -250,17 +265,13 @@ module Hanami
250
265
  end
251
266
 
252
267
  def ensure_root
253
- unless configuration.root
268
+ unless config.root
254
269
  raise SliceLoadError, "Slice must have a `config.root` before it can be prepared"
255
270
  end
256
271
  end
257
272
 
258
- def ensure_prepared
259
- # Load settings so we can fail early in case of non-conformant values
260
- self[:settings] if key?(:settings)
261
- end
262
-
263
273
  def prepare_all
274
+ prepare_settings
264
275
  prepare_container_consts
265
276
  prepare_container_plugins
266
277
  prepare_container_base_config
@@ -269,6 +280,15 @@ module Hanami
269
280
  prepare_container_providers
270
281
  end
271
282
 
283
+ def prepare_settings
284
+ container.register(:settings, settings) if settings
285
+ end
286
+
287
+ def prepare_container_consts
288
+ namespace.const_set :Container, container
289
+ namespace.const_set :Deps, container.injector
290
+ end
291
+
272
292
  def prepare_container_plugins
273
293
  container.use(:env, inferrer: -> { Hanami.env })
274
294
 
@@ -285,8 +305,8 @@ module Hanami
285
305
  container.config.root = root
286
306
  container.config.provider_dirs = [File.join("config", "providers")]
287
307
 
288
- container.config.env = configuration.env
289
- container.config.inflector = configuration.inflector
308
+ container.config.env = config.env
309
+ container.config.inflector = config.inflector
290
310
  end
291
311
 
292
312
  def prepare_container_component_dirs
@@ -304,7 +324,7 @@ module Hanami
304
324
  # When auto-registering components in the root, ignore files in `config/` (this is
305
325
  # for framework config only), `lib/` (these will be auto-registered as above), as
306
326
  # well as the configured no_auto_register_paths
307
- no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] + configuration.no_auto_register_paths)
327
+ no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] + config.no_auto_register_paths)
308
328
  .map { |path|
309
329
  path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
310
330
  }
@@ -335,8 +355,6 @@ module Hanami
335
355
  require_relative "providers/routes"
336
356
  register_provider(:routes, source: Providers::Routes.for_slice(self))
337
357
  end
338
-
339
- Providers::Settings.register_with_slice(self)
340
358
  end
341
359
 
342
360
  def prepare_autoloader
@@ -358,17 +376,14 @@ module Hanami
358
376
  autoloader.setup
359
377
  end
360
378
 
361
- def prepare_container_consts
362
- namespace.const_set :Container, container
363
- namespace.const_set :Deps, container.injector
364
- end
365
-
366
379
  def prepare_slices
367
380
  slices.load_slices.each(&:prepare)
368
381
  slices.freeze
369
382
  end
370
383
 
371
384
  def load_routes
385
+ return false unless Hanami.bundled?("hanami-router")
386
+
372
387
  if root.directory?
373
388
  routes_require_path = File.join(root, ROUTES_PATH)
374
389
 
@@ -393,14 +408,14 @@ module Hanami
393
408
 
394
409
  require_relative "slice/router"
395
410
 
396
- config = configuration
411
+ config = self.config
397
412
  rack_monitor = self["rack.monitor"]
398
413
 
399
414
  Slice::Router.new(
400
415
  inspector: inspector,
401
416
  routes: routes,
402
- resolver: configuration.router.resolver.new(slice: self),
403
- **configuration.router.options
417
+ resolver: config.router.resolver.new(slice: self),
418
+ **config.router.options
404
419
  ) do
405
420
  use(rack_monitor)
406
421
  use(*config.sessions.middleware) if config.sessions.enabled?
@@ -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
@@ -75,6 +75,12 @@ module Hanami
75
75
  slices.values
76
76
  end
77
77
 
78
+ def with_nested
79
+ to_a.flat_map { |slice|
80
+ [slice] + slice.slices.with_nested
81
+ }
82
+ end
83
+
78
84
  private
79
85
 
80
86
  def root
@@ -8,7 +8,7 @@ module Hanami
8
8
  module Version
9
9
  # @since 0.9.0
10
10
  # @api private
11
- VERSION = "2.0.0.beta2"
11
+ VERSION = "2.0.0.beta4"
12
12
 
13
13
  # @since 0.9.0
14
14
  # @api private
data/lib/hanami.rb CHANGED
@@ -114,7 +114,6 @@ module Hanami
114
114
  @_mutex.synchronize do
115
115
  @_bundled[gem_name] ||= begin
116
116
  gem(gem_name)
117
- true
118
117
  rescue Gem::LoadError
119
118
  false
120
119
  end
@@ -14,12 +14,10 @@ RSpec.describe "App action / Routes", :app_integration do
14
14
  write "config/routes.rb", <<~RUBY
15
15
  module TestApp
16
16
  class Routes < Hanami::Routes
17
- define do
18
- root to: "home.index"
17
+ root to: "home.index"
19
18
 
20
- slice :admin, at: "/admin" do
21
- root to: "dashboard.index"
22
- end
19
+ slice :admin, at: "/admin" do
20
+ root to: "dashboard.index"
23
21
  end
24
22
  end
25
23
  end
@@ -16,9 +16,7 @@ RSpec.describe "App routes helper", :app_integration do
16
16
  write "config/routes.rb", <<~RUBY
17
17
  module TestApp
18
18
  class Routes < Hanami::Routes
19
- define do
20
- root to: "home.index"
21
- end
19
+ root to: "home.index"
22
20
  end
23
21
  end
24
22
  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