interjectable 1.1.2 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f02525e04eadb6abb3c2520558035e54e5b4171ebb67d9a32c246166b1859485
4
- data.tar.gz: 964ce3d8b1cbcaa0b4d51b2a1f13739a90f958ef60cec8762225a201733b326b
3
+ metadata.gz: e624aa99a5f6994b0c9809839aee1bd44861c288312a6d0787214478726b587b
4
+ data.tar.gz: d771fc28918888b29df2df4c9fea225e0d74a6dd5bb62860a351de37fcb10816
5
5
  SHA512:
6
- metadata.gz: 63c8bcc8d1ecdd0de16867e69ce4fe991da81e5d3f995f11e9d9281d5c05b588a0735b18c115d80692666bdcd878ceb3a598624523d16ecd6cff6e71792dd2ea
7
- data.tar.gz: 42b09b04c4553b33dd41a1055e73faa5931a124ace98f5935318571670aede005220fa0205c9564aef7b278afda261c014403d50b0eb919a73e9cab623399d87
6
+ metadata.gz: 158e96c14b09efe755a3c21a79a6c975ca66fb0a0eb5f07fe6652639163ff2eb61cb91003c53d459e321bd88d86fe27451901c461eb54b0453548f2f54af43fb
7
+ data.tar.gz: e2dc0909265a90c82f134ed09704742fc9fc65d2cb44721803788625c303f6c5eb72393d04f1a6cb867fbc4c09c0ffbd9533372702e933f3fac108253158b6a5
@@ -0,0 +1,31 @@
1
+ version: 2.1
2
+
3
+ orbs:
4
+ ruby: circleci/ruby@2.1.1
5
+
6
+ jobs:
7
+ build:
8
+ docker:
9
+ - image: cimg/ruby:3.3
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: Which bundler?
14
+ command: bundle -v
15
+ - ruby/install-deps
16
+ test:
17
+ docker:
18
+ - image: cimg/ruby:3.3
19
+ steps:
20
+ - checkout
21
+ - ruby/install-deps
22
+ - ruby/rspec-test:
23
+ include: spec/**/*_spec.rb
24
+
25
+ workflows:
26
+ tests:
27
+ jobs:
28
+ - build
29
+ - test:
30
+ requires:
31
+ - build
data/CHANGES.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Change Log
2
2
 
3
+ # v1.3.0
4
+
5
+ - Add an `injected_methods(include_super = true)` instance and singleton helper method to track what dependency methods
6
+ have been created. This method includes itself in the list of injected methods. The instance method will return both
7
+ injected and static injected methods, while the singleton method will only return static injected methods.
8
+
9
+ # v1.2.0
10
+
11
+ - Ruby 3.x made it an error to override a class variable in a parent class. There was a bug with `inject_static` where
12
+ if a subclass was the first to call a static dependency, the class variable would only be set on that subclass (and
13
+ children of that subsclass). If the injecting class then called the static dependency, it would override the already
14
+ set child's class variable, which is now an error.
15
+
16
+ ```ruby
17
+ class Parent
18
+ include Interjectable
19
+ inject_static(:boom) { "goats" }
20
+ end
21
+
22
+ class Child < Parent; end
23
+
24
+ Child.boom # => sets Child's @@boom = "goats"
25
+ Parent.boom # => sets Parent's @@boom = "goats" and *clear* Child's @@boom.
26
+ Child.boom # => Error on Ruby 3.x because you are trying to read an overriden class variable.
27
+ ```
28
+
29
+ Fix: always set the class variable on the class that called `inject_static`.
30
+
31
+ # v1.1.3
32
+
33
+ - Fix `test_inject` for sub-sub-classes.
34
+
35
+ # v1.1.2
36
+
37
+ - Fix visibility issue with `Module.define_method` for Ruby < 2.5.0.
38
+
3
39
  # v1.1.1
4
40
 
5
41
  - Fix typo in RSpec helper loading error message
data/README.md CHANGED
@@ -12,7 +12,8 @@ gem 'interjectable'
12
12
 
13
13
  ## Usage
14
14
 
15
- Interjectable has one module (`Interjectable`) and two methods. Use them like so!
15
+ Interjectable has one module (`Interjectable`) and two main methods for defining dependencies,
16
+ `inject` and `inject_static`. Use them like so!
16
17
 
17
18
  ```ruby
18
19
  class MyClass
@@ -28,6 +29,16 @@ class MyClass
28
29
  end
29
30
  ```
