hanami 2.0.0.alpha8 → 2.0.0.beta1

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.
Files changed (224) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +442 -241
  3. data/FEATURES.md +30 -9
  4. data/README.md +1 -3
  5. data/hanami.gemspec +21 -11
  6. data/lib/hanami/app.rb +141 -0
  7. data/lib/hanami/assets/application_configuration.rb +10 -4
  8. data/lib/hanami/configuration/actions/content_security_policy.rb +5 -5
  9. data/lib/hanami/configuration/actions/cookies.rb +2 -2
  10. data/lib/hanami/configuration/actions.rb +10 -4
  11. data/lib/hanami/configuration/logger.rb +4 -4
  12. data/lib/hanami/configuration/router.rb +2 -6
  13. data/lib/hanami/configuration/sessions.rb +1 -1
  14. data/lib/hanami/configuration/views.rb +9 -4
  15. data/lib/hanami/configuration.rb +118 -46
  16. data/lib/hanami/constants.rb +24 -2
  17. data/lib/hanami/errors.rb +1 -1
  18. data/lib/hanami/{application → extensions}/action/slice_configured_action.rb +9 -9
  19. data/lib/hanami/extensions/action.rb +79 -0
  20. data/lib/hanami/extensions/view/context.rb +106 -0
  21. data/lib/hanami/{application → extensions}/view/slice_configured_context.rb +10 -10
  22. data/lib/hanami/{application → extensions}/view/slice_configured_view.rb +12 -6
  23. data/lib/hanami/extensions/view.rb +33 -0
  24. data/lib/hanami/extensions.rb +10 -0
  25. data/lib/hanami/providers/inflector.rb +13 -0
  26. data/lib/hanami/providers/logger.rb +13 -0
  27. data/lib/hanami/providers/rack.rb +27 -0
  28. data/lib/hanami/providers/routes.rb +33 -0
  29. data/lib/hanami/providers/settings.rb +23 -0
  30. data/lib/hanami/rake_tasks.rb +61 -0
  31. data/lib/hanami/routes.rb +51 -0
  32. data/lib/hanami/server.rb +1 -1
  33. data/lib/hanami/settings/dotenv_store.rb +58 -0
  34. data/lib/hanami/settings.rb +90 -0
  35. data/lib/hanami/setup.rb +4 -2
  36. data/lib/hanami/{application → slice}/router.rb +18 -13
  37. data/lib/hanami/slice/routes_helper.rb +37 -0
  38. data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
  39. data/lib/hanami/slice/routing/resolver.rb +97 -0
  40. data/lib/hanami/{application → slice}/view_name_inferrer.rb +3 -3
  41. data/lib/hanami/slice.rb +246 -73
  42. data/lib/hanami/slice_configurable.rb +4 -17
  43. data/lib/hanami/slice_name.rb +6 -6
  44. data/lib/hanami/slice_registrar.rb +119 -0
  45. data/lib/hanami/version.rb +1 -1
  46. data/lib/hanami/web/rack_logger.rb +1 -1
  47. data/lib/hanami.rb +34 -26
  48. data/spec/integration/application_middleware_stack_spec.rb +84 -0
  49. data/spec/integration/assets/cdn_spec.rb +48 -0
  50. data/spec/integration/assets/fingerprint_spec.rb +42 -0
  51. data/spec/integration/assets/helpers_spec.rb +50 -0
  52. data/spec/integration/assets/serve_spec.rb +70 -0
  53. data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
  54. data/spec/integration/body_parsers_spec.rb +50 -0
  55. data/spec/integration/cli/assets/precompile_spec.rb +147 -0
  56. data/spec/integration/cli/assets_spec.rb +14 -0
  57. data/spec/integration/cli/console_spec.rb +105 -0
  58. data/spec/integration/cli/db/apply_spec.rb +74 -0
  59. data/spec/integration/cli/db/console_spec.rb +40 -0
  60. data/spec/integration/cli/db/create_spec.rb +50 -0
  61. data/spec/integration/cli/db/drop_spec.rb +54 -0
  62. data/spec/integration/cli/db/migrate_spec.rb +108 -0
  63. data/spec/integration/cli/db/prepare_spec.rb +36 -0
  64. data/spec/integration/cli/db/rollback_spec.rb +96 -0
  65. data/spec/integration/cli/db/version_spec.rb +38 -0
  66. data/spec/integration/cli/db_spec.rb +21 -0
  67. data/spec/integration/cli/destroy/action_spec.rb +143 -0
  68. data/spec/integration/cli/destroy/app_spec.rb +118 -0
  69. data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
  70. data/spec/integration/cli/destroy/migration_spec.rb +70 -0
  71. data/spec/integration/cli/destroy/model_spec.rb +113 -0
  72. data/spec/integration/cli/destroy_spec.rb +18 -0
  73. data/spec/integration/cli/generate/action_spec.rb +469 -0
  74. data/spec/integration/cli/generate/app_spec.rb +215 -0
  75. data/spec/integration/cli/generate/mailer_spec.rb +189 -0
  76. data/spec/integration/cli/generate/migration_spec.rb +72 -0
  77. data/spec/integration/cli/generate/model_spec.rb +290 -0
  78. data/spec/integration/cli/generate/secret_spec.rb +56 -0
  79. data/spec/integration/cli/generate_spec.rb +19 -0
  80. data/spec/integration/cli/new/database_spec.rb +235 -0
  81. data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
  82. data/spec/integration/cli/new/template_spec.rb +118 -0
  83. data/spec/integration/cli/new/test_spec.rb +274 -0
  84. data/spec/integration/cli/new_spec.rb +970 -0
  85. data/spec/integration/cli/plugins_spec.rb +39 -0
  86. data/spec/integration/cli/routes_spec.rb +49 -0
  87. data/spec/integration/cli/server_spec.rb +626 -0
  88. data/spec/integration/cli/version_spec.rb +85 -0
  89. data/spec/integration/early_hints_spec.rb +35 -0
  90. data/spec/integration/handle_exceptions_spec.rb +244 -0
  91. data/spec/integration/head_spec.rb +89 -0
  92. data/spec/integration/http_headers_spec.rb +29 -0
  93. data/spec/integration/mailer_spec.rb +32 -0
  94. data/spec/integration/middleware_spec.rb +81 -0
  95. data/spec/integration/mount_applications_spec.rb +88 -0
  96. data/spec/integration/project_initializers_spec.rb +40 -0
  97. data/spec/integration/rackup_spec.rb +35 -0
  98. data/spec/integration/rake/with_minitest_spec.rb +67 -0
  99. data/spec/integration/rake/with_rspec_spec.rb +69 -0
  100. data/spec/integration/routing_helpers_spec.rb +61 -0
  101. data/spec/integration/security/content_security_policy_spec.rb +46 -0
  102. data/spec/integration/security/csrf_protection_spec.rb +42 -0
  103. data/spec/integration/security/force_ssl_spec.rb +29 -0
  104. data/spec/integration/security/x_content_type_options_spec.rb +46 -0
  105. data/spec/integration/security/x_frame_options_spec.rb +46 -0
  106. data/spec/integration/security/x_xss_protection_spec.rb +46 -0
  107. data/spec/integration/send_file_spec.rb +51 -0
  108. data/spec/integration/sessions_spec.rb +247 -0
  109. data/spec/integration/static_middleware_spec.rb +21 -0
  110. data/spec/integration/streaming_spec.rb +41 -0
  111. data/spec/integration/unsafe_send_file_spec.rb +52 -0
  112. data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
  113. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
  114. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
  115. data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
  116. data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
  117. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
  118. data/spec/isolation/hanami/boot/success_spec.rb +50 -0
  119. data/spec/new_integration/action/configuration_spec.rb +26 -0
  120. data/spec/new_integration/action/cookies_spec.rb +58 -0
  121. data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
  122. data/spec/new_integration/action/routes_spec.rb +73 -0
  123. data/spec/new_integration/action/sessions_spec.rb +50 -0
  124. data/spec/new_integration/action/view_integration_spec.rb +165 -0
  125. data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
  126. data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
  127. data/spec/new_integration/action/view_rendering_spec.rb +107 -0
  128. data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
  129. data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
  130. data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
  131. data/spec/new_integration/container/auto_injection_spec.rb +53 -0
  132. data/spec/new_integration/container/auto_registration_spec.rb +86 -0
  133. data/spec/new_integration/container/autoloader_spec.rb +80 -0
  134. data/spec/new_integration/container/imports_spec.rb +253 -0
  135. data/spec/new_integration/container/prepare_container_spec.rb +123 -0
  136. data/spec/new_integration/container/shutdown_spec.rb +91 -0
  137. data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
  138. data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
  139. data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
  140. data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
  141. data/spec/new_integration/settings_spec.rb +115 -0
  142. data/spec/new_integration/slices/external_slice_spec.rb +92 -0
  143. data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
  144. data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
  145. data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
  146. data/spec/new_integration/slices_spec.rb +101 -0
  147. data/spec/new_integration/view/configuration_spec.rb +49 -0
  148. data/spec/new_integration/view/context/assets_spec.rb +67 -0
  149. data/spec/new_integration/view/context/inflector_spec.rb +48 -0
  150. data/spec/new_integration/view/context/request_spec.rb +61 -0
  151. data/spec/new_integration/view/context/routes_spec.rb +86 -0
  152. data/spec/new_integration/view/context/settings_spec.rb +50 -0
  153. data/spec/new_integration/view/inflector_spec.rb +57 -0
  154. data/spec/new_integration/view/part_namespace_spec.rb +96 -0
  155. data/spec/new_integration/view/path_spec.rb +56 -0
  156. data/spec/new_integration/view/template_spec.rb +68 -0
  157. data/spec/new_integration/view/views_spec.rb +103 -0
  158. data/spec/spec_helper.rb +16 -0
  159. data/spec/support/app_integration.rb +91 -0
  160. data/spec/support/coverage.rb +1 -0
  161. data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
  162. data/spec/support/fixtures/hanami-plugin/README.md +35 -0
  163. data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
  164. data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
  165. data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
  166. data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
  167. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
  168. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
  169. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
  170. data/spec/support/rspec.rb +27 -0
  171. data/spec/support/shared_examples/cli/generate/app.rb +494 -0
  172. data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
  173. data/spec/support/shared_examples/cli/generate/model.rb +81 -0
  174. data/spec/support/shared_examples/cli/new.rb +97 -0
  175. data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
  176. data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
  177. data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
  178. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
  179. data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
  180. data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
  181. data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
  182. data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
  183. data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
  184. data/spec/unit/hanami/configuration/views_spec.rb +120 -0
  185. data/spec/unit/hanami/configuration_spec.rb +43 -0
  186. data/spec/unit/hanami/env_spec.rb +54 -0
  187. data/spec/unit/hanami/routes_spec.rb +25 -0
  188. data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
  189. data/spec/unit/hanami/settings_spec.rb +56 -0
  190. data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
  191. data/spec/unit/hanami/slice_name_spec.rb +47 -0
  192. data/spec/unit/hanami/slice_spec.rb +17 -0
  193. data/spec/unit/hanami/version_spec.rb +7 -0
  194. data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
  195. metadata +353 -57
  196. data/lib/hanami/application/action.rb +0 -72
  197. data/lib/hanami/application/container/providers/inflector.rb +0 -7
  198. data/lib/hanami/application/container/providers/logger.rb +0 -7
  199. data/lib/hanami/application/container/providers/rack_logger.rb +0 -15
  200. data/lib/hanami/application/container/providers/rack_monitor.rb +0 -12
  201. data/lib/hanami/application/container/providers/routes_helper.rb +0 -9
  202. data/lib/hanami/application/container/providers/settings.rb +0 -7
  203. data/lib/hanami/application/routes.rb +0 -55
  204. data/lib/hanami/application/routes_helper.rb +0 -34
  205. data/lib/hanami/application/routing/resolver/node.rb +0 -50
  206. data/lib/hanami/application/routing/resolver/trie.rb +0 -59
  207. data/lib/hanami/application/routing/resolver.rb +0 -87
  208. data/lib/hanami/application/routing/router.rb +0 -36
  209. data/lib/hanami/application/settings/dotenv_store.rb +0 -60
  210. data/lib/hanami/application/settings.rb +0 -93
  211. data/lib/hanami/application/slice_registrar.rb +0 -106
  212. data/lib/hanami/application/view/context.rb +0 -95
  213. data/lib/hanami/application/view.rb +0 -24
  214. data/lib/hanami/application.rb +0 -273
  215. data/lib/hanami/cli/application/cli.rb +0 -40
  216. data/lib/hanami/cli/application/command.rb +0 -47
  217. data/lib/hanami/cli/application/commands/console.rb +0 -81
  218. data/lib/hanami/cli/application/commands.rb +0 -16
  219. data/lib/hanami/cli/base_command.rb +0 -48
  220. data/lib/hanami/cli/commands/command.rb +0 -171
  221. data/lib/hanami/cli/commands/server.rb +0 -88
  222. data/lib/hanami/cli/commands.rb +0 -65
  223. data/lib/hanami/configuration/middleware.rb +0 -20
  224. data/lib/hanami/configuration/source_dirs.rb +0 -42
@@ -7,51 +7,108 @@ require "dry/configurable"
7
7
  require "dry/inflector"
8
8
  require "pathname"
9
9
 
10
- require_relative "application/settings/dotenv_store"
10
+ require_relative "constants"
11
11
  require_relative "configuration/logger"
12
- require_relative "configuration/middleware"
13
12
  require_relative "configuration/router"
14
13
  require_relative "configuration/sessions"
15
- require_relative "configuration/source_dirs"
16
- require_relative "constants"
14
+ require_relative "settings/dotenv_store"
15
+ require_relative "slice/routing/middleware/stack"
17
16
 
18
17
  module Hanami
19
- # Hanami application configuration
18
+ # Hanami app configuration
20
19
  #
21
20
  # @since 2.0.0
22
21
  class Configuration
23
22
  include Dry::Configurable
24
23
 
24
+ setting :root, constructor: ->(path) { Pathname(path) if path }
25
+
26
+ setting :no_auto_register_paths, default: %w[entities]
27
+
28
+ setting :inflector, default: Dry::Inflector.new
29
+
30
+ setting :settings_store, default: Hanami::Settings::DotenvStore
31
+
32
+ setting :slices do
33
+ setting :shared_component_keys, default: %w[
34
+ inflector
35
+ logger
36
+ notifications
37
+ rack.monitor
38
+ routes
39
+ settings
40
+ ]
41
+ end
42
+
43
+ setting :base_url, default: "http://0.0.0.0:2300", constructor: ->(url) { URI(url) }
44
+
45
+ setting :sessions, default: :null, constructor: ->(*args) { Sessions.new(*args) }
46
+
47
+ setting :logger, cloneable: true
48
+
25
49
  DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
