hanami 2.0.0.alpha2 → 2.0.0.alpha3

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.
@@ -9,8 +9,11 @@ module Hanami
9
9
  #
10
10
  # @since 2.0.0
11
11
  class Sessions
12
+ NULL_SESSION_OPTION = :null
13
+ private_constant :NULL_SESSION_OPTION
14
+
12
15
  def self.null
13
- NULL_STORAGE
16
+ self.class.new(NULL_SESSION_OPTION)
14
17
  end
15
18
 
16
19
  attr_reader :storage, :options
@@ -23,7 +26,7 @@ module Hanami
23
26
  end
24
27
 
25
28
  def enabled?
26
- storage != NULL_STORAGE
29
+ storage != NULL_SESSION_OPTION
27
30
  end
28
31
 
29
32
  def middleware
@@ -32,9 +35,6 @@ module Hanami
32
35
 
33
36
  private
34
37
 
35
- NULL_STORAGE = :null
36
- private_constant :NULL_STORAGE
37
-
38
38
  def storage_middleware
39
39
  require_storage
40
40
 
@@ -3,9 +3,15 @@
3
3
  require "uri"
4
4
  require "concurrent/hash"
5
5
  require "concurrent/array"
6
+ require "dry/configurable"
6
7
  require "dry/inflector"
7
8
  require "pathname"
8
- require "zeitwerk"
9
+
10
+ require_relative "application/settings/dotenv_store"
11
+ require_relative "configuration/logger"
12
+ require_relative "configuration/middleware"
13
+ require_relative "configuration/router"
14
+ require_relative "configuration/sessions"
9
15
 
10
16
  module Hanami
11
17
  # Hanami application configuration
@@ -14,207 +20,124 @@ module Hanami
14
20
  #
15
21
  # rubocop:disable Metrics/ClassLength
16
22
  class Configuration
17
- require_relative "configuration/middleware"
18
- require_relative "configuration/router"
19
- require_relative "configuration/sessions"
23
+ include Dry::Configurable
24
+
25
+ DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
26
+ private_constant :DEFAULT_ENVIRONMENTS
20
27
 
21
28
  attr_reader :actions
29
+ attr_reader :middleware
30
+ attr_reader :router
22
31
  attr_reader :views
23
32
 
24
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
25
- def initialize(env:)
26
- @settings = Concurrent::Hash.new
27
-
28
- self.autoloader = Zeitwerk::Loader.new
33
+ attr_reader :environments
34
+ private :environments
29
35
 
30
- self.env = env
31
- self.environments = DEFAULT_ENVIRONMENTS.clone
36
+ def initialize(env:)
37
+ @environments = DEFAULT_ENVIRONMENTS.clone
38
+ config.env = env
32
39
 
40
+ # Some default setting values must be assigned at initialize-time to ensure they
41
+ # have appropriate values for the current application
33
42
  self.root = Dir.pwd
34
- self.slices_dir = DEFAULT_SLICES_DIR
35
- settings[:slices] = {}
36
-
37
- self.settings_path = DEFAULT_SETTINGS_PATH
38
- self.settings_loader_options = {}
39
-
40
- self.base_url = DEFAULT_BASE_URL
41
-
42
- self.logger = DEFAULT_LOGGER.clone
43
- self.rack_logger_filter_params = DEFAULT_RACK_LOGGER_FILTER_PARAMS.clone
44
- self.sessions = DEFAULT_SESSIONS
45
-
46
- self.router = Router.new(base_url)
47
- self.middleware = Middleware.new
48
-
49
- self.inflections = Dry::Inflector.new
43
+ self.settings_store = Application::Settings::DotenvStore.new.with_dotenv_loaded
50
44
 
45
+ # Config for actions (same for views, below) may not be available if the gem isn't
46
+ # loaded; fall back to a null config object if it's missing
51
47
  @actions = begin
52
48
  require_path = "hanami/action/application_configuration"
53
49
  require require_path
54
50
  Hanami::Action::ApplicationConfiguration.new
55
51
  rescue LoadError => e