30
31
 
32
+ It also includes introspection method `injected_methods(include_super = true)` (both instance and class-level)
33
+ to track what dependency methods have been created.
34
+
35
+ ```ruby
36
+ MyClass.injected_methods
37
+ # => [:injected_methods, :shared_value, :shared_value=]
38
+ MyClass.new.injected_methods
39
+ # => [:injected_methods, :dependency, :dependency=, :other_dependency, :other_dependency=, :shared_value, :shared_value=]
40
+ ```
41
+
31
42
  This replaces a pattern we've used before, adding default dependencies in the constructor, or as memoized methods.
32
43
 
33
44
  ```ruby
@@ -39,7 +50,7 @@ class MyClass
39
50
  @dependency=dependency
40
51
  end
41
52
 
42
- def other_depency
53
+ def other_dependency
43
54
  @other_dependency ||= AnotherClass.new
44
55
  end
45
56
  end
@@ -4,40 +4,40 @@ module Interjectable
4
4
  module ClassMethods
5
5
  BLANK = Object.new
6
6
 
7
- class SuperclassInjectStatic < Struct.new(:klass, :dependency)
7
+ SuperclassInjectStatic = Struct.new(:klass, :dependency) do
8
8
  def override(value, &setter)
9
- cvar = "@@#{dependency}"
10
- klass.remove_class_variable(cvar) if klass.class_variable_defined?(cvar)
9
+ var = ::Interjectable::ClassMethods::BLANK
11
10
  klass.define_singleton_method(dependency) do
12
- if class_variable_defined?(cvar)
13
- class_variable_get(cvar)
14
- else
15
- class_variable_set(cvar, value != ::Interjectable::ClassMethods::BLANK ? value : instance_eval(&setter))
16
- end
11
+ return var if var != ::Interjectable::ClassMethods::BLANK
12
+
13
+ var = value != ::Interjectable::ClassMethods::BLANK ? value : instance_eval(&setter)
14
+ end
15
+
16
+ klass.define_singleton_method("#{dependency}=") do |new_value|
17
+ var = new_value
17
18
  end
18
19
  end
19
20
 
20
21
  def restore
21
- cvar = "@@#{dependency}"
22
- klass.remove_class_variable(cvar) if klass.class_variable_defined?(cvar)
23
22
  klass.singleton_class.remove_method(dependency)
23
+ klass.singleton_class.remove_method("#{dependency}=")
24
24
  end
25
25
  end
26
26
 
27
27
  class InjectStatic < SuperclassInjectStatic
28
28
  def override(*)
29
- @meth = klass.singleton_method(dependency)
29
+ @getter = klass.singleton_method(dependency)
30
+ @setter = klass.singleton_method("#{dependency}=")
30
31
  super
31
32
  end
32
33
 
33
34
  def restore
34
- cvar = "@@#{dependency}"
35
- klass.remove_class_variable(cvar) if klass.class_variable_defined?(cvar)
36
- klass.define_singleton_method(dependency, @meth)
35
+ klass.define_singleton_method(dependency, @getter)
36
+ klass.define_singleton_method("#{dependency}=", @setter)
37
37
  end
38
38
  end
39
39
 
40
- class SuperclassInject < Struct.new(:klass, :dependency)
40
+ SuperclassInject = Struct.new(:klass, :dependency) do
41
41
  def override(value, &setter)
42
42
  ivar = "@#{dependency}"
43
43
  klass.send(:define_method, dependency) do
@@ -56,12 +56,12 @@ module Interjectable
56
56
 
57
57
  class Inject < SuperclassInject
58
58
  def override(*)
59
- @meth = klass.instance_method(dependency)
59
+ @getter = klass.instance_method(dependency)
60
60
  super
61
61
  end
62
62
 
63
63
  def restore
64
- klass.send(:define_method, dependency, @meth)
64
+ klass.send(:define_method, dependency, @getter)
65
65
  end
66
66
  end
67
67
 
@@ -86,18 +86,18 @@ module Interjectable
86
86
  raise "#test_inject can only be called from an RSpec ExampleGroup (e.g.: it, before, after)"
87
87
  end
88
88
 
89
- injector = if target.singleton_methods(false).include?(dependency) # inject_static(dependency) on this class
90
- InjectStatic.new(target, dependency)
91
- elsif target.singleton_methods.include?(dependency) # inject_static(dependency) on a superclass of this class
92
- SuperclassInjectStatic.new(target, dependency)
93
- elsif target.instance_methods(false).include?(dependency) # inject(dependency) on this class
94
- Inject.new(target, dependency)
95
- elsif target.instance_methods.include?(dependency) # inject(dependency) on a superclass of this class
96
- SuperclassInject.new(target, dependency)
97
- else
98
- raise ArgumentError, "tried to override a non-existent dependency: #{dependency.inspect}"
99
- end
100
-
89
+ injector =
90
+ if target.singleton_methods(false).include?(dependency) # inject_static(dependency) on this class
91
+ InjectStatic.new(target, dependency)
92
+ elsif target.singleton_methods.include?(dependency) # inject_static(dependency) on a superclass of this class
93
+ SuperclassInjectStatic.new(target, dependency)
94
+ elsif target.instance_methods(false).include?(dependency) # inject(dependency) on this class
95
+ Inject.new(target, dependency)
96
+ elsif target.instance_methods.include?(dependency) # inject(dependency) on a superclass of this class
97
+ SuperclassInject.new(target, dependency)
98
+ else
99
+ raise ArgumentError, "tried to override a non-existent dependency: #{dependency.inspect}"
100
+ end
101
101
 
102
102
  injector.override(value, &setter)
103
103
 
@@ -114,6 +114,7 @@ module Interjectable
114
114
  # for the same #test_inject call since those before hooks only run once,
115
115
  # and therefore only setup a single after hook.
116
116
  return if scope == :each && RESTORE_HOOKS[key].any? { |group| rspec_example_group <= group }
117
+
117
118
  RESTORE_HOOKS[key] << rspec_example_group
118
119
 
119
120
  rspec_example_group.after(scope) do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Interjectable
4
- VERSION = "1.1.2"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/interjectable.rb CHANGED
@@ -7,10 +7,34 @@ module Interjectable
7
7
 
8
8
  def self.included(mod)
9
9
  mod.send(:extend, ClassMethods)
10
+ mod.send(:include, InstanceMethods)
10
11
  end
11
12
 
12
13
  def self.extended(mod)
13
14
  mod.send(:extend, ClassMethods)
15
+ mod.send(:include, InstanceMethods)
16
+ end
17
+
18
+ module InstanceMethods
19
+ def injected_methods(include_super = true)
20
+ injected = self.class.instance_variable_get(:@injected_methods).to_a +
21
+ self.class.instance_variable_get(:@static_injected_methods).to_a
22
+
23
+ if include_super
24
+ super_injected = self.class.ancestors.flat_map do |klass|
25
+ klass.instance_variable_get(:@injected_methods).to_a +
26
+ klass.instance_variable_get(:@static_injected_methods).to_a
27
+ end
28
+
29
+ [
30
+ :injected_methods,
31
+ *super_injected,
32
+ *injected,
33
+ ].uniq
34
+ else
35
+ [:injected_methods, *injected]
36
+ end
37
+ end
14
38
  end
15
39
 
16
40
  module ClassMethods
@@ -42,6 +66,9 @@ module Interjectable
42
66
  instance_variable_set(ivar_name, instance_eval(&default_block))
43
67
  end
44
68
  end
69
+
70
+ @injected_methods ||= []
71
+ @injected_methods += [dependency, :"#{dependency}="]
45
72
  end
46
73
 
47
74
  # Defines helper methods on instances that memoize values per-class.
@@ -64,27 +91,51 @@ module Interjectable
64
91
  raise MethodAlreadyDefined, "#{dependency} is already defined"
65
92
  end
66
93
 
94
+ injecting_class = self
95
+
67
96
  cvar_name = :"@@#{dependency}"
68
97
  setter = :"#{dependency}="
69
98
 
70
99
  define_method(setter) do |value|
71
- self.class.send(setter, value)
100
+ injecting_class.send(setter, value)
72
101
  end
73
102
 
74
103
  define_singleton_method(setter) do |value|
75
- class_variable_set(cvar_name, value)
104
+ injecting_class.class_variable_set(cvar_name, value)
76
105
  end
77
106
 
78
107
  define_method(dependency) do
79
- self.class.send(dependency)
108
+ injecting_class.send(dependency)
80
109
  end
81
110
 
82
111
  define_singleton_method(dependency) do
83
112
  if class_variable_defined?(cvar_name)
84
- class_variable_get(cvar_name)
113
+ injecting_class.class_variable_get(cvar_name)
85
114
  else
86
- class_variable_set(cvar_name, instance_eval(&default_block))
115
+ injecting_class.class_variable_set(cvar_name, instance_eval(&default_block))
116
+ end
117
+ end
118
+
119
+ @static_injected_methods ||= []
120
+ @static_injected_methods += [dependency, :"#{dependency}="]
121
+ end
122
+
123
+ # @return [Array<Symbol>]
124
+ def injected_methods(include_super = true)
125
+ injected = @static_injected_methods.to_a
126
+
127
+ if include_super
128
+ super_injected = ancestors.flat_map do |klass|
129
+ klass.instance_variable_get(:@static_injected_methods).to_a
87
130
  end
131
+
132
+ [
133
+ :injected_methods,
134
+ *super_injected,
135
+ *injected,
136
+ ].uniq
137
+ else
138
+ [:injected_methods, *injected]
88
139
  end
89
140
  end
90
141
  end
@@ -2,19 +2,30 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe "RSpec test helper #test_inject" do
6
- class Klass
7
- extend Interjectable
8
- inject(:dependency) { :dependency }
9
- inject_static(:static_dependency) { :static_dependency }
5
+ class Klass
6
+ extend Interjectable
7
+ inject(:dependency) { :dependency }
8
+ inject_static(:static_dependency) { :static_dependency }
10
9
 
11
- define_method(:foo) { :foo }
12
- end
13
- SubKlass = Class.new(Klass)
10
+ define_method(:foo) { :foo }
11
+ end
12
+ SubKlass = Class.new(Klass)
13
+ SubSubKlass = Class.new(SubKlass)
14
14
 
15
+ describe "RSpec test helper #test_inject" do
15
16
  let(:instance) { Klass.new }
16
17
  let(:subklass_instance) { SubKlass.new }
17
18
 
19
+ before(:all) do
20
+ Klass.static_dependency = :boom
21
+ SubKlass.static_dependency = :boom1
22
+ SubSubKlass.static_dependency = :boom2
23
+ expect(Klass.static_dependency).to eq(:boom2)
24
+ expect(SubKlass.static_dependency).to eq(:boom2)
25
+ expect(SubSubKlass.static_dependency).to eq(:boom2)
26
+ Klass.static_dependency = :static_dependency
27
+ end
28
+
18
29
  after(:all) do
19
30
  # Sanity check after running the tests, verify #test_inject cleans up after itself
20
31
  instance = Klass.new
@@ -25,12 +36,16 @@ describe "RSpec test helper #test_inject" do
25
36
  expect(subklass_instance.dependency).to eq(:dependency)
26
37
  expect(subklass_instance.static_dependency).to eq(:static_dependency)
27
38
  expect(SubKlass.static_dependency).to eq(:static_dependency)
28
-
29
- # Don't leak our test classes
30
- Object.instance_eval do
31
- remove_const(:SubKlass)
32
- remove_const(:Klass)
33
- end
39
+ subsubklass_instance = SubSubKlass.new
40
+ expect(subsubklass_instance.dependency).to eq(:dependency)
41
+ expect(subsubklass_instance.static_dependency).to eq(:static_dependency)
42
+ expect(SubSubKlass.static_dependency).to eq(:static_dependency)
43
+ Klass.static_dependency = :boom
44
+ SubKlass.static_dependency = :boom1
45
+ SubSubKlass.static_dependency = :boom2
46
+ expect(Klass.static_dependency).to eq(:boom2)
47
+ expect(SubKlass.static_dependency).to eq(:boom2)
48
+ expect(SubSubKlass.static_dependency).to eq(:boom2)
34
49
  end
35
50
 
36
51
  context "isoloated context" do
@@ -64,6 +79,15 @@ describe "RSpec test helper #test_inject" do
64
79
  it "still respects instance setters" do
65
80
  instance.dependency = :bar
66
81
  expect(instance.dependency).to eq(:bar)
82
+ expect(Klass.new.dependency).to eq(:foo)
83
+ end
84
+
85
+ it "still respects static setters in the overriden test_injected context only" do
86
+ instance.static_dependency = :bar
87
+ expect(instance.static_dependency).to eq(:bar)
88
+ expect(Klass.static_dependency).to eq(:bar)
89
+ expect(SubKlass.static_dependency).to eq(:bar)
90
+ expect(SubSubKlass.static_dependency).to eq(:bar)
67
91
  end
68
92
 
69
93
  it "errors if you inject a non static default into a static injection" do
@@ -101,7 +125,7 @@ describe "RSpec test helper #test_inject" do
101
125
  SubKlass.test_inject(:static_dependency) { :subklass_double_overriden_static_dependency }
102
126
  end
103
127
 
104
- it "sets the dependency" do
128
+ pending "sets the dependency", aggregate_failures: true do
105
129
  expect(subklass_instance.dependency).to eq(:subklass_override)
106
130
  expect(subklass_instance.static_dependency).to eq(:subklass_double_overriden_static_dependency)
107
131
  expect(Klass.static_dependency).to eq(:double_overriden_static_dependency)
@@ -195,4 +219,86 @@ describe "RSpec test helper #test_inject" do
195
219
  expect { Klass.test_inject(:bad_dependency) { 1 } }.to raise_error(ArgumentError)
196
220
  end
197
221
  end
222
+
223
+ context "double subclass" do
224
+ context "1" do
225
+ it "sets the dependency" do
226
+ calls = 0
227
+ Klass.test_inject(:dependency) { calls += 1; :baz }
228
+ expect(Klass.new.dependency).to eq(:baz)
229
+ subklass = SubKlass.new
230
+ expect(subklass.dependency).to eq(:baz)
231
+ expect(subklass.dependency).to eq(:baz)
232
+ expect(SubSubKlass.new.dependency).to eq(:baz)
233
+ expect(calls).to eq(3)
234
+ end
235
+ end
236
+
237
+ context "2" do
238
+ it "sets the dependency" do
239
+ calls = 0
240
+ SubKlass.test_inject(:dependency) { calls += 1; :baz }
241
+ expect(Klass.new.dependency).to eq(:dependency)
242
+ subklass = SubKlass.new
243
+ expect(subklass.dependency).to eq(:baz)
244
+ expect(subklass.dependency).to eq(:baz)
245
+ expect(SubSubKlass.new.dependency).to eq(:baz)
246
+ expect(calls).to eq(2)
247
+ end
248
+ end
249
+
250
+ context "3" do
251
+ it "sets the dependency" do
252
+ sub_klass_calls = 0
253
+ sub_sub_klass_calls = 0
254
+ SubKlass.test_inject(:dependency) { sub_klass_calls += 1; :bar }
255
+ SubSubKlass.test_inject(:dependency) { sub_sub_klass_calls += 1; :baz }
256
+ expect(Klass.new.dependency).to eq(:dependency)
257
+ subklass = SubKlass.new
258
+ expect(subklass.dependency).to eq(:bar)
259
+ expect(subklass.dependency).to eq(:bar)
260
+ expect(SubSubKlass.new.dependency).to eq(:baz)
261
+ expect(sub_klass_calls).to eq(1)
262
+ expect(sub_sub_klass_calls).to eq(1)
263
+ end
264
+ end
265
+ end
266
+
267
+ context "double subclass static" do
268
+ context "1" do
269
+ it "sets the dependency" do
270
+ calls = 0
271
+ Klass.test_inject(:static_dependency) { calls += 1; :baz }
272
+ expect(Klass.new.static_dependency).to eq(:baz)
273
+ expect(SubKlass.new.static_dependency).to eq(:baz)
274
+ expect(SubSubKlass.new.static_dependency).to eq(:baz)
275
+ expect(calls).to eq(1)
276
+ end
277
+ end
278
+
279
+ context "2" do
280
+ pending "sets the dependency", aggregate_failures: true do
281
+ calls = 0
282
+ SubKlass.test_inject(:static_dependency) { calls += 1; :baz }
283
+ expect(Klass.new.static_dependency).to eq(:static_dependency)
284
+ expect(SubKlass.new.static_dependency).to eq(:baz)
285
+ expect(SubSubKlass.new.static_dependency).to eq(:baz)
286
+ expect(calls).to eq(1)
287
+ end
288
+ end
289
+
290
+ context "3" do
291
+ pending "sets the dependency", aggregate_failures: true do
292
+ sub_klass_calls = 0
293
+ sub_sub_klass_calls = 0
294
+ SubKlass.test_inject(:static_dependency) { sub_klass_calls += 1; :bar }
295
+ SubSubKlass.test_inject(:static_dependency) { sub_sub_klass_calls += 1; :baz }
296
+ expect(Klass.new.static_dependency).to eq(:static_dependency)
297
+ expect(SubKlass.new.static_dependency).to eq(:bar)
298
+ expect(SubSubKlass.new.static_dependency).to eq(:baz)
299
+ expect(sub_klass_calls).to eq(1)
300
+ expect(sub_sub_klass_calls).to eq(1)
301
+ end
302
+ end
303
+ end
198
304
  end
@@ -81,8 +81,24 @@ describe Interjectable do
81
81
  let(:subclass_instance) { subclass.new }
82
82
 
83
83
  it "does not error if the method exists on the superclass" do
84
- subclass.inject(:dependency) { :some_other_value }
85
- expect(subclass_instance.dependency).to eq(:some_other_value)
84
+ subclass.inject(:some_dependency) { :some_other_value }
85
+ expect(subclass_instance.some_dependency).to eq(:some_other_value)
86
+ end
87
+
88
+ it "allows injection on the subclass without injecting on the superclass" do
89
+ subclass.inject(:subclass_dependency) { :brand_new_value }
90
+ expect(subclass_instance.subclass_dependency).to eq(:brand_new_value)
91
+ expect { instance.subclass_dependency }.to raise_error(NoMethodError)
92
+ end
93
+
94
+ context "with a chain of subclasses" do
95
+ let(:lower_subclass) { Class.new(subclass) }
96
+ let(:lower_subclass_instance) { lower_subclass.new }
97
+
98
+ it "retrieves injected methods from all ancestors when requested" do
99
+ subclass.inject(:subclass_dependency) { :subclass_value }
100
+ lower_subclass.inject(:lower_subclass_dependency) { :lower_subclass_value }
101
+ end
86
102
  end
87
103
  end
88
104
  end
@@ -170,6 +186,168 @@ describe Interjectable do
170
186
  expect(subclass_instance.static_dependency).to eq(:some_other_value)
171
187
  expect(subclass.static_dependency).to eq(:some_other_value)
172
188
  end
189
+
190
+ it "does not error when lazily setting the dep from a subclass first" do
191
+ expect(subclass.static_dependency).to eq(:some_value)
192
+ expect(klass.static_dependency).to eq(:some_value)
193
+ expect(subclass.static_dependency).to eq(:some_value)
194
+ end
195
+
196
+ it "only defines the class variable on the injecting class" do
197
+ expect(subclass.static_dependency).to eq(:some_value)
198
+ expect(klass.class_variable_get(:@@static_dependency)).to eq(:some_value)
199
+ end
200
+
201
+ it "allows injection on the subclass without injecting on the superclass" do
202
+ subclass.inject_static(:static_subclass_dependency) { :brand_new_value }
203
+ expect(subclass_instance.static_subclass_dependency).to eq(:brand_new_value)
204
+ expect { klass.static_subclass_dependency }.to raise_error(NoMethodError)
205
+ expect { instance.static_subclass_dependency }.to raise_error(NoMethodError)
206
+ end
207
+
208
+ context "with a chain of subclasses" do
209
+ let(:lower_subclass) { Class.new(subclass) }
210
+ let(:lower_subclass_instance) { lower_subclass.new }
211
+
212
+ it "retrieves injected methods from all ancestors when requested" do
213
+ subclass.inject_static(:static_subclass_dependency) { :subclass_value }
214
+ lower_subclass.inject_static(:static_lower_subclass_dependency) { :lower_subclass_value }
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ describe "#injected_methods" do
221
+ before do
222
+ klass.inject(:a) { :a }
223
+ klass.inject_static(:b) { :b }
224
+ end
225
+
226
+ it "lists injected methods on the instance and static ones too" do
227
+ injected_methods = instance.injected_methods
228
+
229
+ expect(injected_methods).to match_array(
230
+ [
231
+ :injected_methods, :a, :a=, :b, :b=,
232
+ ],
233
+ )
234
+ end
235
+
236
+ context "with a subclass" do
237
+ let(:subclass) do
238
+ Class.new(klass) do
239
+ inject(:c) { :c }
240
+ end
241
+ end
242
+ let(:include_super) { true }
243
+ let(:subclass_instance) { subclass.new }
244
+
245
+ it "includes super methods by default" do
246
+ injected_methods = subclass_instance.injected_methods(include_super)
247
+
248
+ expect(injected_methods).to match_array(
249
+ [
250
+ :injected_methods,
251
+ :a,
252
+ :a=,
253
+ :b,
254
+ :b=,
255
+ :c,
256
+ :c=,
257
+ ],
258
+ )
259
+ end
260
+
261
+ context "with include_super = false" do
262
+ let(:include_super) { false }
263
+
264
+ it "does not include super methods" do
265
+ injected_methods = subclass_instance.injected_methods(include_super)
266
+
267
+ expect(injected_methods).to_not include(:a)
268
+ expect(injected_methods).to_not include(:a=)
269
+ expect(injected_methods).to_not include(:b)
270
+ expect(injected_methods).to_not include(:b=)
271
+
272
+ expect(injected_methods).to match_array(
273
+ [
274
+ :injected_methods,
275
+ :c,
276
+ :c=,
277
+ ],
278
+ )
279
+ end
280
+ end
281
+ end
282
+ end
283
+
284
+ describe ".injected_methods" do
285
+ before do
286
+ klass.inject(:a) { :a }
287
+ klass.inject_static(:b) { :b }
288
+ end
289
+
290
+ it "lists static injected methods class" do
291
+ injected_methods = klass.injected_methods
292
+
293
+ expect(injected_methods).to match_array(
294
+ [
295
+ :injected_methods, :b, :b=,
296
+ ],
297
+ )
298
+ expect(injected_methods).to_not include(:a)
299
+ expect(injected_methods).to_not include(:a=)
300
+ end
301
+
302
+ context "with a subclass" do
303
+ let(:subclass) do
304
+ Class.new(klass) do
305
+ inject(:c) { :c }
306
+ inject_static(:d) { :d }
307
+ end
308
+ end
309
+ let(:include_super) { true }
310
+
311
+ it "includes super methods by default" do
312
+ injected_methods = subclass.injected_methods(include_super)
313
+
314
+ expect(injected_methods).to match_array(
315
+ [
316
+ :injected_methods,
317
+ :b,
318
+ :b=,
319
+ :d,
320
+ :d=
321
+ ],
322
+ )
323
+ expect(injected_methods).to_not include(:a), 'skips instance methods'
324
+ expect(injected_methods).to_not include(:a=), 'skips instance methods'
325
+ expect(injected_methods).to_not include(:c), 'skips instance methods'
326
+ expect(injected_methods).to_not include(:c=), 'skips instance methods'
327
+ end
328
+
329
+ context "with include_super = false" do
330
+ let(:include_super) { false }
331
+
332
+ it "does not include super methods" do
333
+ injected_methods = subclass.injected_methods(include_super)
334
+
335
+ expect(injected_methods).to_not include(:a), 'skips instance methods'
336
+ expect(injected_methods).to_not include(:a=), 'skips instance methods'
337
+ expect(injected_methods).to_not include(:b), 'skips super methods'
338
+ expect(injected_methods).to_not include(:b=), 'skips super methods'
339
+ expect(injected_methods).to_not include(:c), 'skips instance methods'
340
+ expect(injected_methods).to_not include(:c=), 'skips instance methods'
341
+
342
+ expect(injected_methods).to match_array(
343
+ [
344
+ :injected_methods,
345
+ :d,
346
+ :d=,
347
+ ],
348
+ )
349
+ end
350
+ end
173
351
  end
174
352
  end
175
353
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pry'
3
4
  require 'rspec'
4
5
 
5
6
  $LOAD_PATH.unshift(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interjectable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Margolis
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-11 00:00:00.000000000 Z
11
+ date: 2024-07-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple dependency injection library for unit testing
14
14
  email:
@@ -17,6 +17,7 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".circleci/config.yml"
20
21
  - ".gitignore"
21
22
  - CHANGES.md
22
23
  - Gemfile
@@ -34,7 +35,7 @@ homepage: https://github.com/zachmargolis/interjectable
34
35
  licenses:
35
36
  - MIT
36
37
  metadata: {}
37
- post_install_message:
38
+ post_install_message:
38
39
  rdoc_options: []
39
40
  require_paths:
40
41
  - lib
@@ -49,9 +50,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
50
  - !ruby/object:Gem::Version
50
51
  version: '0'
51
52
  requirements: []
52
- rubyforge_project:
53
- rubygems_version: 2.7.8
54
- signing_key:
53
+ rubygems_version: 3.4.6
54
+ signing_key:
55
55
  specification_version: 4
56
56
  summary: A simple dependency injection library for unit testing
57
57
  test_files: