hanami 2.1.1 → 2.2.0.beta2

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +7 -7
  4. data/hanami.gemspec +6 -6
  5. data/lib/hanami/app.rb +5 -1
  6. data/lib/hanami/config/db.rb +33 -0
  7. data/lib/hanami/config.rb +36 -9
  8. data/lib/hanami/constants.rb +4 -0
  9. data/lib/hanami/extensions/db/repo.rb +103 -0
  10. data/lib/hanami/extensions.rb +4 -0
  11. data/lib/hanami/helpers/form_helper/form_builder.rb +4 -6
  12. data/lib/hanami/provider/source.rb +16 -0
  13. data/lib/hanami/provider_registrar.rb +28 -0
  14. data/lib/hanami/providers/assets.rb +2 -20
  15. data/lib/hanami/providers/db/adapter.rb +75 -0
  16. data/lib/hanami/providers/db/adapters.rb +50 -0
  17. data/lib/hanami/providers/db/config.rb +62 -0
  18. data/lib/hanami/providers/db/gateway.rb +70 -0
  19. data/lib/hanami/providers/db/sql_adapter.rb +100 -0
  20. data/lib/hanami/providers/db.rb +298 -0
  21. data/lib/hanami/providers/db_logging.rb +22 -0
  22. data/lib/hanami/providers/inflector.rb +1 -1
  23. data/lib/hanami/providers/logger.rb +1 -1
  24. data/lib/hanami/providers/rack.rb +3 -3
  25. data/lib/hanami/providers/relations.rb +31 -0
  26. data/lib/hanami/providers/routes.rb +2 -14
  27. data/lib/hanami/rake_tasks.rb +8 -7
  28. data/lib/hanami/slice.rb +84 -4
  29. data/lib/hanami/version.rb +1 -1
  30. data/lib/hanami.rb +3 -0
  31. data/spec/integration/container/provider_environment_spec.rb +52 -0
  32. data/spec/integration/db/auto_registration_spec.rb +39 -0
  33. data/spec/integration/db/commands_spec.rb +80 -0
  34. data/spec/integration/db/db_inflector_spec.rb +57 -0
  35. data/spec/integration/db/db_slices_spec.rb +332 -0
  36. data/spec/integration/db/db_spec.rb +245 -0
  37. data/spec/integration/db/gateways_spec.rb +320 -0
  38. data/spec/integration/db/logging_spec.rb +238 -0
  39. data/spec/integration/db/mappers_spec.rb +84 -0
  40. data/spec/integration/db/provider_config_spec.rb +88 -0
  41. data/spec/integration/db/provider_spec.rb +35 -0
  42. data/spec/integration/db/relations_spec.rb +60 -0
  43. data/spec/integration/db/repo_spec.rb +215 -0
  44. data/spec/integration/db/slices_importing_from_parent.rb +130 -0
  45. data/spec/integration/slices/slice_configuration_spec.rb +4 -4
  46. data/spec/support/app_integration.rb +3 -0
  47. data/spec/unit/hanami/config/db_spec.rb +38 -0
  48. data/spec/unit/hanami/config/router_spec.rb +1 -1
  49. data/spec/unit/hanami/helpers/form_helper_spec.rb +35 -4
  50. data/spec/unit/hanami/providers/db/config/default_config_spec.rb +100 -0
  51. data/spec/unit/hanami/providers/db/config_spec.rb +156 -0
  52. data/spec/unit/hanami/slice_spec.rb +32 -0
  53. data/spec/unit/hanami/version_spec.rb +1 -1
  54. metadata +72 -20
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core"
4
+
5
+ module Hanami
6
+ module Providers
7
+ class DB < Hanami::Provider::Source
8
+ # @api public
9
+ # @since 2.2.0
10
+ class Config < Dry::Configurable::Config
11
+ include Dry::Core::Constants
12
+
13
+ # @api public
14
+ # @since 2.2.0
15
+ def gateway(key)
16
+ gateway = (gateways[key] ||= Gateway.new)
17
+ yield gateway if block_given?
18
+ gateway
19
+ end
20
+
21
+ # @api public
22
+ # @since 2.2.0
23
+ def adapter_name
24
+ self[:adapter]
25
+ end
26
+
27
+ # @api public
28
+ # @since 2.2.0
29
+ def adapter(name = Undefined)
30
+ return adapter_name if name.eql?(Undefined)
31
+
32
+ adapter = (adapters[name] ||= Adapter.new)
33
+ yield adapter if block_given?
34
+ adapter
35
+ end
36
+
37
+ # @api public
38
+ # @since 2.2.0
39
+ def any_adapter
40
+ adapter = (adapters[nil] ||= Adapter.new)
41
+ yield adapter if block_given?
42
+ adapter
43
+ end
44
+
45
+ # @api private
46
+ def each_plugin
47
+ return to_enum(__method__) unless block_given?
48
+
49
+ universal_plugins = adapters[nil].plugins
50
+
51
+ gateways.values.group_by(&:adapter_name).each do |adapter_name, adapter_gateways|
52
+ per_adapter_plugins = adapter_gateways.map { _1.adapter.plugins }.flatten(1)
53
+
54
+ (universal_plugins + per_adapter_plugins).uniq.each do |plugin_spec, config_block|
55
+ yield adapter_name, plugin_spec, config_block
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/core"
5
+
6
+ module Hanami
7
+ module Providers
8
+ class DB < Hanami::Provider::Source
9
+ # @api public
10
+ # @since 2.2.0
11
+ class Gateway
12
+ include Dry::Core::Constants
13
+ include Dry::Configurable
14
+
15
+ setting :database_url
16
+ setting :adapter_name, default: :sql
17
+ setting :adapter, mutable: true
18
+
19
+ # @api public
20
+ # @since 2.2.0
21
+ def adapter(name = Undefined)
22
+ return config.adapter if name.eql?(Undefined)
23
+
24
+ if block_given?
25
+ # If a block is given, explicitly configure the gateway's adapter
26
+ config.adapter_name = name
27
+ adapter = (config.adapter ||= Adapters.new_adapter(name))
28
+ yield adapter
29
+ adapter
30
+ else
31
+ # If an adapter name is given without a block, use the default adapter configured with
32
+ # the same name
33
+ config.adapter_name = adapter_name
34
+ end
35
+ end
36
+
37
+ # @api private
38
+ def configure_adapter(default_adapters)
39
+ default_adapter = default_adapters[config.adapter_name]
40
+ config.adapter ||= default_adapter.dup
41
+
42
+ config.adapter.configure_from_adapter(default_adapter)
43
+ config.adapter.configure_from_adapter(default_adapters[nil])
44
+ config.adapter.configure_for_database(config.database_url)
45
+
46
+ self
47
+ end
48
+
49
+ # @api private
50
+ def cache_keys
51
+ [config.database_url, config.adapter.gateway_cache_keys]
52
+ end
53
+
54
+ private
55
+
56
+ def method_missing(name, *args, &block)
57
+ if config.respond_to?(name)
58
+ config.public_send(name, *args, &block)
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ def respond_to_missing?(name, _include_all = false)
65
+ config.respond_to?(name) || super
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Providers
5
+ class DB < Hanami::Provider::Source
6
+ # @api public
7
+ # @since 2.2.0
8
+ class SQLAdapter < Adapter
9
+ # @api public
10
+ # @since 2.2.0
11
+ setting :extensions, mutable: true
12
+
13
+ # @api public
14
+ # @since 2.2.0
15
+ def extension(*extensions)
16
+ self.extensions.concat(extensions)
17
+ end
18
+
19
+ # @api public
20
+ # @since 2.2.0
21
+ def extensions
22
+ config.extensions ||= []
23
+ end
24
+
25
+ # @api private
26
+ def configure_from_adapter(other_adapter)
27
+ super
28
+
29
+ return if skip_defaults?
30
+
31
+ # As part of gateway configuration, every gateway will receive the "any adapter" here,
32
+ # which is a plain `Adapter`, not an `SQLAdapter`. Its configuration will have been merged
33
+ # by `super`, so no further work is required.
34
+ return unless other_adapter.is_a?(self.class)
35
+
36
+ extensions.concat(other_adapter.extensions).uniq! unless skip_defaults?(:extensions)
37
+ end
38
+
39
+ # @api private
40
+ def configure_for_database(database_url)
41
+ return if skip_defaults?
42
+
43
+ configure_plugins
44
+ configure_extensions(database_url)
45
+ end
46
+
47
+ # @api private
48
+ private def configure_plugins
49
+ return if skip_defaults?(:plugins)
50
+
51
+ # Configure the plugin via a frozen proc, so it can be properly uniq'ed when configured
52
+ # for multiple gateways. See `Hanami::Providers::DB::Config#each_plugin`.
53
+ plugin(relations: :instrumentation, &INSTRUMENTATION_PLUGIN_CONFIG)
54
+
55
+ plugin relations: :auto_restrictions
56
+ end
57
+
58
+ # @api private
59
+ INSTRUMENTATION_PLUGIN_CONFIG = -> plugin {
60
+ plugin.notifications = target["notifications"]
61
+ }.freeze
62
+ private_constant :INSTRUMENTATION_PLUGIN_CONFIG
63
+
64
+ # @api private
65
+ private def configure_extensions(database_url)
66
+ return if skip_defaults?(:extensions)
67
+
68
+ # Extensions for all SQL databases
69
+ extension(
70
+ :caller_logging,
71
+ :error_sql,
72
+ :sql_comments
73
+ )
74
+
75
+ # Extensions for specific databases
76
+ if database_url.to_s.start_with?(%r{postgres(ql)*://}) # FIXME: what is the canonical postgres URL?
77
+ extension(
78
+ :pg_array,
79
+ :pg_enum,
80
+ :pg_json,
81
+ :pg_range
82
+ )
83
+ end
84
+ end
85
+
86
+ # @api private
87
+ def gateway_options
88
+ {extensions: config.extensions}
89
+ end
90
+
91
+ # @api public
92
+ # @since 2.2.0
93
+ def clear
94
+ config.extensions = nil
95
+ super
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,298 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/core"
5
+ require "uri"
6
+ require_relative "../constants"
7
+
8
+ module Hanami
9
+ module Providers
10
+ # @api private
11
+ # @since 2.2.0
12
+ class DB < Hanami::Provider::Source
13
+ extend Dry::Core::Cache
14
+
15
+ include Dry::Configurable(config_class: Providers::DB::Config)
16
+
17
+ setting :adapters, mutable: true, default: Adapters.new
18
+ setting :gateways, default: {}
19
+
20
+ def initialize(...)
21
+ super(...)
22
+
23
+ @config_finalized = false
24
+ end
25
+
26
+ def finalize_config
27
+ return if @config_finalized
28
+
29
+ apply_parent_config if apply_parent_config?
30
+
31
+ configure_gateways
32
+
33
+ @config_finalized = true
34
+
35
+ self
36
+ end
37
+
38
+ def prepare
39
+ prepare_and_import_parent_db and return if import_from_parent?
40
+
41
+ override_rom_inflector
42
+
43
+ finalize_config
44
+
45
+ require "hanami-db"
46
+
47
+ gateways = prepare_gateways
48
+
49
+ if gateways[:default]
50
+ register "gateway", gateways[:default]
51
+ elsif gateways.length == 1
52
+ register "gateway", gateways.values.first
53
+ end
54
+ gateways.each do |key, gateway|
55
+ register "gateways.#{key}", gateway
56
+ end
57
+
58
+ @rom_config = ROM::Configuration.new(gateways)
59
+
60
+ config.each_plugin do |adapter_name, plugin_spec, config_block|
61
+ if config_block
62
+ @rom_config.plugin(adapter_name, plugin_spec) do |plugin_config|
63
+ instance_exec(plugin_config, &config_block)
64
+ end
65
+ else
66
+ @rom_config.plugin(adapter_name, plugin_spec)
67
+ end
68
+ end
69
+
70
+ register "config", @rom_config
71
+ end
72
+
73
+ def start
74
+ start_and_import_parent_db and return if import_from_parent?
75
+
76
+ # Set up DB logging for the whole app. We share the app's notifications bus across all
77
+ # slices, so we only need to configure the subsciprtion for DB logging just once.
78
+ slice.app.start :db_logging
79
+
80
+ # Register ROM components
81
+ register_rom_components :relation, "relations"
82
+ register_rom_components :command, File.join("db", "commands")
83
+ register_rom_components :mapper, File.join("db", "mappers")
84
+
85
+ rom = ROM.container(@rom_config)
86
+
87
+ register "rom", rom
88
+ end
89
+
90
+ def stop
91
+ slice["db.rom"].disconnect
92
+ end
93
+
94
+ # @api private
95
+ # @since 2.2.0
96
+ def database_urls
97
+ finalize_config
98
+ config.gateways.transform_values { _1.database_url }
99
+ end
100
+
101
+ private
102
+
103
+ def parent_db_provider
104
+ return @parent_db_provider if instance_variable_defined?(:@parent_db_provider)
105
+
106
+ @parent_db_provider = slice.parent && slice.parent.container.providers[:db]
107
+ end
108
+
109
+ def apply_parent_config
110
+ parent_db_provider.source.finalize_config
111
+
112
+ self.class.settings.keys.each do |key|
113
+ # Preserve settings already configured locally
114
+ next if config.configured?(key)
115
+
116
+ # Do not copy gateways, these are always configured per slice
117
+ next if key == :gateways
118
+
119
+ # Skip adapter config, we handle this below
120
+ next if key == :adapters
121
+
122
+ config[key] = parent_db_provider.source.config[key]
123
+ end
124
+
125
+ parent_db_provider.source.config.adapters.each do |adapter_name, parent_adapter|
126
+ adapter = config.adapters[adapter_name]
127
+
128
+ adapter.class.settings.keys.each do |key|
129
+ next if adapter.config.configured?(key)
130
+
131
+ adapter.config[key] = parent_adapter.config[key]
132
+ end
133
+ end
134
+ end
135
+
136
+ def apply_parent_config?
137
+ slice.config.db.configure_from_parent && parent_db_provider
138
+ end
139
+
140
+ def import_from_parent?
141
+ slice.config.db.import_from_parent && slice.parent
142
+ end
143
+
144
+ def prepare_and_import_parent_db
145
+ return unless parent_db_provider
146
+
147
+ slice.parent.prepare :db
148
+ @rom_config = slice.parent["db.config"]
149
+
150
+ register "config", (@rom_config = slice.parent["db.config"])
151
+ register "gateway", slice.parent["db.gateway"]
152
+ end
153
+
154
+ def start_and_import_parent_db
155
+ return unless parent_db_provider
156
+
157
+ slice.parent.start :db
158
+
159
+ register "rom", slice.parent["db.rom"]
160
+ end
161
+
162
+ # ROM 5.3 doesn't have a configurable inflector.
163
+ #
164
+ # This is a problem in Hanami because using different
165
+ # inflection rules for ROM will lead to constant loading
166
+ # errors.
167
+ def override_rom_inflector
168
+ return if ROM::Inflector == Hanami.app["inflector"]
169
+
170
+ ROM.instance_eval {
171
+ remove_const :Inflector
172
+ const_set :Inflector, Hanami.app["inflector"]
173
+ }
174
+ end
175
+
176
+ def configure_gateways
177
+ # Create gateway configs for gateways detected from database_url ENV vars
178
+ database_urls_from_env = detect_database_urls_from_env
179
+ database_urls_from_env.keys.each do |key|
180
+ config.gateways[key] ||= Gateway.new
181
+ end
182
+
183
+ # Create a single default gateway if none is configured or detected from database URLs
184
+ config.gateways[:default] = Gateway.new if config.gateways.empty?
185
+
186
+ # Leave gateways in a stable order: :default first, followed by others in sort order
187
+ if config.gateways.length > 1
188
+ gateways = config.gateways
189
+ config.gateways = {default: gateways[:default], **gateways.sort.to_h}.compact
190
+ end
191
+
192
+ config.gateways.each do |key, gw_config|
193
+ gw_config.database_url ||= database_urls_from_env.fetch(key) {
194
+ raise Hanami::ComponentLoadError, "A database_url for gateway #{key} is required to start :db."
195
+ }
196
+
197
+ ensure_database_gem(gw_config.database_url)
198
+
199
+ gw_config.configure_adapter(config.adapters)
200
+ end
201
+ end
202
+
203
+ def prepare_gateways
204
+ config.gateways.transform_values { |gw_config|
205
+ # Avoid spurious connections by reusing identically configured gateways across slices
206
+ gateway = fetch_or_store(gw_config.cache_keys) {
207
+ ROM::Gateway.setup(
208
+ gw_config.adapter_name,
209
+ gw_config.database_url,
210
+ **gw_config.adapter.gateway_options
211
+ )
212
+ }
213
+ }
214
+ end
215
+
216
+ def detect_database_urls_from_env
217
+ database_urls = {}
218
+
219
+ env_var_prefix = slice.slice_name.name.gsub("/", "__").upcase + "__" unless slice.app?
220
+
221
+ # Build gateway URLs from ENV vars with specific gateway named suffixes
222
+ gateway_prefix = "#{env_var_prefix}DATABASE_URL__"
223
+ ENV.select { |(k, _)| k.start_with?(gateway_prefix) }
224
+ .each do |(var, _)|
225
+ gateway_name = var.split(gateway_prefix).last.downcase
226
+
227
+ database_urls[gateway_name.to_sym] = ENV[var]
228
+ end
229
+
230
+ # Set the default gateway from ENV var without suffix
231
+ if !database_urls.key?(:default)
232
+ fallback_vars = ["#{env_var_prefix}DATABASE_URL", "DATABASE_URL"].uniq
233
+
234
+ fallback_vars.each do |var|
235
+ url = ENV[var]
236
+ database_urls[:default] = url and break if url
237
+ end
238
+ end
239
+
240
+ if Hanami.env?(:test)
241
+ database_urls.transform_values! { Hanami::DB::Testing.database_url(_1) }
242
+ end
243
+
244
+ database_urls
245
+ end
246
+
247
+ # @api private
248
+ # @since 2.2.0
249
+ DATABASE_GEMS = {
250
+ "mysql2" => "mysql2",
251
+ "postgres" => "pg",
252
+ "sqlite" => "sqlite3"
253
+ }.freeze
254
+ private_constant :DATABASE_GEMS
255
+
256
+ # Raises an error if the relevant database gem for the configured database_url is not
257
+ # installed.
258
+ #
259
+ # Takes a conservative approach to raising errors. It only does so for the database_url
260
+ # schemes generated by the `hanami new` CLI command. Uknown schemes are ignored and no errors
261
+ # are raised.
262
+ def ensure_database_gem(database_url)
263
+ scheme = URI(database_url).scheme
264
+ return unless scheme
265
+
266
+ database_gem = DATABASE_GEMS[scheme]
267
+ return unless database_gem
268
+
269
+ return if Hanami.bundled?(database_gem)
270
+
271
+ raise Hanami::ComponentLoadError, %(The "#{database_gem}" gem is required to connect to #{database_url}. Please add it to your Gemfile.)
272
+ end
273
+
274
+ def register_rom_components(component_type, path)
275
+ components_path = target.source_path.join(path)
276
+ components_path.glob("**/*.rb").each do |component_file|
277
+ component_name = component_file
278
+ .relative_path_from(components_path)
279
+ .sub(RB_EXT_REGEXP, "")
280
+ .to_s
281
+
282
+ component_class = target.inflector.camelize(
283
+ "#{target.slice_name.name}/#{path}/#{component_name}"
284
+ ).then { target.inflector.constantize(_1) }
285
+
286
+ @rom_config.public_send(:"register_#{component_type}", component_class)
287
+ end
288
+ end
289
+ end
290
+
291
+ Dry::System.register_provider_source(
292
+ :db,
293
+ source: DB,
294
+ group: :hanami,
295
+ provider_options: {namespace: true}
296
+ )
297
+ end
298
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Providers
5
+ # @api private
6
+ # @since 2.2.0
7
+ class DBLogging < Hanami::Provider::Source
8
+ # @api private
9
+ # @since 2.2.0
10
+ def prepare
11
+ require "dry/monitor/sql/logger"
12
+ slice["notifications"].register_event :sql
13
+ end
14
+
15
+ # @api private
16
+ # @since 2.2.0
17
+ def start
18
+ Dry::Monitor::SQL::Logger.new(slice["logger"]).subscribe(slice["notifications"])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -7,7 +7,7 @@ module Hanami
7
7
  #
8
8
  # @api private
9
9
  # @since 2.0.0
10
- class Inflector < Dry::System::Provider::Source
10
+ class Inflector < Hanami::Provider::Source
11
11
  # @api private
12
12
  def start
13
13
  register :inflector, Hanami.app.inflector
@@ -9,7 +9,7 @@ module Hanami
9
9
  #
10
10
  # @api private
11
11
  # @since 2.0.0
12
- class Logger < Dry::System::Provider::Source
12
+ class Logger < Hanami::Provider::Source
13
13
  # @api private
14
14
  def start
15
15
  register :logger, Hanami.app.config.logger_instance
@@ -12,7 +12,7 @@ module Hanami
12
12
  #
13
13
  # @api private
14
14
  # @since 2.0.0
15
- class Rack < Dry::System::Provider::Source
15
+ class Rack < Hanami::Provider::Source
16
16
  # @api private
17
17
  def prepare
18
18
  Dry::Monitor.load_extensions(:rack)
@@ -30,14 +30,14 @@ module Hanami
30
30
 
31
31
  # @api private
32
32
  def start
33
- target.start :logger
33
+ slice.start :logger
34
34
 
35
35
  monitor_middleware = Dry::Monitor::Rack::Middleware.new(
36
36
  target["notifications"],
37
37
  clock: Dry::Monitor::Clock.new(unit: :microsecond)
38
38
  )
39
39
 
40
- rack_logger = Hanami::Web::RackLogger.new(target[:logger], env: target.env)
40
+ rack_logger = Hanami::Web::RackLogger.new(target[:logger], env: slice.container.env)
41
41
  rack_logger.attach(monitor_middleware)
42
42
 
43
43
  register "monitor", monitor_middleware
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Providers
5
+ # @api private
6
+ # @since 2.2.0
7
+ class Relations < Hanami::Provider::Source
8
+ def start
9
+ start_and_import_parent_relations and return if slice.parent && slice.config.db.import_from_parent
10
+
11
+ slice.start :db
12
+
13
+ register_relations target["db.rom"]
14
+ end
15
+
16
+ private
17
+
18
+ def register_relations(rom)
19
+ rom.relations.each do |name, _|
20
+ register name, rom.relations[name]
21
+ end
22
+ end
23
+
24
+ def start_and_import_parent_relations
25
+ slice.parent.start :relations
26
+
27
+ register_relations slice.parent["db.rom"]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -9,19 +9,7 @@ module Hanami
9
9
  #
10
10
  # @api private
11
11
  # @since 2.0.0
12
- class Routes < Dry::System::Provider::Source
13
- # @api private
14
- def self.for_slice(slice)
15
- Class.new(self) do |klass|
16
- klass.instance_variable_set(:@slice, slice)
17
- end
18
- end
19
-
20
- # @api private
21
- def self.slice
22
- @slice || Hanami.app
23
- end
24
-
12
+ class Routes < Hanami::Provider::Source
25
13
  # @api private
26
14
  def prepare
27
15
  require "hanami/slice/routes_helper"
@@ -33,7 +21,7 @@ module Hanami
33
21
  # router during the process of booting. This ensures the router's resolver can run strict
34
22
  # action key checks once when it runs on a fully booted slice.
35
23
  register :routes do
36
- Hanami::Slice::RoutesHelper.new(self.class.slice.router)
24
+ Hanami::Slice::RoutesHelper.new(slice.router)
37
25
  end
38
26
  end
39
27
  end
@@ -33,13 +33,14 @@ Hanami::CLI::RakeTasks.register_tasks do
33
33
  #
34
34
  # If you're not in control and your deployment requires these "standard"
35
35
  # Rake tasks, they are here only to solve this specific problem.
36
- #
37
- # namespace :db do
38
- # task :migrate do
39
- # # TODO(@jodosha): Enable when we'll integrate with ROM
40
- # # run_hanami_command("db migrate")
41
- # end
42
- # end
36
+
37
+ if Hanami.bundled?("hanami-db")
38
+ namespace :db do
39
+ task :migrate do
40
+ Hanami::CLI::Commands::App::DB::Migrate.new.call
41
+ end
42
+ end
43
+ end
43
44
 
44
45
  if Hanami.bundled?("hanami-assets")
45
46
  namespace :assets do