injectable 0.0.5 → 1.0.0

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,53 @@
1
+ module Injectable
2
+ module InstanceMethods
3
+ # Initialize the service with the dependencies injected
4
+ def initialize(args = {})
5
+ check_missing_arguments!(self.class.required_initialize_arguments, args)
6
+ variables_for!(self.class.initialize_arguments, args)
7
+ variables_from_dependencies!(args)
8
+ super()
9
+ end
10
+
11
+ # Entry point of the service.
12
+ # Arguments for this method should be declared explicitly with '.argument'
13
+ # and declare this method without arguments
14
+ def call(args = {})
15
+ check_call_definition!
16
+ check_missing_arguments!(self.class.required_call_arguments, args)
17
+ variables_for!(self.class.call_arguments, args)
18
+ super()
19
+ end
20
+
21
+ private
22
+
23
+ def check_call_definition!
24
+ return if (self.class.ancestors - [Injectable::InstanceMethods]).any? do |ancestor|
25
+ ancestor.instance_methods(false).include?(:call)
26
+ end
27
+ raise NoMethodError, "A #call method with zero arity must be defined in #{self.class}"
28
+ end
29
+
30
+ def check_missing_arguments!(expected, args)
31
+ missing = expected - args.keys
32
+ return if missing.empty?
33
+ raise ArgumentError, "missing keywords: #{missing.join(',')}"
34
+ end
35
+
36
+ def variables_for!(subject, args)
37
+ subject.each do |arg, options|
38
+ instance_variable_set("@#{arg}", args.fetch(arg) { options[:default] })
39
+ end
40
+ end
41
+
42
+ def variables_from_dependencies!(args)
43
+ self.class.dependencies.names.each do |name|
44
+ next if self.class.initialize_arguments.key?(name)
45
+ instance_variable_set("@#{name}", args[name]) if args.key?(name)
46
+ end
47
+ end
48
+
49
+ def dependencies_proxy
50
+ @dependencies_proxy ||= self.class.dependencies.proxy
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ module Injectable
2
+ class MissingDependenciesException < RuntimeError
3
+ end
4
+ end
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  module Injectable
3
- VERSION = "0.0.5"
2
+ VERSION = '1.0.0'
4
3
  end
metadata CHANGED
@@ -1,61 +1,112 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: injectable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
- - Durran Jordan
7
+ - Papipo
8
+ - iovis9
9
+ - jantequera
10
+ - amrocco
9
11
  autorequire:
10
- bindir: bin
12
+ bindir: exe
11
13
  cert_chain: []
12
- date: 2013-01-18 00:00:00.000000000 Z
13
- dependencies: []
14
- description: Dead simple Ruby dependency injection
14
+ date: 2019-05-30 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: bundler
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '12.0'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '12.0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: rspec
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '3.0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '3.0'
58
+ description:
15
59
  email:
16
- - durran@gmail.com
60
+ - rodrigo@rubiconmd.com
61
+ - david.marchante@rubiconmd.com
62
+ - julio@rubiconmd.com
63
+ - anthony@rubiconmd.com
17
64
  executables: []
18
65
  extensions: []
19
66
  extra_rdoc_files: []
20
67
  files:
21
- - lib/injectable/container.rb
22
- - lib/injectable/inflector.rb
23
- - lib/injectable/macros.rb
24
- - lib/injectable/registerable.rb
25
- - lib/injectable/registry.rb
26
- - lib/injectable/version.rb
27
- - lib/injectable.rb
68
+ - ".gitignore"
69
+ - ".rspec"
70
+ - ".travis.yml"
71
+ - CODE_OF_CONDUCT.md
72
+ - Gemfile
73
+ - Gemfile.lock
74
+ - LICENSE.txt
28
75
  - README.md
29
- - LICENSE
30
76
  - Rakefile
31
- homepage:
32
- licenses: []
77
+ - bin/console
78
+ - bin/setup
79
+ - injectable.gemspec
80
+ - lib/injectable.rb
81
+ - lib/injectable/class_methods.rb
82
+ - lib/injectable/dependencies_graph.rb
83
+ - lib/injectable/dependencies_proxy.rb
84
+ - lib/injectable/dependency.rb
85
+ - lib/injectable/instance_methods.rb
86
+ - lib/injectable/missing_dependencies_exception.rb
87
+ - lib/injectable/version.rb
88
+ homepage: https://github.com/rubiconmd/injectable
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
33
92
  post_install_message:
34
93
  rdoc_options: []
35
94
  require_paths:
36
95
  - lib
37
96
  required_ruby_version: !ruby/object:Gem::Requirement
38
- none: false
39
97
  requirements:
40
- - - ! '>='
98
+ - - ">="
41
99
  - !ruby/object:Gem::Version
42
100
  version: '0'
43
- segments:
44
- - 0
45
- hash: -1355353312328350373
46
101
  required_rubygems_version: !ruby/object:Gem::Requirement
47
- none: false
48
102
  requirements:
49
- - - ! '>='
103
+ - - ">="
50
104
  - !ruby/object:Gem::Version
51
105
  version: '0'
52
- segments:
53
- - 0
54
- hash: -1355353312328350373
55
106
  requirements: []
56
107
  rubyforge_project:
57
- rubygems_version: 1.8.24
108
+ rubygems_version: 2.7.6
58
109
  signing_key:
59
- specification_version: 3
60
- summary: Dead simple Ruby dependency injection
110
+ specification_version: 4
111
+ summary: A library to help you build nice service objects with dependency injection.
61
112
  test_files: []