56
52
  raise e unless e.path == require_path
57
- Object.new
53
+ require_relative "configuration/null_configuration"
54
+ NullConfiguration.new
58
55
  end
59
56
 
57
+ @middleware = Middleware.new
58
+
59
+ @router = Router.new(self)
60
+
60
61
  @views = begin
61
62
  require_path = "hanami/view/application_configuration"
62
63
  require require_path
63
64
  Hanami::View::ApplicationConfiguration.new
64
65
  rescue LoadError => e
65
66
  raise e unless e.path == require_path
66
- Object.new
67
+ require_relative "configuration/null_configuration"
68
+ NullConfiguration.new
67
69
  end
68
- end
69
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
70
-
71
- def finalize
72
- environment_for(env).each do |blk|
73
- instance_eval(&blk)
74
- end
75
-
76
- # Finalize nested configuration
77
- #
78
- # TODO: would be good to just create empty configurations for actions/views
79
- # instead of plain objects
80
- actions.finalize! if actions.respond_to?(:finalize!)
81
- views.finalize! if views.respond_to?(:finalize!)
82
-
83
- self
84
- end
85
-
86
- def environment(name, &blk)
87
- environment_for(name).push(blk)
88
- end
89
-
90
- def autoloader=(loader)
91
- settings[:autoloader] = loader || nil
92
- end
93
-
94
- def autoloader
95
- settings.fetch(:autoloader)
96
- end
97
-
98
- def env=(value)
99
- settings[:env] = value
100
- end
101
70
 
102
- def env
103
- settings.fetch(:env)
71
+ yield self if block_given?
104
72
  end
105
73
 
106
- def root=(root)
107
- settings[:root] = Pathname(root)
108
- end
109
-
110
- def root
111
- settings.fetch(:root)
112
- end
113
-
114
- def slices_dir=(dir)
115
- settings[:slices_dir] = dir
116
- end
117
-
118
- def slices_dir
119
- settings.fetch(:slices_dir)
120
- end
121
-
122
- def slices_namespace=(namespace)
123
- settings[:slices_namespace] = namespace
124
- end
74
+ def environment(env_name, &block)
75
+ environments[env_name] << block
76
+ apply_env_config
125
77
 
126
- def slices_namespace
127
- settings.fetch(:slices_namespace) { Object }
78
+ self
128
79
  end
129
80
 
130
- def slice(slice_name, &block)
131
- settings[:slices][slice_name] = block
132
- end
81
+ def finalize!
82
+ apply_env_config
133
83
 
134
- def slices
135
- settings[:slices]
136
- end
84
+ # Finalize nested configurations
85
+ actions.finalize!
86
+ views.finalize!
87
+ logger.finalize!
88
+ router.finalize!
137
89
 
138
- def settings_path=(value)
139
- settings[:settings_path] = value
90
+ super
140
91
  end
141
92
 
142
- def settings_path
143
- settings.fetch(:settings_path)
144
- end
93
+ setting :env
145
94
 
146
- def settings_loader=(loader)
147
- settings[:settings_loader] = loader
95
+ def env=(new_env)
96
+ config.env = env
97
+ apply_env_config(new_env)
148
98
  end
149
99
 
150
- def settings_loader
151
- settings.fetch(:settings_loader) {
152
- require "hanami/application/settings/loader"
153
- settings[:settings_loader] = Application::Settings::Loader
154
- }
155
- end
100
+ setting :root, constructor: -> path { Pathname(path) }
156
101
 
157
- def settings_loader_options=(options)
158
- settings[:settings_loader_options] = options
159
- end
102
+ setting :inflector, default: Dry::Inflector.new, cloneable: true
160
103
 
161
- def settings_loader_options
162
- settings[:settings_loader_options]
104
+ def inflections(&block)
105
+ self.inflector = Dry::Inflector.new(&block)
163
106
  end
164
107
 
165
- def base_url=(value)
166
- settings[:base_url] = URI.parse(value)
167
- end
108
+ setting :logger, default: Configuration::Logger.new, cloneable: true
168
109
 
