active_dry_deps 0.2.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5c51fbfc9260e1ca7a34f4c141e2932c60d4781b0a1112296def1a7f76840b8
4
- data.tar.gz: f28c2ebb57b5b207cbca35fe02458d55bdf23ac1fa3ebddc5a7791cd7d123bcf
3
+ metadata.gz: f059375622e4eff80734dc0f2c7a3cda685e3c835fdde8eedb1536ec6d5ef9c0
4
+ data.tar.gz: bc19525a83416b49a359f534dd364d7cd05fb96f298d5c32aa685a4b22577841
5
5
  SHA512:
6
- metadata.gz: 05174e3d2c0f19faaae4c3237e9340ba1bb54eb784157fe3b06374a4529aab3bfb16b4707c6c412e75c36c9fd9e9eba6726f3d24a38c290c9afd0b8a70ef0e89
7
- data.tar.gz: 85552e6a601f5ac0ab8057a6580b3ceb1126d705eea56e221ab703ca8a7b67966fe8f805a123fbeb8fbf932aaea3e195723adec22311feb643723e3b5c24625f
6
+ metadata.gz: 4f2e26b3eb41f24489f2760c78103b51a87bbf77bd9632ed75010aaab62748705e1b2bbe67470bcd7c36fc1c9c98710af71f9aa59a33d5490c928dcb3a0699bf
7
+ data.tar.gz: 9394e4cbc21ba3b1b78f7488a683432412b3c133e5ee508e466858f0d8957bfe293c72e0669014acc54ffdbc850463f257d83932a172ae64261927a911c2f893
data/.rubocop.yml CHANGED
@@ -14,4 +14,10 @@ Gp/ModuleMethodInWrongFile:
14
14
  - lib/active_dry_deps/stub.rb
15
15
 
16
16
  AllCops:
17
- TargetRubyVersion: 3.0
17
+ TargetRubyVersion: 3.0
18
+
19
+ Layout/EmptyLinesAroundClassBody:
20
+ Enabled: false
21
+
22
+ Layout/EmptyLinesAroundModuleBody:
23
+ Enabled: false
data/Gemfile CHANGED
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem 'rake', '~> 13.0'
7
+ gem "rake", "~> 13.0"
8
8
 
9
- gem 'rspec', '~> 3.0'
9
+ gem "rspec", "~> 3.0"
10
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
11
+ gem "activesupport", "~> 7.2"
12
+ gem "combustion"
13
+ gem "railties", "~> 7.2"
14
+ gem "rubocop-gp", github: "corp-gp/rubocop-gp", require: false
data/Gemfile.lock CHANGED
@@ -1,166 +1,200 @@
1
1
  GIT
2
2
  remote: https://github.com/corp-gp/rubocop-gp.git
3
- revision: 94d07b3b370a199f7d26094d3606d79a0cf92f57
3
+ revision: 867f7e1351c3730897cacdc20ce2d727604de245
4
4
  specs:
5
- rubocop-gp (0.0.3)
5
+ rubocop-gp (0.0.4)
6
6
  rubocop
7
+ rubocop-capybara
8
+ rubocop-factory_bot
7
9
  rubocop-performance
8
10
  rubocop-rails
9
11
  rubocop-rspec
12
+ rubocop-rspec_rails
10
13
 
11
14
  PATH
12
15
  remote: .
13
16
  specs:
14
- active_dry_deps (0.1.0)
17
+ active_dry_deps (1.0.0)
15
18
  dry-configurable
16
19
 
17
20
  GEM
18
21
  remote: https://rubygems.org/
19
22
  specs:
20
- actionpack (7.0.7)
21
- actionview (= 7.0.7)
22
- activesupport (= 7.0.7)
23
- rack (~> 2.0, >= 2.2.4)
23
+ actionpack (7.2.2.1)
24
+ actionview (= 7.2.2.1)
25
+ activesupport (= 7.2.2.1)
26
+ nokogiri (>= 1.8.5)
27
+ racc
28
+ rack (>= 2.2.4, < 3.2)
29
+ rack-session (>= 1.0.1)
24
30
  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)
31
+ rails-dom-testing (~> 2.2)
32
+ rails-html-sanitizer (~> 1.6)
33
+ useragent (~> 0.16)
34
+ actionview (7.2.2.1)
35
+ activesupport (= 7.2.2.1)
29
36
  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)
37
+ erubi (~> 1.11)
38
+ rails-dom-testing (~> 2.2)
39
+ rails-html-sanitizer (~> 1.6)
40
+ activesupport (7.2.2.1)
41
+ base64
42
+ benchmark (>= 0.3)
43
+ bigdecimal
44
+ concurrent-ruby (~> 1.0, >= 1.3.1)
45
+ connection_pool (>= 2.2.5)
46
+ drb
35
47
  i18n (>= 1.6, < 2)
48
+ logger (>= 1.4.2)
36
49
  minitest (>= 5.1)
37
- tzinfo (~> 2.0)
50
+ securerandom (>= 0.3)
51
+ tzinfo (~> 2.0, >= 2.0.5)
38
52
  ast (2.4.2)
39
- base64 (0.1.1)
40
- builder (3.2.4)
41
- combustion (1.3.7)
53
+ base64 (0.2.0)
54
+ benchmark (0.4.0)
55
+ bigdecimal (3.1.9)
56
+ builder (3.3.0)
57
+ combustion (1.5.0)
42
58
  activesupport (>= 3.0.0)
43
59
  railties (>= 3.0.0)
44
60
  thor (>= 0.14.6)
45
- concurrent-ruby (1.2.2)
61
+ concurrent-ruby (1.3.5)
62
+ connection_pool (2.5.0)
46
63
  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)
64
+ date (3.4.1)
65
+ diff-lcs (1.5.1)
66
+ drb (2.2.1)
67
+ dry-configurable (1.3.0)
68
+ dry-core (~> 1.1)
53
69
  zeitwerk (~> 2.6)
54
- dry-container (0.11.0)
55
- concurrent-ruby (~> 1.0)
56
- dry-core (1.0.1)
70
+ dry-core (1.1.0)
57
71
  concurrent-ruby (~> 1.0)
72
+ logger
58
73
  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)
74
+ erubi (1.13.1)
75
+ i18n (1.14.7)
67
76
  concurrent-ruby (~> 1.0)
68
- json (2.6.3)
69
- language_server-protocol (3.17.0.3)
70
- loofah (2.21.3)
77
+ io-console (0.8.0)
78
+ irb (1.15.1)
79
+ pp (>= 0.6.0)
80
+ rdoc (>= 4.0.0)
81
+ reline (>= 0.4.2)
82
+ json (2.9.1)
83
+ language_server-protocol (3.17.0.4)
84
+ logger (1.6.5)
85
+ loofah (2.24.0)
71
86
  crass (~> 1.0.2)
72
87
  nokogiri (>= 1.12.0)
73
- method_source (1.0.0)
74
- minitest (5.19.0)
75
- nokogiri (1.15.4-arm64-darwin)
88
+ minitest (5.25.4)
89
+ nokogiri (1.18.2-arm64-darwin)
76
90
  racc (~> 1.4)
77
- nokogiri (1.15.4-x86_64-linux)
91
+ nokogiri (1.18.2-x86_64-linux-gnu)
78
92
  racc (~> 1.4)
79
- parallel (1.23.0)
80
- parser (3.2.2.3)
93
+ parallel (1.26.3)
94
+ parser (3.3.7.0)
81
95
  ast (~> 2.4.1)
82
96
  racc
83
- racc (1.7.1)
84
- rack (2.2.8)
85
- rack-test (2.1.0)
97
+ pp (0.6.2)
98
+ prettyprint
99
+ prettyprint (0.2.0)
100
+ psych (5.2.3)
101
+ date
102
+ stringio
103
+ racc (1.8.1)
104
+ rack (3.1.8)
105
+ rack-session (2.1.0)
106
+ base64 (>= 0.1.0)
107
+ rack (>= 3.0.0)
108
+ rack-test (2.2.0)
86
109
  rack (>= 1.3)
110
+ rackup (2.2.1)
111
+ rack (>= 3)
87
112
  rails-dom-testing (2.2.0)
88
113
  activesupport (>= 5.0.0)
89
114
  minitest
90
115
  nokogiri (>= 1.6)
91
- rails-html-sanitizer (1.6.0)
116
+ rails-html-sanitizer (1.6.2)
92
117
  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
