hanami 2.0.0.alpha7.1 → 2.0.0.beta1.1

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 (223) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +457 -237
  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 +118 -0
  9. data/lib/hanami/configuration/actions/cookies.rb +29 -0
  10. data/lib/hanami/configuration/actions/sessions.rb +46 -0
  11. data/lib/hanami/configuration/actions.rb +23 -12
  12. data/lib/hanami/configuration/logger.rb +13 -10
  13. data/lib/hanami/configuration/router.rb +2 -6
  14. data/lib/hanami/configuration/sessions.rb +1 -1
  15. data/lib/hanami/configuration/views.rb +86 -0
  16. data/lib/hanami/configuration.rb +139 -82
  17. data/lib/hanami/constants.rb +30 -2
  18. data/lib/hanami/errors.rb +4 -1
  19. data/lib/hanami/extensions/action/slice_configured_action.rb +103 -0
  20. data/lib/hanami/extensions/action.rb +79 -0
  21. data/lib/hanami/extensions/view/context.rb +106 -0
  22. data/lib/hanami/extensions/view/slice_configured_context.rb +71 -0
  23. data/lib/hanami/extensions/view/slice_configured_view.rb +107 -0
  24. data/lib/hanami/extensions/view.rb +33 -0
  25. data/lib/hanami/extensions.rb +10 -0
  26. data/lib/hanami/providers/inflector.rb +13 -0
  27. data/lib/hanami/providers/logger.rb +13 -0
  28. data/lib/hanami/providers/rack.rb +27 -0
  29. data/lib/hanami/providers/routes.rb +33 -0
  30. data/lib/hanami/providers/settings.rb +23 -0
  31. data/lib/hanami/rake_tasks.rb +61 -0
  32. data/lib/hanami/routes.rb +51 -0
  33. data/lib/hanami/server.rb +1 -1
  34. data/lib/hanami/settings/dotenv_store.rb +58 -0
  35. data/lib/hanami/settings.rb +90 -0
  36. data/lib/hanami/setup.rb +4 -2
  37. data/lib/hanami/{application → slice}/router.rb +18 -13
  38. data/lib/hanami/slice/routes_helper.rb +37 -0
  39. data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
  40. data/lib/hanami/slice/routing/resolver.rb +97 -0
  41. data/lib/hanami/slice/view_name_inferrer.rb +63 -0
  42. data/lib/hanami/slice.rb +252 -82
  43. data/lib/hanami/slice_configurable.rb +62 -0
  44. data/lib/hanami/slice_name.rb +111 -0
  45. data/lib/hanami/slice_registrar.rb +119 -0
  46. data/lib/hanami/version.rb +1 -1
  47. data/lib/hanami/web/rack_logger.rb +1 -1
  48. data/lib/hanami.rb +34 -26
  49. data/spec/integration/application_middleware_stack_spec.rb +84 -0
  50. data/spec/integration/assets/cdn_spec.rb +48 -0
  51. data/spec/integration/assets/fingerprint_spec.rb +42 -0
  52. data/spec/integration/assets/helpers_spec.rb +50 -0
  53. data/spec/integration/assets/serve_spec.rb +70 -0
  54. data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
  55. data/spec/integration/body_parsers_spec.rb +50 -0
  56. data/spec/integration/cli/assets/precompile_spec.rb +147 -0
  57. data/spec/integration/cli/assets_spec.rb +14 -0
  58. data/spec/integration/cli/console_spec.rb +105 -0
  59. data/spec/integration/cli/db/apply_spec.rb +74 -0
  60. data/spec/integration/cli/db/console_spec.rb +40 -0
  61. data/spec/integration/cli/db/create_spec.rb +50 -0
  62. data/spec/integration/cli/db/drop_spec.rb +54 -0
  63. data/spec/integration/cli/db/migrate_spec.rb +108 -0
  64. data/spec/integration/cli/db/prepare_spec.rb +36 -0
  65. data/spec/integration/cli/db/rollback_spec.rb +96 -0
  66. data/spec/integration/cli/db/version_spec.rb +38 -0
  67. data/spec/integration/cli/db_spec.rb +21 -0
  68. data/spec/integration/cli/destroy/action_spec.rb +143 -0
  69. data/spec/integration/cli/destroy/app_spec.rb +118 -0
  70. data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
  71. data/spec/integration/cli/destroy/migration_spec.rb +70 -0
  72. data/spec/integration/cli/destroy/model_spec.rb +113 -0
  73. data/spec/integration/cli/destroy_spec.rb +18 -0
  74. data/spec/integration/cli/generate/action_spec.rb +469 -0
  75. data/spec/integration/cli/generate/app_spec.rb +215 -0
  76. data/spec/integration/cli/generate/mailer_spec.rb +189 -0
  77. data/spec/integration/cli/generate/migration_spec.rb +72 -0
  78. data/spec/integration/cli/generate/model_spec.rb +290 -0
  79. data/spec/integration/cli/generate/secret_spec.rb +56 -0
  80. data/spec/integration/cli/generate_spec.rb +19 -0
  81. data/spec/integration/cli/new/database_spec.rb +235 -0
  82. data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
  83. data/spec/integration/cli/new/template_spec.rb +118 -0
  84. data/spec/integration/cli/new/test_spec.rb +274 -0
  85. data/spec/integration/cli/new_spec.rb +970 -0
  86. data/spec/integration/cli/plugins_spec.rb +39 -0
  87. data/spec/integration/cli/routes_spec.rb +49 -0
  88. data/spec/integration/cli/server_spec.rb +626 -0
  89. data/spec/integration/cli/version_spec.rb +85 -0
  90. data/spec/integration/early_hints_spec.rb +35 -0
  91. data/spec/integration/handle_exceptions_spec.rb +244 -0
  92. data/spec/integration/head_spec.rb +89 -0
  93. data/spec/integration/http_headers_spec.rb +29 -0
  94. data/spec/integration/mailer_spec.rb +32 -0
  95. data/spec/integration/middleware_spec.rb +81 -0
  96. data/spec/integration/mount_applications_spec.rb +88 -0
  97. data/spec/integration/project_initializers_spec.rb +40 -0
  98. data/spec/integration/rackup_spec.rb +35 -0
  99. data/spec/integration/rake/with_minitest_spec.rb +67 -0
  100. data/spec/integration/rake/with_rspec_spec.rb +69 -0
  101. data/spec/integration/routing_helpers_spec.rb +61 -0
  102. data/spec/integration/security/content_security_policy_spec.rb +46 -0
  103. data/spec/integration/security/csrf_protection_spec.rb +42 -0
  104. data/spec/integration/security/force_ssl_spec.rb +29 -0
  105. data/spec/integration/security/x_content_type_options_spec.rb +46 -0
  106. data/spec/integration/security/x_frame_options_spec.rb +46 -0
  107. data/spec/integration/security/x_xss_protection_spec.rb +46 -0
  108. data/spec/integration/send_file_spec.rb +51 -0
  109. data/spec/integration/sessions_spec.rb +247 -0
  110. data/spec/integration/static_middleware_spec.rb +21 -0
  111. data/spec/integration/streaming_spec.rb +41 -0
  112. data/spec/integration/unsafe_send_file_spec.rb +52 -0
  113. data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
  114. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
  115. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
  116. data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
  117. data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
  118. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
  119. data/spec/isolation/hanami/boot/success_spec.rb +50 -0
  120. data/spec/new_integration/action/configuration_spec.rb +26 -0
  121. data/spec/new_integration/action/cookies_spec.rb +58 -0
  122. data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
  123. data/spec/new_integration/action/routes_spec.rb +73 -0
  124. data/spec/new_integration/action/sessions_spec.rb +50 -0
  125. data/spec/new_integration/action/view_integration_spec.rb +165 -0
  126. data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
  127. data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
  128. data/spec/new_integration/action/view_rendering_spec.rb +107 -0
  129. data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
  130. data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
  131. data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
  132. data/spec/new_integration/container/auto_injection_spec.rb +53 -0
  133. data/spec/new_integration/container/auto_registration_spec.rb +86 -0
  134. data/spec/new_integration/container/autoloader_spec.rb +80 -0
  135. data/spec/new_integration/container/imports_spec.rb +253 -0
  136. data/spec/new_integration/container/prepare_container_spec.rb +123 -0
  137. data/spec/new_integration/container/shutdown_spec.rb +91 -0
  138. data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
  139. data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
  140. data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
  141. data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
  142. data/spec/new_integration/settings_spec.rb +115 -0
  143. data/spec/new_integration/slices/external_slice_spec.rb +92 -0
  144. data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
  145. data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
  146. data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
  147. data/spec/new_integration/slices_spec.rb +101 -0
  148. data/spec/new_integration/view/configuration_spec.rb +49 -0
  149. data/spec/new_integration/view/context/assets_spec.rb +67 -0
  150. data/spec/new_integration/view/context/inflector_spec.rb +48 -0
  151. data/spec/new_integration/view/context/request_spec.rb +61 -0
  152. data/spec/new_integration/view/context/routes_spec.rb +86 -0
  153. data/spec/new_integration/view/context/settings_spec.rb +50 -0
  154. data/spec/new_integration/view/inflector_spec.rb +57 -0
  155. data/spec/new_integration/view/part_namespace_spec.rb +96 -0
  156. data/spec/new_integration/view/path_spec.rb +56 -0
  157. data/spec/new_integration/view/template_spec.rb +68 -0
  158. data/spec/new_integration/view/views_spec.rb +103 -0
  159. data/spec/spec_helper.rb +16 -0
  160. data/spec/support/app_integration.rb +91 -0
  161. data/spec/support/coverage.rb +1 -0
  162. data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
  163. data/spec/support/fixtures/hanami-plugin/README.md +35 -0
  164. data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
  165. data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
  166. data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
  167. data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
  168. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
  169. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
  170. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
  171. data/spec/support/rspec.rb +27 -0
  172. data/spec/support/shared_examples/cli/generate/app.rb +494 -0
  173. data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
  174. data/spec/support/shared_examples/cli/generate/model.rb +81 -0
  175. data/spec/support/shared_examples/cli/new.rb +97 -0
  176. data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
  177. data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
  178. data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
  179. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
  180. data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
  181. data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
  182. data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
  183. data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
  184. data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
  185. data/spec/unit/hanami/configuration/views_spec.rb +120 -0
  186. data/spec/unit/hanami/configuration_spec.rb +43 -0
  187. data/spec/unit/hanami/env_spec.rb +54 -0
  188. data/spec/unit/hanami/routes_spec.rb +25 -0
  189. data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
  190. data/spec/unit/hanami/settings_spec.rb +56 -0
  191. data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
  192. data/spec/unit/hanami/slice_name_spec.rb +47 -0
  193. data/spec/unit/hanami/slice_spec.rb +17 -0
  194. data/spec/unit/hanami/version_spec.rb +7 -0
  195. data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
  196. metadata +363 -55
  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.rb +0 -299
  213. data/lib/hanami/boot/source_dirs.rb +0 -44
  214. data/lib/hanami/cli/application/cli.rb +0 -40
  215. data/lib/hanami/cli/application/command.rb +0 -47
  216. data/lib/hanami/cli/application/commands/console.rb +0 -81
  217. data/lib/hanami/cli/application/commands.rb +0 -16
  218. data/lib/hanami/cli/base_command.rb +0 -48
  219. data/lib/hanami/cli/commands/command.rb +0 -171
  220. data/lib/hanami/cli/commands/server.rb +0 -88
  221. data/lib/hanami/cli/commands.rb +0 -65
  222. data/lib/hanami/configuration/middleware.rb +0 -20
  223. data/lib/hanami/configuration/source_dirs.rb +0 -42
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "hanami/view"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Hanami actions configuration
9
+ #
10
+ # @since 2.0.0
11
+ class Views
12
+ include Dry::Configurable
13
+
14
+ setting :parts_path, default: "views/parts"
15
+
16
+ attr_reader :base_configuration
17
+ protected :base_configuration
18
+
19
+ def initialize(*)
20
+ super
21
+
22
+ @base_configuration = Hanami::View.config.dup
23
+
24
+ configure_defaults
25
+ end
26
+
27
+ def initialize_copy(source)
28
+ super
29
+ @base_configuration = source.base_configuration.dup
30
+ end
31
+
32
+ # Returns the list of available settings
33
+ #
34
+ # @return [Set]
35
+ #
36
+ # @since 2.0.0
37
+ # @api private
38
+ def settings
39
+ self.class.settings + View.settings - NON_FORWARDABLE_METHODS
40
+ end
41
+
42
+ def finalize!
43
+ return self if frozen?
44
+
45
+ base_configuration.finalize!
46
+
47
+ super
48
+ end
49
+
50
+ private
51
+
52
+ def configure_defaults
53
+ self.paths = ["templates"]
54
+ self.template_inference_base = "views"
55
+ self.layout = "app"
56
+ end
57
+
58
+ # An inflector for views is not configurable via `config.views.inflector` on an
59
+ # `Hanami::App`. The app-wide inflector is already configurable
60
+ # there as `config.inflector` and will be used as the default inflector for views.
61
+ #
62
+ # A custom inflector may still be provided in an `Hanami::View` subclass, via
63
+ # `config.inflector=`.
64
+ NON_FORWARDABLE_METHODS = %i[inflector inflector=].freeze
65
+ private_constant :NON_FORWARDABLE_METHODS
66
+
67
+ def method_missing(name, *args, &block)
68
+ return super if NON_FORWARDABLE_METHODS.include?(name)
69
+
70
+ if config.respond_to?(name)
71
+ config.public_send(name, *args, &block)
72
+ elsif base_configuration.respond_to?(name)
73
+ base_configuration.public_send(name, *args, &block)
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ def respond_to_missing?(name, _include_all = false)
80
+ return false if NON_FORWARDABLE_METHODS.include?(name)
81
+
82
+ config.respond_to?(name) || base_configuration.respond_to?(name) || super
83
+ end
84
+ end
85
+ end
86
+ end
@@ -7,88 +7,134 @@ 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
- #
23
- # rubocop:disable Metrics/ClassLength
24
21
  class Configuration