169
- def base_url
170
- settings.fetch(:base_url)
110
+ def logger=(logger_instance)
111
+ @logger_instance = logger_instance
171
112
  end
172
113
 
173
- def logger=(options)
174
- settings[:logger] = options
114
+ def logger_instance
115
+ @logger_instance || logger.logger_class.new(**logger.options)
175
116
  end
176
117
 
177
- def logger
178
- settings.fetch(:logger)
179
- end
118
+ setting :settings_path, default: File.join("config", "settings")
180
119
 
181
- def rack_logger_filter_params=(params)
182
- settings[:rack_logger_filter_params] = params
183
- end
120
+ setting :settings_class_name, default: "Settings"
184
121
 
185
- def rack_logger_filter_params
186
- settings[:rack_logger_filter_params]
187
- end
122
+ setting :settings_store, default: Application::Settings::DotenvStore
188
123
 
189
- def router=(value)
190
- settings[:router] = value
191
- end
124
+ setting :slices_dir, default: "slices"
192
125
 
193
- def router
194
- settings.fetch(:router)
195
- end
126
+ setting :slices_namespace, default: Object
196
127
 
197
- def sessions=(*args)
198
- settings[:sessions] = Sessions.new(args)
199
- end
128
+ # TODO: convert into a dedicated object with explicit behaviour around blocks per
129
+ # slice, etc.
130
+ setting :slices, default: {}, constructor: :dup.to_proc
200
131
 
201
- def sessions
202
- settings.fetch(:sessions)
203
- end
132
+ # TODO: turn this into a richer "source dirs" setting that can support enabling
133
+ # of container component loading as an opt in behvior
134
+ setting :component_dir_paths, default: %w[actions repositories views]
204
135
 
205
- def middleware
206
- settings.fetch(:middleware)
207
- end
208
-
209
- def inflections(&blk)
210
- if blk.nil?
211
- settings.fetch(:inflections)
212
- else
213
- settings[:inflections] = Dry::Inflector.new(&blk)
214
- end
136
+ def slice(slice_name, &block)
137
+ slices[slice_name] = block
215
138
  end
216
139
 
217
- alias inflector inflections
140
+ setting :base_url, default: "http://0.0.0.0:2300", constructor: -> url { URI(url) }
218
141
 
219
142
  def for_each_middleware(&blk)
220
143
  stack = middleware.stack.dup
@@ -223,48 +146,26 @@ module Hanami
223
146
  stack.each(&blk)
224
147
  end
225
148
 
226
- protected
149
+ setting :sessions, default: :null, constructor: -> *args { Sessions.new(*args) }
227
150
 
228
- def environment_for(name)
229
- settings[:environments][name]
230
- end
151
+ private
231
152
 
232
- def environments=(values)
233
- settings[:environments] = values
153
+ def apply_env_config(env = self.env)
154
+ environments[env].each do |block|
155
+ instance_eval(&block)
156
+ end
234
157
  end
235
158
 
236
- def middleware=(value)
237
- settings[:middleware] = value
159
+ def method_missing(name, *args, &block)
160
+ if config.respond_to?(name)
161
+ config.public_send(name, *args, &block)
162
+ else
163
+ super
164
+ end
238
165
  end
239
166
 
240
- def inflections=(value)
241
- settings[:inflections] = value
167
+ def respond_to_missing?(name, _incude_all = false)
168
+ config.respond_to?(name) || super
242
169
  end
243
-
244
- private
245
-
246
- DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
247
- private_constant :DEFAULT_ENVIRONMENTS
248
-
249
- DEFAULT_SLICES_DIR = "slices"
250
- private_constant :DEFAULT_SLICES_DIR
251
-
252
- DEFAULT_BASE_URL = "http://0.0.0.0:2300"
253
- private_constant :DEFAULT_BASE_URL
254
-
255
- DEFAULT_LOGGER = { level: :debug }.freeze
256
- private_constant :DEFAULT_LOGGER
257
-
258
- DEFAULT_RACK_LOGGER_FILTER_PARAMS = %w[_csrf password password_confirmation].freeze
259
- private_constant :DEFAULT_RACK_LOGGER_FILTER_PARAMS
260
-
261
- DEFAULT_SETTINGS_PATH = File.join("config", "settings")
262
- private_constant :DEFAULT_SETTINGS_PATH
263
-
264
- DEFAULT_SESSIONS = Sessions.null
265
- private_constant :DEFAULT_SESSIONS
266
-
267
- attr_reader :settings
268
170
  end
