dry-system 0.5.0
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 +7 -0
- data/.gitignore +39 -0
- data/.rspec +3 -0
- data/.rubocop.yml +34 -0
- data/.rubocop_todo.yml +26 -0
- data/.travis.yml +26 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +158 -0
- data/Gemfile +9 -0
- data/LICENSE +20 -0
- data/README.md +23 -0
- data/Rakefile +12 -0
- data/dry-system.gemspec +30 -0
- data/examples/standalone/Gemfile +5 -0
- data/examples/standalone/lib/user_repo.rb +5 -0
- data/examples/standalone/run.rb +7 -0
- data/examples/standalone/system/boot/persistence.rb +13 -0
- data/examples/standalone/system/container.rb +9 -0
- data/examples/standalone/system/import.rb +3 -0
- data/lib/dry-system.rb +1 -0
- data/lib/dry/system.rb +4 -0
- data/lib/dry/system/auto_registrar.rb +80 -0
- data/lib/dry/system/booter.rb +101 -0
- data/lib/dry/system/component.rb +167 -0
- data/lib/dry/system/constants.rb +9 -0
- data/lib/dry/system/container.rb +500 -0
- data/lib/dry/system/errors.rb +62 -0
- data/lib/dry/system/importer.rb +53 -0
- data/lib/dry/system/injector.rb +68 -0
- data/lib/dry/system/lifecycle.rb +104 -0
- data/lib/dry/system/loader.rb +69 -0
- data/lib/dry/system/version.rb +5 -0
- data/spec/fixtures/components/bar.rb +5 -0
- data/spec/fixtures/components/bar/baz.rb +4 -0
- data/spec/fixtures/components/foo.rb +2 -0
- data/spec/fixtures/import_test/config/application.yml +2 -0
- data/spec/fixtures/import_test/lib/test/bar.rb +4 -0
- data/spec/fixtures/import_test/lib/test/foo.rb +5 -0
- data/spec/fixtures/import_test/system/boot/bar.rb +11 -0
- data/spec/fixtures/lazytest/config/application.yml +2 -0
- data/spec/fixtures/lazytest/lib/test/dep.rb +4 -0
- data/spec/fixtures/lazytest/lib/test/foo.rb +5 -0
- data/spec/fixtures/lazytest/lib/test/models.rb +4 -0
- data/spec/fixtures/lazytest/lib/test/models/book.rb +6 -0
- data/spec/fixtures/lazytest/lib/test/models/user.rb +6 -0
- data/spec/fixtures/lazytest/system/boot/bar.rb +15 -0
- data/spec/fixtures/namespaced_components/namespaced/bar.rb +5 -0
- data/spec/fixtures/namespaced_components/namespaced/foo.rb +4 -0
- data/spec/fixtures/other/config/boot/bar.rb +11 -0
- data/spec/fixtures/other/lib/test/dep.rb +4 -0
- data/spec/fixtures/other/lib/test/foo.rb +5 -0
- data/spec/fixtures/other/lib/test/models.rb +4 -0
- data/spec/fixtures/other/lib/test/models/book.rb +6 -0
- data/spec/fixtures/other/lib/test/models/user.rb +6 -0
- data/spec/fixtures/test/config/application.yml +2 -0
- data/spec/fixtures/test/config/subapp.yml +2 -0
- data/spec/fixtures/test/lib/test/dep.rb +4 -0
- data/spec/fixtures/test/lib/test/foo.rb +5 -0
- data/spec/fixtures/test/lib/test/models.rb +4 -0
- data/spec/fixtures/test/lib/test/models/book.rb +6 -0
- data/spec/fixtures/test/lib/test/models/user.rb +6 -0
- data/spec/fixtures/test/lib/test/singleton_dep.rb +7 -0
- data/spec/fixtures/test/log/.gitkeep +0 -0
- data/spec/fixtures/test/system/boot/bar.rb +11 -0
- data/spec/fixtures/test/system/boot/client.rb +7 -0
- data/spec/fixtures/test/system/boot/db.rb +1 -0
- data/spec/fixtures/test/system/boot/logger.rb +5 -0
- data/spec/fixtures/umbrella/system/boot/db.rb +10 -0
- data/spec/integration/boot_spec.rb +18 -0
- data/spec/integration/import_spec.rb +63 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/unit/component_spec.rb +116 -0
- data/spec/unit/container/auto_register_spec.rb +85 -0
- data/spec/unit/container/finalize_spec.rb +85 -0
- data/spec/unit/container/import_spec.rb +70 -0
- data/spec/unit/container/injector_spec.rb +29 -0
- data/spec/unit/container_spec.rb +165 -0
- data/spec/unit/injector_spec.rb +72 -0
- data/spec/unit/loader_spec.rb +64 -0
- metadata +295 -0
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
# this is just for Container.finalize spec, actual finalization code is in the test
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RSpec.describe 'boot files' do
|
2
|
+
subject(:system) { Test::Container }
|
3
|
+
|
4
|
+
before do
|
5
|
+
class Test::Container < Dry::System::Container
|
6
|
+
configure do |config|
|
7
|
+
config.root = SPEC_ROOT.join('fixtures/test').realpath
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'auto-boots dependency of a bootable component' do
|
13
|
+
system.boot!(:client)
|
14
|
+
|
15
|
+
expect(system[:client]).to be_a(Client)
|
16
|
+
expect(system[:client].logger).to be_a(Logger)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
RSpec.describe 'Lazy-booting external deps' do
|
2
|
+
before do
|
3
|
+
module Test
|
4
|
+
class Umbrella < Dry::System::Container
|
5
|
+
configure do |config|
|
6
|
+
config.name = :core
|
7
|
+
config.root = SPEC_ROOT.join('fixtures/umbrella').realpath
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class App < Dry::System::Container
|
12
|
+
configure do |config|
|
13
|
+
config.name = :main
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for 'lazy booted dependency' do
|
20
|
+
it 'lazy boots an external dep provided by top-level container' do
|
21
|
+
expect(user_repo.repo).to be_instance_of(Db::Repo)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'loads an external dep during finalization' do
|
25
|
+
system.finalize!
|
26
|
+
expect(user_repo.repo).to be_instance_of(Db::Repo)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when top-level container provides the dependency' do
|
31
|
+
let(:user_repo) do
|
32
|
+
Class.new { include Test::Import['db.repo'] }.new
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:system) { Test::Umbrella }
|
36
|
+
|
37
|
+
before do
|
38
|
+
module Test
|
39
|
+
Umbrella.import(App)
|
40
|
+
Import = Umbrella.injector
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it_behaves_like 'lazy booted dependency'
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when top-level container provides the dependency through import' do
|
48
|
+
let(:user_repo) do
|
49
|
+
Class.new { include Test::Import['core.db.repo'] }.new
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:system) { Test::App }
|
53
|
+
|
54
|
+
before do
|
55
|
+
module Test
|
56
|
+
App.import(Umbrella)
|
57
|
+
Import = App.injector
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it_behaves_like 'lazy booted dependency'
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
if RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '2.3'
|
4
|
+
require 'codeclimate-test-reporter'
|
5
|
+
CodeClimate::TestReporter.start
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'byebug'
|
10
|
+
rescue LoadError; end
|
11
|
+
|
12
|
+
SPEC_ROOT = Pathname(__FILE__).dirname
|
13
|
+
|
14
|
+
Dir[SPEC_ROOT.join('support/*.rb').to_s].each { |f| require f }
|
15
|
+
Dir[SPEC_ROOT.join('shared/*.rb').to_s].each { |f| require f }
|
16
|
+
|
17
|
+
require 'dry/system/container'
|
18
|
+
|
19
|
+
module TestNamespace
|
20
|
+
def remove_constants
|
21
|
+
constants.each do |name|
|
22
|
+
remove_const(name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.disable_monkey_patching!
|
29
|
+
|
30
|
+
config.before do
|
31
|
+
@load_paths = $LOAD_PATH.dup
|
32
|
+
@loaded_features = $LOADED_FEATURES.dup
|
33
|
+
Object.const_set(:Test, Module.new { |m| m.extend(TestNamespace) })
|
34
|
+
end
|
35
|
+
|
36
|
+
config.after do
|
37
|
+
($LOAD_PATH - @load_paths).each do |path|
|
38
|
+
$LOAD_PATH.delete(path)
|
39
|
+
end
|
40
|
+
($LOADED_FEATURES - @loaded_features).each do |file|
|
41
|
+
$LOADED_FEATURES.delete(file)
|
42
|
+
end
|
43
|
+
|
44
|
+
Test.remove_constants
|
45
|
+
Object.send(:remove_const, :Test)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'dry/system/component'
|
2
|
+
|
3
|
+
RSpec.describe Dry::System::Component do
|
4
|
+
subject(:component) { Dry::System::Component.new(name) }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
it 'caches components' do
|
8
|
+
create = -> {
|
9
|
+
Dry::System::Component.new('foo.bar', namespace: 'foo')
|
10
|
+
}
|
11
|
+
|
12
|
+
expect(create.()).to be(create.())
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises when namespace includes a separator' do
|
16
|
+
expect { Dry::System::Component.new('foo.bar.baz', namespace: 'foo.bar') }
|
17
|
+
.to raise_error(Dry::System::InvalidNamespaceError, /foo\.bar/)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises when identifier/path has duplicated keys' do
|
21
|
+
expect { Dry::System::Component.new('foo.bar.foo', namespace: 'foo') }
|
22
|
+
.to raise_error(Dry::System::InvalidComponentError, /foo\.bar\.foo/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when name is a symbol' do
|
27
|
+
let(:name) { :foo }
|
28
|
+
|
29
|
+
describe '#identifier' do
|
30
|
+
it 'returns qualified identifier' do
|
31
|
+
expect(component.identifier).to eql('foo')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#namespace' do
|
36
|
+
it 'returns configured namespace' do
|
37
|
+
expect(component.namespace).to be(nil)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#root_key' do
|
42
|
+
it 'returns component key' do
|
43
|
+
expect(component.root_key).to be(:foo)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#instance' do
|
48
|
+
it 'builds an instance' do
|
49
|
+
class Foo; end
|
50
|
+
expect(component.instance).to be_instance_of(Foo)
|
51
|
+
Object.send(:remove_const, :Foo)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
shared_examples_for 'a valid component' do
|
57
|
+
describe '#identifier' do
|
58
|
+
it 'returns qualified identifier' do
|
59
|
+
expect(component.identifier).to eql('test.foo')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#file' do
|
64
|
+
it 'returns relative path to the component file' do
|
65
|
+
expect(component.file).to eql('test/foo.rb')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#namespace' do
|
70
|
+
it 'returns configured namespace' do
|
71
|
+
expect(component.namespace).to be(nil)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#namespaced' do
|
76
|
+
it 'returns a namespaced component' do
|
77
|
+
namespaced = component.namespaced(:test)
|
78
|
+
|
79
|
+
expect(namespaced.identifier).to eql('foo')
|
80
|
+
expect(namespaced.path).to eql('test/foo')
|
81
|
+
expect(namespaced.file).to eql('test/foo.rb')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#root_key' do
|
86
|
+
it 'returns component key' do
|
87
|
+
expect(component.root_key).to be(:test)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#instance' do
|
92
|
+
it 'builds an instance' do
|
93
|
+
module Test; class Foo; end; end
|
94
|
+
expect(component.instance).to be_instance_of(Test::Foo)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when name is a path' do
|
100
|
+
let(:name) { 'test/foo' }
|
101
|
+
|
102
|
+
it_behaves_like 'a valid component'
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'when name is a qualified string identifier' do
|
106
|
+
let(:name) { 'test.foo' }
|
107
|
+
|
108
|
+
it_behaves_like 'a valid component'
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when name is a qualified symbol identifier' do
|
112
|
+
let(:name) { :'test.foo' }
|
113
|
+
|
114
|
+
it_behaves_like 'a valid component'
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'dry/system/container'
|
2
|
+
|
3
|
+
RSpec.describe Dry::System::Container, '.auto_register!' do
|
4
|
+
context 'standard loader' do
|
5
|
+
before do
|
6
|
+
class Test::Container < Dry::System::Container
|
7
|
+
configure do |config|
|
8
|
+
config.root = SPEC_ROOT.join('fixtures').realpath
|
9
|
+
end
|
10
|
+
|
11
|
+
load_paths!('components')
|
12
|
+
auto_register!('components')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it { expect(Test::Container['foo']).to be_an_instance_of(Foo) }
|
17
|
+
it { expect(Test::Container['bar']).to be_an_instance_of(Bar) }
|
18
|
+
it { expect(Test::Container['bar.baz']).to be_an_instance_of(Bar::Baz) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with custom registration block' do
|
22
|
+
before do
|
23
|
+
class Test::Container < Dry::System::Container
|
24
|
+
configure do |config|
|
25
|
+
config.root = SPEC_ROOT.join('fixtures').realpath
|
26
|
+
end
|
27
|
+
|
28
|
+
load_paths!('components')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'yields found components' do
|
33
|
+
Test::Container.auto_register!('components') do |component|
|
34
|
+
component.identifier
|
35
|
+
end
|
36
|
+
|
37
|
+
expect(Test::Container['foo']).to eql('foo')
|
38
|
+
expect(Test::Container['bar']).to eql('bar')
|
39
|
+
expect(Test::Container['bar.baz']).to eql('bar.baz')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'standard loader with a default namespace configured' do
|
44
|
+
before do
|
45
|
+
class Test::Container < Dry::System::Container
|
46
|
+
configure do |config|
|
47
|
+
config.root = SPEC_ROOT.join('fixtures').realpath
|
48
|
+
config.default_namespace = 'namespaced'
|
49
|
+
end
|
50
|
+
|
51
|
+
load_paths!('namespaced_components')
|
52
|
+
auto_register!('namespaced_components')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
specify { expect(Test::Container['bar']).to be_a(Namespaced::Bar) }
|
57
|
+
specify { expect(Test::Container['bar'].foo).to be_a(Namespaced::Foo) }
|
58
|
+
specify { expect(Test::Container['foo']).to be_a(Namespaced::Foo) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with a custom loader' do
|
62
|
+
before do
|
63
|
+
class Test::Loader < Dry::System::Loader
|
64
|
+
def call(*args)
|
65
|
+
constant.respond_to?(:call) ? constant : constant.new(*args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Test::Container < Dry::System::Container
|
70
|
+
configure do |config|
|
71
|
+
config.root = SPEC_ROOT.join('fixtures').realpath
|
72
|
+
config.loader = ::Test::Loader
|
73
|
+
end
|
74
|
+
|
75
|
+
load_paths!('components')
|
76
|
+
auto_register!('components')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it { expect(Test::Container['foo']).to be_an_instance_of(Foo) }
|
81
|
+
it { expect(Test::Container['bar']).to eq(Bar) }
|
82
|
+
it { expect(Test::Container['bar'].call).to eq("Welcome to my Moe's Tavern!") }
|
83
|
+
it { expect(Test::Container['bar.baz']).to be_an_instance_of(Bar::Baz) }
|
84
|
+
end
|
85
|
+
end
|