dry-auto_inject 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +6 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +19 -0
- data/.rubocop_todo.yml +6 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +243 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +16 -0
- data/LICENSE +20 -0
- data/README.md +58 -0
- data/Rakefile +14 -0
- data/bin/console +11 -0
- data/bin/setup +7 -0
- data/dry-auto_inject.gemspec +29 -0
- data/lib/dry-auto_inject.rb +3 -0
- data/lib/dry/auto_inject.rb +46 -0
- data/lib/dry/auto_inject/builder.rb +40 -0
- data/lib/dry/auto_inject/dependency_map.rb +55 -0
- data/lib/dry/auto_inject/injector.rb +39 -0
- data/lib/dry/auto_inject/method_parameters.rb +92 -0
- data/lib/dry/auto_inject/strategies.rb +21 -0
- data/lib/dry/auto_inject/strategies/args.rb +68 -0
- data/lib/dry/auto_inject/strategies/constructor.rb +56 -0
- data/lib/dry/auto_inject/strategies/hash.rb +41 -0
- data/lib/dry/auto_inject/strategies/kwargs.rb +105 -0
- data/lib/dry/auto_inject/version.rb +7 -0
- data/rakelib/rubocop.rake +20 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 04bd4b08425b83733006cac8ef6612068304a720a63f8896e8f0369e1d26b0cc
|
4
|
+
data.tar.gz: 38b6073a686faa24ec5b628e70125ae97ea2c402403c410608acd57506a7482c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd8267844c208234397d5b8a62113bc5bc665daad2c3410d27342489b5894b24b92e6dec1df721028e5967a0f2f7c24465e52bac7b4e469ad0e6e98d65bb0c25
|
7
|
+
data.tar.gz: 64be14b7a6797e064690838ec867c0d2056101387e5a652cf20213ef40f5a86822420f50a42d8f85288bc66af30f710c05b2c8dde4e4ca0952b2d3cd71b0a3d5
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Generated by `rubocop --auto-gen-config`
|
2
|
+
inherit_from: .rubocop_todo.yml
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Max: 110
|
6
|
+
|
7
|
+
Lint/HandleExceptions:
|
8
|
+
Exclude:
|
9
|
+
- rakelib/*.rake
|
10
|
+
|
11
|
+
Style/LambdaCall:
|
12
|
+
EnforcedStyle: braces
|
13
|
+
|
14
|
+
Style/Documentation:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Style/FileName:
|
18
|
+
Exclude:
|
19
|
+
- lib/dry-pipeline.rb
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2015-08-19 22:11:28 +0100 using RuboCop version 0.32.0.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/.travis.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
bundler_args: --without tools
|
4
|
+
after_success:
|
5
|
+
- '[ -d coverage ] && bundle exec codeclimate-test-reporter'
|
6
|
+
rvm:
|
7
|
+
- 2.6.2
|
8
|
+
- 2.5.5
|
9
|
+
- 2.4.5
|
10
|
+
- 2.3.8
|
11
|
+
- jruby-9.2.6.0
|
12
|
+
- truffleruby
|
13
|
+
matrix:
|
14
|
+
allow_failures:
|
15
|
+
- rvm: truffleruby
|
16
|
+
env:
|
17
|
+
global:
|
18
|
+
- COVERAGE=true
|
19
|
+
notifications:
|
20
|
+
email: false
|
21
|
+
webhooks:
|
22
|
+
urls:
|
23
|
+
- https://webhooks.gitter.im/e/19098b4253a72c9796db
|
24
|
+
on_success: change # options: [always|never|change] default: always
|
25
|
+
on_failure: always # options: [always|never|change] default: always
|
26
|
+
on_start: false # default: false
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
# 0.6.1 / 2019-04-16
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
|
5
|
+
- Allow explicit injection of falsey values (timriley in [#58](https://github.com/dry-rb/dry-auto_inject/pull/58))
|
6
|
+
|
7
|
+
[Compare v0.6.0...v0.6.1](https://github.com/dry-rb/dry-auto_inject/compare/v0.6.0...v0.6.1)
|
8
|
+
|
9
|
+
# 0.6.0 / 2018-11-29
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- [BREAKING] 0.6.0 supports Ruby 2.3 and above. If you're on 2.3 keep in mind its EOL is scheduled at the end of March, 2019
|
14
|
+
|
15
|
+
### Added
|
16
|
+
|
17
|
+
- Enhanced support for integrating with existing constructors. The kwargs strategy will now pass dependencies up to the next constructor if it accepts an arbitrary number of arguments with `*args`. Note that this change may break existing code though we think it's unlikely to happen. If something doesn't work for you please report and we'll try to sort it out (flash-gordon + timriley in [#48](https://github.com/dry-rb/dry-auto_inject/pull/48))
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
|
21
|
+
- A couple of regressions were fixed along the way, see [#46](https://github.com/dry-rb/dry-auto_inject/issues/46) and [#49](https://github.com/dry-rb/dry-auto_inject/issues/49) (flash-gordon + timriley in [#48](https://github.com/dry-rb/dry-auto_inject/pull/48))
|
22
|
+
|
23
|
+
[Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.5.0...v0.6.0)
|
24
|
+
|
25
|
+
# 0.5.0 / 2018-11-09
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
- Only assign `nil` dependency instance variables from generated `#initialize` if the instance variable has not been previously defined. This improves compatibility with objects initialized in non-conventional ways (see example below) (timriley in [#47](https://github.com/dry-rb/dry-auto_inject/pull/47))
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
module SomeFramework
|
33
|
+
class Action
|
34
|
+
def self.new(configuration:, **args)
|
35
|
+
# Do some trickery so `#initialize` on subclasses don't need to worry
|
36
|
+
# about handling a configuration kwarg and passing it to super
|
37
|
+
allocate.tap do |obj|
|
38
|
+
obj.instance_variable_set :@configuration, configuration
|
39
|
+
obj.send :initialize, **args
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module MyApp
|
46
|
+
class Action < SomeFramework::Action
|
47
|
+
# Inject the configuration object, which is passed to
|
48
|
+
# SomeFramework::Action.new but not all the way through to any subsequent
|
49
|
+
# `#initialize` calls
|
50
|
+
include Import[configuration: "web.action.configuration"]
|
51
|
+
end
|
52
|
+
|
53
|
+
class SomeAction < Action
|
54
|
+
# Subclasses of MyApp::Action don't need to concern themselves with
|
55
|
+
# `configuration` dependency
|
56
|
+
include Import["some_repo"]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
[Compare v0.4.6...v0.5.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.6...v0.5.0)
|
62
|
+
|
63
|
+
# 0.4.6 / 2018-03-27
|
64
|
+
|
65
|
+
### Changed
|
66
|
+
|
67
|
+
- In injector-generated `#initialize` methods, set dependency instance variables before calling `super` (timriley)
|
68
|
+
|
69
|
+
[Compare v0.4.5...v0.4.6](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.5...v0.4.6)
|
70
|
+
|
71
|
+
# 0.4.5 / 2018-01-02
|
72
|
+
|
73
|
+
### Added
|
74
|
+
|
75
|
+
- Improved handling of kwargs being passed to #initialize’s super method (timriley)
|
76
|
+
|
77
|
+
[Compare v0.4.4...v0.4.5](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.4...v0.4.5)
|
78
|
+
|
79
|
+
# 0.4.4 / 2017-09-14
|
80
|
+
|
81
|
+
### Added
|
82
|
+
|
83
|
+
- Determine name for dependencies by splitting identifiers on any invalid local variable name characters (e.g. "/", "?", "!"), instead of splitting on dots only (raventid in [#39](https://github.com/dry-rb/dry-auto_inject/pull/39))
|
84
|
+
|
85
|
+
# 0.4.3 / 2017-05-27
|
86
|
+
|
87
|
+
### Added
|
88
|
+
|
89
|
+
- Push sequential arguments along with keywords in the kwargs strategy (hbda + vladra in [#32](https://github.com/dry-rb/dry-auto_inject/pull/32))
|
90
|
+
|
91
|
+
[Compare v0.4.2...v0.4.3](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.2...v0.4.3)
|
92
|
+
|
93
|
+
# 0.4.2 / 2016-10-10
|
94
|
+
|
95
|
+
### Fixed
|
96
|
+
|
97
|
+
- Fixed issue where injectors for different containers could not be used on different classes in an inheritance hierarchy (timriley in [#31](https://github.com/dry-rb/dry-auto_inject/pull/31))
|
98
|
+
|
99
|
+
[Compare v0.4.1...v0.4.2](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.1...v0.4.2)
|
100
|
+
|
101
|
+
# 0.4.1 / 2016-08-14
|
102
|
+
|
103
|
+
### Changed
|
104
|
+
|
105
|
+
- Loosened version dependency on dry-container (AMHOL)
|
106
|
+
|
107
|
+
[Compare v0.4.0...v0.4.1](https://github.com/dry-rb/dry-auto_inject/compare/v0.4.0...v0.4.1)
|
108
|
+
|
109
|
+
# 0.4.0 / 2016-07-26
|
110
|
+
|
111
|
+
### Added
|
112
|
+
|
113
|
+
- Support for strategy chaining, which is helpful in opting for alternatives to an application's normal strategy (timriley in [#25](https://github.com/dry-rb/dry-auto_inject/pull/25))
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# Define the application's injector with a non-default
|
117
|
+
MyInject = Dry::AutoInject(MyContainer).hash
|
118
|
+
|
119
|
+
# Opt for a different strategy in a particular class
|
120
|
+
class MyClass
|
121
|
+
include MyInject.args["foo"]
|
122
|
+
end
|
123
|
+
|
124
|
+
# You can chain as long as you want (silly example to demonstrate the flexibility)
|
125
|
+
class OtherClass
|
126
|
+
include MyInject.args.hash.kwargs.args["foo"]
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
### Changed
|
131
|
+
|
132
|
+
- Use a `BasicObject`-based environment for the injector builder API instead of the previous `define_singleton_method`-based approach, which had negative performance characteristics (timriley in [#26](https://github.com/dry-rb/dry-auto_inject/pull/26))
|
133
|
+
|
134
|
+
### Fixed
|
135
|
+
|
136
|
+
- Fixed issue with kwargs injectors used at multiple points in a class inheritance heirarchy (flash-gordon in [#27](https://github.com/dry-rb/dry-auto_inject/pull/27))
|
137
|
+
|
138
|
+
[Compare v0.3.0...v0.4.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.3.0...v0.4.0)
|
139
|
+
|
140
|
+
# 0.3.0, 2016-06-02
|
141
|
+
|
142
|
+
### Added
|
143
|
+
|
144
|
+
* Support for new `kwargs` and `hash` injection strategies
|
145
|
+
|
146
|
+
These strategies can be accessed via methods on the main builder object:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
MyInject = Dry::AutoInject(my_container)
|
150
|
+
|
151
|
+
class MyClass
|
152
|
+
include MyInject.hash["my_dep"]
|
153
|
+
end
|
154
|
+
```
|
155
|
+
* Support for user-provided injection strategies
|
156
|
+
|
157
|
+
All injection strategies are now held in their own `Dry::AutoInject::Strategies` container. You can add register your own strategies to this container, or choose to provide a strategies container of your own:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class CustomStrategy < Module
|
161
|
+
# Your strategy code goes here :)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Registering your own strategy (globally)
|
165
|
+
Dry::AutoInject::Strategies.register :custom, CustomStrategy
|
166
|
+
|
167
|
+
MyInject = Dry::AutoInject(my_container)
|
168
|
+
|
169
|
+
class MyClass
|
170
|
+
include MyInject.custom["my_dep"]
|
171
|
+
end
|
172
|
+
|
173
|
+
# Providing your own container (keeping the existing strategies in place)
|
174
|
+
class MyStrategies < Dry::AutoInject::Strategies
|
175
|
+
register :custom, CustomStrategy
|
176
|
+
end
|
177
|
+
|
178
|
+
MyInject = Dry::AutoInject(my_container, strategies: MyStrategies)
|
179
|
+
|
180
|
+
class MyClass
|
181
|
+
include MyInject.custom["my_dep"]
|
182
|
+
end
|
183
|
+
|
184
|
+
# Proiding a completely separated container
|
185
|
+
class MyStrategies
|
186
|
+
extend Dry::Container::Mixin
|
187
|
+
register :custom, CustomStrategy
|
188
|
+
end
|
189
|
+
|
190
|
+
MyInject = Dry::AutoInject(my_container, strategies: MyStrategies)
|
191
|
+
|
192
|
+
class MyClass
|
193
|
+
include MyInject.custom["my_dep"]
|
194
|
+
end
|
195
|
+
```
|
196
|
+
* User-specified aliases for dependencies
|
197
|
+
|
198
|
+
These aliases enable you to specify your own name for dependencies, both for their local readers and their keys in the kwargs- and hash-based initializers. Specify aliases by passing a hash of names:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
MyInject = Dry::AutoInject(my_container)
|
202
|
+
|
203
|
+
class MyClass
|
204
|
+
include MyInject[my_dep: "some_other.dep"]
|
205
|
+
|
206
|
+
# Refer to the dependency as `my_dep` inside the class
|
207
|
+
end
|
208
|
+
|
209
|
+
# Pass your own replacements using the `my_dep` initializer key
|
210
|
+
my_obj = MyClass.new(my_dep: something_else)
|
211
|
+
```
|
212
|
+
|
213
|
+
A mix of both regular and aliased dependencies can also be injected:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
include MyInject["some_dep", another_dep: "some_other.dep"]
|
217
|
+
```
|
218
|
+
|
219
|
+
* Inspect the `super` method of the including class’s `#initialize` and send it arguments that will match its own arguments list/arity. This allows auto_inject to be used more easily in existing class inheritance heirarchies.
|
220
|
+
|
221
|
+
### Changed
|
222
|
+
|
223
|
+
* `kwargs` is the new default injection strategy
|
224
|
+
* Rubinius support is not available for the `kwargs` strategy (see [#18](https://github.com/dry-rb/dry-auto_inject/issues/18))
|
225
|
+
|
226
|
+
[Compare v0.2.0...v0.3.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.2.0...v0.3.0)
|
227
|
+
|
228
|
+
# v0.2.0 2016-02-09
|
229
|
+
|
230
|
+
### Added
|
231
|
+
|
232
|
+
* Support for hashes as constructor arguments via `Import.hash` interface (solnic)
|
233
|
+
|
234
|
+
[Compare v0.1.0...v0.2.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.1.0...v0.2.0)
|
235
|
+
|
236
|
+
# v0.1.0 2015-11-12
|
237
|
+
|
238
|
+
Changed interface from `Dry::AutoInject.new { container(some_container) }` to
|
239
|
+
`Dry::AutoInject(some_container)`.
|
240
|
+
|
241
|
+
# v0.0.1 2015-08-20
|
242
|
+
|
243
|
+
First public release \o/
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Issue Guidelines
|
2
|
+
|
3
|
+
## Reporting bugs
|
4
|
+
|
5
|
+
If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
|
6
|
+
|
7
|
+
## Reporting feature requests
|
8
|
+
|
9
|
+
Report a feature request **only after discussing it first on [discourse.dry-rb.org](https://discourse.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
|
10
|
+
|
11
|
+
## Reporting questions, support requests, ideas, concerns etc.
|
12
|
+
|
13
|
+
**PLEASE DON'T** - use [discourse.dry-rb.org](https://discourse.dry-rb.org) instead.
|
14
|
+
|
15
|
+
# Pull Request Guidelines
|
16
|
+
|
17
|
+
A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
|
18
|
+
|
19
|
+
Other requirements:
|
20
|
+
|
21
|
+
1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
|
22
|
+
2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
|
23
|
+
3) Add API documentation if it's a new feature
|
24
|
+
4) Update API documentation if it changes an existing feature
|
25
|
+
5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
|
26
|
+
|
27
|
+
# Asking for help
|
28
|
+
|
29
|
+
If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org).
|
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in dry-auto_inject.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
group :test do
|
9
|
+
gem "simplecov"
|
10
|
+
gem "codeclimate-test-reporter", require: nil
|
11
|
+
end
|
12
|
+
|
13
|
+
group :tools do
|
14
|
+
gem 'byebug', platforms: :mri
|
15
|
+
gem 'pry'
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2015-2016 Piotr Solnica
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
[gem]: https://rubygems.org/gems/dry-auto_inject
|
2
|
+
[travis]: https://travis-ci.org/dry-rb/dry-auto_inject
|
3
|
+
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-auto_inject
|
4
|
+
[coveralls]: https://coveralls.io/r/dry-rb/dry-auto_inject
|
5
|
+
[inchpages]: http://inch-ci.org/github/dry-rb/dry-auto_inject
|
6
|
+
[chat]: https://dry-rb.zulipchat.com
|
7
|
+
|
8
|
+
# dry-auto_inject [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
|
9
|
+
|
10
|
+
[![Gem Version](https://badge.fury.io/rb/dry-auto_inject.svg)][gem]
|
11
|
+
[![Build Status](https://travis-ci.org/dry-rb/dry-auto_inject.svg?branch=master)][travis]
|
12
|
+
[![Code Climate](https://codeclimate.com/github/dry-rb/dry-auto_inject/badges/gpa.svg)][codeclimate]
|
13
|
+
[![Test Coverage](https://codeclimate.com/github/dry-rb/dry-auto_inject/badges/coverage.svg)][codeclimate]
|
14
|
+
[![Inline docs](http://inch-ci.org/github/dry-rb/dry-auto_inject.svg?branch=master)][inchpages]
|
15
|
+
![No monkey-patches](https://img.shields.io/badge/monkey--patches-0-brightgreen.svg)
|
16
|
+
|
17
|
+
A simple extension which allows you to automatically inject dependencies to your
|
18
|
+
object constructors from a configured container.
|
19
|
+
|
20
|
+
It does 3 things:
|
21
|
+
|
22
|
+
- Defines a constructor which accepts dependencies
|
23
|
+
- Defines attribute readers for dependencies
|
24
|
+
- Injects dependencies automatically to the constructor with overridden `.new`
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'dry-auto_inject'
|
32
|
+
```
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
|
36
|
+
```sh
|
37
|
+
$ bundle
|
38
|
+
```
|
39
|
+
|
40
|
+
Or install it yourself as:
|
41
|
+
```sh
|
42
|
+
$ gem install dry-auto_inject
|
43
|
+
```
|
44
|
+
|
45
|
+
## Links
|
46
|
+
|
47
|
+
* [Documentation](http://dry-rb.org/gems/dry-auto_inject/)
|
48
|
+
|
49
|
+
## Development
|
50
|
+
|
51
|
+
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.
|
52
|
+
|
53
|
+
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
54
|
+
|
55
|
+
## Contributing
|
56
|
+
|
57
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dry-rb/dry-auto_inject.
|
58
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
7
|
+
|
8
|
+
require 'rspec/core'
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
|
11
|
+
task default: :spec
|
12
|
+
|
13
|
+
desc 'Run all specs in spec directory'
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'dry-auto_inject'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
data/bin/setup
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'dry/auto_inject/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'dry-auto_inject'
|
9
|
+
spec.version = Dry::AutoInject::VERSION.dup
|
10
|
+
spec.authors = ['Piotr Solnica']
|
11
|
+
spec.email = ['piotr.solnica@gmail.com']
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.summary = 'Container-agnostic automatic constructor injection'
|
15
|
+
spec.homepage = 'https://github.com/dry-rb/dry-auto_inject'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = 'exe'
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.required_ruby_version = '>= 2.3.0'
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'dry-container', '>= 0.3.4'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'rake'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/builder'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
# Configure an auto-injection module
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# module MyApp
|
10
|
+
# # set up your container
|
11
|
+
# container = Dry::Container.new
|
12
|
+
#
|
13
|
+
# container.register(:data_store, -> { DataStore.new })
|
14
|
+
# container.register(:user_repository, -> { container[:data_store][:users] })
|
15
|
+
# container.register(:persist_user, -> { PersistUser.new })
|
16
|
+
#
|
17
|
+
# # set up your auto-injection function
|
18
|
+
# AutoInject = Dry::AutoInject(container)
|
19
|
+
#
|
20
|
+
# # define your injection function
|
21
|
+
# def self.Inject(*keys)
|
22
|
+
# AutoInject[*keys]
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# # then simply include it in your class providing which dependencies should be
|
27
|
+
# # injected automatically from the configured container
|
28
|
+
# class PersistUser
|
29
|
+
# include MyApp::Inject(:user_repository)
|
30
|
+
#
|
31
|
+
# def call(user)
|
32
|
+
# user_repository << user
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# persist_user = container[:persist_user]
|
37
|
+
#
|
38
|
+
# persist_user.call(name: 'Jane')
|
39
|
+
#
|
40
|
+
# @return [Proc] calling the returned proc builds an auto-injection module
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def self.AutoInject(container, options = {})
|
44
|
+
AutoInject::Builder.new(container, options)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/strategies'
|
4
|
+
require 'dry/auto_inject/injector'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module AutoInject
|
8
|
+
class Builder < BasicObject
|
9
|
+
# @api private
|
10
|
+
attr_reader :container
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
attr_reader :strategies
|
14
|
+
|
15
|
+
def initialize(container, options = {})
|
16
|
+
@container = container
|
17
|
+
@strategies = options.fetch(:strategies) { Strategies }
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
def [](*dependency_names)
|
22
|
+
default[*dependency_names]
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to?(name, include_private = false)
|
26
|
+
Builder.public_instance_methods.include?(name) || strategies.key?(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def method_missing(name, *args, &block)
|
32
|
+
if strategies.key?(name)
|
33
|
+
Injector.new(container, strategies[name], builder: self)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module AutoInject
|
5
|
+
DuplicateDependencyError = Class.new(StandardError)
|
6
|
+
DependencyNameInvalid = Class.new(StandardError)
|
7
|
+
|
8
|
+
VALID_NAME = /([a-z_][a-zA-Z_0-9]*)$/
|
9
|
+
|
10
|
+
class DependencyMap
|
11
|
+
def initialize(*dependencies)
|
12
|
+
@map = {}
|
13
|
+
|
14
|
+
dependencies = dependencies.dup
|
15
|
+
aliases = dependencies.last.is_a?(Hash) ? dependencies.pop : {}
|
16
|
+
|
17
|
+
dependencies.each do |identifier|
|
18
|
+
name = name_for(identifier)
|
19
|
+
add_dependency(name, identifier)
|
20
|
+
end
|
21
|
+
|
22
|
+
aliases.each do |name, identifier|
|
23
|
+
add_dependency(name, identifier)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
@map.inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
def names
|
32
|
+
@names ||= @map.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_h
|
36
|
+
@map.dup
|
37
|
+
end
|
38
|
+
alias_method :to_hash, :to_h
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def name_for(identifier)
|
43
|
+
matched = VALID_NAME.match(identifier.to_s)
|
44
|
+
raise DependencyNameInvalid, "name +#{identifier}+ is not a valid Ruby identifier" unless matched
|
45
|
+
matched[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_dependency(name, identifier)
|
49
|
+
name = name.to_sym
|
50
|
+
raise DuplicateDependencyError, "name +#{name}+ is already used" if @map.key?(name)
|
51
|
+
@map[name] = identifier
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/strategies'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module AutoInject
|
7
|
+
class Injector < BasicObject
|
8
|
+
# @api private
|
9
|
+
attr_reader :container
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :strategy
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
attr_reader :builder
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
def initialize(container, strategy, builder:)
|
19
|
+
@container = container
|
20
|
+
@strategy = strategy
|
21
|
+
@builder = builder
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](*dependency_names)
|
25
|
+
strategy.new(container, *dependency_names)
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(name, include_private = false)
|
29
|
+
Injector.instance_methods.include?(name) || builder.respond_to?(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def method_missing(name, *args, &block)
|
35
|
+
builder.__send__(name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module AutoInject
|
5
|
+
# @api private
|
6
|
+
class MethodParameters
|
7
|
+
PASS_THROUGH = [[:rest]]
|
8
|
+
|
9
|
+
if RUBY_VERSION >= '2.4.4.' && !defined? JRUBY_VERSION
|
10
|
+
def self.of(obj, name)
|
11
|
+
Enumerator.new do |y|
|
12
|
+
begin
|
13
|
+
method = obj.instance_method(name)
|
14
|
+
rescue NameError
|
15
|
+
end
|
16
|
+
|
17
|
+
loop do
|
18
|
+
break if method.nil?
|
19
|
+
|
20
|
+
y << MethodParameters.new(method.parameters)
|
21
|
+
method = method.super_method
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
# see https://bugs.ruby-lang.org/issues/13973
|
27
|
+
def self.of(obj, name)
|
28
|
+
Enumerator.new do |y|
|
29
|
+
ancestors = obj.ancestors
|
30
|
+
|
31
|
+
loop do
|
32
|
+
klass = ancestors.shift
|
33
|
+
break if klass.nil?
|
34
|
+
|
35
|
+
begin
|
36
|
+
method = klass.instance_method(name)
|
37
|
+
|
38
|
+
next unless method.owner.equal?(klass)
|
39
|
+
rescue NameError
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
43
|
+
y << MethodParameters.new(method.parameters)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :parameters
|
50
|
+
|
51
|
+
def initialize(parameters)
|
52
|
+
@parameters = parameters
|
53
|
+
end
|
54
|
+
|
55
|
+
def splat?
|
56
|
+
return @splat if defined? @splat
|
57
|
+
@splat = parameters.any? { |type, _| type == :rest }
|
58
|
+
end
|
59
|
+
|
60
|
+
def sequential_arguments?
|
61
|
+
return @sequential_arguments if defined? @sequential_arguments
|
62
|
+
@sequential_arguments = parameters.any? { |type, _|
|
63
|
+
type == :req || type == :opt
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def keyword_names
|
68
|
+
@keyword_names ||= parameters.each_with_object(Set.new) { |(type, name), names|
|
69
|
+
names << name if type == :key || type == :keyreq
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def keyword?(name)
|
74
|
+
keyword_names.include?(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def empty?
|
78
|
+
parameters.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
def length
|
82
|
+
parameters.length
|
83
|
+
end
|
84
|
+
|
85
|
+
def pass_through?
|
86
|
+
parameters.eql?(PASS_THROUGH)
|
87
|
+
end
|
88
|
+
|
89
|
+
EMPTY = new([])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-container'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module AutoInject
|
7
|
+
class Strategies
|
8
|
+
extend Dry::Container::Mixin
|
9
|
+
|
10
|
+
# @api public
|
11
|
+
def self.register_default(name, strategy)
|
12
|
+
register name, strategy
|
13
|
+
register :default, strategy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'dry/auto_inject/strategies/args'
|
20
|
+
require 'dry/auto_inject/strategies/hash'
|
21
|
+
require 'dry/auto_inject/strategies/kwargs'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/strategies/constructor'
|
4
|
+
require 'dry/auto_inject/method_parameters'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module AutoInject
|
8
|
+
class Strategies
|
9
|
+
# @api private
|
10
|
+
class Args < Constructor
|
11
|
+
private
|
12
|
+
|
13
|
+
def define_new
|
14
|
+
class_mod.class_exec(container, dependency_map) do |container, dependency_map|
|
15
|
+
define_method :new do |*args|
|
16
|
+
deps = dependency_map.to_h.values.map.with_index { |identifier, i|
|
17
|
+
args[i] || container[identifier]
|
18
|
+
}
|
19
|
+
|
20
|
+
super(*deps, *args[deps.size..-1])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_initialize(klass)
|
26
|
+
super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
|
27
|
+
# Look upwards past `def foo(*)` methods until we get an explicit list of parameters
|
28
|
+
break ps unless ps.pass_through?
|
29
|
+
end
|
30
|
+
|
31
|
+
if super_parameters.empty?
|
32
|
+
define_initialize_with_params
|
33
|
+
else
|
34
|
+
define_initialize_with_splat(super_parameters)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_initialize_with_params
|
39
|
+
initialize_args = dependency_map.names.join(', ')
|
40
|
+
|
41
|
+
instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
42
|
+
def initialize(#{initialize_args})
|
43
|
+
#{dependency_map.names.map { |name| "@#{name} = #{name}" }.join("\n")}
|
44
|
+
super()
|
45
|
+
end
|
46
|
+
RUBY
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_initialize_with_splat(super_parameters)
|
50
|
+
super_pass = if super_parameters.splat?
|
51
|
+
'*args'
|
52
|
+
else
|
53
|
+
"*args.take(#{super_parameters.length})"
|
54
|
+
end
|
55
|
+
|
56
|
+
instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
57
|
+
def initialize(*args)
|
58
|
+
#{dependency_map.names.map.with_index { |name, i| "@#{name} = args[#{i}]" }.join("\n")}
|
59
|
+
super(#{super_pass})
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
register :args, Args
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/dependency_map'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module AutoInject
|
7
|
+
class Strategies
|
8
|
+
class Constructor < Module
|
9
|
+
ClassMethods = Class.new(Module)
|
10
|
+
InstanceMethods = Class.new(Module)
|
11
|
+
|
12
|
+
attr_reader :container
|
13
|
+
attr_reader :dependency_map
|
14
|
+
attr_reader :instance_mod
|
15
|
+
attr_reader :class_mod
|
16
|
+
|
17
|
+
def initialize(container, *dependency_names)
|
18
|
+
@container = container
|
19
|
+
@dependency_map = DependencyMap.new(*dependency_names)
|
20
|
+
@instance_mod = InstanceMethods.new
|
21
|
+
@class_mod = ClassMethods.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def included(klass)
|
26
|
+
define_readers
|
27
|
+
|
28
|
+
define_new
|
29
|
+
define_initialize(klass)
|
30
|
+
|
31
|
+
klass.send(:include, instance_mod)
|
32
|
+
klass.extend(class_mod)
|
33
|
+
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def define_readers
|
40
|
+
instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
41
|
+
attr_reader #{dependency_map.names.map { |name| ":#{name}" }.join(', ')}
|
42
|
+
RUBY
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def define_new
|
47
|
+
raise NotImplementedError, "must be implemented by a subclass"
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_initialize(klass)
|
51
|
+
raise NotImplementedError, "must be implemented by a subclass"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/strategies/constructor'
|
4
|
+
require 'dry/auto_inject/method_parameters'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module AutoInject
|
8
|
+
class Strategies
|
9
|
+
# @api private
|
10
|
+
class Hash < Constructor
|
11
|
+
private
|
12
|
+
|
13
|
+
def define_new
|
14
|
+
class_mod.class_exec(container, dependency_map) do |container, dependency_map|
|
15
|
+
define_method :new do |options = {}|
|
16
|
+
deps = dependency_map.to_h.each_with_object({}) { |(name, identifier), obj|
|
17
|
+
obj[name] = options[name] || container[identifier]
|
18
|
+
}.merge(options)
|
19
|
+
|
20
|
+
super(deps)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_initialize(klass)
|
26
|
+
super_params = MethodParameters.of(klass, :initialize).first
|
27
|
+
super_pass = super_params.empty? ? '' : 'options'
|
28
|
+
|
29
|
+
instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
30
|
+
def initialize(options)
|
31
|
+
#{dependency_map.names.map { |name| "@#{name} = options[:#{name}] unless !options.key?(#{name}) && instance_variable_defined?(:'@#{name}')" }.join("\n")}
|
32
|
+
super(#{super_pass})
|
33
|
+
end
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
register :hash, Hash
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/auto_inject/strategies/constructor'
|
4
|
+
require 'dry/auto_inject/method_parameters'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module AutoInject
|
8
|
+
class Strategies
|
9
|
+
# @api private
|
10
|
+
class Kwargs < Constructor
|
11
|
+
private
|
12
|
+
|
13
|
+
def define_new
|
14
|
+
class_mod.class_exec(container, dependency_map) do |container, dependency_map|
|
15
|
+
map = dependency_map.to_h
|
16
|
+
|
17
|
+
define_method :new do |*args, **kwargs|
|
18
|
+
map.each do |name, identifier|
|
19
|
+
kwargs[name] = container[identifier] unless kwargs.key?(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
super(*args, **kwargs)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_initialize(klass)
|
28
|
+
super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
|
29
|
+
# Look upwards past `def foo(*)` methods until we get an explicit list of parameters
|
30
|
+
break ps unless ps.pass_through?
|
31
|
+
end
|
32
|
+
|
33
|
+
if super_parameters.splat? || super_parameters.sequential_arguments?
|
34
|
+
define_initialize_with_splat(super_parameters)
|
35
|
+
else
|
36
|
+
define_initialize_with_keywords(super_parameters)
|
37
|
+
end
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def define_initialize_with_keywords(super_parameters)
|
43
|
+
assign_dependencies = method(:assign_dependencies)
|
44
|
+
slice_kwargs = method(:slice_kwargs)
|
45
|
+
|
46
|
+
instance_mod.class_exec do
|
47
|
+
define_method :initialize do |**kwargs|
|
48
|
+
assign_dependencies.(kwargs, self)
|
49
|
+
|
50
|
+
super_kwargs = slice_kwargs.(kwargs, super_parameters)
|
51
|
+
|
52
|
+
if super_kwargs.any?
|
53
|
+
super(super_kwargs)
|
54
|
+
else
|
55
|
+
super()
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_initialize_with_splat(super_parameters)
|
62
|
+
assign_dependencies = method(:assign_dependencies)
|
63
|
+
slice_kwargs = method(:slice_kwargs)
|
64
|
+
|
65
|
+
instance_mod.class_exec do
|
66
|
+
define_method :initialize do |*args, **kwargs|
|
67
|
+
assign_dependencies.(kwargs, self)
|
68
|
+
|
69
|
+
if super_parameters.splat?
|
70
|
+
super(*args, kwargs)
|
71
|
+
else
|
72
|
+
super_kwargs = slice_kwargs.(kwargs, super_parameters)
|
73
|
+
|
74
|
+
if super_kwargs.any?
|
75
|
+
super(*args, super_kwargs)
|
76
|
+
else
|
77
|
+
super(*args)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def assign_dependencies(kwargs, destination)
|
85
|
+
dependency_map.names.each do |name|
|
86
|
+
# Assign instance variables, but only if the ivar is not
|
87
|
+
# previously defined (this improves compatibility with objects
|
88
|
+
# initialized in unconventional ways)
|
89
|
+
if kwargs.key?(name) || !destination.instance_variable_defined?(:"@#{name}")
|
90
|
+
destination.instance_variable_set :"@#{name}", kwargs[name]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def slice_kwargs(kwargs, super_parameters)
|
96
|
+
kwargs.select do |key|
|
97
|
+
!dependency_map.names.include?(key) || super_parameters.keyword?(key)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
register_default :kwargs, Kwargs
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubocop/rake_task'
|
5
|
+
|
6
|
+
Rake::Task[:default].enhance [:rubocop]
|
7
|
+
|
8
|
+
RuboCop::RakeTask.new do |task|
|
9
|
+
task.options << '--display-cop-names'
|
10
|
+
end
|
11
|
+
|
12
|
+
namespace :rubocop do
|
13
|
+
desc 'Generate a configuration file acting as a TODO list.'
|
14
|
+
task :auto_gen_config do
|
15
|
+
exec 'bundle exec rubocop --auto-gen-config'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dry-auto_inject
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Piotr Solnica
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-container
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.8'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- piotr.solnica@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".codeclimate.yml"
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".rubocop.yml"
|
80
|
+
- ".rubocop_todo.yml"
|
81
|
+
- ".travis.yml"
|
82
|
+
- CHANGELOG.md
|
83
|
+
- CONTRIBUTING.md
|
84
|
+
- Gemfile
|
85
|
+
- LICENSE
|
86
|
+
- README.md
|
87
|
+
- Rakefile
|
88
|
+
- bin/console
|
89
|
+
- bin/setup
|
90
|
+
- dry-auto_inject.gemspec
|
91
|
+
- lib/dry-auto_inject.rb
|
92
|
+
- lib/dry/auto_inject.rb
|
93
|
+
- lib/dry/auto_inject/builder.rb
|
94
|
+
- lib/dry/auto_inject/dependency_map.rb
|
95
|
+
- lib/dry/auto_inject/injector.rb
|
96
|
+
- lib/dry/auto_inject/method_parameters.rb
|
97
|
+
- lib/dry/auto_inject/strategies.rb
|
98
|
+
- lib/dry/auto_inject/strategies/args.rb
|
99
|
+
- lib/dry/auto_inject/strategies/constructor.rb
|
100
|
+
- lib/dry/auto_inject/strategies/hash.rb
|
101
|
+
- lib/dry/auto_inject/strategies/kwargs.rb
|
102
|
+
- lib/dry/auto_inject/version.rb
|
103
|
+
- rakelib/rubocop.rake
|
104
|
+
homepage: https://github.com/dry-rb/dry-auto_inject
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.3.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.0.3
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Container-agnostic automatic constructor injection
|
127
|
+
test_files: []
|