active_dry_deps 0.1.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/.rspec +3 -0
- data/.rubocop.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +169 -0
- data/LICENSE +21 -0
- data/README.md +244 -0
- data/Rakefile +12 -0
- data/lib/active_dry_deps/configuration.rb +13 -0
- data/lib/active_dry_deps/deps.rb +68 -0
- data/lib/active_dry_deps/railtie.rb +17 -0
- data/lib/active_dry_deps/rspec/matchers.rb +33 -0
- data/lib/active_dry_deps/rspec.rb +3 -0
- data/lib/active_dry_deps/stub.rb +27 -0
- data/lib/active_dry_deps/version.rb +7 -0
- data/lib/active_dry_deps.rb +12 -0
- metadata +81 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3e34547d07b50b0e36072bb5123235a49d239207c3c9bb9b24ed83522e4f5d61
|
|
4
|
+
data.tar.gz: 8373575064af5701a718d33326d24e916acca2554ddf86281668cd7fb4f2fd97
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c4db158d948e6ece58fe516c6e3417c15be87c863470cc881ae66dfa7b131e13c9ee5f8883472fe6a3aaf64c46509ce08eefa3c0a3c375f86b466477cf4779eb
|
|
7
|
+
data.tar.gz: fd0255de69130a9a9bebe07e95032d06bf77fe940f37bdaacfd1439afce4972b6ec83db58c154a5a824dbee80ceb73f516cbb89cded78e20ff25c6bda48e9993
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
inherit_gem:
|
|
2
|
+
rubocop-gp:
|
|
3
|
+
- ./config/default.yml
|
|
4
|
+
|
|
5
|
+
Gp/OptArgParameters:
|
|
6
|
+
Enabled: false
|
|
7
|
+
|
|
8
|
+
Gp/ClassOrModuleDeclaredInWrongFile:
|
|
9
|
+
Exclude:
|
|
10
|
+
- lib/active_dry_deps/stub.rb
|
|
11
|
+
|
|
12
|
+
Gp/ModuleMethodInWrongFile:
|
|
13
|
+
Exclude:
|
|
14
|
+
- lib/active_dry_deps/stub.rb
|
|
15
|
+
|
|
16
|
+
AllCops:
|
|
17
|
+
TargetRubyVersion: 3.0
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
gem 'rake', '~> 13.0'
|
|
8
|
+
|
|
9
|
+
gem 'rspec', '~> 3.0'
|
|
10
|
+
|
|
11
|
+
gem 'activesupport'
|
|
12
|
+
gem 'combustion'
|
|
13
|
+
gem 'dry-container'
|
|
14
|
+
gem 'dry-system'
|
|
15
|
+
gem 'rubocop-gp', github: 'corp-gp/rubocop-gp', require: false
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
GIT
|
|
2
|
+
remote: https://github.com/corp-gp/rubocop-gp.git
|
|
3
|
+
revision: 94d07b3b370a199f7d26094d3606d79a0cf92f57
|
|
4
|
+
specs:
|
|
5
|
+
rubocop-gp (0.0.3)
|
|
6
|
+
rubocop
|
|
7
|
+
rubocop-performance
|
|
8
|
+
rubocop-rails
|
|
9
|
+
rubocop-rspec
|
|
10
|
+
|
|
11
|
+
PATH
|
|
12
|
+
remote: .
|
|
13
|
+
specs:
|
|
14
|
+
active_dry_deps (0.1.0)
|
|
15
|
+
dry-configurable
|
|
16
|
+
|
|
17
|
+
GEM
|
|
18
|
+
remote: https://rubygems.org/
|
|
19
|
+
specs:
|
|
20
|
+
actionpack (7.0.7)
|
|
21
|
+
actionview (= 7.0.7)
|
|
22
|
+
activesupport (= 7.0.7)
|
|
23
|
+
rack (~> 2.0, >= 2.2.4)
|
|
24
|
+
rack-test (>= 0.6.3)
|
|
25
|
+
rails-dom-testing (~> 2.0)
|
|
26
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
27
|
+
actionview (7.0.7)
|
|
28
|
+
activesupport (= 7.0.7)
|
|
29
|
+
builder (~> 3.1)
|
|
30
|
+
erubi (~> 1.4)
|
|
31
|
+
rails-dom-testing (~> 2.0)
|
|
32
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
33
|
+
activesupport (7.0.7)
|
|
34
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
35
|
+
i18n (>= 1.6, < 2)
|
|
36
|
+
minitest (>= 5.1)
|
|
37
|
+
tzinfo (~> 2.0)
|
|
38
|
+
ast (2.4.2)
|
|
39
|
+
base64 (0.1.1)
|
|
40
|
+
builder (3.2.4)
|
|
41
|
+
combustion (1.3.7)
|
|
42
|
+
activesupport (>= 3.0.0)
|
|
43
|
+
railties (>= 3.0.0)
|
|
44
|
+
thor (>= 0.14.6)
|
|
45
|
+
concurrent-ruby (1.2.2)
|
|
46
|
+
crass (1.0.6)
|
|
47
|
+
diff-lcs (1.5.0)
|
|
48
|
+
dry-auto_inject (1.0.1)
|
|
49
|
+
dry-core (~> 1.0)
|
|
50
|
+
zeitwerk (~> 2.6)
|
|
51
|
+
dry-configurable (1.1.0)
|
|
52
|
+
dry-core (~> 1.0, < 2)
|
|
53
|
+
zeitwerk (~> 2.6)
|
|
54
|
+
dry-container (0.11.0)
|
|
55
|
+
concurrent-ruby (~> 1.0)
|
|
56
|
+
dry-core (1.0.1)
|
|
57
|
+
concurrent-ruby (~> 1.0)
|
|
58
|
+
zeitwerk (~> 2.6)
|
|
59
|
+
dry-inflector (1.0.0)
|
|
60
|
+
dry-system (1.0.1)
|
|
61
|
+
dry-auto_inject (~> 1.0, < 2)
|
|
62
|
+
dry-configurable (~> 1.0, < 2)
|
|
63
|
+
dry-core (~> 1.0, < 2)
|
|
64
|
+
dry-inflector (~> 1.0, < 2)
|
|
65
|
+
erubi (1.12.0)
|
|
66
|
+
i18n (1.14.1)
|
|
67
|
+
concurrent-ruby (~> 1.0)
|
|
68
|
+
json (2.6.3)
|
|
69
|
+
language_server-protocol (3.17.0.3)
|
|
70
|
+
loofah (2.21.3)
|
|
71
|
+
crass (~> 1.0.2)
|
|
72
|
+
nokogiri (>= 1.12.0)
|
|
73
|
+
method_source (1.0.0)
|
|
74
|
+
minitest (5.19.0)
|
|
75
|
+
nokogiri (1.15.4-arm64-darwin)
|
|
76
|
+
racc (~> 1.4)
|
|
77
|
+
nokogiri (1.15.4-x86_64-linux)
|
|
78
|
+
racc (~> 1.4)
|
|
79
|
+
parallel (1.23.0)
|
|
80
|
+
parser (3.2.2.3)
|
|
81
|
+
ast (~> 2.4.1)
|
|
82
|
+
racc
|
|
83
|
+
racc (1.7.1)
|
|
84
|
+
rack (2.2.8)
|
|
85
|
+
rack-test (2.1.0)
|
|
86
|
+
rack (>= 1.3)
|
|
87
|
+
rails-dom-testing (2.2.0)
|
|
88
|
+
activesupport (>= 5.0.0)
|
|
89
|
+
minitest
|
|
90
|
+
nokogiri (>= 1.6)
|
|
91
|
+
rails-html-sanitizer (1.6.0)
|
|
92
|
+
loofah (~> 2.21)
|
|
93
|
+
nokogiri (~> 1.14)
|
|
94
|
+
railties (7.0.7)
|
|
95
|
+
actionpack (= 7.0.7)
|
|
96
|
+
activesupport (= 7.0.7)
|
|
97
|
+
method_source
|
|
98
|
+
rake (>= 12.2)
|
|
99
|
+
thor (~> 1.0)
|
|
100
|
+
zeitwerk (~> 2.5)
|
|
101
|
+
rainbow (3.1.1)
|
|
102
|
+
rake (13.0.6)
|
|
103
|
+
regexp_parser (2.8.1)
|
|
104
|
+
rexml (3.2.6)
|
|
105
|
+
rspec (3.12.0)
|
|
106
|
+
rspec-core (~> 3.12.0)
|
|
107
|
+
rspec-expectations (~> 3.12.0)
|
|
108
|
+
rspec-mocks (~> 3.12.0)
|
|
109
|
+
rspec-core (3.12.2)
|
|
110
|
+
rspec-support (~> 3.12.0)
|
|
111
|
+
rspec-expectations (3.12.3)
|
|
112
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
113
|
+
rspec-support (~> 3.12.0)
|
|
114
|
+
rspec-mocks (3.12.6)
|
|
115
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
116
|
+
rspec-support (~> 3.12.0)
|
|
117
|
+
rspec-support (3.12.1)
|
|
118
|
+
rubocop (1.56.0)
|
|
119
|
+
base64 (~> 0.1.1)
|
|
120
|
+
json (~> 2.3)
|
|
121
|
+
language_server-protocol (>= 3.17.0)
|
|
122
|
+
parallel (~> 1.10)
|
|
123
|
+
parser (>= 3.2.2.3)
|
|
124
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
125
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
126
|
+
rexml (>= 3.2.5, < 4.0)
|
|
127
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
|
128
|
+
ruby-progressbar (~> 1.7)
|
|
129
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
130
|
+
rubocop-ast (1.29.0)
|
|
131
|
+
parser (>= 3.2.1.0)
|
|
132
|
+
rubocop-capybara (2.18.0)
|
|
133
|
+
rubocop (~> 1.41)
|
|
134
|
+
rubocop-factory_bot (2.23.1)
|
|
135
|
+
rubocop (~> 1.33)
|
|
136
|
+
rubocop-performance (1.19.0)
|
|
137
|
+
rubocop (>= 1.7.0, < 2.0)
|
|
138
|
+
rubocop-ast (>= 0.4.0)
|
|
139
|
+
rubocop-rails (2.20.2)
|
|
140
|
+
activesupport (>= 4.2.0)
|
|
141
|
+
rack (>= 1.1)
|
|
142
|
+
rubocop (>= 1.33.0, < 2.0)
|
|
143
|
+
rubocop-rspec (2.23.2)
|
|
144
|
+
rubocop (~> 1.33)
|
|
145
|
+
rubocop-capybara (~> 2.17)
|
|
146
|
+
rubocop-factory_bot (~> 2.22)
|
|
147
|
+
ruby-progressbar (1.13.0)
|
|
148
|
+
thor (1.2.2)
|
|
149
|
+
tzinfo (2.0.6)
|
|
150
|
+
concurrent-ruby (~> 1.0)
|
|
151
|
+
unicode-display_width (2.4.2)
|
|
152
|
+
zeitwerk (2.6.11)
|
|
153
|
+
|
|
154
|
+
PLATFORMS
|
|
155
|
+
arm64-darwin-21
|
|
156
|
+
x86_64-linux
|
|
157
|
+
|
|
158
|
+
DEPENDENCIES
|
|
159
|
+
active_dry_deps!
|
|
160
|
+
activesupport
|
|
161
|
+
combustion
|
|
162
|
+
dry-container
|
|
163
|
+
dry-system
|
|
164
|
+
rake (~> 13.0)
|
|
165
|
+
rspec (~> 3.0)
|
|
166
|
+
rubocop-gp!
|
|
167
|
+
|
|
168
|
+
BUNDLED WITH
|
|
169
|
+
2.4.6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 corp-gp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
## Installation
|
|
2
|
+
|
|
3
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
4
|
+
|
|
5
|
+
$ bundle add active_dry_deps
|
|
6
|
+
|
|
7
|
+
## Dependency Injection
|
|
8
|
+
|
|
9
|
+
Dependency injection helps to break explicit dependencies between objects making
|
|
10
|
+
it much easier to maintain a [single
|
|
11
|
+
responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle)
|
|
12
|
+
and reduce [coupling](https://en.wikipedia.org/wiki/Coupling_(computer_programming))
|
|
13
|
+
in our class designs. This leads to more testable code and code that is more
|
|
14
|
+
resilient to change.
|
|
15
|
+
|
|
16
|
+
For a deeper background on Dependency Injection consider the
|
|
17
|
+
[Wikipedia](https://en.wikipedia.org/wiki/Dependency_injection) article on the
|
|
18
|
+
subject.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Basic
|
|
23
|
+
Under the hood `active_dry_deps` uses a container like [dry-container](https://dry-rb.org/gems/dry-container) and convert key to underscore for fetch from container.
|
|
24
|
+
For auto-registration dependencies use [dry-system](https://dry-rb.org/gems/dry-system/).
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
MyApp::Container.register('warehouse.create_departure_service', Class.new { def self.call = 'failure' })
|
|
28
|
+
include Deps['Warehouse::CreateDepartureService.call']
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`Deps['Warehouse::CreateDepartureService.call']` this notation is familiar to Ruby developers, helps to find code in the project, and simplifies the migration from constants in code to defining dependencies.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
class CreateOrderService < ServiceObject
|
|
35
|
+
|
|
36
|
+
include Deps[
|
|
37
|
+
'Warehouse::CreateDepartureService.call',
|
|
38
|
+
'Warehouse::ReserveJob.perform_later',
|
|
39
|
+
'OrderMailer',
|
|
40
|
+
'redis',
|
|
41
|
+
track: 'StatsApi.message',
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
def call(params)
|
|
45
|
+
order = Order.create(params)
|
|
46
|
+
|
|
47
|
+
ReserveJob(order)
|
|
48
|
+
track(order.id, order.created_at)
|
|
49
|
+
|
|
50
|
+
redis.with do |conn|
|
|
51
|
+
conn.incr('order_count')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
OrderMailer().with(user: user).deliver_later
|
|
55
|
+
|
|
56
|
+
CreateDepartureService(order.slice(:id, :departure_at))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
describe 'CreateOrderService' do
|
|
64
|
+
it 'success create order' do
|
|
65
|
+
service = described_class.new(user: create(:user), zip_code: 67_345)
|
|
66
|
+
expect(service).to deps(CreateDepartureService: double(success?: true), ReserveJob: spy, track: spy)
|
|
67
|
+
|
|
68
|
+
expect(service.call.success?).to be true
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Import methods
|
|
75
|
+
You can inject any method from object in your container
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
MyApp::Container.register(:str, 'str')
|
|
79
|
+
MyApp::Container.register(:service, Module.new { def self.success? = true } )
|
|
80
|
+
|
|
81
|
+
include Deps['str.reverse', 'service.success?']
|
|
82
|
+
reverse # => "rts"
|
|
83
|
+
success? # => true
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Import callable methods
|
|
87
|
+
By default, when `call` or `perform_later` methods are imported, the name of the dependency is taken from the name of the constant:
|
|
88
|
+
```ruby
|
|
89
|
+
include Deps[
|
|
90
|
+
'Warehouse::CreateDepartureService.call', # callable
|
|
91
|
+
'Warehouse::ReserveJob.perform_later', # callable
|
|
92
|
+
'Warehouse::ReserveJob.perform_now',
|
|
93
|
+
'Warehouse::ProductActivateQuery',
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
# use as
|
|
97
|
+
CreateDepartureService()
|
|
98
|
+
ReserveJob()
|
|
99
|
+
perform_now
|
|
100
|
+
ProductActivateQuery().run
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Recommends using prefixes (`Service`, `Job`, `Query`) in the name of the constant for easy reading of the dependency type.
|
|
104
|
+
|
|
105
|
+
### Aliases
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
include Deps[string: 'str.reverse', m: 'module']
|
|
109
|
+
string # => "rts"
|
|
110
|
+
m # => "success"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Tests (Rspec)
|
|
114
|
+
#### deps
|
|
115
|
+
gem adds rspec matcher for stub dependency, put `require 'active_dry_deps/rspec'` to rspec setup
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
GpApp::Container.register('order.dependency', Class.new { def self.call = 'failure' })
|
|
119
|
+
|
|
120
|
+
let(:service_klass) do
|
|
121
|
+
Class.new do
|
|
122
|
+
include Deps['Order::Dependency.call']
|
|
123
|
+
|
|
124
|
+
def call = Dependency()
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'failure' do
|
|
129
|
+
expect(service_klass.new.call).to be 'failure'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'success' do
|
|
133
|
+
service = service_klass.new
|
|
134
|
+
expect(service).to deps(Dependency: 'success')
|
|
135
|
+
|
|
136
|
+
expect(service.call).to be 'success'
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### stub, unstub
|
|
141
|
+
```ruby
|
|
142
|
+
it 'stub' do
|
|
143
|
+
Deps.stub('Order::Dependency', double(call: 'success'))
|
|
144
|
+
expect(service_klass.new.call).to be 'success'
|
|
145
|
+
|
|
146
|
+
Deps.unstub('Order::Dependency') # or simple Deps.unstub for unsub all keys
|
|
147
|
+
expect(service_klass.new.call).to be 'failure'
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
## Configuration
|
|
151
|
+
gem auto-configuring, but you can override settings
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
ActiveDryDeps.config.container = 'MyApp::Container'
|
|
155
|
+
ActiveDryDeps.config.inflector = ActiveSupport::Inflector
|
|
156
|
+
ActiveDryDeps.config.inject_global_constant = 'Deps'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Recommended container setup with [dry-system](https://dry-rb.org/gems/dry-system/) for Rails
|
|
160
|
+
|
|
161
|
+
`config/initializers/system.rb`
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
require 'dry/system/container'
|
|
165
|
+
|
|
166
|
+
module GpApp
|
|
167
|
+
class ContainerRailtie < Rails::Railtie
|
|
168
|
+
|
|
169
|
+
LOADER =
|
|
170
|
+
Class.new(Dry::System::Loader) do
|
|
171
|
+
def self.call(component, *args)
|
|
172
|
+
constant = self.constant(component)
|
|
173
|
+
|
|
174
|
+
if singleton?(constant)
|
|
175
|
+
constant.instance(*args)
|
|
176
|
+
else
|
|
177
|
+
constant # constant.new(*args) - THIS LINE REWRITED from Dry::System::Loader
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# https://api.rubyonrails.org/classes/Rails/Railtie.html
|
|
183
|
+
# Add a to_prepare block which is executed once in production
|
|
184
|
+
# and before each request in development.
|
|
185
|
+
config.to_prepare do
|
|
186
|
+
ContainerRailtie.finalize
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def finalize
|
|
190
|
+
set_or_reload(:Container, create_container)
|
|
191
|
+
Dry::System.register_provider_sources(Pathname(__dir__).join('../system/providers').realpath)
|
|
192
|
+
create_container.finalize!(freeze: !(::Rails.env.test? || ::Rails.env.development?))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def create_container
|
|
196
|
+
Class.new(Dry::System::Container) do
|
|
197
|
+
configure do |config|
|
|
198
|
+
config.inflector = ActiveSupport::Inflector
|
|
199
|
+
config.root = Rails.root.join('app')
|
|
200
|
+
|
|
201
|
+
%w[domains jobs queries services mailers].each do |dir_name|
|
|
202
|
+
config.component_dirs.add dir_name do |dir|
|
|
203
|
+
dir.loader = LOADER
|
|
204
|
+
dir.memoize = true
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
config.component_dirs.add '../lib' do |dir|
|
|
209
|
+
dir.loader = LOADER
|
|
210
|
+
dir.memoize = true
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def set_or_reload(const_name, const)
|
|
217
|
+
remove_constant(const_name)
|
|
218
|
+
GpApp.const_set(const_name, const)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def remove_constant(const_name)
|
|
222
|
+
if GpApp.const_defined?(const_name, false)
|
|
223
|
+
GpApp.__send__(:remove_const, const_name)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
235
|
+
|
|
236
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
237
|
+
|
|
238
|
+
## Contributing
|
|
239
|
+
|
|
240
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/corp-gp/active_dry_deps.
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry-configurable'
|
|
4
|
+
|
|
5
|
+
module ActiveDryDeps
|
|
6
|
+
|
|
7
|
+
extend Dry::Configurable
|
|
8
|
+
|
|
9
|
+
setting :container
|
|
10
|
+
setting :inflector, default: ActiveSupport::Inflector
|
|
11
|
+
setting :inject_global_constant, default: 'Deps'
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveDryDeps
|
|
4
|
+
module Deps
|
|
5
|
+
|
|
6
|
+
VALID_NAME = /([a-zA-Z_0-9]*)$/
|
|
7
|
+
METHODS_AS_KLASS = %w[perform_later call].freeze
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# include Deps[routes_admin: 'Lib::Routes.admin'] use as `routes_admin`
|
|
12
|
+
# include Deps['Lib::Routes.admin'] use as `admin`
|
|
13
|
+
# include Deps['Lib::Routes'] use as `Routes()`
|
|
14
|
+
# include Deps['OrderService::Recalculate.call'] use as `Recalculate()`
|
|
15
|
+
def [](*keys, **aliases)
|
|
16
|
+
str_methods = +''
|
|
17
|
+
|
|
18
|
+
keys.each { |resolver| str_methods << str_method(resolver, nil) }
|
|
19
|
+
aliases.each { |alias_method, resolver| str_methods << str_method(resolver, alias_method) }
|
|
20
|
+
|
|
21
|
+
m = Module.new
|
|
22
|
+
m.module_eval(str_methods)
|
|
23
|
+
m
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private def str_method(resolve, alias_method)
|
|
27
|
+
resolve_klass, extract_method = resolve.split('.')
|
|
28
|
+
|
|
29
|
+
alias_method ||=
|
|
30
|
+
if extract_method && METHODS_AS_KLASS.exclude?(extract_method)
|
|
31
|
+
extract_method
|
|
32
|
+
else
|
|
33
|
+
resolve_klass.split('::').last
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if alias_method && !VALID_NAME.match?(alias_method.to_s)
|
|
37
|
+
raise DependencyNameInvalid, "name +#{alias_method}+ is not a valid Ruby identifier"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
key = ActiveDryDeps.config.inflector.underscore(resolve_klass).tr('/', '.')
|
|
41
|
+
|
|
42
|
+
if extract_method
|
|
43
|
+
%(def #{alias_method}(...); ::#{ActiveDryDeps.config.container}['#{key}'].#{extract_method}(...) end\n)
|
|
44
|
+
else
|
|
45
|
+
%(def #{alias_method}; ::#{ActiveDryDeps.config.container}['#{key}'] end\n)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def resolve_key(key)
|
|
50
|
+
if key.include?('::')
|
|
51
|
+
ActiveDryDeps.config.inflector.underscore(key).tr('/', '.')
|
|
52
|
+
else
|
|
53
|
+
key
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
instance_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
58
|
+
# def resolve(key)
|
|
59
|
+
# ::MyApp::Container[resolve_key(key)]
|
|
60
|
+
# end
|
|
61
|
+
|
|
62
|
+
def resolve(key)
|
|
63
|
+
::#{ActiveDryDeps.config.container}[resolve_key(key)]
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveDryDeps
|
|
4
|
+
class Railtie < ::Rails::Railtie
|
|
5
|
+
|
|
6
|
+
config.before_initialize do
|
|
7
|
+
app_namespace = ::Rails.application.class.to_s.split('::').first
|
|
8
|
+
ActiveDryDeps.config.container ||= "#{app_namespace}::Container"
|
|
9
|
+
|
|
10
|
+
require_relative 'deps'
|
|
11
|
+
|
|
12
|
+
Object.const_set(ActiveDryDeps.config.inject_global_constant, ::ActiveDryDeps::Deps)
|
|
13
|
+
ActiveDryDeps.config.finalize!(freeze_values: true)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec::Matchers.define :deps do |*methods|
|
|
4
|
+
include RSpec::Mocks::Matchers::Matcher
|
|
5
|
+
|
|
6
|
+
# expect(described_instance).to deps(send_blanks: spy)
|
|
7
|
+
match do |stubbed_object|
|
|
8
|
+
methods[0].each do |method, returned_value|
|
|
9
|
+
expect(stubbed_object).to receive(method).and_return(returned_value)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# expect(described_instance).not_to deps(:send_blanks)
|
|
14
|
+
match_when_negated do |stubbed_object|
|
|
15
|
+
methods.each do |method|
|
|
16
|
+
expect(stubbed_object).not_to receive(method)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# allow(described_instance).to deps(send_blanks: spy)
|
|
21
|
+
def setup_allowance(subject)
|
|
22
|
+
proxy_subject = proxy_on(subject)
|
|
23
|
+
|
|
24
|
+
expected.each do |method, returned_value|
|
|
25
|
+
proxy_on(returned_value).add_simple_stub(:call, returned_value.call)
|
|
26
|
+
proxy_subject.add_simple_stub(method, returned_value)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private def proxy_on(subject)
|
|
31
|
+
RSpec::Mocks.space.proxy_for(subject)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveDryDeps
|
|
4
|
+
|
|
5
|
+
module Stub
|
|
6
|
+
|
|
7
|
+
CONTAINER_CONST = Object.const_get(ActiveDryDeps.config.container)
|
|
8
|
+
|
|
9
|
+
def stub(path, ...)
|
|
10
|
+
CONTAINER_CONST.stub(Deps.resolve_key(path), ...)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def unstub(*keys)
|
|
14
|
+
CONTAINER_CONST.unstub(*keys.map { resolve_key(_1) })
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module Deps
|
|
20
|
+
|
|
21
|
+
def self.enable_stubs!
|
|
22
|
+
extend Stub
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'active_dry_deps/version'
|
|
4
|
+
require_relative 'active_dry_deps/configuration'
|
|
5
|
+
require_relative 'active_dry_deps/railtie'
|
|
6
|
+
|
|
7
|
+
module ActiveDryDeps
|
|
8
|
+
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
class DependencyNameInvalid < Error; end
|
|
11
|
+
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: active_dry_deps
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ermolaev Andrey
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: dry-configurable
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
description: |-
|
|
28
|
+
ActiveDryDeps not modify constructor and support Dependency Injection for modules.
|
|
29
|
+
Also you can import method from any object in your container.
|
|
30
|
+
Adding extra dependencies is easy and improve readability your code.
|
|
31
|
+
email:
|
|
32
|
+
- andruhafirst@yandex.ru
|
|
33
|
+
executables: []
|
|
34
|
+
extensions: []
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- ".rspec"
|
|
38
|
+
- ".rubocop.yml"
|
|
39
|
+
- CHANGELOG.md
|
|
40
|
+
- Gemfile
|
|
41
|
+
- Gemfile.lock
|
|
42
|
+
- LICENSE
|
|
43
|
+
- README.md
|
|
44
|
+
- Rakefile
|
|
45
|
+
- lib/active_dry_deps.rb
|
|
46
|
+
- lib/active_dry_deps/configuration.rb
|
|
47
|
+
- lib/active_dry_deps/deps.rb
|
|
48
|
+
- lib/active_dry_deps/railtie.rb
|
|
49
|
+
- lib/active_dry_deps/rspec.rb
|
|
50
|
+
- lib/active_dry_deps/rspec/matchers.rb
|
|
51
|
+
- lib/active_dry_deps/stub.rb
|
|
52
|
+
- lib/active_dry_deps/version.rb
|
|
53
|
+
homepage: https://github.com/corp-gp/active_dry_deps
|
|
54
|
+
licenses:
|
|
55
|
+
- MIT
|
|
56
|
+
metadata:
|
|
57
|
+
allowed_push_host: https://rubygems.org
|
|
58
|
+
homepage_uri: https://github.com/corp-gp/active_dry_deps
|
|
59
|
+
source_code_uri: https://github.com/corp-gp/active_dry_deps
|
|
60
|
+
changelog_uri: https://github.com/corp-gp/active_dry_deps/CHANGELOG.md
|
|
61
|
+
rubygems_mfa_required: 'true'
|
|
62
|
+
post_install_message:
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: 3.0.0
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.3.17
|
|
78
|
+
signing_key:
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: Dependency injection and resolution support for classes and modules.
|
|
81
|
+
test_files: []
|