carb-inject 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []