hanami 2.1.1 → 2.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -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/extensions/db/repo.rb +103 -0
- data/lib/hanami/extensions.rb +4 -0
- data/lib/hanami/helpers/form_helper/form_builder.rb +2 -4
- data/lib/hanami/provider_registrar.rb +26 -0
- data/lib/hanami/providers/assets.rb +2 -20
- data/lib/hanami/providers/db/adapter.rb +68 -0
- data/lib/hanami/providers/db/adapters.rb +44 -0
- data/lib/hanami/providers/db/config.rb +66 -0
- data/lib/hanami/providers/db/sql_adapter.rb +80 -0
- data/lib/hanami/providers/db.rb +203 -0
- data/lib/hanami/providers/db_logging.rb +22 -0
- data/lib/hanami/providers/rack.rb +1 -1
- data/lib/hanami/providers/relations.rb +31 -0
- data/lib/hanami/providers/routes.rb +1 -13
- 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/db_inflector_spec.rb +57 -0
- data/spec/integration/db/db_slices_spec.rb +327 -0
- data/spec/integration/db/db_spec.rb +220 -0
- data/spec/integration/db/logging_spec.rb +238 -0
- data/spec/integration/db/provider_config_spec.rb +88 -0
- data/spec/integration/db/provider_spec.rb +35 -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 +31 -0
- data/spec/unit/hanami/providers/db/config/default_config_spec.rb +107 -0
- data/spec/unit/hanami/providers/db/config_spec.rb +206 -0
- data/spec/unit/hanami/slice_spec.rb +32 -0
- data/spec/unit/hanami/version_spec.rb +1 -1
- metadata +61 -19
@@ -12,7 +12,7 @@ RSpec.describe "Slices / Slice configuration", :app_integration do
|
|
12
12
|
class App < Hanami::App
|
13
13
|
config.logger.stream = StringIO.new
|
14
14
|
|
15
|
-
config.no_auto_register_paths
|
15
|
+
config.no_auto_register_paths = ["structs"]
|
16
16
|
end
|
17
17
|
end
|
18
18
|
RUBY
|
@@ -34,9 +34,9 @@ RSpec.describe "Slices / Slice configuration", :app_integration do
|
|
34
34
|
|
35
35
|
require "hanami/prepare"
|
36
36
|
|
37
|
-
expect(TestApp::App.config.no_auto_register_paths).to eq %w[
|
38
|
-
expect(Main::Slice.config.no_auto_register_paths).to eq %w[
|
39
|
-
expect(Search::Slice.config.no_auto_register_paths).to eq %w[
|
37
|
+
expect(TestApp::App.config.no_auto_register_paths).to eq %w[structs]
|
38
|
+
expect(Main::Slice.config.no_auto_register_paths).to eq %w[structs schemas]
|
39
|
+
expect(Search::Slice.config.no_auto_register_paths).to eq %w[structs]
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -118,6 +118,9 @@ RSpec.configure do |config|
|
|
118
118
|
Hanami.instance_variable_set(:@_bundled, {})
|
119
119
|
Hanami.remove_instance_variable(:@_app) if Hanami.instance_variable_defined?(:@_app)
|
120
120
|
|
121
|
+
# Clear cached DB gateways across slices
|
122
|
+
Hanami::Providers::DB.cache.clear
|
123
|
+
|
121
124
|
$LOAD_PATH.replace(@load_paths)
|
122
125
|
|
123
126
|
# Remove example-specific LOADED_FEATURES added when running each example
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/config"
|
4
|
+
|
5
|
+
RSpec.describe Hanami::Config, "#db" do
|
6
|
+
let(:config) { described_class.new(app_name: app_name, env: :development) }
|
7
|
+
let(:app_name) { "MyApp::App" }
|
8
|
+
|
9
|
+
subject(:db) { config.db }
|
10
|
+
|
11
|
+
context "hanami-router is bundled" do
|
12
|
+
it "is a full router configuration" do
|
13
|
+
is_expected.to be_an_instance_of(Hanami::Config::DB)
|
14
|
+
|
15
|
+
is_expected.to respond_to(:import_from_parent)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can be finalized" do
|
19
|
+
is_expected.to respond_to(:finalize!)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "hanami-db is not bundled" do
|
24
|
+
before do
|
25
|
+
allow(Hanami).to receive(:bundled?).and_call_original
|
26
|
+
allow(Hanami).to receive(:bundled?).with("hanami-db").and_return(false)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not expose any settings" do
|
30
|
+
is_expected.to be_an_instance_of(Hanami::Config::NullConfig)
|
31
|
+
is_expected.not_to respond_to(:import_from_parent)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can be finalized" do
|
35
|
+
is_expected.to respond_to(:finalize!)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -29,7 +29,7 @@ RSpec.describe Hanami::Config, "#router" do
|
|
29
29
|
context "hanami-router is not bundled" do
|
30
30
|
before do
|
31
31
|
allow(Hanami).to receive(:bundled?).and_call_original
|
32
|
-
|
32
|
+
allow(Hanami).to receive(:bundled?).with("hanami-router").and_return(false)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "does not expose any settings" do
|
@@ -393,6 +393,29 @@ RSpec.describe Hanami::Helpers::FormHelper do
|
|
393
393
|
|
394
394
|
expect(html).to eq_html(expected)
|
395
395
|
end
|
396
|
+
|
397
|
+
context "with base name" do
|
398
|
+
let(:params) { {book: {categories: [{name: "foo"}, {name: "bar"}]}} }
|
399
|
+
|
400
|
+
it "renders" do
|
401
|
+
html = render(<<~ERB)
|
402
|
+
<%= form_for("book", "/books") do |f| %>
|
403
|
+
<% f.fields_for_collection "categories" do |fa| %>
|
404
|
+
<%= fa.text_field :name %>
|
405
|
+
<% end %>
|
406
|
+
<% end %>
|
407
|
+
ERB
|
408
|
+
|
409
|
+
expected = <<~HTML
|
410
|
+
<form action="/books" accept-charset="utf-8" method="POST">
|
411
|
+
<input type="text" name="book[categories][][name]" id="book-categories-0-name" value="foo">
|
412
|
+
<input type="text" name="book[categories][][name]" id="book-categories-1-name" value="bar">
|
413
|
+
</form>
|
414
|
+
HTML
|
415
|
+
|
416
|
+
expect(html).to eq_html(expected)
|
417
|
+
end
|
418
|
+
end
|
396
419
|
end
|
397
420
|
|
398
421
|
describe "#label" do
|
@@ -404,6 +427,14 @@ RSpec.describe Hanami::Helpers::FormHelper do
|
|
404
427
|
expect(html).to include %(<label for="book-free-shipping">Free shipping</label>)
|
405
428
|
end
|
406
429
|
|
430
|
+
it "accepts a symbol" do
|
431
|
+
html = form_for("/books") do |f|
|
432
|
+
f.label :free_shipping
|
433
|
+
end
|
434
|
+
|
435
|
+
expect(html).to include %(<label for="free-shipping">Free shipping</label>)
|
436
|
+
end
|
437
|
+
|
407
438
|
it "accepts a string as custom content" do
|
408
439
|
html = form_for("/books") do |f|
|
409
440
|
f.label "Free Shipping!", for: "book.free_shipping"
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/system"
|
4
|
+
require "hanami/providers/db"
|
5
|
+
|
6
|
+
RSpec.describe "Hanami::Providers::DB / Config / Default config", :app_integration do
|
7
|
+
subject(:config) { provider.source.config }
|
8
|
+
|
9
|
+
let(:provider) {
|
10
|
+
Hanami.app.configure_provider(:db)
|
11
|
+
Hanami.app.container.providers[:db]
|
12
|
+
}
|
13
|
+
|
14
|
+
before do
|
15
|
+
module TestApp
|
16
|
+
class App < Hanami::App
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "database_url = nil" do
|
22
|
+
expect(config.database_url).to be nil
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "adapter = :sql" do
|
26
|
+
expect(config.adapter).to eq :sql
|
27
|
+
end
|
28
|
+
|
29
|
+
specify %(relations_path = "relations") do
|
30
|
+
expect(config)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "sql adapter" do
|
34
|
+
before do
|
35
|
+
skip_defaults if respond_to?(:skip_defaults)
|
36
|
+
config.adapter(:sql).configure_for_database("mysql://localhost/test_app_development")
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "plugins" do
|
40
|
+
specify do
|
41
|
+
expect(config.adapter(:sql).plugins).to match [
|
42
|
+
[{relations: :instrumentation}, instance_of(Proc)],
|
43
|
+
[{relations: :auto_restrictions}, nil],
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "skipping defaults" do
|
48
|
+
def skip_defaults
|
49
|
+
config.adapter(:sql).skip_defaults :plugins
|
50
|
+
end
|
51
|
+
|
52
|
+
it "configures no plugins" do
|
53
|
+
expect(config.adapter(:sql).plugins).to eq []
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "extensions" do
|
59
|
+
specify do
|
60
|
+
expect(config.adapter(:sql).extensions).to eq [
|
61
|
+
:caller_logging,
|
62
|
+
:error_sql,
|
63
|
+
:sql_comments
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "skipping defaults" do
|
68
|
+
def skip_defaults
|
69
|
+
config.adapter(:sql).skip_defaults :extensions
|
70
|
+
end
|
71
|
+
|
72
|
+
it "configures no extensions" do
|
73
|
+
expect(config.adapter(:sql).extensions).to eq []
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "skipping all defaults" do
|
79
|
+
def skip_defaults
|
80
|
+
config.adapter(:sql).skip_defaults
|
81
|
+
end
|
82
|
+
|
83
|
+
it "configures no plugins or extensions" do
|
84
|
+
expect(config.adapter(:sql).plugins).to eq []
|
85
|
+
expect(config.adapter(:sql).extensions).to eq []
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "sql adapter for postgres" do
|
91
|
+
before do
|
92
|
+
config.adapter(:sql).configure_for_database("postgresql://localhost/test_app_development")
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "extensions" do
|
96
|
+
expect(config.adapters[:sql].extensions).to eq [
|
97
|
+
:caller_logging,
|
98
|
+
:error_sql,
|
99
|
+
:sql_comments,
|
100
|
+
:pg_array,
|
101
|
+
:pg_enum,
|
102
|
+
:pg_json,
|
103
|
+
:pg_range
|
104
|
+
]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/system"
|
4
|
+
require "hanami/providers/db"
|
5
|
+
|
6
|
+
RSpec.describe "Hanami::Providers::DB.config", :app_integration do
|
7
|
+
subject(:config) { provider.source.config }
|
8
|
+
|
9
|
+
let(:provider) {
|
10
|
+
Hanami.app.configure_provider(:db)
|
11
|
+
Hanami.app.container.providers[:db]
|
12
|
+
}
|
13
|
+
|
14
|
+
before do
|
15
|
+
module TestApp
|
16
|
+
class App < Hanami::App
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#adapter_name" do
|
22
|
+
it "aliases #adapter" do
|
23
|
+
expect { config.adapter = :yaml }
|
24
|
+
.to change { config.adapter_name }
|
25
|
+
.to :yaml
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#adapter" do
|
30
|
+
it "adds an adapter" do
|
31
|
+
expect { config.adapter(:yaml) }
|
32
|
+
.to change { config.adapters.to_h }
|
33
|
+
.to hash_including(:yaml)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "yields the adapter for configuration" do
|
37
|
+
expect { |b| config.adapter(:yaml, &b) }
|
38
|
+
.to yield_with_args(an_instance_of(Hanami::Providers::DB::Adapter))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#any_adapter" do
|
43
|
+
it "adds an adapter keyed without a name" do
|
44
|
+
expect { config.any_adapter }
|
45
|
+
.to change { config.adapters.to_h }
|
46
|
+
.to hash_including(nil)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "yields the adapter for configuration" do
|
50
|
+
expect { |b| config.any_adapter(&b) }
|
51
|
+
.to yield_with_args(an_instance_of(Hanami::Providers::DB::Adapter))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "adapters" do
|
56
|
+
subject(:adapter) { config.adapter(:yaml) }
|
57
|
+
|
58
|
+
describe "#plugin" do
|
59
|
+
it "adds a plugin without a block" do
|
60
|
+
expect { adapter.plugin relations: :foo }
|
61
|
+
.to change { adapter.plugins }
|
62
|
+
.to [[{relations: :foo}, nil]]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "adds a plugin with a block" do
|
66
|
+
block = -> plugin_config { }
|
67
|
+
|
68
|
+
expect {
|
69
|
+
adapter.plugin(relations: :foo, &block)
|
70
|
+
}
|
71
|
+
.to change { adapter.plugins }
|
72
|
+
.to [[{relations: :foo}, block]]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#plugins" do
|
77
|
+
it "can be cleared" do
|
78
|
+
adapter.plugin relations: :foo
|
79
|
+
|
80
|
+
expect { adapter.plugins.clear }
|
81
|
+
.to change { adapter.plugins }
|
82
|
+
.to []
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#gateway_cache_keys" do
|
87
|
+
it "includes the configured extensions" do
|
88
|
+
expect(adapter.gateway_cache_keys).to eq({})
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#gateway_options" do
|
93
|
+
specify do
|
94
|
+
expect(adapter.gateway_options).to eq({})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#clear" do
|
99
|
+
it "clears previously configured plugins" do
|
100
|
+
adapter.plugin relations: :foo
|
101
|
+
|
102
|
+
expect { adapter.clear }.to change { adapter.plugins }.to([])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ":sql adapter" do
|
107
|
+
subject(:adapter) { config.adapter(:sql) }
|
108
|
+
|
109
|
+
describe "#extension" do
|
110
|
+
it "adds an extension" do
|
111
|
+
adapter.clear
|
112
|
+
expect { adapter.extension :foo }
|
113
|
+
.to change { adapter.extensions }
|
114
|
+
.to [:foo]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "adds multiple extensions" do
|
118
|
+
adapter.clear
|
119
|
+
expect { adapter.extension :foo, :bar }
|
120
|
+
.to change { adapter.extensions }
|
121
|
+
.to [:foo, :bar]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "#extensions" do
|
126
|
+
it "can be cleareed" do
|
127
|
+
adapter.extension :foo
|
128
|
+
|
129
|
+
expect { adapter.extensions.clear }
|
130
|
+
.to change { adapter.extensions }
|
131
|
+
.to []
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#gateway_cache_keys" do
|
136
|
+
it "includes the configured extensions" do
|
137
|
+
adapter.clear
|
138
|
+
adapter.extension :foo, :bar
|
139
|
+
expect(adapter.gateway_cache_keys).to eq(extensions: [:foo, :bar])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#gateway_options" do
|
144
|
+
it "includes the configured extensions" do
|
145
|
+
adapter.clear
|
146
|
+
adapter.extension :foo, :bar
|
147
|
+
expect(adapter.gateway_options).to eq(extensions: [:foo, :bar])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#clear" do
|
152
|
+
it "clears previously configured plugins and extensions" do
|
153
|
+
adapter.plugin relations: :foo
|
154
|
+
adapter.extension :foo
|
155
|
+
|
156
|
+
expect { adapter.clear }
|
157
|
+
.to change { adapter.plugins }.to([])
|
158
|
+
.and change { adapter.extensions }.to([])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# TODO clear
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#gateway_cache_keys" do
|
167
|
+
it "returns the cache keys from the currently configured adapter" do
|
168
|
+
config.adapter(:sql) { |a| a.clear; a.extension :foo }
|
169
|
+
config.adapter = :sql
|
170
|
+
|
171
|
+
expect(config.gateway_cache_keys).to eq(config.adapter(:sql).gateway_cache_keys)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#gateway_options" do
|
176
|
+
it "returns the options from the currently configured adapter" do
|
177
|
+
config.adapter(:sql) { |a| a.clear; a.extension :foo }
|
178
|
+
config.adapter = :sql
|
179
|
+
|
180
|
+
expect(config.gateway_options).to eq(config.adapter(:sql).gateway_options)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#each_plugin" do
|
185
|
+
before do
|
186
|
+
config.any_adapter { |a| a.plugin relations: :any_foo }
|
187
|
+
config.adapter(:yaml) { |a| a.plugin relations: :yaml_foo }
|
188
|
+
config.adapter = :yaml
|
189
|
+
end
|
190
|
+
|
191
|
+
it "yields the plugins specified for any adapter as well as the currently configured adapter" do
|
192
|
+
expect { |b| config.each_plugin(&b) }
|
193
|
+
.to yield_successive_args(
|
194
|
+
[{relations: :any_foo}, nil],
|
195
|
+
[{relations: :yaml_foo}, nil]
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns the plugins as an enumerator if no block is given" do
|
200
|
+
expect(config.each_plugin.to_a).to eq [
|
201
|
+
[{relations: :any_foo}, nil],
|
202
|
+
[{relations: :yaml_foo}, nil]
|
203
|
+
]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -8,6 +8,26 @@ RSpec.describe Hanami::Slice, :app_integration do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
describe ".app" do
|
12
|
+
subject(:slice) { Hanami.app.register_slice(:main) }
|
13
|
+
|
14
|
+
it "returns the top-level Hanami App slice" do
|
15
|
+
expect(slice.app).to eq Hanami.app
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".app?" do
|
20
|
+
it "returns true if the slice is Hanami.app" do
|
21
|
+
subject = Hanami.app
|
22
|
+
expect(subject.app?).to eq true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns false if the slice is not Hanami.app" do
|
26
|
+
subject = Hanami.app.register_slice(:main)
|
27
|
+
expect(subject.app?).to eq false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
describe ".environment" do
|
12
32
|
subject(:slice) { Hanami.app.register_slice(:main) }
|
13
33
|
|
@@ -46,4 +66,16 @@ RSpec.describe Hanami::Slice, :app_integration do
|
|
46
66
|
.to raise_error Hanami::SliceLoadError, /Slice must have a class name/
|
47
67
|
end
|
48
68
|
end
|
69
|
+
|
70
|
+
describe ".source_path" do
|
71
|
+
it "provides a path to the app directory for Hanami.app" do
|
72
|
+
subject = Hanami.app
|
73
|
+
expect(subject.source_path).to eq Hanami.app.root.join("app")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "provides a path to the slice root for a Slice" do
|
77
|
+
subject = Hanami.app.register_slice(:main)
|
78
|
+
expect(subject.source_path).to eq subject.root
|
79
|
+
end
|
80
|
+
end
|
49
81
|
end
|