carb-inject 2.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e7e8fdd6dec580c4ea33d02cfb2796409c964c9
4
+ data.tar.gz: 941f73b2350fc7f498d50d09e0dafdb5760a7f2c
5
+ SHA512:
6
+ metadata.gz: 0e22b75b3e7d5790ced046f8637ea6bbaa0431a04e18bfaea3a686921dd00685a343e11c38241fa3227300b4101b0f59d5206ab03e38e9df7aa169f31c154c93
7
+ data.tar.gz: 2d8bfe0487510f41c98f1883e838d395c4c48cc29b1530452ddf359a18fa4410d33cac3be340bff71109c70f545a1f79a19d0972a7553746e57bf9c10504a951
@@ -0,0 +1,40 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ .project
12
+ .directory
13
+ *.swp
14
+ *~
15
+ .DS_Store
16
+ .idea
17
+ .envrc
18
+ .nvmrc
19
+ .ruby-gemset
20
+ .ruby-version
21
+ .rspec-local
22
+ public
23
+ *.backup
24
+ npm-debug.log
25
+ .rspec_status
26
+
27
+ # Ignore bundler config
28
+ /.bundle
29
+ /vendor/bundle
30
+
31
+ # Ignore all logfiles and tempfiles.
32
+ /log/*.log
33
+ /tmp
34
+ /binstubs/
35
+
36
+ # Ignore user import data
37
+ /data
38
+
39
+ # YARD
40
+ .yardoc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in carb-inject.gemspec
4
+ gemspec
@@ -0,0 +1,25 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ Copyright © `2016` `Predictable Revenue, Inc.`
5
+
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the “Software”), to deal in the Software without
9
+ restriction, including without limitation the rights to use,
10
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the
12
+ Software is furnished to do so, subject to the following
13
+ conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,251 @@
1
+ # carb-inject
2
+
3
+ `carb-inject` is an utility library for automated dependency injection.
4
+ Together with a generic container (even a simple `Hash`!), it will cover all
5
+ the needs for an IoC container.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'carb-inject'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install carb-inject
22
+
23
+ ## Glossary
24
+
25
+ <table>
26
+ <tr>
27
+ <th>Term</th>
28
+ <th>Meaning</th>
29
+ </tr>
30
+ <tr>
31
+ <th>Dependency</th>
32
+ <td>
33
+ The actual Object a dependency is (a number for example). Can be
34
+ extracted from the container with <code>container[dependency_name]</code>
35
+ </td>
36
+ </tr>
37
+ <tr>
38
+ <th>Dependency name</th>
39
+ <td>
40
+ An object which allows extracting a <code>Dependency</code> from the
41
+ container
42
+ </td>
43
+ </tr>
44
+ <tr>
45
+ <th>Dependency alias</th>
46
+ <td>
47
+ A symbol representing <code>Dependency name</code>, must be a valid method
48
+ name
49
+ </td>
50
+ </tr>
51
+ <tr>
52
+ <th>Array of dependency names</th>
53
+ <td>
54
+ An array of <code>Dependency name</code>. When passed to the injector,
55
+ every object must support <code>to_s</code> and the returned
56
+ <code>String</code> must be a valid method name
57
+ </td>
58
+ </tr>
59
+ <tr>
60
+ <th>Hash of dependency aliases (or hash of aliases)</th>
61
+ <td>
62
+ A hash consisting of <code>Dependency alias => Dependency name</code>
63
+ </td>
64
+ </tr>
65
+ </table>
66
+
67
+ ## Usage
68
+
69
+ First you'll need a container object.
70
+ [dry-container](https://github.com/dry-rb/dry-container) will do the trick,
71
+ otherwise just check the implementation of
72
+ [Carb::SimpleContainer](https://github.com/Carburetor/carb-inject/blob/b3e9fea68672284aff53c8b78ba0064474d94021/spec/support/simple_container.rb) in
73
+ tests
74
+
75
+ ```ruby
76
+ container = { name: "john", age: 30 }
77
+ ```
78
+
79
+ Create an injector that you'll use across your application. Usually you want to
80
+ put this in a constant
81
+
82
+ ```ruby
83
+ require "carb-inject"
84
+ Inject = Carb::Inject::Injector.new(container)
85
+ ```
86
+
87
+ Then, create a class you want dependencies injected automatically
88
+
89
+ ```ruby
90
+ class JohnPerson
91
+ include Inject[:name, :age]
92
+
93
+ def initialize(**deps)
94
+ inject_dependencies!(deps)
95
+ end
96
+
97
+ def hello
98
+ "Hello I'm #{name}, #{age} years old"
99
+ end
100
+ end
101
+ ```
102
+
103
+ And finally, use the class!
104
+
105
+ ```ruby
106
+ john = JohnPerson.new
107
+ john.hello # => Hello I'm john, 30 years old
108
+ ```
109
+
110
+ You can overwrite dependencies on the fly
111
+
112
+ ```ruby
113
+ john = JohnPerson.new(age: 20)
114
+ john.hello # => Hello I'm john, 20 years old
115
+ ```
116
+
117
+ You can still require different arguments in the constructor
118
+
119
+ ```ruby
120
+ class JohnPerson
121
+ include Inject[:name, :age]
122
+
123
+ def initialize(last_name, **dependencies)
124
+ inject_dependencies!(dependencies)
125
+ @last_name = last_name
126
+ end
127
+
128
+ def hello
129
+ "Hello I'm #{name} #{@last_name}, #{age} years old"
130
+ end
131
+ end
132
+
133
+ john = JohnPerson.new("snow", age: 20)
134
+ john.hello # => Hello I'm john snow, 20 years old
135
+ ```
136
+
137
+ Finally, you can alias dependencies
138
+
139
+ ```ruby
140
+ class JohnPerson
141
+ include Inject[special_name: :name, a_number: :age]
142
+
143
+ def initialize(**deps)
144
+ inject_dependencies!(deps)
145
+ end
146
+
147
+ def hello
148
+ "special_name is #{special_name}, a_number is #{a_number}"
149
+ end
150
+ end
151
+
152
+ john = JohnPerson.new(a_number: 20)
153
+ john.hello # => special_name is john, a_number is 20
154
+ ```
155
+
156
+ Be aware, you can't pass on-the-fly dependencies that were not defined on that
157
+ class. If you do, you must be the one taking care of them!
158
+
159
+ ```ruby
160
+ class JohnPerson
161
+ include Inject[:name]
162
+
163
+ def initialize(**deps)
164
+ inject_dependencies!(deps)
165
+ end
166
+
167
+ def hello
168
+ "Hello I'm #{name}, #{age} years old"
169
+ end
170
+ end
171
+
172
+ john = JohnPerson.new(age: 20)
173
+ john.hello # => NameError: undefined local variable or method `age'
174
+ ```
175
+
176
+ ### Auto invoke inject_dependencies!
177
+
178
+ Instead of manually calling `inject_dependencies!`, you can invoke the
179
+ injector with `true` as second argument. This has the downsides of including
180
+ a module which creates an initializer, with all the consequences it creates
181
+ (some issues with inheritance). It's not recommended, but if you don't use
182
+ inheritance, it does the trick.
183
+
184
+ ```ruby
185
+ require "carb-inject"
186
+
187
+ container = { name: "john", age: 30 }
188
+ Inject = Carb::Inject::Injector.new(container, true)
189
+
190
+ class JohnPerson
191
+ include Inject[:name, :age]
192
+
193
+ def hello
194
+ "Hello I'm #{name}, #{age} years old"
195
+ end
196
+ end
197
+
198
+ john = JohnPerson.new
199
+ john.hello # => Hello I'm john, 30 years old
200
+ ```
201
+
202
+ ### Passing lambdas
203
+
204
+ There is an alternative way to use the library in a _containerless_ fashion.
205
+ You will pass a list of dependency as usual, but instead of aliasing them,
206
+ pass a lambda and it will be resolved when used
207
+
208
+ ```ruby
209
+ require "carb-inject"
210
+
211
+ container = { name: "John", last_name: "Snow" }
212
+ Inject = Carb::Inject::Injector.new(container, true)
213
+
214
+ class JohnPerson
215
+ include Inject[:last_name, foo: :name, age: -> { 30 }]
216
+
217
+ def hello
218
+ "Hello I'm #{foo} #{last_name}, #{age} years old"
219
+ end
220
+ end
221
+
222
+ john = JohnPerson.new
223
+ john.hello # => Hello I'm John Snow, 30 years old
224
+ ```
225
+
226
+ ## Gotchas
227
+
228
+ - Alias hash **must have symbols as keys**
229
+ - Straight dependency names, when used in array and not as values for alias
230
+ hash, must support `to_s` and the resulting `String` must be a valid method
231
+ name (an exception is raised otherwise)
232
+
233
+ ## Features
234
+
235
+ - Supports inheritance (as long as you call `super`)
236
+ - Can write your own injector if you don't like the syntax of the existing one
237
+ - Can alias dependencies
238
+ - Supports any container which responds to `[]`
239
+ - Can write your own initializer with your own arguments (as long as you call
240
+ `super`)
241
+
242
+ ## Development
243
+
244
+ 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.
245
+
246
+ 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).
247
+
248
+ ## Contributing
249
+
250
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Carburetor/carb-inject.
251
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "carb-inject"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "pry-byebug"
14
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "carb/inject/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "carb-inject"
8
+ spec.version = Carb::Inject::VERSION
9
+ spec.authors = ["Fire-Dragon-DoL"]
10
+ spec.email = ["francesco.belladonna@gmail.com"]
11
+
12
+ spec.summary = %q{Utility for automatic dependency injection}
13
+ spec.description = %q{Utility for automatic dependency injection with support for aliases}
14
+ spec.homepage = "https://github.com/Carburetor/carb-inject"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "carb-core", ">= 1.0.0"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.13"
26
+ spec.add_development_dependency "rake", "~> 11.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "pry-byebug"
29
+ end
@@ -0,0 +1 @@
1
+ require "carb/inject"
@@ -0,0 +1,11 @@
1
+ require "carb/inject/version"
2
+ require "carb/inject/dependency_list_cache_name"
3
+ require "carb/inject/dependency_list"
4
+ require "carb/inject/injectable"
5
+ require "carb/inject/injector"
6
+ require "carb/inject/dependency_missing_error"
7
+
8
+ module Carb
9
+ module Inject
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ require "carb"
2
+
3
+ module Carb::Inject
4
+ # Provides an initializer which sets instance variables with names of
5
+ # dependencies and as value the dependency itself.
6
+ # Requires {#inject_dependencies!} to be available
7
+ module AutoInjectable
8
+ # Initializes the object with passed dependencies
9
+ # @param dependencies [Hash] map where key is the name of the dependency
10
+ # and value is the actual dependency being injected
11
+ # @raise [TypeError] raises if doesn't respond to {#inject_dependencies!}
12
+ def initialize(**dependencies)
13
+ unless respond_to?(:inject_dependencies!, true)
14
+ raise TypeError, "#{ self.class } must be injectable"
15
+ end
16
+
17
+ inject_dependencies!(**dependencies)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_missing_error"
3
+
4
+ module Carb::Inject
5
+ # Simple container for holding dependency hashmap with Lambda callable values
6
+ class CallableContainer
7
+ private
8
+
9
+ attr_reader :dependencies
10
+
11
+ public
12
+
13
+ # @param dependencies [Hash{Object => Proc}] dependency name with proc
14
+ # as value, which will be `call`ed to extract the dependency
15
+ def initialize(dependencies)
16
+ @dependencies = dependencies
17
+ end
18
+
19
+ # @param name [Object] dependency name
20
+ # @return [Object] dependency for given name, obtained by calling lambda
21
+ # @raise [DependencyMissingError]
22
+ def [](name)
23
+ return dependencies[name].call if has_key?(name)
24
+
25
+ error_class = ::Carb::Inject::DependencyMissingError
26
+ raise error_class.new(name), format(error_class::MESSAGE, name.to_s)
27
+ end
28
+
29
+ # @param name [Object] dependency name
30
+ # @return [Boolean] true if dependency is present, false otherwise
31
+ def has_key?(name)
32
+ dependencies.has_key?(name)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_missing_error"
3
+
4
+ module Carb::Inject
5
+ # Container which requests dependency in sequence to a list of containers
6
+ # otherwise and if none returns, it raises
7
+ class DelegateContainer
8
+ private
9
+
10
+ attr_reader :containers
11
+
12
+ public
13
+
14
+ # @param containers [Array<#[], #has_key?>] Must have at least one
15
+ # container
16
+ def initialize(*containers)
17
+ @containers = containers
18
+
19
+ if containers.size < 1
20
+ raise ArgumentError, "At least one container is required"
21
+ end
22
+ end
23
+
24
+ # @param name [Object] dependency name
25
+ # @return [Object] dependency for given name if present in any container
26
+ # (only the first in sequence is returned), otherwise raises
27
+ # @raise [DependencyMissingError]
28
+ def [](name)
29
+ containers.each do |container|
30
+ return container[name] if container.has_key?(name)
31
+ end
32
+
33
+ error_class = ::Carb::Inject::DependencyMissingError
34
+ raise error_class.new(name), format(error_class::MESSAGE, name.to_s)
35
+ end
36
+
37
+ # @param name [Object] dependency name
38
+ # @return [Boolean] true if dependency is present in any container, false
39
+ # otherwise
40
+ def has_key?(name)
41
+ containers.any? { |container| container.has_key?(name) }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,96 @@
1
+ require "carb"
2
+ require "carb/inject/injectable"
3
+ require "carb/inject/auto_injectable"
4
+ require "carb/inject/dependency_list_cache_name"
5
+
6
+ module Carb::Inject
7
+ # Provides the list of dependencies required by the object to be initialized
8
+ class DependencyList < Module
9
+ private
10
+
11
+ attr_reader :container
12
+ attr_reader :auto_inject
13
+ attr_reader :dependencies
14
+
15
+ public
16
+
17
+ # Uses the passed container to resolve listed dependencies
18
+ # @param container [#[]] resolve dependencies by using the value of
19
+ # `dependencies` hash passed to this initializer
20
+ # @param auto_inject [Boolean] if true, includes
21
+ # {::Carb::Inject::AutoInjectable} which provides an initializer that
22
+ # inject dependencies automatically
23
+ # @param dependencies [Hash{Symbol => Object}] a hash representing
24
+ # required dependencies for the object. The key represent the alias used
25
+ # to access the real dependency name, which is the value of the hash.
26
+ # If you want to access the dependency using its real name, just set the
27
+ # alias to the real name. Example: `{ alias: :real_name }`
28
+ def initialize(container, auto_inject = true, **dependencies)
29
+ ensure_correct_types!(container, dependencies)
30
+
31
+ @container = container
32
+ @auto_inject = auto_inject
33
+ @dependencies = dependencies
34
+ end
35
+
36
+ def included(klass)
37
+ memoize_dependency_list(klass)
38
+ include_injectable(klass)
39
+ define_readers(klass)
40
+ end
41
+
42
+ # Loops over each available dependency and yields it using its alias and
43
+ # the dependency itself
44
+ # @yieldparam name [Object] alias of the dependency for this dependency_list
45
+ # @yieldparam dependency [Object] the dependency object for this container
46
+ def each
47
+ dependencies.each do |key, value|
48
+ yield(key, container[value])
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def dependency_names
55
+ dependencies.keys
56
+ end
57
+
58
+ def memoize_dependency_list(klass)
59
+ if klass.instance_variable_defined?(DependencyListCacheName)
60
+ raise TypeError, "class already injecting"
61
+ end
62
+
63
+ klass.instance_variable_set(DependencyListCacheName, self)
64
+ end
65
+
66
+ def include_injectable(klass)
67
+ klass.include(::Carb::Inject::Injectable)
68
+ klass.include(::Carb::Inject::AutoInjectable) if auto_inject
69
+ end
70
+
71
+ def define_readers(klass)
72
+ dependencies.each do |name, _|
73
+ define_reader(klass, name.to_s)
74
+ end
75
+ end
76
+
77
+ def define_reader(klass, name)
78
+ unless klass.method_defined?(name)
79
+ klass.send(:attr_reader, name)
80
+ klass.send(:protected, name)
81
+ end
82
+ end
83
+
84
+ def ensure_correct_types!(container, dependencies)
85
+ raise TypeError, "container can't be nil" if container.nil?
86
+ unless container.respond_to?(:[])
87
+ raise TypeError, "container doesn't respond to #[]"
88
+ end
89
+
90
+ raise TypeError, "dependencies can't be nil" if dependencies.nil?
91
+ unless dependencies.respond_to?(:[])
92
+ raise TypeError, "dependencies doesn't respond to #[]"
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,5 @@
1
+ require "carb"
2
+
3
+ module Carb::Inject
4
+ DependencyListCacheName = :@__carb_inject_dependency_list__
5
+ end
@@ -0,0 +1,13 @@
1
+ require "carb"
2
+
3
+ module Carb::Inject
4
+ class DependencyMissingError < StandardError
5
+ MESSAGE = "Dependency %s can't be fetched".freeze
6
+
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_missing_error"
3
+
4
+ module Carb::Inject
5
+ # Container which holds no dependency and will raise every time
6
+ # one is being fetched
7
+ class ErrorContainer
8
+ # This method will always raise an error
9
+ # @param name [Object] dependency name
10
+ # @raise [::Carb::Inject::DependencyMissingError] raised
11
+ # whenever a dependency is being fetched from this container
12
+ def [](name)
13
+ error_class = ::Carb::Inject::DependencyMissingError
14
+ raise error_class.new(name), format(error_class::MESSAGE, name.to_s)
15
+ end
16
+
17
+ # @param name [Object] dependency name
18
+ # @return [false]
19
+ def has_key?(name)
20
+ false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_list_cache_name"
3
+ require "carb/inject/store_dependencies"
4
+
5
+ module Carb::Inject
6
+ # Provides a method which sets instance variables with names of
7
+ # dependencies and as value the dependency itself
8
+ module Injectable
9
+ protected
10
+
11
+ # Inject passed dependencies
12
+ # @param dependencies [Hash] map where key is the name of the dependency
13
+ # and value is the actual dependency being injected
14
+ def inject_dependencies!(**dependencies)
15
+ store_dependencies = StoreDependencies.new
16
+ store_dependencies.(self, **dependencies)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,99 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_list"
3
+ require "carb/inject/error_container"
4
+ require "carb/inject/delegate_container"
5
+ require "carb/inject/callable_container"
6
+
7
+ module Carb::Inject
8
+ # Creates an injector with the specified container
9
+ class Injector
10
+ private
11
+
12
+ attr_reader :container
13
+ attr_reader :auto_inject
14
+
15
+ public
16
+
17
+ # Initialize an injector that can be attached to a constant with the given
18
+ # container
19
+ # @param container [#[], #has_key?] must return dependencies based on
20
+ # dependency name
21
+ # @param auto_inject [Boolean] if true, provides an initializer that auto
22
+ # injects dependencies by including {::Carb::Inject::AutoInjectable},
23
+ # false by default
24
+ def initialize(container = nil, auto_inject = false)
25
+ @container = container || ErrorContainer.new
26
+ @auto_inject = auto_inject
27
+ end
28
+
29
+ # @param dependencies [Array<#to_s>] Array of dependency names, which will
30
+ # be converted using {Object#to_s}, make sure the string version is a
31
+ # valid method name or it will raise, it will be used to create
32
+ # attr_readers on the object
33
+ # @param aliased [Hash{Symbol => Object, Proc}] if value is an {Object},
34
+ # alias => dependency name hash, the alias must be a valid method name
35
+ # or it will raise. The aliases will be used to create attr_readers
36
+ # which will return the dependency from the container.
37
+ # If value is {Proc}, an attr_reader with the key as bane is created and
38
+ # value the output of invoking the {Proc}
39
+ # @return [DependencyList] module which can be included and will take care
40
+ # of automatically injecting not-supplied dependency
41
+ # @raise [ArgumentError] if passed dependencies or aliased_dependencies
42
+ # contain objects not convertible to valid method names
43
+ def [](*dependencies, **aliased)
44
+ deps, lambdas = merge_dependencies(dependencies, aliased)
45
+ wrapper = container
46
+ wrapper = build_delegate(container, lambdas) unless lambdas.empty?
47
+
48
+ Carb::Inject::DependencyList.new(wrapper, auto_inject, **deps)
49
+ end
50
+
51
+ private
52
+
53
+ def build_delegate(container, lambdas)
54
+ callable = CallableContainer.new(lambdas)
55
+ DelegateContainer.new(callable, container)
56
+ end
57
+
58
+ def merge_dependencies(dependencies, aliased_dependencies)
59
+ deps = {}
60
+
61
+ dependencies.each { |name| deps[name] = name }
62
+ lambdas = add_aliased_dependencies(deps, aliased_dependencies)
63
+
64
+ [clean_names(deps), lambdas]
65
+ end
66
+
67
+ def add_aliased_dependencies(deps, aliased_deps)
68
+ aliased_deps.each_with_object({}) do |(alias_name, value), lambdas|
69
+ lambdas[alias_name] = value if value.is_a?(::Proc)
70
+ deps[alias_name] = alias_name if value.is_a?(::Proc)
71
+ deps[alias_name] = value unless value.is_a?(::Proc)
72
+ end
73
+ end
74
+
75
+ def clean_names(dependencies)
76
+ deps = {}
77
+
78
+ dependencies.each do |aliased, name|
79
+ deps[clean!(aliased.to_s)] = name
80
+ end
81
+
82
+ deps
83
+ end
84
+
85
+ def clean!(name)
86
+ clean_name = name.gsub(".", "_").to_sym
87
+
88
+ unless method_name?(clean_name)
89
+ raise ArgumentError, "Invalid dependency name #{ name }"
90
+ end
91
+
92
+ clean_name
93
+ end
94
+
95
+ def method_name?(name)
96
+ /[@$"]/ !~ name.inspect
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,47 @@
1
+ require "carb"
2
+ require "carb/inject/dependency_list_cache_name"
3
+
4
+ module Carb::Inject
5
+ # Store dependencies on the specified object, setting instance variables
6
+ # having as name the key of the hash and as value the value of the hash
7
+ # @api private
8
+ class StoreDependencies
9
+ def call(injectable, **dependencies)
10
+ @injectable = injectable
11
+ @dependencies = dependencies
12
+
13
+ inject_on(injectable.class)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :injectable
19
+ attr_reader :dependencies
20
+
21
+ def inject_on(klass)
22
+ return unless dependency_list_present?(klass)
23
+
24
+ parent_class = klass.superclass
25
+ inject_on(klass.superclass) if dependency_list_present?(parent_class)
26
+
27
+ list = dependency_list_for(klass)
28
+ store_using_dependency_list(list)
29
+ end
30
+
31
+ def store_using_dependency_list(list)
32
+ list.each do |name, dependency|
33
+ # Ensure `nil` is an acceptable dependency
34
+ dependency = dependencies[name] if dependencies.has_key?(name)
35
+ injectable.instance_variable_set(:"@#{ name }", dependency)
36
+ end
37
+ end
38
+
39
+ def dependency_list_present?(klass)
40
+ klass.instance_variable_defined?(DependencyListCacheName)
41
+ end
42
+
43
+ def dependency_list_for(klass)
44
+ klass.instance_variable_get(DependencyListCacheName)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ module Carb
2
+ module Inject
3
+ VERSION = "2.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: carb-inject
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Fire-Dragon-DoL
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: carb-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '11.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '11.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.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Utility for automatic dependency injection with support for aliases
84
+ email:
85
+ - francesco.belladonna@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.md
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - carb-inject.gemspec
100
+ - lib/carb-inject.rb
101
+ - lib/carb/inject.rb
102
+ - lib/carb/inject/auto_injectable.rb
103
+ - lib/carb/inject/callable_container.rb
104
+ - lib/carb/inject/delegate_container.rb
105
+ - lib/carb/inject/dependency_list.rb
106
+ - lib/carb/inject/dependency_list_cache_name.rb
107
+ - lib/carb/inject/dependency_missing_error.rb
108
+ - lib/carb/inject/error_container.rb
109
+ - lib/carb/inject/injectable.rb
110
+ - lib/carb/inject/injector.rb
111
+ - lib/carb/inject/store_dependencies.rb
112
+ - lib/carb/inject/version.rb
113
+ homepage: https://github.com/Carburetor/carb-inject
114
+ licenses: []
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.6.10
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Utility for automatic dependency injection
136
+ test_files: []