dry-auto_inject 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46f037cd532619db63a95ff30c43c93b1146b4dbb8bccd2b803100d8fef8dff5
4
- data.tar.gz: 8e9c7512c0ac99c16b8aa7a5308a241e1714b5239923f4a9876aaa3d9e47abc9
3
+ metadata.gz: 4d3bcb1fb6fde005af77e71608739263f5866bf4cd1b00c4ad7c9cd6d6a6d61a
4
+ data.tar.gz: b8eb767e090882f4a6102c594b7e1da1798c550ea0fbfa734a693c032ee2ba3e
5
5
  SHA512:
6
- metadata.gz: 8a1f3de5df6540384bb87e915e471d0e8470b7848ac0cf29759e2ac9985d428885d733d1376185a2cfcc6d2afdcad7e2567a82dd2d07b9be7cecaeb5d22c740e
7
- data.tar.gz: 07cacefa01e1802cad3290ce9ceee9a9e7611414bcb02593753844bc00ec7e05ff89f990454dc22e4d97cbbcc5f1fef93c96bf1a016689fa9ff4dd8d7226efe3
6
+ metadata.gz: fe6dc512e6e3d0de8ca362cb5d34c069dd3f88b1fd90f77056281c073d659003a687195637ef6fbac24763362dc41cc536a24f7ff98cb5225c34856b855ee431
7
+ data.tar.gz: a3c1629d8437135fd16c5e02599ed6acf1d6bb55fd114f139855cc1c7ea6275bca9f726a59a9e63668698fe2a7c410d7a5a26485a054936ea5f175b62f7186f7
@@ -6,10 +6,10 @@ script:
6
6
  after_success:
7
7
  - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
8
8
  rvm:
9
- - 2.5.0
10
- - 2.4.3
11
- - 2.3.6
12
- - jruby-9.1.10.0
9
+ - 2.5.3
10
+ - 2.4.5
11
+ - 2.3.8
12
+ - jruby-9.2.4.1
13
13
  env:
14
14
  global:
15
15
  - COVERAGE=true
