dry-system 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|