118
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
119
+ railties (7.2.2.1)
120
+ actionpack (= 7.2.2.1)
121
+ activesupport (= 7.2.2.1)
122
+ irb (~> 1.13)
123
+ rackup (>= 1.0.0)
98
124
  rake (>= 12.2)
99
- thor (~> 1.0)
100
- zeitwerk (~> 2.5)
125
+ thor (~> 1.0, >= 1.2.2)
126
+ zeitwerk (~> 2.6)
101
127
  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)
128
+ rake (13.2.1)
129
+ rdoc (6.11.0)
130
+ psych (>= 4.0.0)
131
+ regexp_parser (2.10.0)
132
+ reline (0.6.0)
133
+ io-console (~> 0.5)
134
+ rspec (3.13.0)
135
+ rspec-core (~> 3.13.0)
136
+ rspec-expectations (~> 3.13.0)
137
+ rspec-mocks (~> 3.13.0)
138
+ rspec-core (3.13.2)
139
+ rspec-support (~> 3.13.0)
140
+ rspec-expectations (3.13.3)
112
141
  diff-lcs (>= 1.2.0, < 2.0)
113
- rspec-support (~> 3.12.0)
114
- rspec-mocks (3.12.6)
142
+ rspec-support (~> 3.13.0)
143
+ rspec-mocks (3.13.2)
115
144
  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)
145
+ rspec-support (~> 3.13.0)
146
+ rspec-support (3.13.2)
147
+ rubocop (1.71.0)
120
148
  json (~> 2.3)
121
149
  language_server-protocol (>= 3.17.0)
122
150
  parallel (~> 1.10)
123
- parser (>= 3.2.2.3)
151
+ parser (>= 3.3.0.2)
124
152
  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)
153
+ regexp_parser (>= 2.9.3, < 3.0)
154
+ rubocop-ast (>= 1.36.2, < 2.0)
128
155
  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)
156
+ unicode-display_width (>= 2.4.0, < 4.0)
157
+ rubocop-ast (1.38.0)
158
+ parser (>= 3.3.1.0)
159
+ rubocop-capybara (2.21.0)
133
160
  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)
161
+ rubocop-factory_bot (2.26.1)
162
+ rubocop (~> 1.61)
163
+ rubocop-performance (1.23.1)
164
+ rubocop (>= 1.48.1, < 2.0)
165
+ rubocop-ast (>= 1.31.1, < 2.0)
166
+ rubocop-rails (2.29.1)
140
167
  activesupport (>= 4.2.0)
141
168
  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)
169
+ rubocop (>= 1.52.0, < 2.0)
170
+ rubocop-ast (>= 1.31.1, < 2.0)
171
+ rubocop-rspec (3.4.0)
172
+ rubocop (~> 1.61)
173
+ rubocop-rspec_rails (2.30.0)
174
+ rubocop (~> 1.61)
175
+ rubocop-rspec (~> 3, >= 3.0.1)
147
176
  ruby-progressbar (1.13.0)
148
- thor (1.2.2)
177
+ securerandom (0.4.1)
178
+ stringio (3.1.2)
179
+ thor (1.3.2)
149
180
  tzinfo (2.0.6)
150
181
  concurrent-ruby (~> 1.0)
151
- unicode-display_width (2.4.2)
152
- zeitwerk (2.6.11)
182
+ unicode-display_width (3.1.4)
183
+ unicode-emoji (~> 4.0, >= 4.0.4)
184
+ unicode-emoji (4.0.4)
185
+ useragent (0.16.11)
186
+ zeitwerk (2.6.18)
153
187
 
154
188
  PLATFORMS
155
189
  arm64-darwin-21
190
+ arm64-darwin-24
156
191
  x86_64-linux
157
192
 
158
193
  DEPENDENCIES
159
194
  active_dry_deps!
160
- activesupport
195
+ activesupport (~> 7.2)
161
196
  combustion
162
- dry-container
163
- dry-system
197
+ railties (~> 7.2)
164
198
  rake (~> 13.0)
165
199
  rspec (~> 3.0)
166
200
  rubocop-gp!
data/README.md CHANGED
@@ -20,19 +20,10 @@ subject.
20
20
  ## Usage
21
21
 
22
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/).
23
+ Dependencies are injected by listing their names: `Deps['Warehouse::CreateDepartureService.call']`. This notation is familiar to Ruby developers. It helps to find code in the project (compares to abstract container keys), and simplifies the migration from constants in code to defining dependencies
25
24
 