@@ -1,3 +1,19 @@
1
+ # 0.6.0 / 2018-11-29
2
+
3
+ ### Changed
4
+
5
+ - [BREAKING] 0.6.0 supports Ruby 2.3 and above. If you're on 2.3 keep in mind its EOL is scheduled at the end of March, 2019
6
+
7
+ ### Added
8
+
9
+ - Enhanced support for integrating with existing constructors. The kwargs strategy will now pass dependencies up to the next constructor if it accepts an arbitrary number of arguments with `*args`. Note that this change may break existing code though we think it's unlikely to happen. If something doesn't work for you please report and we'll try to sort it out (flash-gordon + timriley in [#48](https://github.com/dry-rb/dry-auto_inject/pull/48))
10
+
11
+ ### Fixed
12
+
13
+ - A couple of regressions were fixed along the way, see [#46](https://github.com/dry-rb/dry-auto_inject/issues/46) and [#49](https://github.com/dry-rb/dry-auto_inject/issues/49) (flash-gordon + timriley in [#48](https://github.com/dry-rb/dry-auto_inject/pull/48))
14
+
15
+ [Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-auto_inject/compare/v0.5.0...v0.6.0)
16
+
1
17
  # 0.5.0 / 2018-11-09
2
18
 
3
19
  ### Changed
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  lib = File.expand_path('../lib', __FILE__)
@@ -20,11 +19,11 @@ Gem::Specification.new do |spec|
20
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
20
  spec.require_paths = ['lib']
22
21
 
23
- spec.required_ruby_version = '>= 2.1.0'
22
+ spec.required_ruby_version = '>= 2.3.0'
24
23
 
25
24
  spec.add_runtime_dependency 'dry-container', '>= 0.3.4'
26
25
 
27
26
  spec.add_development_dependency 'bundler'
28
27
  spec.add_development_dependency 'rake'
29
- spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency 'rspec', '~> 3.8'
30
29
  end
@@ -43,12 +43,4 @@ module Dry
43
43
  def self.AutoInject(container, options = {})
44
44
  AutoInject::Builder.new(container, options)
45
45
  end
46
-
47
- module AutoInject
48
- # @api private
49
- def self.super_method(klass, method)
50
- method = klass.instance_method(method)
51
- method unless method.owner.equal?(klass)
52
- end
53
- end
54
46
  end
@@ -29,11 +29,11 @@ module Dry
29
29
  end
30
30
 
31
31
  def names
32
- @map.keys
32
+ @name ||= @map.keys
33
33
  end
34
34
 
35
35
  def to_h
36
- @map.dup.to_h
36
+ @map.dup
37
37
  end
38
38
  alias_method :to_hash, :to_h
39
39
 
@@ -0,0 +1,92 @@
1
+ require 'set'
2
+
3
+ module Dry
4
+ module AutoInject
5
+ # @api private
6
+ class MethodParameters
7
+ PASS_THROUGH = [[:rest]]
8
+
9
+ if RUBY_VERSION >= '2.4.4.' && !defined? JRUBY_VERSION
10
+ def self.of(obj, name)
11
+ Enumerator.new do |y|
12
+ begin
13
+ method = obj.instance_method(name)
14
+ rescue NameError
15
+ end
16
+
17
+ loop do
18
+ break if method.nil?
19
+
20
+ y << MethodParameters.new(method.parameters)
21
+ method = method.super_method
22
+ end
23
+ end
24
+ end
25
+ else
26
+ # see https://bugs.ruby-lang.org/issues/13973
27
+ def self.of(obj, name)
28
+ Enumerator.new do |y|
29
+ ancestors = obj.ancestors
30
+
31
+ loop do
32
+ klass = ancestors.shift
33
+ break if klass.nil?
34
+
35
+ begin
36
+ method = klass.instance_method(name)
37
+
38
+ next unless method.owner.equal?(klass)
39
+ rescue NameError
40
+ next
41
+ end
42
+
43
+ y << MethodParameters.new(method.parameters)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ attr_reader :parameters
50
+
51
+ def initialize(parameters)
52
+ @parameters = parameters
53
+ end
54
+
55
+ def splat?
56
+ return @splat if defined? @splat
57
+ @splat = parameters.any? { |type, _| type == :rest }
58
+ end
59
+
60
+ def sequential_arguments?
61
+ return @sequential_arguments if defined? @sequential_arguments
62
+ @sequential_arguments = parameters.any? { |type, _|
63
+ type == :req || type == :opt
64
+ }
65
+ end
66
+
67
+ def keyword_names
68
+ @keyword_names ||= parameters.each_with_object(Set.new) { |(type, name), names|
69
+ names << name if type == :key || type == :keyreq
70
+ }
71
+ end
72
+
73
+ def keyword?(name)
74
+ keyword_names.include?(name)
75
+ end
76
+
77
+ def empty?
78
+ parameters.empty?
79
+ end
80
+
81
+ def length
82
+ parameters.length
83
+ end
84
+
85
+ def pass_through?
86
+ parameters.eql?(PASS_THROUGH)
87
+ end
88
+
89
+ EMPTY = new([])
90
+ end
91
+ end
92
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/auto_inject/strategies/constructor'
4
+ require 'dry/auto_inject/method_parameters'
4
5
 
5
6
  module Dry
6
7
  module AutoInject
@@ -22,12 +23,15 @@ module Dry
22
23
  end
23
24
 
24
25
  def define_initialize(klass)
25
- super_method = find_super(klass, :initialize)
26
+ super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
27
+ # Look upwards past `def foo(*)` methods until we get an explicit list of parameters
28
+ break ps unless ps.pass_through?
29
+ end
26
30
 
27
- if super_method.nil? || super_method.parameters.empty?
31
+ if super_parameters.empty?
28
32
  define_initialize_with_params
29
33
  else
30
- define_initialize_with_splat(super_method)
34
+ define_initialize_with_splat(super_parameters)
31
35
  end
32
36
  end
33
37
 
@@ -42,31 +46,20 @@ module Dry
42
46
  RUBY
43
47
  end
44
48
 
45
- def define_initialize_with_splat(super_method)
46
- super_params = if super_method.parameters.any? { |type, _| type == :rest }
49
+ def define_initialize_with_splat(super_parameters)
50
+ super_pass = if super_parameters.splat?
47
51
  '*args'
48
52
  else
49
- "*args[0..#{super_method.parameters.length - 1}]"
53
+ "*args.take(#{super_parameters.length})"
50
54
  end
51
55
 
52
56
  instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
53
57
  def initialize(*args)
54
58
  #{dependency_map.names.map.with_index { |name, i| "@#{name} = args[#{i}]" }.join("\n")}
55
- super(#{super_params})
59
+ super(#{super_pass})
56
60
  end
57
61
  RUBY
58
62
  end
59
-
60
- def find_super(klass, method_name)
61
- super_method = Dry::AutoInject.super_method(klass, method_name)
62
-
63
- # Look upwards past `def foo(*)` methods until we get an explicit list of parameters
64
- while super_method && super_method.parameters == [[:rest]]
65
- super_method = Dry::AutoInject.super_method(super_method.owner, :initialize)
66
- end
67
-
68
- super_method
69
- end
70
63
  end
71
64
 
72
65
  register :args, Args
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/auto_inject/strategies/constructor'
4
+ require 'dry/auto_inject/method_parameters'
4
5
 
5
6
  module Dry
6
7
  module AutoInject
@@ -22,13 +23,13 @@ module Dry
22
23
  end
23
24
 
24
25
  def define_initialize(klass)
25
- super_method = Dry::AutoInject.super_method(klass, :initialize)
26
- super_params = super_method.nil? || super_method.parameters.empty? ? '' : 'options'
26
+ super_params = MethodParameters.of(klass, :initialize).first
27
+ super_pass = super_params.empty? ? '' : 'options'
27
28
 
28
29
  instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
29
30
  def initialize(options)
30
31
  #{dependency_map.names.map { |name| "@#{name} = options[:#{name}] unless !options.key?(#{name}) && instance_variable_defined?(:'@#{name}')" }.join("\n")}
31
- super(#{super_params})
32
+ super(#{super_pass})
32
33
  end
33
34
  RUBY
34
35
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/auto_inject/strategies/constructor'
4
+ require 'dry/auto_inject/method_parameters'
4
5
 
5
6
  module Dry
6
7
  module AutoInject
@@ -11,7 +12,7 @@ module Dry
11
12
 
12
13
  def define_new
13
14
  class_mod.class_exec(container, dependency_map) do |container, dependency_map|
14
- map = dependency_map.to_h.to_a
15
+ map = dependency_map.to_h
15
16
 
16
17
  define_method :new do |*args, **kwargs|
17
18
  map.each do |name, identifier|
@@ -24,59 +25,77 @@ module Dry
24
25
  end
25
26
 
26
27
  def define_initialize(klass)
27
- super_method = Dry::AutoInject.super_method(klass, :initialize)
28
+ super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
29
+ # Look upwards past `def foo(*)` methods until we get an explicit list of parameters
30
+ break ps unless ps.pass_through?
31
+ end
28
32
 
29
- if super_method.nil? || super_method.parameters.empty?
30
- define_initialize_with_keywords
33
+ if super_parameters.splat? || super_parameters.sequential_arguments?
34
+ define_initialize_with_splat(super_parameters)
31
35
  else
32
- define_initialize_with_splat(super_method)
36
+ define_initialize_with_keywords(super_parameters)
33
37
  end
34
38
 
35
39
  self
36
40
  end
37
41
 
38
- def define_initialize_with_keywords
39
- initialize_params = dependency_map.names.map { |name| "#{name}: nil" }.join(", ")
42
+ def define_initialize_with_keywords(super_parameters)
43
+ assign_dependencies = method(:assign_dependencies)
44
+ slice_kwargs = method(:slice_kwargs)
40
45
 
41
- instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
42
- def initialize(#{initialize_params})
43
- #{dependency_map.names.map { |name| "@#{name} = #{name} unless #{name}.nil? && instance_variable_defined?(:'@#{name}')" }.join("\n")}
44
- super()
45
- end
46
- RUBY
46
+ instance_mod.class_exec do
47
+ define_method :initialize do |**kwargs|
48
+ assign_dependencies.(kwargs, self)
47
49
 
48
- self
50
+ super_kwargs = slice_kwargs.(kwargs, super_parameters)
51
+
52
+ if super_kwargs.any?
53
+ super(super_kwargs)
54
+ else
55
+ super()
56
+ end
57
+ end
58
+ end
49
59
  end
50
60
 
51
- def define_initialize_with_splat(super_method)
52
- super_kwarg_names = super_method.parameters.each_with_object([]) { |(type, name), names|
53
- names << name if [:key, :keyreq].include?(type)
54
- }
61
+ def define_initialize_with_splat(super_parameters)
62
+ assign_dependencies = method(:assign_dependencies)
63
+ slice_kwargs = method(:slice_kwargs)
55
64
 
56
- instance_mod.class_exec(dependency_map) do |dependency_map|
65
+ instance_mod.class_exec do
57
66
  define_method :initialize do |*args, **kwargs|
58
- dependency_map.names.each do |name|
59
- # Assign instance variables, but only if the ivar is not
60
- # previously defined (this improves compatibility with objects
61
- # initialized in unconventional ways)
62
- instance_variable_set :"@#{name}", kwargs[name] unless kwargs[name].nil? && instance_variable_defined?(:"@#{name}")
63
- end
64
-
65
- super_kwargs = kwargs.each_with_object({}) { |(key, _), hsh|
66
- if !dependency_map.names.include?(key) || super_kwarg_names.include?(key)
67
- hsh[key] = kwargs[key]
68
- end
69
- }
67
+ assign_dependencies.(kwargs, self)
70
68
 
71
- if super_kwargs.any?
72
- super(*args, **super_kwargs)
69
+ if super_parameters.splat?
70
+ super(*args, kwargs)
73
71
  else
74
- super(*args)
72
+ super_kwargs = slice_kwargs.(kwargs, super_parameters)
73
+
74
+ if super_kwargs.any?
75
+ super(*args, super_kwargs)
76
+ else
77
+ super(*args)
78
+ end
75
79
  end
76
80
  end
77
81
  end
82
+ end
78
83
 
79
- self
84
+ def assign_dependencies(kwargs, destination)
85
+ dependency_map.names.each do |name|
86
+ # Assign instance variables, but only if the ivar is not
87
+ # previously defined (this improves compatibility with objects
88
+ # initialized in unconventional ways)
89
+ if kwargs.key?(name) || !destination.instance_variable_defined?(:"@#{name}")
90
+ destination.instance_variable_set :"@#{name}", kwargs[name]
91
+ end
92
+ end
93
+ end
94
+
95
+ def slice_kwargs(kwargs, super_parameters)
96
+ kwargs.select do |key|
97
+ !dependency_map.names.include?(key) || super_parameters.keyword?(key)
98
+ end
80
99
  end
81
100
  end
82
101
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module AutoInject
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,72 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-auto_inject
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-09 00:00:00.000000000 Z
11
+ date: 2018-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: dry-container
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
18
  version: 0.3.4
20
- type: :runtime
19
+ name: dry-container
21
20
  prerelease: false
21
+ type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.3.4
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
32
  version: '0'
34
- type: :development
33
+ name: bundler
35
34
  prerelease: false
35
+ type: :development
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
46
  version: '0'
48
- type: :development
47
+ name: rake
49
48
  prerelease: false
49
+ type: :development
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - ">="
58
+ - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
60
+ version: '3.8'
61
+ name: rspec
63
62
  prerelease: false
63
+ type: :development
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
69
- description:
68
+ version: '3.8'
69
+ description:
70
70
  email:
71
71
  - piotr.solnica@gmail.com
72
72
  executables: []
@@ -93,6 +93,7 @@ files:
93
93
  - lib/dry/auto_inject/builder.rb
94
94
  - lib/dry/auto_inject/dependency_map.rb
95
95
  - lib/dry/auto_inject/injector.rb
96
+ - lib/dry/auto_inject/method_parameters.rb
96
97
  - lib/dry/auto_inject/strategies.rb
97
98
  - lib/dry/auto_inject/strategies/args.rb
98
99
  - lib/dry/auto_inject/strategies/constructor.rb
@@ -104,7 +105,7 @@ homepage: https://github.com/dryrb/dry-auto_inject
104
105
  licenses:
105
106
  - MIT
106
107
  metadata: {}
107
- post_install_message:
108
+ post_install_message:
108
109
  rdoc_options: []
109
110
  require_paths:
110
111
  - lib
@@ -112,16 +113,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
113
  requirements:
113
114
  - - ">="
114
115
  - !ruby/object:Gem::Version
115
- version: 2.1.0
116
+ version: 2.3.0
116
117
  required_rubygems_version: !ruby/object:Gem::Requirement
117
118
  requirements:
118
119
  - - ">="
119
120
  - !ruby/object:Gem::Version
120
121
  version: '0'
121
122
  requirements: []
122
- rubyforge_project:
123
- rubygems_version: 2.7.7
124
- signing_key:
123
+ rubyforge_project:
124
+ rubygems_version: 2.7.6
125
+ signing_key:
125
126
  specification_version: 4
126
127
  summary: Container-agnostic automatic constructor injection
127
128
  test_files: []