injectable 0.0.5 → 1.0.0

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