25
22
  include Dry::Configurable
26
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
+
27
49
  DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
28
50
  private_constant :DEFAULT_ENVIRONMENTS
29
51
 
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
30
60
  attr_reader :env
31
61
 
62
+ # @return [Hanami::Configuration::Actions]
63
+ #
64
+ # @api public
32
65
  attr_reader :actions
66
+
67
+ # @return [Hanami::Slice::Routing::Middleware::Stack]
68
+ #
69
+ # @api public
33
70
  attr_reader :middleware
71
+
72
+ # @api private
73
+ alias_method :middleware_stack, :middleware
74
+
75
+ # @return [Hanami::Configuration::Router]
76
+ #
77
+ # @api public
34
78
  attr_reader :router
35
- attr_reader :views, :assets
36
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
37
93
  attr_reader :environments
38
94
  private :environments
39
95
 
40
- def initialize(application_name:, env:)
41
- @namespace = application_name.split(MODULE_DELIMITER)[0..-2].join(MODULE_DELIMITER)
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
50
-
51
- config.logger = Configuration::Logger.new(env: env, application_name: method(:application_name))
52
-
53
- @assets = begin
54
- require_path = "hanami/assets/application_configuration"
55
- require require_path
56
- Hanami::Assets::ApplicationConfiguration.new
57
- rescue LoadError => e
58
- raise e unless e.path == require_path
59
- require_relative "configuration/null_configuration"
60
- NullConfiguration.new
61
- end
106
+ self.settings_store = Hanami::Settings::DotenvStore.new.with_dotenv_loaded
62
107
 