26
50
  private_constant :DEFAULT_ENVIRONMENTS
27
51
 
28
- attr_reader :application_name
52
+ # @return [Symbol] The name of the application
53
+ #
54
+ # @api public
55
+ attr_reader :app_name
56
+
57
+ # @return [String] The current environment
58
+ #
59
+ # @api public
29
60
  attr_reader :env
30
61
 
62
+ # @return [Hanami::Configuration::Actions]
63
+ #
64
+ # @api public
31
65
  attr_reader :actions
66
+
67
+ # @return [Hanami::Slice::Routing::Middleware::Stack]
68
+ #
69
+ # @api public
32
70
  attr_reader :middleware
71
+
72
+ # @api private
73
+ alias_method :middleware_stack, :middleware
74
+
75
+ # @return [Hanami::Configuration::Router]
76
+ #
77
+ # @api public
33
78
  attr_reader :router
34
- attr_reader :views, :assets
35
79
 
80
+ # @return [Hanami::Configuration::Views]
81
+ #
82
+ # @api public
83
+ attr_reader :views
84
+
85
+ # @return [Hanami::Assets::AppConfiguration]
86
+ #
87
+ # @api public
88
+ attr_reader :assets
89
+
90
+ # @return [Concurrent::Hash] A hash of default environments
91
+ #
92
+ # @api private
36
93
  attr_reader :environments