26
25
  ```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
-
26
+ class CreateOrderService
36
27
  include Deps[
37
28
  'Warehouse::CreateDepartureService.call',
38
29
  'Warehouse::ReserveJob.perform_later',
@@ -59,8 +50,10 @@ class CreateOrderService < ServiceObject
59
50
  end
60
51
  ```
61
52
 
53
+ Rspec matcher `deps` allows to isolate dependencies in tests. It simplifies unit testing
54
+
62
55
  ```ruby
63
- describe 'CreateOrderService' do
56
+ Rspec.describe CreateOrderService do
64
57
  it 'success create order' do
65
58
  service = described_class.new(user: create(:user), zip_code: 67_345)
66
59
  expect(service).to deps(CreateDepartureService: double(success?: true), ReserveJob: spy, track: spy)
@@ -71,51 +64,93 @@ end
71
64
 
72
65
  ```
73
66
 
67
+ #### Register custom dependency
68
+ You can define an arbitrary object as a dependency with method `Deps.register`
69
+
70
+ ```ruby
71
+ class OrderMailer
72
+ def send_mail = 'email sent'
73
+ end
74
+
75
+ Deps.register('mailer') { OrderMailer.new }
76
+
77
+ class CreateOrderService
78
+ include Deps['mailer']
79
+
80
+ def call
81
+ mailer.send_mail
82
+ end
83
+ end
84
+
85
+ CreateOrderService.new.call # => email sent
86
+ ```
87
+
74
88
  ### Import methods
75
- You can inject any method from object in your container
89
+ You can inject any method from constant as dependency
76
90
 
77
91
  ```ruby
78
- MyApp::Container.register(:str, 'str')
79
- MyApp::Container.register(:service, Module.new { def self.success? = true } )
92
+ class OrderRepository
93
+ def self.overdue_order_ids = [1, 2, 3]
94
+ end
95
+
96
+ include Deps['OrderRepository.overdue_order_ids']
80
97
 
81
- include Deps['str.reverse', 'service.success?']
82
- reverse # => "rts"
83
- success? # => true
98
+ overdue_order_ids # => [1, 2, 3]
84
99
  ```
85
100
 
86
101
  ### 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
- ]
102
+ There is a special convention for naming some methods. By default, when `call` or `perform_later` methods are imported, the name of the dependency is taken from the name of the constant, not by method name
95
103
 
96
- # use as
97
- CreateDepartureService()
98
- ReserveJob()
99
- perform_now
100
- ProductActivateQuery().run
104
+ ```ruby
105
+ include Deps[
106
+ 'Warehouse::CreateDepartureService.call', # callable
107
+ 'Warehouse::ReserveJob.perform_later', # callable
108
+ 'Warehouse::ReserveJob.perform_now',
109
+ 'Warehouse::ProductActivateQuery',
110
+ ]
111
+
112
+ # use as
113
+ CreateDepartureService() # Warehouse::CreateDepartureService.call
114
+ ReserveJob() # Warehouse::ReserveJob.perform_later
115
+ perform_now # Warehouse::ReserveJob.perform_now
116
+ ProductActivateQuery().run # Warehouse::ProductActivateQuery.run
101
117
  ```
102
118
 
103
- Recommends using prefixes (`Service`, `Job`, `Query`) in the name of the constant for easy reading of the dependency type.
119
+ Recommends using suffixes (`Service`, `Job`, `Query`) in the name of the constant for easy reading of the dependency type.
104
120
 
105
121
  ### Aliases
122
+ Dependency can have an alias for more intuitive access. Keep in mind that dependencies with aliases should go at the end of the list (this is Ruby feature)
106
123
 
107
124
  ```ruby
108
- include Deps[string: 'str.reverse', m: 'module']
109
- string # => "rts"
110
- m # => "success"
125
+ include Deps['OrderMailer', product_repo: 'Warehouse::ProductRepository']
126
+
127
+ product_repo # Warehouse::ProductRepository
128
+ OrderMailer() # OrderMailer
111
129
  ```
112
130
 
113
131
  ### Tests (Rspec)
