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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +7 -7
- data/hanami.gemspec +6 -6
- data/lib/hanami/app.rb +5 -1
- data/lib/hanami/config/db.rb +33 -0
- data/lib/hanami/config.rb +36 -9
- data/lib/hanami/constants.rb +4 -0
- data/lib/hanami/extensions/db/repo.rb +103 -0
- data/lib/hanami/extensions.rb +4 -0
- data/lib/hanami/helpers/form_helper/form_builder.rb +4 -6
- data/lib/hanami/provider/source.rb +16 -0
- data/lib/hanami/provider_registrar.rb +28 -0
- data/lib/hanami/providers/assets.rb +2 -20
- data/lib/hanami/providers/db/adapter.rb +75 -0
- data/lib/hanami/providers/db/adapters.rb +50 -0
- data/lib/hanami/providers/db/config.rb +62 -0
- data/lib/hanami/providers/db/gateway.rb +70 -0
- data/lib/hanami/providers/db/sql_adapter.rb +100 -0
- data/lib/hanami/providers/db.rb +298 -0
- data/lib/hanami/providers/db_logging.rb +22 -0
- data/lib/hanami/providers/inflector.rb +1 -1
- data/lib/hanami/providers/logger.rb +1 -1
- data/lib/hanami/providers/rack.rb +3 -3
- data/lib/hanami/providers/relations.rb +31 -0
- data/lib/hanami/providers/routes.rb +2 -14
- data/lib/hanami/rake_tasks.rb +8 -7
- data/lib/hanami/slice.rb +84 -4
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami.rb +3 -0
- data/spec/integration/container/provider_environment_spec.rb +52 -0
- data/spec/integration/db/auto_registration_spec.rb +39 -0
- data/spec/integration/db/commands_spec.rb +80 -0
- data/spec/integration/db/db_inflector_spec.rb +57 -0
- data/spec/integration/db/db_slices_spec.rb +332 -0
- data/spec/integration/db/db_spec.rb +245 -0
- data/spec/integration/db/gateways_spec.rb +320 -0
- data/spec/integration/db/logging_spec.rb +238 -0
- data/spec/integration/db/mappers_spec.rb +84 -0
- data/spec/integration/db/provider_config_spec.rb +88 -0
- data/spec/integration/db/provider_spec.rb +35 -0
- data/spec/integration/db/relations_spec.rb +60 -0
- data/spec/integration/db/repo_spec.rb +215 -0
- data/spec/integration/db/slices_importing_from_parent.rb +130 -0
- data/spec/integration/slices/slice_configuration_spec.rb +4 -4
- data/spec/support/app_integration.rb +3 -0
- data/spec/unit/hanami/config/db_spec.rb +38 -0
- data/spec/unit/hanami/config/router_spec.rb +1 -1
- data/spec/unit/hanami/helpers/form_helper_spec.rb +35 -4
- data/spec/unit/hanami/providers/db/config/default_config_spec.rb +100 -0
- data/spec/unit/hanami/providers/db/config_spec.rb +156 -0
- data/spec/unit/hanami/slice_spec.rb +32 -0
- data/spec/unit/hanami/version_spec.rb +1 -1
- metadata +72 -20
data/lib/hanami/slice.rb
CHANGED
@@ -101,6 +101,16 @@ module Hanami
|
|
101
101
|
Hanami.app
|
102
102
|
end
|
103
103
|
|
104
|
+
# Returns true if the slice is Hanami.app
|
105
|
+
#
|
106
|
+
# @return [Boolean]
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
# @since 2.2.0
|
110
|
+
def app?
|
111
|
+
eql?(app)
|
112
|
+
end
|
113
|
+
|
104
114
|
# Returns the slice's config.
|
105
115
|
#
|
106
116
|
# A slice's config is copied from the app config at time of first access.
|
@@ -203,6 +213,16 @@ module Hanami
|
|
203
213
|
config.root || app.root.join(SLICES_DIR, slice_name.to_s)
|
204
214
|
end
|
205
215
|
|
216
|
+
# Returns the slice's root component directory, accounting for App as a special case.
|
217
|
+
#
|
218
|
+
# @return [Pathname]
|
219
|
+
#
|
220
|
+
# @api public
|
221
|
+
# @since 2.2.0
|
222
|
+
def source_path
|
223
|
+
app? ? root.join(APP_DIR) : root
|
224
|
+
end
|
225
|
+
|
206
226
|
# Returns the slice's configured inflector.
|
207
227
|
#
|
208
228
|
# Unless explicitly re-configured for the slice, this will be the app's inflector.
|
@@ -507,6 +527,12 @@ module Hanami
|
|
507
527
|
container.register_provider(...)
|
508
528
|
end
|
509
529
|
|
530
|
+
# @api public
|
531
|
+
# @since 2.1.0
|
532
|
+
def configure_provider(*args, **kwargs, &block)
|
533
|
+
container.register_provider(*args, **kwargs, from: :hanami, &block)
|
534
|
+
end
|
535
|
+
|
510
536
|
# @overload start(provider_name)
|
511
537
|
# Starts a provider.
|
512
538
|
#
|
@@ -559,6 +585,13 @@ module Hanami
|
|
559
585
|
container.key?(...)
|
560
586
|
end
|
561
587
|
|
588
|
+
# Required for the slice to act as a provider target
|
589
|
+
# @api public
|
590
|
+
# @since 2.2.0
|
591
|
+
def registered?(...)
|
592
|
+
container.registered?(...)
|
593
|
+
end
|
594
|
+
|
562
595
|
# Returns an array of keys for all currently registered components in the container.
|
563
596
|
#
|
564
597
|
# For a prepared slice, this will be the set of components that have been previously resolved.
|
@@ -842,6 +875,7 @@ module Hanami
|
|
842
875
|
def prepare_container_base_config
|
843
876
|
container.config.name = slice_name.to_sym
|
844
877
|
container.config.root = root
|
878
|
+
container.config.provider_registrar = ProviderRegistrar.for_slice(self)
|
845
879
|
container.config.provider_dirs = [File.join("config", "providers")]
|
846
880
|
container.config.registrations_dir = File.join("config", "registrations")
|
847
881
|
|
@@ -893,12 +927,29 @@ module Hanami
|
|
893
927
|
# point we're still in the process of preparing.
|
894
928
|
if routes
|
895
929
|
require_relative "providers/routes"
|
896
|
-
register_provider(:routes, source: Providers::Routes
|
930
|
+
register_provider(:routes, source: Providers::Routes)
|
897
931
|
end
|
898
932
|
|
899
933
|
if assets_dir? && Hanami.bundled?("hanami-assets")
|
900
934
|
require_relative "providers/assets"
|
901
|
-
register_provider(:assets, source: Providers::Assets
|
935
|
+
register_provider(:assets, source: Providers::Assets)
|
936
|
+
end
|
937
|
+
|
938
|
+
if Hanami.bundled?("hanami-db")
|
939
|
+
# Explicit require here to ensure the provider source registers itself, to allow the user
|
940
|
+
# to configure it within their own concrete provider file.
|
941
|
+
require_relative "providers/db"
|
942
|
+
|
943
|
+
if register_db_provider?
|
944
|
+
# Only register providers if the user hasn't provided their own
|
945
|
+
if !container.providers[:db]
|
946
|
+
register_provider(:db, namespace: true, source: Providers::DB)
|
947
|
+
end
|
948
|
+
|
949
|
+
if !container.providers[:relations]
|
950
|
+
register_provider(:relations, namespace: true, source: Providers::Relations)
|
951
|
+
end
|
952
|
+
end
|
902
953
|
end
|
903
954
|
end
|
904
955
|
|
@@ -1028,8 +1079,37 @@ module Hanami
|
|
1028
1079
|
private_constant :ROUTER_NOT_FOUND_HANDLER
|
1029
1080
|
|
1030
1081
|
def assets_dir?
|
1031
|
-
|
1032
|
-
|
1082
|
+
source_path.join("assets").directory?
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
def register_db_provider?
|
1086
|
+
concrete_db_provider? ||
|
1087
|
+
db_config_dir? ||
|
1088
|
+
relations_dir? ||
|
1089
|
+
db_source_dir? ||
|
1090
|
+
import_db_from_parent?
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def concrete_db_provider?
|
1094
|
+
root.join(CONFIG_DIR, "providers", "db.rb").exist?
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
def db_config_dir?
|
1098
|
+
root.join("config", "db").directory?
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
def relations_dir?
|
1102
|
+
source_path.join("relations").directory?
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def db_source_dir?
|
1106
|
+
source_path.join("db").directory?
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
def import_db_from_parent?
|
1110
|
+
parent &&
|
1111
|
+
config.db.import_from_parent &&
|
1112
|
+
parent.container.providers[:db]
|
1033
1113
|
end
|
1034
1114
|
|
1035
1115
|
# rubocop:enable Metrics/AbcSize
|
data/lib/hanami/version.rb
CHANGED
data/lib/hanami.rb
CHANGED
@@ -17,6 +17,9 @@ module Hanami
|
|
17
17
|
# @since 2.0.0
|
18
18
|
def self.loader
|
19
19
|
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
20
|
+
loader.inflector.inflect "db" => "DB"
|
21
|
+
loader.inflector.inflect "db_logging" => "DBLogging"
|
22
|
+
loader.inflector.inflect "sql_adapter" => "SQLAdapter"
|
20
23
|
loader.ignore(
|
21
24
|
"#{loader.dirs.first}/hanami/{constants,boot,errors,extensions/router/errors,prepare,rake_tasks,setup}.rb"
|
22
25
|
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe "Container / Provider environment", :app_integration do
|
4
|
+
let!(:app) {
|
5
|
+
module TestApp
|
6
|
+
class App < Hanami::App
|
7
|
+
class << self
|
8
|
+
attr_accessor :test_provider_target
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before_prepare if respond_to?(:before_prepare)
|
14
|
+
|
15
|
+
Hanami.app.prepare
|
16
|
+
Hanami.app
|
17
|
+
}
|
18
|
+
|
19
|
+
context "app provider" do
|
20
|
+
before do
|
21
|
+
Hanami.app.register_provider :test_provider, namespace: true do
|
22
|
+
start do
|
23
|
+
Hanami.app.test_provider_target = target
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "exposes the app as the provider target" do
|
29
|
+
Hanami.app.start :test_provider
|
30
|
+
expect(Hanami.app.test_provider_target).to be Hanami.app
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "slice provider" do
|
35
|
+
def before_prepare
|
36
|
+
Hanami.app.register_slice :main
|
37
|
+
end
|
38
|
+
|
39
|
+
before do
|
40
|
+
Main::Slice.register_provider :test_provider, namespace: true do
|
41
|
+
start do
|
42
|
+
Hanami.app.test_provider_target = target
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "exposes the slice as the provider target" do
|
48
|
+
Main::Slice.start :test_provider
|
49
|
+
expect(Hanami.app.test_provider_target).to be Main::Slice
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe "DB / auto-registration", :app_integration do
|
4
|
+
before do
|
5
|
+
@env = ENV.to_h
|
6
|
+
allow(Hanami::Env).to receive(:loaded?).and_return(false)
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
ENV.replace(@env)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "does not auto-register files in entities/, structs/, or db/" do
|
14
|
+
with_tmp_directory(@dir = Dir.mktmpdir) do
|
15
|
+
write "config/app.rb", <<~RUBY
|
16
|
+
require "hanami"
|
17
|
+
|
18
|
+
module TestApp
|
19
|
+
class App < Hanami::App
|
20
|
+
end
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
|
24
|
+
write "app/db/changesets/update_posts.rb", ""
|
25
|
+
write "app/entities/post.rb", ""
|
26
|
+
write "app/structs/post.rb", ""
|
27
|
+
|
28
|
+
ENV["DATABASE_URL"] = "sqlite::memory"
|
29
|
+
|
30
|
+
require "hanami/boot"
|
31
|
+
|
32
|
+
expect(Hanami.app.keys).not_to include(*[
|
33
|
+
"db.changesets.update_posts",
|
34
|
+
"entities.post",
|
35
|
+
"structs.post"
|
36
|
+
])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe "DB / Commands", :app_integration do
|
4
|
+
before do
|
5
|
+
@env = ENV.to_h
|
6
|
+
allow(Hanami::Env).to receive(:loaded?).and_return(false)
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
ENV.replace(@env)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "registers custom commands" do
|
14
|
+
with_tmp_directory(@dir = Dir.mktmpdir) do
|
15
|
+
write "config/app.rb", <<~RUBY
|
16
|
+
require "hanami"
|
17
|
+
|
18
|
+
module TestApp
|
19
|
+
class App < Hanami::App
|
20
|
+
config.logger.stream = File::NULL
|
21
|
+
end
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
write "app/relations/posts.rb", <<~RUBY
|
26
|
+
module TestApp
|
27
|
+
module Relations
|
28
|
+
class Posts < Hanami::DB::Relation
|
29
|
+
schema :posts, infer: true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
|
35
|
+
write "app/db/commands/nested/create_post_with_default_title.rb", <<~RUBY
|
36
|
+
module TestApp
|
37
|
+
module DB
|
38
|
+
module Commands
|
39
|
+
module Nested
|
40
|
+
class CreatePostWithDefaultTitle < ROM::SQL::Commands::Create
|
41
|
+
relation :posts
|
42
|
+
register_as :create_with_default_title
|
43
|
+
result :one
|
44
|
+
|
45
|
+
before :set_title
|
46
|
+
|
47
|
+
def set_title(tuple, *)
|
48
|
+
tuple[:title] ||= "Default title from command"
|
49
|
+
tuple
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
RUBY
|
57
|
+
|
58
|
+
ENV["DATABASE_URL"] = "sqlite::memory"
|
59
|
+
|
60
|
+
require "hanami/prepare"
|
61
|
+
|
62
|
+
Hanami.app.prepare :db
|
63
|
+
|
64
|
+
# Manually run a migration and add a test record
|
65
|
+
gateway = TestApp::App["db.gateway"]
|
66
|
+
migration = gateway.migration do
|
67
|
+
change do
|
68
|
+
create_table :posts do
|
69
|
+
primary_key :id
|
70
|
+
column :title, :text, null: false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
migration.apply(gateway, :up)
|
75
|
+
|
76
|
+
post = TestApp::App["relations.posts"].command(:create_with_default_title).call({})
|
77
|
+
expect(post[:title]).to eq "Default title from command"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/system"
|
4
|
+
|
5
|
+
RSpec.describe "ROM::Inflector", :app_integration do
|
6
|
+
before do
|
7
|
+
@env = ENV.to_h
|
8
|
+
allow(Hanami::Env).to receive(:loaded?).and_return(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
ENV.replace(@env)
|
13
|
+
end
|
14
|
+
|
15
|
+
around :each do |example|
|
16
|
+
inflector = ROM::Inflector
|
17
|
+
ROM.instance_eval {
|
18
|
+
remove_const :Inflector
|
19
|
+
const_set :Inflector, Dry::Inflector.new
|
20
|
+
}
|
21
|
+
example.run
|
22
|
+
ensure
|
23
|
+
ROM.instance_eval {
|
24
|
+
remove_const :Inflector
|
25
|
+
const_set :Inflector, inflector
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "replaces ROM::Inflector with the Hanami inflector" do
|
30
|
+
with_tmp_directory(Dir.mktmpdir) do
|
31
|
+
write "config/app.rb", <<~RUBY
|
32
|
+
require "hanami"
|
33
|
+
|
34
|
+
module TestApp
|
35
|
+
class App < Hanami::App
|
36
|
+
end
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
|
40
|
+
write "app/relations/posts.rb", <<~RUBY
|
41
|
+
module TestApp
|
42
|
+
module Relations
|
43
|
+
class Posts < Hanami::DB::Relation
|
44
|
+
schema :posts, infer: true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
RUBY
|
49
|
+
|
50
|
+
ENV["DATABASE_URL"] = "sqlite::memory"
|
51
|
+
|
52
|
+
require "hanami/prepare"
|
53
|
+
|
54
|
+
expect { Hanami.app.prepare :db }.to change { ROM::Inflector == Hanami.app["inflector"] }.from(false).to(true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|