37
94
  private :environments
38
95
 
39
- # rubocop:disable Metrics/AbcSize
40
- def initialize(application_name:, env:)
41
- @application_name = application_name
96
+ # @api private
97
+ def initialize(app_name:, env:)
98
+ @app_name = app_name
42
99
 
43
100
  @environments = DEFAULT_ENVIRONMENTS.clone
44
101
  @env = env
45
102
 
46
103
  # Some default setting values must be assigned at initialize-time to ensure they
47
- # have appropriate values for the current application
104
+ # have appropriate values for the current app
48
105
  self.root = Dir.pwd
49
- self.settings_store = Application::Settings::DotenvStore.new.with_dotenv_loaded
106
+ self.settings_store = Hanami::Settings::DotenvStore.new.with_dotenv_loaded
50
107
 
51
- config.logger = Configuration::Logger.new(env: env, application_name: application_name)
108
+ config.logger = Configuration::Logger.new(env: env, app_name: app_name)
52
109
 
53
- @assets = load_dependent_config("hanami/assets/application_configuration") {
54
- Hanami::Assets::ApplicationConfiguration.new
110
+ @assets = load_dependent_config("hanami/assets/app_configuration") {
111
+ Hanami::Assets::AppConfiguration.new
55
112
  }
56
113
 
57
114
  @actions = load_dependent_config("hanami/action") {
@@ -59,7 +116,7 @@ module Hanami
59
116
  Actions.new
60
117
  }
61
118
 
62
- @middleware = Middleware.new
119
+ @middleware = Slice::Routing::Middleware::Stack.new
63
120
 
64
121
  @router = Router.new(self)
65
122
 
@@ -70,8 +127,14 @@ module Hanami
70
127
 
71
128
  yield self if block_given?
72
129
  end
73
- # rubocop:enable Metrics/AbcSize
74
130
 
131
+ # Apply configuration for the given environment
132
+ #
133
+ # @param env [String] the environment name
134
+ #
135
+ # @return [Hanami::Configuration]
136
+ #
137
+ # @api public
75
138
  def environment(env_name, &block)
76
139
  environments[env_name] << block
77
140
  apply_env_config
@@ -79,6 +142,34 @@ module Hanami
79
142
  self
80
143
  end
81
144
 
145
+ # Configure application's inflections
146
+ #
147
+ # @see https://dry-rb.org/gems/dry-inflector
148
+ #
149
+ # @return [Dry::Inflector]
150
+ #
151
+ # @api public
152
+ def inflections(&block)
153
+ self.inflector = Dry::Inflector.new(&block)
154
+ end
155
+
156
+ # @api private
157
+ def initialize_copy(source)
158
+ super
159
+
160
+ @app_name = app_name.dup
161
+ @environments = environments.dup
162
+
163
+ @assets = source.assets.dup
164
+ @actions = source.actions.dup
165
+ @middleware = source.middleware.dup
166
+ @router = source.router.dup.tap do |router|
167
+ router.instance_variable_set(:@base_configuration, self)
168
+ end
169
+ @views = source.views.dup
170
+ end
171
+
172
+ # @api private
82
173
  def finalize!
83
174
  apply_env_config
84
175
 
@@ -92,51 +183,30 @@ module Hanami
92
183
  super
93
184
  end
94
185
 
95
- setting :root, constructor: -> path { Pathname(path) }
96
-
97
- setting :inflector, default: Dry::Inflector.new
98
-
99
- def inflections(&block)
100
- self.inflector = Dry::Inflector.new(&block)
101
- end
102
-
103
- setting :logger, cloneable: true
104
-
186
+ # Set a default global logger instance
187
+ #
188
+ # @api public
105
189
  def logger=(logger_instance)
106
190
  @logger_instance = logger_instance
107
191
  end
108
192
 
193
+ # Return configured logger instance
194
+ #
195
+ # @api public
109
196
  def logger_instance
110
197
  @logger_instance || logger.instance
111
198
  end
112
199
 
113
- setting :settings_path, default: File.join("config", "settings")
114
-
115
- setting :settings_class_name, default: "Settings"
116
-
117
- setting :settings_store, default: Application::Settings::DotenvStore
118
-
119
- setting :source_dirs, default: Configuration::SourceDirs.new, cloneable: true
120
-
121
- setting :base_url, default: "http://0.0.0.0:2300", constructor: -> url { URI(url) }
122
-
123
- def for_each_middleware(&blk)
124
- stack = middleware.stack.dup
125
- stack += sessions.middleware if sessions.enabled?
126
-
127
- stack.each(&blk)
128
- end
129
-
130
- setting :sessions, default: :null, constructor: -> *args { Sessions.new(*args) }
131
-
132
200
  private
133
201
 
202
+ # @api private
134
203
  def apply_env_config(env = self.env)
135
204
  environments[env].each do |block|
136
205
  instance_eval(&block)
137
206
  end
138
207
  end
139
208
 
209
+ # @api private
140
210
  def load_dependent_config(require_path, &block)
141
211
  require require_path
142
212
  yield
@@ -147,6 +217,7 @@ module Hanami
147
217
  NullConfiguration.new
148
218
  end
149
219
 
220
+ # @api private
150
221
  def method_missing(name, *args, &block)
151
222
  if config.respond_to?(name)
152
223
  config.public_send(name, *args, &block)
@@ -155,6 +226,7 @@ module Hanami
155
226
  end
156
227
  end
157
228
 
229
+ # @api private
158
230
  def respond_to_missing?(name, _incude_all = false)
159
231
  config.respond_to?(name) || super
160
232
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanami
4
+ # @api private
4
5
  CONTAINER_KEY_DELIMITER = "."
5
6
  private_constant :CONTAINER_KEY_DELIMITER
6
7
 
@@ -8,6 +9,7 @@ module Hanami
8
9
  MODULE_DELIMITER = "::"
9
10
  private_constant :MODULE_DELIMITER
10
11
 
12
+ # @api private
11
13
  PATH_DELIMITER = "/"
12
14
  private_constant :PATH_DELIMITER
13
15
 
@@ -16,13 +18,33 @@ module Hanami
16
18
  private_constant :CONFIG_DIR
17
19
 
18
20
  # @api private
19
- SLICES_DIR = "slices"
20
- private_constant :SLICES_DIR
21
+ APP_DIR = "app"
22
+ private_constant :APP_DIR
21
23
 
22
24
  # @api private
23
25
  LIB_DIR = "lib"
24
26
  private_constant :LIB_DIR
25
27
 
28
+ # @api private
29
+ SLICES_DIR = "slices"
30
+ private_constant :SLICES_DIR
31
+
32
+ # @api private
33
+ ROUTES_PATH = File.join(CONFIG_DIR, "routes")
34
+ private_constant :ROUTES_PATH
35
+
36
+ # @api private
37
+ ROUTES_CLASS_NAME = "Routes"
38
+ private_constant :ROUTES_CLASS_NAME
39
+
40
+ # @api private
41
+ SETTINGS_PATH = File.join(CONFIG_DIR, "settings")
42
+ private_constant :SETTINGS_PATH
43
+
44
+ # @api private
45
+ SETTINGS_CLASS_NAME = "Settings"
46
+ private_constant :SETTINGS_CLASS_NAME
47
+
26
48
  # @api private
27
49
  RB_EXT = ".rb"
28
50
  private_constant :RB_EXT
data/lib/hanami/errors.rb CHANGED
@@ -5,7 +5,7 @@ module Hanami
5
5
  Error = Class.new(StandardError)
6
6
 
7
7
  # @since 2.0.0
8
- ApplicationLoadError = Class.new(Error)
8
+ AppLoadError = Class.new(Error)
9
9
 
10
10
  # @since 2.0.0
11
11
  SliceLoadError = Class.new(Error)
@@ -3,8 +3,8 @@
3
3
  require "hanami/action"
4
4
 
5
5
  module Hanami
6
- class Application
7
- class Action < Hanami::Action
6
+ module Extensions
7
+ module Action
8
8
  # Provides slice-specific configuration and behavior for any action class defined
9
9
  # within a slice's module namespace.
10
10
  #
@@ -30,7 +30,7 @@ module Hanami
30
30
 
31
31
  private
32
32
 
33
- # @see Hanami::Application::Action#initialize
33
+ # @see Hanami::Extensions::Action::InstanceMethods#initialize
34
34
  def define_new
35
35
  resolve_view = method(:resolve_paired_view)
36
36
  resolve_view_context = method(:resolve_view_context)
@@ -39,7 +39,7 @@ module Hanami
39
39
  define_method(:new) do |**kwargs|
40
40
  super(
41
41
  view: kwargs.fetch(:view) { resolve_view.(self) },
42
- view_context: kwargs.fetch(:view_context) { resolve_view_context.(self) },
42
+ view_context: kwargs.fetch(:view_context) { resolve_view_context.() },
43
43
  routes: kwargs.fetch(:routes) { resolve_routes.() },
44
44
  **kwargs,
45
45
  )
@@ -80,22 +80,22 @@ module Hanami
80
80
  end
81
81
  end
82
82
 
83
- def resolve_view_context(_action_class)
83
+ def resolve_view_context
84
84
  identifier = actions_config.view_context_identifier
85
85
 
86
86
  if slice.key?(identifier)
87
87
  slice[identifier]
88
- elsif slice.application.key?(identifier)
89
- slice.application[identifier]
88
+ elsif slice.app.key?(identifier)
89
+ slice.app[identifier]
90
90
  end
91
91
  end
92
92
 
93
93
  def resolve_routes
94
- slice.application[:routes_helper] if slice.application.key?(:routes_helper)
94
+ slice.app["routes"] if slice.app.key?("routes")
95
95
  end
96
96
 
97
97
  def actions_config
98
- slice.application.config.actions
98
+ slice.config.actions
99
99
  end
100
100
  end
101
101
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action"
4
+ require_relative "../slice_configurable"
5
+ require_relative "action/slice_configured_action"
6
+
7
+ module Hanami
8
+ module Extensions
9
+ # Extended behavior for actions intended for use within an Hanami app.
10
+ #
11
+ # @see Hanami::Action
12
+ #
13
+ # @api public
14
+ # @since 2.0.0
15
+ module Action
16
+ def self.included(action_class)
17
+ super
18
+
19
+ action_class.extend(Hanami::SliceConfigurable)
20
+ action_class.extend(ClassMethods)
21
+ action_class.prepend(InstanceMethods)
22
+ end
23
+
24
+ module ClassMethods
25
+ def configure_for_slice(slice)
26
+ extend SliceConfiguredAction.new(slice)
27
+ end
28
+ end
29
+
30
+ module InstanceMethods
31
+ attr_reader :view, :view_context, :routes
32
+
33
+ def initialize(view: nil, view_context: nil, routes: nil, **kwargs)
34
+ @view = view
35
+ @view_context = view_context
36
+ @routes = routes
37
+
38
+ super(**kwargs)
39
+ end
40
+
41
+ private
42
+
43
+ def build_response(**options)
44
+ options = options.merge(view_options: method(:view_options))
45
+ super(**options)
46
+ end
47
+
48
+ def finish(req, res, halted)
49
+ res.render(view, **req.params) if !halted && auto_render?(res)
50
+ super
51
+ end
52
+
53
+ def view_options(req, res)
54
+ {context: view_context&.with(**view_context_options(req, res))}.compact
55
+ end
56
+
57
+ def view_context_options(req, res)
58
+ {request: req, response: res}
59
+ end
60
+
61
+ # Returns true if a view should automatically be rendered onto the response body.
62
+ #
63
+ # This may be overridden to enable/disable automatic rendering.
64
+ #
65
+ # @param res [Hanami::Action::Response]
66
+ #
67
+ # @return [Boolean]
68
+ #
69
+ # @since 2.0.0
70
+ # @api public
71
+ def auto_render?(res)
72
+ view && res.body.empty?
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ Hanami::Action.include(Hanami::Extensions::Action)
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/view"
4
+ require "hanami/view/context"
5
+ require_relative "../../errors"
6
+ require_relative "../../slice_configurable"
7
+ require_relative "slice_configured_context"
8
+
9
+ module Hanami
10
+ module Extensions
11
+ module View
12
+ # View context for views in Hanami apps.
13
+ #
14
+ # @api public
15
+ # @since 2.0.0
16
+ module Context
17
+ def self.included(context_class)
18
+ super
19
+
20
+ context_class.extend(Hanami::SliceConfigurable)
21
+ context_class.extend(ClassMethods)
22
+ context_class.prepend(InstanceMethods)
23
+ end
24
+
25
+ module ClassMethods
26
+ def configure_for_slice(slice)
27
+ extend SliceConfiguredContext.new(slice)
28
+ end
29
+ end
30
+
31
+ module InstanceMethods
32
+ # @see SliceConfiguredContext#define_new
33
+ def initialize(**kwargs)
34
+ defaults = {content: {}}
35
+
36
+ super(**kwargs, **defaults)
37
+ end
38
+
39
+ def inflector
40
+ _options.fetch(:inflector)
41
+ end
42
+
43
+ def routes
44
+ _options.fetch(:routes)
45
+ end
46
+
47
+ def settings
48
+ _options.fetch(:settings)
49
+ end
50
+
51
+ def assets
52
+ unless _options[:assets]
53
+ raise Hanami::ComponentLoadError, "hanami-assets gem is required to access assets"
54
+ end
55
+
56
+ _options[:assets]
57
+ end
58
+
59
+ def content_for(key, value = nil, &block)
60
+ content = _options[:content]
61
+ output = nil
62
+
63
+ if block
64
+ content[key] = yield
65
+ elsif value
66
+ content[key] = value
67
+ else
68
+ output = content[key]
69
+ end
70
+
71
+ output
72
+ end
73
+
74
+ def current_path
75
+ request.fullpath
76
+ end
77
+
78
+ def csrf_token
79
+ request.session[Hanami::Action::CSRFProtection::CSRF_TOKEN]
80
+ end
81
+
82
+ def request
83
+ _options.fetch(:request)
84
+ end
85
+
86
+ def session
87
+ request.session
88
+ end
89
+
90
+ def flash
91
+ response.flash
92
+ end
93
+
94
+ private
95
+
96
+ # TODO: create `Request#flash` so we no longer need the `response`
97
+ def response
98
+ _options.fetch(:response)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ Hanami::View::Context.include(Hanami::Extensions::View::Context)
@@ -3,8 +3,8 @@
3
3
  require "hanami/view"
4
4
 
5
5
  module Hanami
6
- class Application
7
- class View < Hanami::View
6
+ module Extensions
7
+ module View
8
8
  # Provides slice-specific configuration and behavior for any view context class
9
9
  # defined within a slice's module namespace.
10
10
  #
@@ -29,15 +29,15 @@ module Hanami
29
29
  private
30
30
 
31
31
  # Defines a {.new} method on the context class that resolves key components from
32
- # the application container and provides them to {#initialize} as injected
32
+ # the app container and provides them to {#initialize} as injected
33
33
  # dependencies.
34
34
  #
35
- # This includes the following application components:
35
+ # This includes the following app components:
36
36
  #
37
37
  # - the configured inflector as `inflector`
38
- # - "settings" from the application container as `settings`
39
- # - "routes" from the application container as `routes`
40
- # - "assets" from the application container as `assets`
38
+ # - "settings" from the app container as `settings`
39
+ # - "routes" from the app container as `routes`
40
+ # - "assets" from the app container as `assets`
41
41
  def define_new
42
42
  inflector = slice.inflector
43
43
  resolve_settings = method(:resolve_settings)
@@ -55,15 +55,15 @@ module Hanami
55
55
  end
56
56
 
57
57
  def resolve_settings
58
- slice.application[:settings] if slice.application.key?(:settings)
58
+ slice.app[:settings] if slice.app.key?(:settings)
59
59
  end
60
60
 
61
61
  def resolve_routes
62
- slice.application[:routes_helper] if slice.application.key?(:routes_helper)
62
+ slice.app["routes"] if slice.app.key?("routes")
63
63
  end
64
64
 
65
65
  def resolve_assets
66
- slice.application[:assets] if slice.application.key?(:assets)
66
+ slice.app[:assets] if slice.app.key?(:assets)
67
67
  end
68
68
  end
69
69
  end
@@ -3,8 +3,8 @@
3
3
  require "hanami/view"
4
4
 
5
5
  module Hanami
6
- class Application
7
- class View < Hanami::View
6
+ module Extensions
7
+ module View
8
8
  # Provides slice-specific configuration and behavior for any view class defined
9
9
  # within a slice's module namespace.
10
10
  #
@@ -32,10 +32,10 @@ module Hanami
32
32
  # rubocop:disable Metrics/AbcSize
33
33
  def configure_view(view_class)
34
34
  view_class.settings.each do |setting|
35
- if slice.application.config.views.respond_to?(:"#{setting}")
35
+ if slice.config.views.respond_to?(:"#{setting}")
36
36
  view_class.config.public_send(
37
37
  :"#{setting}=",
38
- slice.application.config.views.public_send(:"#{setting}")
38
+ slice.config.views.public_send(:"#{setting}")
39
39
  )
40
40
  end
41
41
  end
@@ -44,7 +44,7 @@ module Hanami
44
44
  view_class.config.paths = prepare_paths(slice, view_class.config.paths)
45
45
  view_class.config.template = template_name(view_class)
46
46
 
47
- if (part_namespace = namespace_from_path(slice.application.config.views.parts_path))
47
+ if (part_namespace = namespace_from_path(slice.config.views.parts_path))
48
48
  view_class.config.part_namespace = part_namespace
49
49
  end
50
50
  end
@@ -62,7 +62,13 @@ module Hanami
62
62
  def prepare_paths(slice, configured_paths)
63
63
  configured_paths.map { |path|
64
64
  if path.dir.relative?
65
- slice.root.join(path.dir)
65
+ if slice.app.equal?(slice)
66
+ # App-level templates are in app/
67
+ slice.root.join(APP_DIR, path.dir)
68
+ else
69
+ # Other slice templates are in the root slice dir
70
+ slice.root.join(path.dir)
71
+ end
66
72
  else
67
73
  path
68
74
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/view"
4
+ require_relative "../slice_configurable"
5
+ require_relative "view/slice_configured_view"
6
+
7
+ module Hanami
8
+ module Extensions
9
+ # Extended behavior for actions intended for use within an Hanami app.
10
+ #
11
+ # @see Hanami::View
12
+ #
13
+ # @api public
14
+ # @since 2.0.0
15
+ module View
16
+ def self.included(view_class)
17
+ super
18
+
19
+ view_class.extend(Hanami::SliceConfigurable)
20
+ view_class.extend(ClassMethods)
21
+ end
22
+
23
+ module ClassMethods
24
+ # @api private
25
+ def configure_for_slice(slice)
26
+ extend SliceConfiguredView.new(slice)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Hanami::View.include(Hanami::Extensions::View)