injectable 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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(UserService).post_to_wall(params[:message])
128
- respond_with container.get(User)
168
+ container.get(:user_service).post_to_wall(params[:message])
169
+ respond_with container.get(:user)
129
170
  end
130
171
 
131
172
  private
@@ -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(klass.name.underscore.to_sym, klass)
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
@@ -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(UserService)
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
- klass = implementing_class(name)
24
- if instantiated_objects.has_key?(klass)
26
+ classes = registered_implementations(name)
27
+ if klass = instantiated_class(classes)
25
28
  instantiated_objects[klass]
26
29
  else
27
- instantiated_objects[klass] = instantiate(klass)
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
- # Register that instances of klass will perform the given role in this
47
- # container context.
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.1
57
- def register_implementation(name, klass)
58
- implementing_classes[name] = klass
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 instantiate(klass)
70
- klass.new(*dependencies(klass))
85
+ def registered_implementations(name)
86
+ implementations[name] || Registry.implementation(name)
71
87
  end
72
88
 
73
- def instantiated_objects
74
- @instantiated_objects ||= {}
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 implementing_class(name)
78
- implementing_classes[name] || Registry.implementation(name)
98
+ def instantiated_objects
99
+ @instantiated_objects ||= {}
79
100
  end
80
101
 
81
- def implementing_classes
82
- @implementing_classes ||= {}
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
@@ -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] || raise(NotRegistered.new(name))
23
- end
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
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Injectable
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
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.3
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: 2012-12-02 00:00:00.000000000 Z
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: -519657374346971908
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: -519657374346971908
54
+ hash: 1285461619687616243
68
55
  requirements: []
69
56
  rubyforge_project:
70
57
  rubygems_version: 1.8.24