63
- # Config for actions (same for views, below) may not be available if the gem isn't
64
- # loaded; fall back to a null config object if it's missing
65
- @actions = begin
66
- require_path = "hanami/action/application_configuration"
67
- require require_path
68
- Hanami::Action::ApplicationConfiguration.new(assets_server_url: assets.server_url)
69
- rescue LoadError => e
70
- raise e unless e.path == require_path
71
- require_relative "configuration/null_configuration"
72
- NullConfiguration.new
73
- end
108
+ config.logger = Configuration::Logger.new(env: env, app_name: app_name)
109
+
110
+ @assets = load_dependent_config("hanami/assets/app_configuration") {
111
+ Hanami::Assets::AppConfiguration.new
112
+ }
113
+
114
+ @actions = load_dependent_config("hanami/action") {
115
+ require_relative "configuration/actions"
116
+ Actions.new
117
+ }
74
118
 
75
- @middleware = Middleware.new
119
+ @middleware = Slice::Routing::Middleware::Stack.new
76
120
 
77
121
  @router = Router.new(self)
78
122
 
79
- @views = begin
80
- require_path = "hanami/view/application_configuration"
81
- require require_path
82
- Hanami::View::ApplicationConfiguration.new
83
- rescue LoadError => e
84
- raise e unless e.path == require_path
85
- require_relative "configuration/null_configuration"
86
- NullConfiguration.new
87
- end
123
+ @views = load_dependent_config("hanami/view") {
124
+ require_relative "configuration/views"
125
+ Views.new
126
+ }
88
127
 