269
- # rubocop:enable Metrics/ClassLength
270
171
  end
data/lib/hanami/slice.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/system/container"
4
+ require "dry/system/loader/autoloading"
4
5
  require "pathname"
5
6
 
6
7
  module Hanami
@@ -103,29 +104,57 @@ module Hanami
103
104
  config.name = name
104
105
  config.inflector = application.configuration.inflector
105
106
 
106
- if application.configuration.autoloader
107
- require "dry/system/loader/autoloading"
108
- config.component_dirs.loader = Dry::System::Loader::Autoloading
109
- config.component_dirs.add_to_load_path = false
110
- end
107
+ config.component_dirs.loader = Dry::System::Loader::Autoloading
108
+ config.component_dirs.add_to_load_path = false
111
109
 
112
110
  if root&.directory?
113
111
  config.root = root
114
112
  config.bootable_dirs = ["config/boot"]
115
113
 
114
+ # Add the "lib" component dir; all slices will load components from lib
116
115
  if root.join("lib").directory?
117
- config.component_dirs.add "lib" do |dir|
118
- dir.default_namespace = namespace_path.tr(File::SEPARATOR, config.namespace_separator)
116
+ config.component_dirs.add("lib") do |component_dir|
117
+ # Expect component files in the root of the lib
118
+ # component dir to define classes inside the slice's namespace.
119
+ #
120
+ # e.g. "lib/foo.rb" should define SliceNamespace::Foo, and will be
121
+ # registered as "foo"
122
+ component_dir.namespaces.root(key: nil, const: namespace_path)
123
+
124
+ application.autoloader.push_dir(root.join("lib"), namespace: namespace)
119
125
  end
126
+ end
127
+
128
+ # Add component dirs for each configured component path
129
+ application.configuration.component_dir_paths.each do |slice_dir|
130
+ next unless root.join(slice_dir).directory?
131
+
132
+ config.component_dirs.add(slice_dir) do |component_dir|
133
+ # Expect component files in the root of these component dirs to define
134
+ # classes inside a namespace matching the dir.
135
+ #
136
+ # e.g. "actions/foo.rb" should define SliceNamespace::Actions::Foo, and
137
+ # will be registered as "actions.foo"
138
+
139
+ dir_namespace_path = File.join(namespace_path, slice_dir)
120
140
 
121
- application.configuration.autoloader&.push_dir(root.join("lib"))
141
+ autoloader_namespace = begin
142
+ inflector.constantize(inflector.camelize(dir_namespace_path))
143
+ rescue NameError
144
+ namespace.const_set(inflector.camelize(slice_dir), Module.new)
145
+ end
146
+
147
+ component_dir.namespaces.root(const: dir_namespace_path, key: slice_dir)
148
+
149
+ application.autoloader.push_dir(
150
+ container.root.join(slice_dir),
151
+ namespace: autoloader_namespace
152
+ )
153
+ end
122
154
  end
123
155
  end
124
156
  end
125
157
 
126
- # Force after configure hook to run
127
- container.configure do; end
128
-
129
158
  if namespace
130
159
  namespace.const_set :Container, container
131
160
  namespace.const_set :Deps, container.injector
@@ -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.alpha2"
11
+ VERSION = "2.0.0.alpha3"
12
12
 
13
13
  # @since 0.9.0
14
14
  # @api private
data/lib/hanami.rb CHANGED
@@ -73,6 +73,10 @@ module Hanami
73
73
  end
74
74
  end
75
75
 
76
+ def self.shutdown
77
+ application.shutdown
78
+ end
79
+
76
80
  def self.bundler_groups
77
81
  [:plugins]