data/LICENSE DELETED
@@ -1,20 +0,0 @@
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.
@@ -1,127 +0,0 @@
1
- # encoding: utf-8
2
- require "injectable/registerable"
3
-
4
- module Injectable
5
-
6
- # A simple container that can resolve dependencies.
7
- #
8
- # @since 0.0.0
9
- class Container
10
- include Registerable
11
-
12
- # Get an instance of an object from the container with the provided class.
13
- #
14
- # @example Get an instance of an object for class UserService.
15
- # container.get(:user_service)
16
- #
17
- # @param [ Symbol ] name the role which the returned object should perform.
18
- #
19
- # @return [ Object ] The instantiated object.
20
- #
21
- # @raise [ Injectable::RoleNotRegistered ] if queried for a role which is not
22
- # registered
23
- #
24
- # @since 0.0.0
25
- def get(name)
26
- classes = registered_implementations(name)
27
- if klass = instantiated_class(classes)
28
- instantiated_objects[klass]
29
- else
30
- object = instantiate(classes)
31
- instantiated_objects[object.class] = object
32
- end
33
- end
34
-
35
- # Create a new container with the objects needed to resolve dependencies
36
- # and create new objects.
37
- #
38
- # @example Create the new container.
39
- # Injectable::Container.new(user, user_finder)
40
- #
41
- # @param [ Array<Object> ] objects The dependencies.
42
- #
43
- # @since 0.0.0
44
- def initialize(*objects)
45
- objects.each do |object|
46
- instantiated_objects[object.class] = object
47
- end
48
- end
49
-
50
- # Put objects into the container. Can be a single or multiple instances.
51
- #
52
- # @example Put an object in the container.
53
- # container.put(user)
54
- #
55
- # @example Put multiple objects in the container.
56
- # container.put(user, user_finder)
57
- #
58
- # @param [ *Object ] objects The objects to put in the container.
59
- #
60
- # @return [ Injectable::Container ] The container itself.
61
- #
62
- # @since 0.0.0
63
- def put(*objects)
64
- objects.flatten.each do |object|
65
- instantiated_objects[object.class] = object
66
- end and self
67
- end
68
-
69
- # This error is raised when asking for an object out of the container that
70
- # cannot be resolved.
71
- #
72
- # @since 0.0.4
73
- class Unresolvable < Exception
74
-
75
- # @attribute [r] classes The classes that were attempted to instantiate.
76
- attr_reader :classes
77
-
78
- # Initialize the new error.
79
- #
80
- # @example Initialize the error.
81
- # Unresolvable.new([ Persistable, Saveable ])
82
- #
83
- # @param [ Array<Class> ] classes The classes that were attempted to
84
- # instantiate with.
85
- #
86
- # @since 0.0.4
87
- def initialize(classes)
88
- @classes = classes
89
- super(
90
- "Could not instantiate an object for any of: #{classes.join(", ")}. " +
91
- "Please ensure all required dependencies are in the container."
92
- )
93
- end
94
- end
95
-
96
- private
97
-
98
- def dependencies(klass)
99
- (Registry.signature(klass) || []).map do |dependency|
100
- get(dependency)
101
- end
102
- end
103
-
104
- def registered_implementations(name)
105
- implementations[name] || Registry.implementation(name)
106
- end
107
-
108
- def instantiate(classes)
109
- classes.each do |klass|
110
- begin
111
- return klass.new(*dependencies(klass))
112
- rescue ArgumentError; end
113
- end
114
- raise Unresolvable.new(classes)
115
- end
116
-
117
- def instantiated_objects
118
- @instantiated_objects ||= {}
119
- end
120
-
121
- def instantiated_class(classes)
122
- classes.detect do |klass|
123
- instantiated_objects[klass]
124
- end
125
- end
126
- end
127
- end
@@ -1,30 +0,0 @@
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
@@ -1,60 +0,0 @@
1
- # encoding: utf-8
2
- module Injectable
3
-
4
- # Provides class level macros for setting up dependencies.
5
- #
6
- # @since 0.0.0
7
- module Macros
8
-
9
- # Sets up the dependencies for the class.
10
- #
11
- # @example Define a UserService that has two dependencies.
12
- # class User; end
13
- # class UserFinder; end
14
- #
15
- # class UserService
16
- # include Injectable
17
- # dependencies :user, :user_finder
18
- # end
19
- #
20
- # @note A constructor will get created for the object that takes the same
21
- # number or arguments as provided to the dependencies macro. The types
22
- # of these arguments must match the "classified" name of the provided
23
- # symbol. For example :user would be a User class, :user_finder would be
24
- # a UserFinder class. Order matters.
25
- #
26
- # @param [ Array<Symbol> ] injectables The dependency list.
27
- #
28
- # @since 0.0.0
29
- def dependencies(*injectables)
30
- define_constructor(*injectables)
31
- define_readers(*injectables)
32
- Registry.register_signature(self, injectables)
33
- end
34
-
35
- private
36
-
37
- def define_constructor(*injectables)
38
- names = names(*injectables)
39
- class_eval <<-CONST
40
- def initialize(#{names})
41
- #{ivars(*injectables)} = #{names}
42
- end
43
- CONST
44
- end
45
-
46
- def define_readers(*injectables)
47
- injectables.each do |dependency|
48
- attr_reader(dependency)
49
- end
50
- end
51
-
52
- def ivars(*injectables)
53
- injectables.map { |name| "@#{name}" }.join(",")
54
- end
55
-
56
- def names(*injectables)
57
- injectables.join(",")
58
- end
59
- end
60
- end