132
+
133
+ #### setup
134
+ For dependency testing, add the following to Rspec setup
135
+
136
+ # spec/rails_helper.rb
137
+ ```ruby
138
+ # ...
139
+ require 'active_dry_deps/rspec'
140
+ require 'active_dry_deps/stub'
141
+
142
+ Deps.enable_stubs!
143
+
144
+ RSpec.configure do |config|
145
+ config.after(:each) { Deps.reset }
146
+ end
147
+ ```
148
+
114
149
  #### deps
115
- gem adds rspec matcher for stub dependency, put `require 'active_dry_deps/rspec'` to rspec setup
150
+ The gem adds Rspec matcher `deps` for stub dependency
116
151
 
117
152
  ```ruby
118
- GpApp::Container.register('order.dependency', Class.new { def self.call = 'failure' })
153
+ Deps.register('order.dependency', Class.new { def self.call = 'failure' })
119
154
 
120
155
  let(:service_klass) do
121
156
  Class.new do
@@ -137,96 +172,51 @@ it 'success' do
137
172
  end
138
173
  ```
139
174
 
140
- #### stub, unstub
175
+ #### stub, unstub, reset
176
+ Dependency can be stubbed at the container level. This allows to override all calls to it
177
+
141
178
  ```ruby
142
179
  it 'stub' do
143
180
  Deps.stub('Order::Dependency', double(call: 'success'))
144
181
  expect(service_klass.new.call).to be 'success'
145
182
 
146
- Deps.unstub('Order::Dependency') # or simple Deps.unstub for unsub all keys
183
+ Deps.unstub('Order::Dependency') # or Deps.reset for unsub all keys
147
184
  expect(service_klass.new.call).to be 'failure'
148
185
  end
149
186
  ```
150
- ## Configuration
151
- gem auto-configuring, but you can override settings
152
187
 
188
+ #### global_stub, global_unstub
189
+ Sometimes it is necessary to stub dependencies for all or almost all tests
190
+
191
+ # spec/rails_helper.rb
153
192
  ```ruby
154
- ActiveDryDeps.config.container = 'MyApp::Container'
155
- ActiveDryDeps.config.inflector = ActiveSupport::Inflector
156
- ActiveDryDeps.config.inject_global_constant = 'Deps'
157
- ```
193
+ # ...
194
+ Deps.enable_stubs!
158
195
 
159
- ### Recommended container setup with [dry-system](https://dry-rb.org/gems/dry-system/) for Rails
196
+ Deps.global_stub('PushService', Class.new { def self.call = 'global-stub-push' })
197
+ ```
160
198
 
161
- `config/initializers/system.rb`
199
+ Dependency stubbed with `global_stub` may be restored only with `global_unstub`. You can unstub dependency when it really needed and ignore in all other cases
162
200
 
163
201
  ```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
202
+ it 'sends webpush' do
203
+ Deps.global_unstub('PushService')
204
+
205
+ # expect(PushService.call).to ...
206
+ end
207
+ ```
215
208
 
216
- def set_or_reload(const_name, const)
217
- remove_constant(const_name)
218
- GpApp.const_set(const_name, const)
219
- end
209
+ *`Deps.global_stub` should not be used within examples*
220
210
 
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
211
+ ## Configuration
212
+ The gem is auto-configuring, but you can override settings
226
213
 
227
- end
214
+ ```ruby
215
+ # config/initializers/active_dry_deps.rb
216
+ ActiveDryDeps.configure do |config|
217
+ config.inflector = ActiveSupport::Inflector
218
+ config.inject_global_constant = 'Deps'
228
219
  end
229
-
230
220
  ```
231
221
 
232
222
  ## Development
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require 'rubocop/rake_task'
8
+ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-configurable'
3
+ require "dry-configurable"
4
4
 
5
5
  module ActiveDryDeps
6
6
 
7
7
  extend Dry::Configurable
8
8
 
9
- setting :container
10
9
  setting :inflector, default: ActiveSupport::Inflector
11
- setting :inject_global_constant, default: 'Deps'
10
+ setting :inject_global_constant, default: "Deps"
12
11
 
13
12
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveDryDeps
4
+ class Container < Hash
5
+
6
+ def resolve_internal(container_key)
7
+ value = self[container_key]
8
+ value.is_a?(Proc) ? value.call : value
9
+ end
10
+
11
+ def register(container_key, value = nil)
12
+ unless container_key.is_a?(String)
13
+ raise ArgumentError, "+#{container_key}+ must be a String"
14
+ end
15
+
16
+ self[container_key] = block_given? ? yield : value
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveDryDeps
4
+ class Dependency
5
+
6
+ attr_reader :receiver_method_name, :container_key, :const_name, :method_name
7
+
8
+ def initialize(resolver, receiver_method_alias: nil)
9
+ parse_resolver(resolver, receiver_method_alias: receiver_method_alias)
10
+ end
11
+
12
+ if Rails.env.test?
13
+ def receiver_method_string
14
+ Kernel.sprintf(
15
+ METHOD_TEMPLATES.fetch(container: true, method_call: !@method_name.nil?),
16
+ receiver_method_name: @receiver_method_name,
17
+ container_key: @container_key || @const_name, # @const_name fallback for stub constant in test
18
+ const_name: @const_name || dependency_not_registered_error_string,
19
+ method_name: @method_name,
20
+ )
21
+ end
22
+ else
23
+ def receiver_method_string
24
+ Kernel.sprintf(
25
+ METHOD_TEMPLATES.fetch(container: !@container_key.nil?, method_call: !@method_name.nil?),
26
+ receiver_method_name: @receiver_method_name,
27
+ container_key: @container_key,
28
+ const_name: @const_name || dependency_not_registered_error_string,
29
+ method_name: @method_name,
30
+ )
31
+ end
32
+ end
33
+
34
+ private def dependency_not_registered_error_string
35
+ dependency_name = @container_key || @const_name
36
+ <<~RUBY
37
+ raise(::ActiveDryDeps::DependencyNotRegistered, <<~TEXT)
38
+ Dependency +#{dependency_name}+ not registered.
39
+ Register it with `ActiveDryDeps::Deps.register('#{dependency_name}', ...)`
40
+ TEXT
41
+ RUBY
42
+ end
43
+
44
+ VALID_CONST_NAME = /^[[:upper:]][[[:alnum:]]:_]*$/
45
+ VALID_METHOD_NAME = /^[[[:alnum:]]_]+$/
46
+ LOWER = /[[:lower:]]/
47
+ METHODS_AS_KLASS = %w[perform_later call].freeze
48
+
49
+ private def parse_resolver(resolver, receiver_method_alias:)
50
+ container_key_or_const_name, method_name = resolver.to_s.split(".", 2)
51
+
52
+ container_key, const_name =
53
+ if LOWER.match?(container_key_or_const_name[0])
54
+ [container_key_or_const_name, nil]
55
+ else
56
+ [nil, container_key_or_const_name]
57
+ end
58
+
59
+ if container_key && !VALID_METHOD_NAME.match?(container_key)
60
+ raise DependencyNameInvalid, "name +#{container_key}+ is not a valid Ruby identifier"
61
+ end
62
+
63
+ if const_name && !VALID_CONST_NAME.match?(const_name)
64
+ raise DependencyNameInvalid, "+#{const_name}+ must contains valid constant name"
65
+ end
66
+
67
+ if method_name && !VALID_METHOD_NAME.match?(method_name)
68
+ raise DependencyNameInvalid, "name +#{method_name}+ is not a valid Ruby identifier"
69
+ end
70
+
71
+ receiver_method_name =
72
+ if receiver_method_alias
73
+ receiver_method_alias
74
+ elsif method_name && METHODS_AS_KLASS.exclude?(method_name)
75
+ method_name
76
+ elsif const_name
77
+ const_name.split("::").last
78
+ else
79
+ resolver
80
+ end
81
+
82
+ unless VALID_METHOD_NAME.match?(receiver_method_name.to_s)
83
+ raise DependencyNameInvalid, "name +#{receiver_method_name}+ is not a valid Ruby identifier"
84
+ end
85
+
86
+ @receiver_method_name = receiver_method_name.to_sym
87
+ @container_key = container_key
88
+ @const_name = const_name
89
+ @method_name = method_name&.to_sym
90
+ end
91
+
92
+ METHOD_TEMPLATES = {
93
+ { container: true, method_call: true } => <<~RUBY,
94
+ # def CreateOrder
95
+ # dependency_const =
96
+ # if ::ActiveDryDeps::Deps::CONTAINER.key?("OrderService::Create")
97
+ # ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create")
98
+ # else
99
+ # OrderService::Create
100
+ # end
101
+ # dependency_const.call(...)
102
+ # end
103
+
104
+ def %<receiver_method_name>s(...)
105
+ dependency_const =
106
+ if ::ActiveDryDeps::Deps::CONTAINER.key?("%<container_key>s")
107
+ ::ActiveDryDeps::Deps::CONTAINER.resolve_internal("%<container_key>s")
108
+ else
109
+ %<const_name>s
110
+ end
111
+ dependency_const.%<method_name>s(...)
112
+ end
113
+ RUBY
114
+ { container: true, method_call: false } => <<~RUBY,
115
+ # def CreateOrder
116
+ # if ::ActiveDryDeps::Deps::CONTAINER.key?("OrderService::Create")
117
+ # ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create")
118
+ # else
119
+ # OrderService::Create
120
+ # end
121
+ # end
122
+
123
+ def %<receiver_method_name>s
124
+ if ::ActiveDryDeps::Deps::CONTAINER.key?("%<container_key>s")
125
+ ::ActiveDryDeps::Deps::CONTAINER.resolve_internal("%<container_key>s")
126
+ else
127
+ %<const_name>s
128
+ end
129
+ end
130
+ RUBY
131
+ { container: false, method_call: true } => <<~RUBY,
132
+ # def CreateOrder(...)
133
+ # OrderService::Create.call(...)
134
+ # end
135
+
136
+ def %<receiver_method_name>s(...)
137
+ %<const_name>s.%<method_name>s(...)
138
+ end
139
+ RUBY
140
+ { container: false, method_call: false } => <<~RUBY,
141
+ # def CreateOrder
142
+ # OrderService::Create
143
+ # end
144
+
145
+ def %<receiver_method_name>s
146
+ %<const_name>s
147
+ end
148
+ RUBY
149
+ }.freeze
150
+
151
+ end
152
+ end
@@ -3,8 +3,7 @@
3
3
  module ActiveDryDeps
4
4
  module Deps
5
5
 
6
- VALID_NAME = /([a-zA-Z_0-9]*)$/
7
- METHODS_AS_KLASS = %w[perform_later call].freeze
6
+ CONTAINER = Container.new
8
7
 
9
8
  module_function
10
9
 
@@ -13,56 +12,25 @@ module ActiveDryDeps
13
12
  # include Deps['Lib::Routes'] use as `Routes()`
14
13
  # include Deps['OrderService::Recalculate.call'] use as `Recalculate()`
15
14
  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
15
  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
16
 
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
17
+ receiver_methods = +""
35
18
 
36
- if alias_method && !VALID_NAME.match?(alias_method.to_s)
37
- raise DependencyNameInvalid, "name +#{alias_method}+ is not a valid Ruby identifier"
19
+ keys.each do |resolver|
20
+ receiver_methods << Dependency.new(resolver).receiver_method_string << "\n"
38
21
  end
39
22
 
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)
23
+ aliases.each do |alias_method, resolver|
24
+ receiver_methods << Dependency.new(resolver, receiver_method_alias: alias_method).receiver_method_string << "\n"
46
25
  end
47
- end
48
26
 
49
- def resolve_key(key)
50
- if key.include?('::')
51
- ActiveDryDeps.config.inflector.underscore(key).tr('/', '.')
52
- else
53
- key
54
- end
27
+ m.module_eval(receiver_methods)
28
+ m
55
29
  end
56
30
 
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
31
+ def register(...)
32
+ CONTAINER.register(...)
33
+ end
66
34
 
67
35
  end
68
36
  end
@@ -3,12 +3,7 @@
3
3
  module ActiveDryDeps
4
4
  class Railtie < ::Rails::Railtie
5
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
-
6
+ config.after_initialize do
12
7
  Object.const_set(ActiveDryDeps.config.inject_global_constant, ::ActiveDryDeps::Deps)
13
8
  ActiveDryDeps.config.finalize!(freeze_values: true)
14
9
  end
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'rspec/matchers'
3
+ require_relative "rspec/matchers"
@@ -1,27 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveDryDeps
4
+ module StubDeps
5
+ def stub(key, value)
6
+ self::CONTAINER.stub(key, value)
7
+ end
4
8
 
5
- module Stub
6
-
7
- CONTAINER_CONST = Object.const_get(ActiveDryDeps.config.container)
9
+ def unstub(*keys)
10
+ self::CONTAINER.unstub(*keys, container: ORIGINAL_WITH_GLOBAL)
11
+ end
8
12
 
9
- def stub(path, ...)
10
- CONTAINER_CONST.stub(Deps.resolve_key(path), ...)
13
+ def reset
14
+ self::CONTAINER.reset(ORIGINAL_WITH_GLOBAL)
11
15
  end
12
16
 
13
- def unstub(*keys)
14
- CONTAINER_CONST.unstub(*keys.map { resolve_key(_1) })
17
+ def global_stub(key, value)
18
+ ORIGINAL_WITH_GLOBAL[key] = value
19
+ self::CONTAINER.stub(key, value)
15
20
  end
16
21
 
22
+ def global_unstub(*keys)
23
+ self::CONTAINER.unstub(*keys, container: ORIGINAL)
24
+ end
17
25
  end
18
26
 
19
- module Deps
27
+ module StubContainer
28
+ def stub(key, value)
29
+ self[key] = value
30
+ end
20
31
 
21
- def self.enable_stubs!
22
- extend Stub
32
+ def reset(container)
33
+ replace(container)
23
34
  end
24
35
 
36
+ def unstub(*unstub_keys, container:)
37
+ raise ArgumentError, "Аrguments must be a Array of Strings" if unstub_keys.empty?
38
+
39
+ unstub_keys.each do |key|
40
+ raise ArgumentError, "+#{key}+ must be a String" unless key.is_a?(String)
41
+
42
+ if container.key?(key)
43
+ self[key] = container[key]
44
+ else
45
+ delete(key)
46
+ end
47
+ end
48
+ end
25
49
  end
26
50
 
51
+ module Deps
52
+ def self.enable_stubs!
53
+ StubDeps.const_set(:ORIGINAL, Deps::CONTAINER.dup)
54
+ StubDeps.const_set(:ORIGINAL_WITH_GLOBAL, Deps::CONTAINER.dup)
55
+
56
+ Deps::CONTAINER.extend(StubContainer)
57
+ Deps.extend StubDeps
58
+ end
59
+ end
27
60
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveDryDeps
4
4
 
5
- VERSION = '0.2.0'
5
+ VERSION = "1.0.0"
6
6
 
7
7
  end
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'active_dry_deps/version'
4
- require_relative 'active_dry_deps/configuration'
5
- require_relative 'active_dry_deps/railtie'
3
+ require "active_dry_deps/version"
4
+ require "active_dry_deps/configuration"
5
+ require "active_dry_deps/railtie"
6
6
 
7
7
  module ActiveDryDeps
8
8
 
9
+ autoload :Deps, "active_dry_deps/deps"
10
+ autoload :Dependency, "active_dry_deps/dependency"
11
+ autoload :Container, "active_dry_deps/container"
12
+
9
13
  class Error < StandardError; end
10
14
  class DependencyNameInvalid < Error; end
15
+ class DependencyNotRegistered < Error; end
11
16
 
12
17
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_dry_deps
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ermolaev Andrey
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2025-10-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: dry-configurable
@@ -23,10 +23,10 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
- description: |-
27
- ActiveDryDeps not modify constructor and support Dependency Injection for modules.
28
- Also you can import method from any object in your container.
29
- Adding extra dependencies is easy and improve readability your code.
26
+ description: |
27
+ ActiveDryDeps does not modify constructor and supports Dependency Injection for modules.
28
+ Also you can import method from any object in your container.
29
+ Adding extra dependencies is easy and improve readability your code.
30
30
  email:
31
31
  - andruhafirst@yandex.ru
32
32
  executables: []
@@ -43,6 +43,8 @@ files:
43
43
  - Rakefile
44
44
  - lib/active_dry_deps.rb
45
45
  - lib/active_dry_deps/configuration.rb
46
+ - lib/active_dry_deps/container.rb
47
+ - lib/active_dry_deps/dependency.rb
46
48
  - lib/active_dry_deps/deps.rb
47
49
  - lib/active_dry_deps/railtie.rb
48
50
  - lib/active_dry_deps/rspec.rb
@@ -72,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
74
  - !ruby/object:Gem::Version
73
75
  version: '0'
74
76
  requirements: []
75
- rubygems_version: 3.6.9
77
+ rubygems_version: 3.6.2
76
78
  specification_version: 4
77
79
  summary: Dependency injection and resolution support for classes and modules.
78
80
  test_files: []