78
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.alpha2
4
+ version: 2.0.0.alpha3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-04 00:00:00.000000000 Z
11
+ date: 2021-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,6 +30,26 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: dry-configurable
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.12'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.12.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.12'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.12.1
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: dry-core
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -50,20 +70,20 @@ dependencies:
50
70
  requirements:
51
71
  - - "~>"
52
72
  - !ruby/object:Gem::Version
53
- version: '0.1'
73
+ version: '0.2'
54
74
  - - ">="
55
75
  - !ruby/object:Gem::Version
56
- version: 0.1.2
76
+ version: 0.2.1
57
77
  type: :runtime
58
78
  prerelease: false
59
79
  version_requirements: !ruby/object:Gem::Requirement
60
80
  requirements:
61
81
  - - "~>"
62
82
  - !ruby/object:Gem::Version
63
- version: '0.1'
83
+ version: '0.2'
64
84
  - - ">="
65
85
  - !ruby/object:Gem::Version
66
- version: 0.1.2
86
+ version: 0.2.1
67
87
  - !ruby/object:Gem::Dependency
68
88
  name: dry-monitor
69
89
  requirement: !ruby/object:Gem::Requirement
@@ -87,7 +107,7 @@ dependencies:
87
107
  version: '0.19'
88
108
  - - ">="
89
109
  - !ruby/object:Gem::Version
90
- version: 0.19.0
110
+ version: 0.21.0
91
111
  type: :runtime
92
112
  prerelease: false
93
113
  version_requirements: !ruby/object:Gem::Requirement
@@ -97,7 +117,7 @@ dependencies:
97
117
  version: '0.19'
98
118
  - - ">="
99
119
  - !ruby/object:Gem::Version
100
- version: 0.19.0
120
+ version: 0.21.0
101
121
  - !ruby/object:Gem::Dependency
102
122
  name: hanami-cli
103
123
  requirement: !ruby/object:Gem::Requirement
@@ -204,14 +224,13 @@ files:
204
224
  - lib/hanami/application/container/boot/rack_monitor.rb
205
225
  - lib/hanami/application/container/boot/settings.rb
206
226
  - lib/hanami/application/router.rb
227
+ - lib/hanami/application/routes.rb
207
228
  - lib/hanami/application/routing/middleware/stack.rb
208
229
  - lib/hanami/application/routing/resolver.rb
209
230
  - lib/hanami/application/routing/resolver/node.rb
210
231
  - lib/hanami/application/routing/resolver/trie.rb
211
232
  - lib/hanami/application/settings.rb
212
- - lib/hanami/application/settings/definition.rb
213
- - lib/hanami/application/settings/loader.rb
214
- - lib/hanami/application/settings/struct.rb
233
+ - lib/hanami/application/settings/dotenv_store.rb
215
234
  - lib/hanami/boot.rb
216
235
  - lib/hanami/cli/application/cli.rb
217
236
  - lib/hanami/cli/application/command.rb
@@ -222,7 +241,9 @@ files:
222
241
  - lib/hanami/cli/commands/command.rb
223
242
  - lib/hanami/cli/commands/server.rb
224
243
  - lib/hanami/configuration.rb
244
+ - lib/hanami/configuration/logger.rb
225
245
  - lib/hanami/configuration/middleware.rb
246
+ - lib/hanami/configuration/null_configuration.rb
226
247
  - lib/hanami/configuration/router.rb
227
248
  - lib/hanami/configuration/sessions.rb
228
249
  - lib/hanami/init.rb
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/utils/basic_object"
4
-
5
- module Hanami
6
- class Application
7
- module Settings
8
- # Application settings definition DSL
9
- #
10
- # @since 2.0.0
11
- # @api private
12
- class Definition < Hanami::Utils::BasicObject
13
- attr_reader :settings
14
-
15
- def initialize(&block)
16
- @settings = []
17
- instance_eval(&block) if block
18
- end
19
-
20
- def setting(name, *args)
21
- @settings << [name, args]
22
- end
23
- end
24
- end
25
- end
26
- end