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 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