interjectable 1.0.0 → 1.1.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
  SHA1:
3
- metadata.gz: fe0594cf0a21a6f303ae028952f2922aa4ccc389
4
- data.tar.gz: 19c22afac4eb1264b760d94f3a24b774b2b9ab0c
3
+ metadata.gz: bf87a04b27e693b7dec66842c4c51ea738a7942e
4
+ data.tar.gz: 6c8fdb8852fa3fc8dea1bb3f17ae47dbab4d29b5
5
5
  SHA512:
6
- metadata.gz: 5c433d1585325e8ddd5e40af9cd0b9bc4166eeeccb863b370dbef3da32cba6f604108fbbd6112673324f205034d2e11391c304b0fc9626fe3f926bdd6b949693
7
- data.tar.gz: 611df0ea5914b1d94781024d828a2ae5f76b9c27f2127164e6448449028ca78fcbb701ab984acb89f3c52907ecf7473a62547e7223fca351618d96a5f51f2ace
6
+ metadata.gz: cbe3d00e061ff7b5a39ba5f49564ee58f7cd07b69b9f927b4f2bf766d7cef3656ea452ef5305bbb2c75c4f0280fb1785c190bd2836d8547098af91d378b454ad
7
+ data.tar.gz: 980a68ad0da41b00ad1d0663f049137d9984bcc0be716dd592bd985247396ef8f6fd0657d802fe683415785643433c7fe7993a805cac93fec08844a07ef9ac7b
data/CHANGES.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ # v1.1.0
4
+
5
+ - Add another RSpec helper `test_inject` to avoid needing a local variable for
6
+ the setter block to reference. Again, see the [README.md](README.md) for
7
+ usage.
8
+
3
9
  # v1.0.0
4
10
 
5
11
  - Calling `#inject` or `#inject_static` multiple times is now an error. Use
data/README.md CHANGED
@@ -79,12 +79,12 @@ require "interjectable/rspec"
79
79
  describe A do
80
80
  describe "#read" do
81
81
  before do
82
- instance_double(B, parse: "result").tap do |fake_b|
83
- a.test_inject(:b) { fake_b }
84
- end
85
- instance_double(C, boom: "goat").tap do |fake_c|
86
- a.test_inject(:c) { fake_c }
87
- end
82
+ # You can use the block form of #test_inject to inject a fake object that references methods on a.
83
+ a.test_inject(:b) { FakeB.new(foo) }
84
+
85
+ # You can use the test_inject RSpec helper if you just want to inject an object that doesn't
86
+ # need to reference anything on a.
87
+ test_inject(described_class, :c, instance_double(C, boom: "goat"))
88
88
  end
89
89
 
90
90
  it "parses from its b, and foos from its c" do
@@ -93,6 +93,9 @@ describe A do
93
93
  end
94
94
  end
95
95
 
96
+ # Both Interjectable.test_inject and the RSpec test_inject helper will setup
97
+ # RSpec after hooks to cleanup any test_inject-ed dependencies after the
98
+ # context they are defined in.
96
99
  it "doesn't pollute other tests" do
97
100
  expect(subject.read).to eq(B.new.parse)
98
101
  expect(subject.foo).to eq(C.new.boom)
@@ -2,15 +2,17 @@
2
2
 
3
3
  module Interjectable
4
4
  module ClassMethods
5
+ BLANK = Object.new
6
+
5
7
  class SuperclassInjectStatic < Struct.new(:klass, :dependency)
6
- def override(setter)
8
+ def override(value, &setter)
7
9
  cvar = "@@#{dependency}"
8
10
  klass.remove_class_variable(cvar) if klass.class_variable_defined?(cvar)
9
11
  klass.define_singleton_method(dependency) do
10
12
  if class_variable_defined?(cvar)
11
13
  class_variable_get(cvar)
12
14
  else
13
- class_variable_set(cvar, instance_eval(&setter))
15
+ class_variable_set(cvar, value != ::Interjectable::ClassMethods::BLANK ? value : instance_eval(&setter))
14
16
  end
15
17
  end
16
18
  end
@@ -36,13 +38,13 @@ module Interjectable
36
38
  end
37
39
 
38
40
  class SuperclassInject < Struct.new(:klass, :dependency)
39
- def override(setter)
41
+ def override(value, &setter)
40
42
  ivar = "@#{dependency}"
41
43
  klass.define_method(dependency) do
42
44
  if instance_variable_defined?(ivar)
43
45
  instance_variable_get(ivar)
44
46
  else
45
- instance_variable_set(ivar, instance_eval(&setter))
47
+ instance_variable_set(ivar, value != ::Interjectable::ClassMethods::BLANK ? value : instance_eval(&setter))
46
48
  end
47
49
  end
48
50
  end
@@ -72,31 +74,36 @@ module Interjectable
72
74
  end
73
75
  rspec_example_group = setter.binding.receiver.class
74
76
 
77
+ ClassMethods.test_inject(rspec_example_group, self, dependency, BLANK, &setter)
78
+ end
79
+
80
+ def self.test_inject(rspec_example_group, target, dependency, value, &setter)
81
+ unless value || setter
82
+ raise ArgumentError, "missing value or setter for #{target}'s #{dependency.inspect}"
83
+ end
84
+
75
85
  unless rspec_example_group < RSpec::Core::ExampleGroup
76
86
  raise "#test_inject can only be called from an RSpec ExampleGroup (e.g.: it, before, after)"
77
87
  end
78
88
 
79
- injector = if singleton_methods(false).include?(dependency) # inject_static(dependency) on this class
80
- InjectStatic.new(self, dependency)
81
- elsif singleton_methods.include?(dependency) # inject_static(dependency) on a superclass of this class
82
- SuperclassInjectStatic.new(self, dependency)
83
- elsif instance_methods(false).include?(dependency) # inject(dependency) on this class
84
- Inject.new(self, dependency)
85
- elsif instance_methods.include?(dependency) # inject(dependency) on a superclass of this class
86
- SuperclassInject.new(self, dependency)
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)
87
97
  else
88
98
  raise ArgumentError, "tried to override a non-existent dependency: #{dependency.inspect}"
89
99
  end
90
100
 
91
- injector.override(setter)
92
101
 
93
- scope = rspec_example_group.currently_executing_a_context_hook? ? :context : :each
102
+ injector.override(value, &setter)
94
103
 
95
- key = [self, dependency, scope]
96
- # if dependency == :dependency && scope == :each
97
- # puts "override: #{key.inspect} #{rspec_example_group}"
98
- # end
104
+ scope = rspec_example_group.currently_executing_a_context_hook? ? :context : :each
99
105
 
106
+ key = [target, dependency, scope]
100
107
  # If we already have a restore after(:each) hook for this class +
101
108
  # dependency + scope, don't add another. To check if we already have an
102
109
  # after(:each) hook, we look at all previous after(:each) hooks we've
@@ -107,21 +114,29 @@ module Interjectable
107
114
  # for the same #test_inject call since those before hooks only run once,
108
115
  # and therefore only setup a single after hook.
109
116
  return if scope == :each && RESTORE_HOOKS[key].any? { |group| rspec_example_group <= group }
110
- # if dependency == :dependency && scope == :each
111
- # puts "adding new after=#{key.inspect} hooks=#{RESTORE_HOOKS[key]} group=#{rspec_example_group}"
112
- # end
113
117
  RESTORE_HOOKS[key] << rspec_example_group
114
118
 
115
- # if dependency == :dependency && scope == :each
116
- # puts RESTORE_HOOKS.select { |(_, d, s)| d == :dependency && s == :each }
117
- # end
118
-
119
119
  rspec_example_group.after(scope) do
120
- # if dependency == :dependency && scope == :each
121
- # puts "restore: #{key.inspect} #{rspec_example_group}"
122
- # end
123
120
  injector.restore
124
121
  end
125
122
  end
126
123
  end
124
+
125
+ module RSpecHelper
126
+ def test_inject(target, dependency, value)
127
+ unless value
128
+ raise ArgumentError, "missing value for #{dependency.inspect}, correct usage: test_inject(my_thing, #{dependency.inspect}, FakeDependency.new)"
129
+ end
130
+
131
+ ClassMethods.test_inject(self.class, target, dependency, value)
132
+ end
133
+ end
134
+ end
135
+
136
+ if defined?(RSpec)
137
+ RSpec.configure do |c|
138
+ c.include(Interjectable::RSpecHelper)
139
+ end
140
+ else
141
+ raise "RSpec helper was required but RSpec has not beed defined"
127
142
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Interjectable
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -50,7 +50,7 @@ describe "RSpec test helper #test_inject" do
50
50
  end
51
51
 
52
52
  before do
53
- Klass.test_inject(:dependency) { :another_unused_dependency }
53
+ test_inject(Klass, :dependency, :another_unused_dependency)
54
54
  Klass.test_inject(:dependency) { foo }
55
55
  Klass.test_inject(:static_dependency) { :overriden_static_dependency }
56
56
  end
@@ -75,7 +75,7 @@ describe "RSpec test helper #test_inject" do
75
75
  context "override dependency" do
76
76
  before(:all) do
77
77
  Klass.test_inject(:dependency) { :yet_another_unused_dependency }
78
- Klass.test_inject(:static_dependency) { :unused_static_dependency }
78
+ test_inject(Klass, :static_dependency, :unused_static_dependency)
79
79
  end
80
80
 
81
81
  before do
@@ -143,7 +143,8 @@ describe "RSpec test helper #test_inject" do
143
143
  context "isoloated context: subclass before :all" do
144
144
  before(:all) do
145
145
  SubKlass.test_inject(:static_dependency) { :bar }
146
- SubKlass.test_inject(:static_dependency) { :baz }
146
+ SubKlass.test_inject(:static_dependency) { :zoo }
147
+ test_inject(SubKlass, :static_dependency, :baz)
147
148
  end
148
149
 
149
150
  it "sets the static_dependency" do
@@ -152,7 +153,7 @@ describe "RSpec test helper #test_inject" do
152
153
 
153
154
  context "subcontext" do
154
155
  before(:all) do
155
- SubKlass.test_inject(:static_dependency) { :goat }
156
+ test_inject(SubKlass, :static_dependency, :goat)
156
157
  end
157
158
 
158
159
  it "sets the static_dependency" do
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.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Margolis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-16 00:00:00.000000000 Z
11
+ date: 2018-10-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple dependency injection library for unit testing
14
14
  email: