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 +4 -4
- data/.circleci/config.yml +31 -0
- data/CHANGES.md +36 -0
- data/README.md +13 -2
- data/lib/interjectable/rspec.rb +30 -29
- data/lib/interjectable/version.rb +1 -1
- data/lib/interjectable.rb +56 -5
- data/spec/interjectable/rspec_spec.rb +121 -15
- data/spec/interjectable_spec.rb +180 -2
- data/spec/spec_helper.rb +1 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e624aa99a5f6994b0c9809839aee1bd44861c288312a6d0787214478726b587b
|
4
|
+
data.tar.gz: d771fc28918888b29df2df4c9fea225e0d74a6dd5bb62860a351de37fcb10816
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
53
|
+
def other_dependency
|
43
54
|
@other_dependency ||= AnotherClass.new
|
44
55
|
end
|
45
56
|
end
|
data/lib/interjectable/rspec.rb
CHANGED
@@ -4,40 +4,40 @@ module Interjectable
|
|
4
4
|
module ClassMethods
|
5
5
|
BLANK = Object.new
|
6
6
|
|
7
|
-
|
7
|
+
SuperclassInjectStatic = Struct.new(:klass, :dependency) do
|
8
8
|
def override(value, &setter)
|
9
|
-
|
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
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
@
|
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
|
-
|
35
|
-
klass.
|
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
|
-
|
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
|
-
@
|
59
|
+
@getter = klass.instance_method(dependency)
|
60
60
|
super
|
61
61
|
end
|
62
62
|
|
63
63
|
def restore
|
64
|
-
klass.send(:define_method, dependency, @
|
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 =
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
data/spec/interjectable_spec.rb
CHANGED
@@ -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(:
|
85
|
-
expect(subclass_instance.
|
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
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.
|
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:
|
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
|
-
|
53
|
-
|
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:
|