89
128
  yield self if block_given?
90
129
  end
91
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
92
138
  def environment(env_name, &block)
93
139
  environments[env_name] << block
94
140
  apply_env_config
@@ -96,6 +142,34 @@ module Hanami
96
142
  self
97
143
  end
98
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
99
173
  def finalize!
100
174
  apply_env_config
101
175
 
@@ -109,59 +183,41 @@ module Hanami
109
183
  super
110
184
  end
111
185
 
112
- def namespace
113
- inflector.constantize(@namespace)
114
- end
115
-
116
- def application_name
117
- inflector.underscore(@namespace).to_sym
118
- end
119
-
120
- setting :root, constructor: -> path { Pathname(path) }
121
-
122
- setting :inflector, default: Dry::Inflector.new
123
-
124
- def inflections(&block)
125
- self.inflector = Dry::Inflector.new(&block)
126
- end
127
-
128
- setting :logger, cloneable: true
129
-
186
+ # Set a default global logger instance
187
+ #
188
+ # @api public
130
189
  def logger=(logger_instance)
131
190
  @logger_instance = logger_instance
132
191
  end
133
192
 
193
+ # Return configured logger instance
194
+ #
195
+ # @api public
134
196
  def logger_instance
135
197
  @logger_instance || logger.instance
136
198
  end
