injectable 0.0.3 → 0.0.4
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.
- data/LICENSE +20 -0
- data/README.md +43 -2
- data/lib/injectable.rb +5 -2
- data/lib/injectable/container.rb +47 -24
- data/lib/injectable/inflector.rb +30 -0
- data/lib/injectable/registerable.rb +26 -0
- data/lib/injectable/registry.rb +6 -20
- data/lib/injectable/version.rb +1 -1
- metadata +8 -21
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Durran Jordan
|
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
CHANGED
@@ -86,6 +86,47 @@ user_service = container.get(:user_service)
|
|
86
86
|
# `user_service`'s facebook_service will be an instance of DifferentFacebookService
|
87
87
|
```
|
88
88
|
|
89
|
+
Multiple implementations for the same role can be configured - the rules on how
|
90
|
+
they will be used are as follows: Check for an already instantiated instance in
|
91
|
+
the container. If one or both exist, take the first. If none exist, then attempt to
|
92
|
+
resolve the dependency with each registered role until one succeeds. An example:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
module Portable
|
96
|
+
def charge
|
97
|
+
# Some logic here.
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Phone
|
102
|
+
include Injectable
|
103
|
+
include Portable
|
104
|
+
end
|
105
|
+
|
106
|
+
class Tablet
|
107
|
+
include Injectable
|
108
|
+
include Portable
|
109
|
+
end
|
110
|
+
|
111
|
+
class Application
|
112
|
+
include Injectable
|
113
|
+
dependencies :portable
|
114
|
+
end
|
115
|
+
|
116
|
+
Injectable.configure do |config|
|
117
|
+
config.register_implementation(:portable, Phone, Tablet)
|
118
|
+
end
|
119
|
+
|
120
|
+
container = Injectable::Container.new
|
121
|
+
application = container.get(:application)
|
122
|
+
application.portable #=> Returns a new Phone.
|
123
|
+
|
124
|
+
tablet = Tablet.new
|
125
|
+
container = Injectable::Container.new(tablet)
|
126
|
+
application = container.get(:application)
|
127
|
+
application.portable #=> Returns the existing instance of a Tablet.
|
128
|
+
```
|
129
|
+
|
89
130
|
Setter injection is not supported.
|
90
131
|
|
91
132
|
How about the real world
|
@@ -124,8 +165,8 @@ related to what one might do in a real application.
|
|
124
165
|
class UsersController < ApplicationController
|
125
166
|
|
126
167
|
def post_to_wall
|
127
|
-
container.get(
|
128
|
-
respond_with container.get(
|
168
|
+
container.get(:user_service).post_to_wall(params[:message])
|
169
|
+
respond_with container.get(:user)
|
129
170
|
end
|
130
171
|
|
131
172
|
private
|
data/lib/injectable.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require "active_support/inflector"
|
3
2
|
require "injectable/container"
|
3
|
+
require "injectable/inflector"
|
4
4
|
require "injectable/macros"
|
5
5
|
require "injectable/registry"
|
6
6
|
|
@@ -41,7 +41,10 @@ module Injectable
|
|
41
41
|
#
|
42
42
|
# @since 0.0.0
|
43
43
|
def included(klass)
|
44
|
-
Registry.register_implementation(
|
44
|
+
Registry.register_implementation(
|
45
|
+
Inflector.underscore(klass.name).to_sym,
|
46
|
+
klass
|
47
|
+
)
|
45
48
|
klass.extend(Macros)
|
46
49
|
end
|
47
50
|
end
|
data/lib/injectable/container.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require "injectable/registerable"
|
3
|
+
|
2
4
|
module Injectable
|
3
5
|
|
4
6
|
# A simple container that can resolve dependencies.
|
5
7
|
#
|
6
8
|
# @since 0.0.0
|
7
9
|
class Container
|
10
|
+
include Registerable
|
8
11
|
|
9
12
|
# Get an instance of an object from the container with the provided class.
|
10
13
|
#
|
11
14
|
# @example Get an instance of an object for class UserService.
|
12
|
-
# container.get(
|
15
|
+
# container.get(:user_service)
|
13
16
|
#
|
14
17
|
# @param [ Symbol ] name the role which the returned object should perform.
|
15
18
|
#
|
@@ -20,11 +23,12 @@ module Injectable
|
|
20
23
|
#
|
21
24
|
# @since 0.0.0
|
22
25
|
def get(name)
|
23
|
-
|
24
|
-
if
|
26
|
+
classes = registered_implementations(name)
|
27
|
+
if klass = instantiated_class(classes)
|
25
28
|
instantiated_objects[klass]
|
26
29
|
else
|
27
|
-
|
30
|
+
object = instantiate(classes)
|
31
|
+
instantiated_objects[object.class] = object
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
@@ -43,19 +47,31 @@ module Injectable
|
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# @example Register that the user_finder role will be performed by
|
50
|
-
# instances of DatabaseUserFinder
|
51
|
-
# container.register_implementation(:user_finder, DatabaseUserFinder)
|
52
|
-
#
|
53
|
-
# @param [ Symbol ] name The name of the role.
|
54
|
-
# @param [ Class ] klass The name of the class performing this role.
|
50
|
+
# This error is raised when asking for an object out of the container that
|
51
|
+
# cannot be resolved.
|
55
52
|
#
|
56
|
-
# @since 0.0.
|
57
|
-
|
58
|
-
|
53
|
+
# @since 0.0.4
|
54
|
+
class Unresolvable < Exception
|
55
|
+
|
56
|
+
# @attribute [r] classes The classes that were attempted to instantiate.
|
57
|
+
attr_reader :classes
|
58
|
+
|
59
|
+
# Initialize the new error.
|
60
|
+
#
|
61
|
+
# @example Initialize the error.
|
62
|
+
# Unresolvable.new([ Persistable, Saveable ])
|
63
|
+
#
|
64
|
+
# @param [ Array<Class> ] classes The classes that were attempted to
|
65
|
+
# instantiate with.
|
66
|
+
#
|
67
|
+
# @since 0.0.4
|
68
|
+
def initialize(classes)
|
69
|
+
@classes = classes
|
70
|
+
super(
|
71
|
+
"Could not instantiate an object for any of: #{classes.join(", ")}. " +
|
72
|
+
"Please ensure all required dependencies are in the container."
|
73
|
+
)
|
74
|
+
end
|
59
75
|
end
|
60
76
|
|
61
77
|
private
|
@@ -66,20 +82,27 @@ module Injectable
|
|
66
82
|
end
|
67
83
|
end
|
68
84
|
|
69
|
-
def
|
70
|
-
|
85
|
+
def registered_implementations(name)
|
86
|
+
implementations[name] || Registry.implementation(name)
|
71
87
|
end
|
72
88
|
|
73
|
-
def
|
74
|
-
|
89
|
+
def instantiate(classes)
|
90
|
+
classes.each do |klass|
|
91
|
+
begin
|
92
|
+
return klass.new(*dependencies(klass))
|
93
|
+
rescue ArgumentError; end
|
94
|
+
end
|
95
|
+
raise Unresolvable.new(classes)
|
75
96
|
end
|
76
97
|
|
77
|
-
def
|
78
|
-
|
98
|
+
def instantiated_objects
|
99
|
+
@instantiated_objects ||= {}
|
79
100
|
end
|
80
101
|
|
81
|
-
def
|
82
|
-
|
102
|
+
def instantiated_class(classes)
|
103
|
+
classes.detect do |klass|
|
104
|
+
instantiated_objects[klass]
|
105
|
+
end
|
83
106
|
end
|
84
107
|
end
|
85
108
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Injectable
|
3
|
+
|
4
|
+
# Used so we don't need active support around to perform inflections.
|
5
|
+
#
|
6
|
+
# @since 0.0.4
|
7
|
+
module Inflector
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# Underscore a string. This is partially taken from Active Support, but
|
12
|
+
# dumbed down for our purposes - we don't handle namespacing.
|
13
|
+
#
|
14
|
+
# @example Underscore a string.
|
15
|
+
# Inflector.underscore("Band")
|
16
|
+
#
|
17
|
+
# @param [ String ] string The string to underscore.
|
18
|
+
#
|
19
|
+
# @since 0.0.4
|
20
|
+
#
|
21
|
+
# @return [ String ] The underscored string.
|
22
|
+
def underscore(string)
|
23
|
+
value = string.dup
|
24
|
+
value.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
25
|
+
value.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
26
|
+
value.downcase!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Injectable
|
3
|
+
module Registerable
|
4
|
+
|
5
|
+
# Register that instances of klass will perform the given role in this
|
6
|
+
# container context.
|
7
|
+
#
|
8
|
+
# @example Register that the user_finder role will be performed by
|
9
|
+
# instances of DatabaseUserFinder
|
10
|
+
# container.register_implementation(:user_finder, DatabaseUserFinder)
|
11
|
+
#
|
12
|
+
# @param [ Symbol ] name The name of the role.
|
13
|
+
# @param [ *Class ] classes The names of the classes performing this role.
|
14
|
+
#
|
15
|
+
# @since 0.0.1
|
16
|
+
def register_implementation(name, *classes)
|
17
|
+
implementations[name] = classes.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def implementations
|
23
|
+
@implementations ||= {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/injectable/registry.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require "injectable/registerable"
|
3
|
+
|
2
4
|
module Injectable
|
3
5
|
|
4
6
|
# The registry keeps track of all objects and their dependencies that need
|
@@ -6,6 +8,7 @@ module Injectable
|
|
6
8
|
#
|
7
9
|
# @since 0.0.0
|
8
10
|
module Registry
|
11
|
+
include Registerable
|
9
12
|
extend self
|
10
13
|
|
11
14
|
# Get an implementation for the provided name.
|
@@ -19,22 +22,9 @@ module Injectable
|
|
19
22
|
#
|
20
23
|
# @since 0.0.2
|
21
24
|
def implementation(name)
|
22
|
-
implementations[name]
|
23
|
-
|
24
|
-
|
25
|
-
# Add an implementing class for a name to the registry.
|
26
|
-
#
|
27
|
-
# @example Add an implementation.
|
28
|
-
# Injectable::Registry.register_implementation(
|
29
|
-
# :persistable, User
|
30
|
-
# )
|
31
|
-
#
|
32
|
-
# @param [ Symbol ] name The name of the implementation.
|
33
|
-
# @param [ Class ] klass The implementing class.
|
34
|
-
#
|
35
|
-
# @since 0.0.2
|
36
|
-
def register_implementation(name, klass)
|
37
|
-
implementations[name] = klass
|
25
|
+
impl = implementations[name]
|
26
|
+
raise(NotRegistered.new(name)) unless impl && !impl.empty?
|
27
|
+
impl
|
38
28
|
end
|
39
29
|
|
40
30
|
# Add a constructor method signature to the registry.
|
@@ -92,10 +82,6 @@ module Injectable
|
|
92
82
|
|
93
83
|
private
|
94
84
|
|
95
|
-
def implementations
|
96
|
-
@implementations ||= {}
|
97
|
-
end
|
98
|
-
|
99
85
|
def signatures
|
100
86
|
@signatures ||= {}
|
101
87
|
end
|
data/lib/injectable/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: injectable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: activesupport
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '3'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '3'
|
12
|
+
date: 2013-01-17 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
30
14
|
description: Dead simple Ruby dependency injection
|
31
15
|
email:
|
32
16
|
- durran@gmail.com
|
@@ -35,11 +19,14 @@ extensions: []
|
|
35
19
|
extra_rdoc_files: []
|
36
20
|
files:
|
37
21
|
- lib/injectable/container.rb
|
22
|
+
- lib/injectable/inflector.rb
|
38
23
|
- lib/injectable/macros.rb
|
24
|
+
- lib/injectable/registerable.rb
|
39
25
|
- lib/injectable/registry.rb
|
40
26
|
- lib/injectable/version.rb
|
41
27
|
- lib/injectable.rb
|
42
28
|
- README.md
|
29
|
+
- LICENSE
|
43
30
|
- Rakefile
|
44
31
|
homepage:
|
45
32
|
licenses: []
|
@@ -55,7 +42,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
42
|
version: '0'
|
56
43
|
segments:
|
57
44
|
- 0
|
58
|
-
hash:
|
45
|
+
hash: 1285461619687616243
|
59
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
47
|
none: false
|
61
48
|
requirements:
|
@@ -64,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
51
|
version: '0'
|
65
52
|
segments:
|
66
53
|
- 0
|
67
|
-
hash:
|
54
|
+
hash: 1285461619687616243
|
68
55
|
requirements: []
|
69
56
|
rubyforge_project:
|
70
57
|
rubygems_version: 1.8.24
|