137
199
 
138
- setting :settings_path, default: File.join("config", "settings")
139
-
140
- setting :settings_class_name, default: "Settings"
141
-
142
- setting :settings_store, default: Application::Settings::DotenvStore
143
-
144
- setting :source_dirs, default: Configuration::SourceDirs.new, cloneable: true
145
-
146
- setting :base_url, default: "http://0.0.0.0:2300", constructor: -> url { URI(url) }
147
-
148
- def for_each_middleware(&blk)
149
- stack = middleware.stack.dup
150
- stack += sessions.middleware if sessions.enabled?
151
-
152
- stack.each(&blk)
153
- end
154
-
155
- setting :sessions, default: :null, constructor: -> *args { Sessions.new(*args) }
156
-
157
200
  private
158
201
 
202
+ # @api private
159
203
  def apply_env_config(env = self.env)
160
204
  environments[env].each do |block|
161
205
  instance_eval(&block)
162
206
  end
163
207
  end
164
208
 
209
+ # @api private
210
+ def load_dependent_config(require_path, &block)
211
+ require require_path
212
+ yield
213
+ rescue LoadError => e
214
+ raise e unless e.path == require_path
215
+
216
+ require_relative "configuration/null_configuration"
217
+ NullConfiguration.new
218
+ end
219
+
220
+ # @api private
165
221
  def method_missing(name, *args, &block)
166
222
  if config.respond_to?(name)
167
223
  config.public_send(name, *args, &block)
@@ -170,6 +226,7 @@ module Hanami
170
226
  end
171
227
  end
172
228
 
229
+ # @api private
173
230
  def respond_to_missing?(name, _incude_all = false)
174
231
  config.respond_to?(name) || super
175
232
  end
@@ -1,22 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanami
4
+ # @api private
5
+ CONTAINER_KEY_DELIMITER = "."
6
+ private_constant :CONTAINER_KEY_DELIMITER
7
+
4
8
  # @api private
5
9
  MODULE_DELIMITER = "::"
6
10
  private_constant :MODULE_DELIMITER
7
11
 
12
+ # @api private
13
+ PATH_DELIMITER = "/"
14
+ private_constant :PATH_DELIMITER
15
+
8
16
  # @api private
9
17
  CONFIG_DIR = "config"
10
18
  private_constant :CONFIG_DIR
11
19
 
12
20
  # @api private
13
- SLICES_DIR = "slices"
14
- private_constant :SLICES_DIR
21
+ APP_DIR = "app"
22
+ private_constant :APP_DIR
15
23
 
16
24
  # @api private
17
25
  LIB_DIR = "lib"
18
26
  private_constant :LIB_DIR
19
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
+
20
48
  # @api private
21
49
  RB_EXT = ".rb"
22
50
  private_constant :RB_EXT
data/lib/hanami/errors.rb CHANGED
@@ -5,8 +5,11 @@ 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)
12
+
13
+ # @since 2.0.0
14
+ ComponentLoadError = Class.new(Error)
12
15
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action"
4
+
5
+ module Hanami
6
+ module Extensions
7
+ module Action
8
+ # Provides slice-specific configuration and behavior for any action class defined
9
+ # within a slice's module namespace.
10
+ #
11
+ # @api private
12
+ # @since 2.0.0
13
+ class SliceConfiguredAction < Module
14
+ attr_reader :slice
15
+
16
+ def initialize(slice)
17
+ super()
18
+ @slice = slice
19
+ end
20
+
21
+ def extended(action_class)
22
+ configure_action(action_class)
23
+ extend_behavior(action_class)
24
+ define_new
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class.name}[#{slice.name}]>"
29
+ end
30
+
31
+ private
32
+
33
+ # @see Hanami::Extensions::Action::InstanceMethods#initialize
34
+ def define_new
35
+ resolve_view = method(:resolve_paired_view)
36
+ resolve_view_context = method(:resolve_view_context)
37
+ resolve_routes = method(:resolve_routes)
38
+
39
+ define_method(:new) do |**kwargs|
40
+ super(
41
+ view: kwargs.fetch(:view) { resolve_view.(self) },
42
+ view_context: kwargs.fetch(:view_context) { resolve_view_context.() },
43
+ routes: kwargs.fetch(:routes) { resolve_routes.() },
44
+ **kwargs,
45
+ )
46
+ end
47
+ end
48
+
49
+ def configure_action(action_class)
50
+ action_class.config.settings.each do |setting|
51
+ action_class.config.public_send :"#{setting}=", actions_config.public_send(:"#{setting}")
52
+ end
53
+ end
54
+
55
+ def extend_behavior(action_class)
56
+ if actions_config.sessions.enabled?
57
+ require "hanami/action/session"
58
+ action_class.include Hanami::Action::Session
59
+ end
60
+
61
+ if actions_config.csrf_protection
62
+ require "hanami/action/csrf_protection"
63
+ action_class.include Hanami::Action::CSRFProtection
64
+ end
65
+
66
+ if actions_config.cookies.enabled?
67
+ require "hanami/action/cookies"
68
+ action_class.include Hanami::Action::Cookies
69
+ end
70
+ end
71
+
72
+ def resolve_paired_view(action_class)
73
+ view_identifiers = actions_config.view_name_inferrer.call(
74
+ action_class_name: action_class.name,
75
+ slice: slice,
76
+ )
77
+
78
+ view_identifiers.detect do |identifier|
79
+ break slice[identifier] if slice.key?(identifier)
80
+ end
81
+ end
82
+
83
+ def resolve_view_context
84
+ identifier = actions_config.view_context_identifier
85
+
86
+ if slice.key?(identifier)
87
+ slice[identifier]
88
+ elsif slice.app.key?(identifier)
89
+ slice.app[identifier]
90
+ end
91
+ end
92
+
93
+ def resolve_routes
94
+ slice.app["routes"] if slice.app.key?("routes")
95
+ end
96
+
97
+ def actions_config
98
+ slice.config.actions
99
+ end
100
+ end
101
+ end
102
+ end
103
+ 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)