surrogate 0.5.5 → 0.6.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.
- data/.rvmrc +0 -3
- data/Changelog.md +21 -0
- data/Readme.md +64 -76
- data/Readme.md.mountain_berry_fields +70 -77
- data/gemfiles/rspec_mocks_2.11 +1 -1
- data/lib/surrogate.rb +1 -1
- data/lib/surrogate/api_comparer.rb +59 -61
- data/lib/surrogate/argument_errorizer.rb +43 -0
- data/lib/surrogate/endower.rb +41 -37
- data/lib/surrogate/hatchery.rb +6 -1
- data/lib/surrogate/hatchling.rb +5 -0
- data/lib/surrogate/method_definition.rb +55 -0
- data/lib/surrogate/porc_reflector.rb +43 -0
- data/lib/surrogate/rspec/invocation_matcher.rb +2 -1
- data/lib/surrogate/rspec/substitute_for.rb +23 -7
- data/lib/surrogate/rspec/with_filter.rb +0 -1
- data/lib/surrogate/surrogate_class_reflector.rb +65 -0
- data/lib/surrogate/surrogate_instance_reflector.rb +15 -0
- data/lib/surrogate/version.rb +1 -1
- data/spec/acceptance_spec.rb +1 -1
- data/spec/defining_api_methods_spec.rb +51 -77
- data/spec/other_shit_spec.rb +131 -0
- data/spec/rspec/block_support_spec.rb +2 -2
- data/spec/rspec/initialization_matcher_spec.rb +12 -4
- data/spec/rspec/rspec_mocks_integration_spec.rb +1 -1
- data/spec/rspec/substitute_for_spec.rb +90 -4
- data/spec/unit/api_comparer_spec.rb +120 -4
- data/spec/unit/argument_errorizer_spec.rb +50 -0
- data/surrogate.gemspec +0 -2
- data/todo +44 -0
- metadata +21 -24
- data/lib/surrogate/options.rb +0 -41
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'set'
|
2
|
+
class Surrogate
|
3
|
+
# reflects on the Plain Old Ruby Class to give info about methods that are useful for the comparer
|
4
|
+
class PorcReflector < Struct.new(:actual)
|
5
|
+
def methods
|
6
|
+
{ instance: {
|
7
|
+
inherited: instance_inherited_methods,
|
8
|
+
other: instance_other_methods,
|
9
|
+
without_bodies: instance_without_bodies,
|
10
|
+
},
|
11
|
+
class: {
|
12
|
+
inherited: class_inherited_methods,
|
13
|
+
other: class_other_methods,
|
14
|
+
without_bodies: class_without_bodies,
|
15
|
+
},
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def instance_inherited_methods
|
20
|
+
Set.new actual.instance_methods - actual.instance_methods(false)
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance_other_methods
|
24
|
+
Set.new(actual.instance_methods) - instance_inherited_methods
|
25
|
+
end
|
26
|
+
|
27
|
+
def class_inherited_methods
|
28
|
+
Set.new actual.singleton_class.instance_methods - actual.singleton_class.instance_methods(false)
|
29
|
+
end
|
30
|
+
|
31
|
+
def class_other_methods
|
32
|
+
Set.new(actual.singleton_class.instance_methods) - class_inherited_methods
|
33
|
+
end
|
34
|
+
|
35
|
+
def class_without_bodies
|
36
|
+
Set.new actual.methods.select { |name| actual.method(name).parameters.any? { |param| param.size == 1 } }
|
37
|
+
end
|
38
|
+
|
39
|
+
def instance_without_bodies
|
40
|
+
Set.new actual.instance_methods.select { |name| actual.instance_method(name).parameters.any? { |param| param.size == 1 } }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'surrogate/rspec/abstract_failure_message'
|
2
2
|
require 'surrogate/rspec/times_predicate'
|
3
3
|
require 'surrogate/rspec/with_filter'
|
4
|
+
require 'surrogate/surrogate_instance_reflector'
|
4
5
|
|
5
6
|
class Surrogate
|
6
7
|
module RSpec
|
@@ -23,7 +24,7 @@ class Surrogate
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def invocations
|
26
|
-
surrogate.invocations(method_name)
|
27
|
+
SurrogateInstanceReflector.new(surrogate).invocations(method_name)
|
27
28
|
end
|
28
29
|
|
29
30
|
def failure_message_for_should
|
@@ -1,16 +1,24 @@
|
|
1
1
|
class Surrogate
|
2
2
|
::RSpec::Matchers.define :substitute_for do |original_class, options={}|
|
3
3
|
|
4
|
-
comparison
|
4
|
+
comparison = nil
|
5
5
|
subset_only = options[:subset]
|
6
|
+
types = options.fetch :types, true
|
7
|
+
|
8
|
+
def comparing_fields(comparison, subset_only, types)
|
9
|
+
fields = {}
|
10
|
+
fields[:instance_not_on_actual ] = comparison[:instance][:not_on_actual]
|
11
|
+
fields[:class_not_on_actual ] = comparison[:class ][:not_on_actual]
|
12
|
+
fields[:instance_not_on_surrogate] = comparison[:instance][:not_on_surrogate] unless subset_only
|
13
|
+
fields[:class_not_on_surrogate ] = comparison[:class ][:not_on_surrogate] unless subset_only
|
14
|
+
fields[:instance_types ] = comparison[:instance][:types] if types
|
15
|
+
fields[:class_types ] = comparison[:class ][:types] if types
|
16
|
+
fields
|
17
|
+
end
|
6
18
|
|
7
19
|
match do |mocked_class|
|
8
20
|
comparison = ApiComparer.new(mocked_class, original_class).compare
|
9
|
-
|
10
|
-
(comparison[:instance][:not_on_actual] + comparison[:class][:not_on_actual]).empty?
|
11
|
-
else
|
12
|
-
(comparison[:instance].values + comparison[:class].values).inject(:+).empty?
|
13
|
-
end
|
21
|
+
comparing_fields(comparison, subset_only, types).values.inject(:+).empty?
|
14
22
|
end
|
15
23
|
|
16
24
|
failure_message_for_should do
|
@@ -18,13 +26,21 @@ class Surrogate
|
|
18
26
|
extra_class_methods = comparison[:class ][:not_on_actual ].to_a
|
19
27
|
missing_instance_methods = comparison[:instance][:not_on_surrogate].to_a
|
20
28
|
missing_class_methods = comparison[:class ][:not_on_surrogate].to_a
|
29
|
+
instance_type_mismatch = comparison[:instance][:types ]
|
30
|
+
class_type_mismatch = comparison[:class ][:types ]
|
31
|
+
|
21
32
|
|
22
33
|
differences = []
|
23
34
|
differences << "has extra instance methods: #{extra_instance_methods.inspect}" if extra_instance_methods.any?
|
24
35
|
differences << "has extra class methods: #{extra_class_methods.inspect}" if extra_class_methods.any?
|
25
36
|
differences << "is missing instance methods: #{missing_instance_methods}" if !subset_only && missing_instance_methods.any?
|
26
37
|
differences << "is missing class methods: #{missing_class_methods}" if !subset_only && missing_class_methods.any?
|
27
|
-
|
38
|
+
|
39
|
+
if types # this conditional is not tested, nor are these error messages
|
40
|
+
instance_type_mismatch.each { |name, types| differences << "##{name} had types #{types.inspect}" }
|
41
|
+
class_type_mismatch.each { |name, types| differences << ".#{name} had types #{types.inspect}" }
|
42
|
+
end
|
43
|
+
"Was not substitutable because surrogate " << differences.join("\n")
|
28
44
|
end
|
29
45
|
|
30
46
|
failure_message_for_should_not do
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'set'
|
2
|
+
class Surrogate
|
3
|
+
|
4
|
+
# Reflects on the surrogate class to give info about methods that are useful for the comparer
|
5
|
+
#
|
6
|
+
# It might make sense to not treat instance and class differently, but instead let whoever wants to use this
|
7
|
+
# instantiate it with both the class and the singleton class.
|
8
|
+
class SurrogateClassReflector < Struct.new(:surrogate_class)
|
9
|
+
def methods
|
10
|
+
{ instance: {
|
11
|
+
api: instance_api_methods,
|
12
|
+
inherited: instance_inherited_methods,
|
13
|
+
other: instance_other_methods,
|
14
|
+
without_bodies: instance_without_bodies,
|
15
|
+
},
|
16
|
+
class: {
|
17
|
+
api: class_api_methods,
|
18
|
+
inherited: class_inherited_methods,
|
19
|
+
other: class_other_methods,
|
20
|
+
without_bodies: class_without_bodies,
|
21
|
+
},
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def instance_api_methods
|
26
|
+
Set.new class_hatchery.api_method_names
|
27
|
+
end
|
28
|
+
|
29
|
+
def instance_inherited_methods
|
30
|
+
Set.new surrogate_class.instance_methods - surrogate_class.instance_methods(false)
|
31
|
+
end
|
32
|
+
|
33
|
+
def instance_other_methods
|
34
|
+
Set.new(surrogate_class.instance_methods false) - instance_api_methods
|
35
|
+
end
|
36
|
+
|
37
|
+
def instance_without_bodies
|
38
|
+
Set.new class_hatchery.api_method_names.reject { |name| class_hatchery.api_method_for name }
|
39
|
+
end
|
40
|
+
|
41
|
+
def class_api_methods
|
42
|
+
Set.new singleton_class_hatchery.api_method_names
|
43
|
+
end
|
44
|
+
|
45
|
+
def class_inherited_methods
|
46
|
+
Set.new surrogate_class.singleton_class.instance_methods - surrogate_class.singleton_class.instance_methods(false)
|
47
|
+
end
|
48
|
+
|
49
|
+
def class_other_methods
|
50
|
+
Set.new(surrogate_class.singleton_class.instance_methods false) - class_api_methods - class_inherited_methods
|
51
|
+
end
|
52
|
+
|
53
|
+
def class_without_bodies
|
54
|
+
Set.new singleton_class_hatchery.api_method_names.reject { |name| singleton_class_hatchery.api_method_for name }
|
55
|
+
end
|
56
|
+
|
57
|
+
def class_hatchery
|
58
|
+
@class_hatchery ||= surrogate_class.instance_variable_get :@hatchery
|
59
|
+
end
|
60
|
+
|
61
|
+
def singleton_class_hatchery
|
62
|
+
@singleton_class_hatchery ||= surrogate_class.singleton_class.instance_variable_get :@hatchery
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Surrogate
|
2
|
+
|
3
|
+
# Utilities for reflecting on surrogate instances
|
4
|
+
#
|
5
|
+
# Primarily it exists to avoid having to pollute the surrogate with reflection methods
|
6
|
+
class SurrogateInstanceReflector < Struct.new(:surrogate)
|
7
|
+
def invocations(method_name)
|
8
|
+
hatchling.invocations(method_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def hatchling
|
12
|
+
@hatchling ||= surrogate.instance_variable_get :@hatchling
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/surrogate/version.rb
CHANGED
data/spec/acceptance_spec.rb
CHANGED
@@ -116,7 +116,7 @@ describe Surrogate do
|
|
116
116
|
|
117
117
|
# real user must have all of mock user's methods to be substitutable
|
118
118
|
substitutable_real_user_class = Class.new do
|
119
|
-
def self.find() end
|
119
|
+
def self.find(id) end
|
120
120
|
def initialize(id) end
|
121
121
|
def id() end
|
122
122
|
def name() end
|
@@ -1,13 +1,25 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'define' do
|
4
|
+
def class_method_names(surrogate)
|
5
|
+
Surrogate::SurrogateClassReflector.new(surrogate).class_api_methods
|
6
|
+
end
|
7
|
+
|
8
|
+
def instance_method_names(surrogate)
|
9
|
+
Surrogate::SurrogateClassReflector.new(surrogate).instance_api_methods
|
10
|
+
end
|
11
|
+
|
12
|
+
def invocations(surrogate, method_name)
|
13
|
+
Surrogate::SurrogateInstanceReflector.new(surrogate).invocations(method_name)
|
14
|
+
end
|
15
|
+
|
4
16
|
let(:mocked_class) { Surrogate.endow Class.new }
|
5
17
|
let(:instance) { mocked_class.new }
|
6
18
|
|
7
19
|
describe 'in the block' do
|
8
20
|
it 'is an api method for the class' do
|
9
21
|
pristine_klass = Class.new { Surrogate.endow(self) { define :find } }
|
10
|
-
pristine_klass.
|
22
|
+
class_method_names(pristine_klass).should == Set[:find]
|
11
23
|
end
|
12
24
|
end
|
13
25
|
|
@@ -15,18 +27,11 @@ describe 'define' do
|
|
15
27
|
describe 'out of the block' do
|
16
28
|
it 'is an api method for the instance' do
|
17
29
|
mocked_class.define :book
|
18
|
-
mocked_class.
|
30
|
+
instance_method_names(mocked_class).should == Set[:book]
|
19
31
|
end
|
20
32
|
end
|
21
33
|
|
22
34
|
describe 'definition default block invocation' do
|
23
|
-
xit "something about raising an error if arity is wrong" do
|
24
|
-
mocked_class.define(:a) { |arg| 1 }
|
25
|
-
mocked_class.new.a.should == 1
|
26
|
-
mocked_class.new.a(2).should == 1
|
27
|
-
mocked_class.new.a(3).should == 1
|
28
|
-
end
|
29
|
-
|
30
35
|
it "is passed the arguments" do
|
31
36
|
arg = nil
|
32
37
|
mocked_class.define(:meth) { |inner_arg| arg = inner_arg }.new.meth(1212)
|
@@ -45,7 +50,6 @@ describe 'define' do
|
|
45
50
|
end
|
46
51
|
|
47
52
|
describe 'declaring the behaviour' do
|
48
|
-
|
49
53
|
describe 'for verbs' do
|
50
54
|
before { mocked_class.define :wink }
|
51
55
|
|
@@ -144,11 +148,11 @@ describe 'define' do
|
|
144
148
|
|
145
149
|
|
146
150
|
context 'the api method' do
|
147
|
-
it '
|
148
|
-
mocked_class.define(:meth) {
|
149
|
-
mocked_class.new.meth.should == 1
|
151
|
+
it 'has the same arity as the method' do
|
152
|
+
mocked_class.define(:meth) { |a| a }
|
150
153
|
mocked_class.new.meth(1).should == 1
|
151
|
-
mocked_class.new.meth
|
154
|
+
expect { mocked_class.new.meth }.to raise_error ArgumentError, /0 for 1/
|
155
|
+
expect { mocked_class.new.meth 1, 2 }.to raise_error ArgumentError, /2 for 1/
|
152
156
|
end
|
153
157
|
|
154
158
|
it 'raises an UnpreparedMethodError when it has no default block' do
|
@@ -177,58 +181,26 @@ describe 'define' do
|
|
177
181
|
mocked.meth.should == 123
|
178
182
|
end
|
179
183
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
specify 'initialize exsits even if error is raised' do
|
187
|
-
mocked_class.define(:initialize) { raise "simulate runtime error" }
|
188
|
-
expect { mocked_class.new }.to raise_error(RuntimeError, /simulate/)
|
189
|
-
expect { mocked_class.new }.to raise_error(RuntimeError, /simulate/)
|
190
|
-
end
|
191
|
-
|
192
|
-
specify 'receives args' do
|
193
|
-
mocked_class.define(:initialize) { |num1, num2| @num = num1 + num2 }
|
194
|
-
mocked_class.new(25, 75).instance_variable_get(:@num).should == 100
|
195
|
-
end
|
196
|
-
|
197
|
-
specify 'even works with inheritance' do
|
198
|
-
superclass = Class.new
|
199
|
-
superclass.send(:define_method, :initialize) { @a = 1 }
|
200
|
-
subclass = Surrogate.endow Class.new superclass
|
201
|
-
subclass.define :abc
|
202
|
-
subclass.new.instance_variable_get(:@a).should == 1
|
203
|
-
end
|
184
|
+
it 'raises arity errors, even if the value is overridden' do
|
185
|
+
mocked_class.define(:meth) { }
|
186
|
+
mocked = mocked_class.new
|
187
|
+
mocked.instance_variable_set :@meth, "abc"
|
188
|
+
expect { mocked.meth "extra", "args" }.to raise_error ArgumentError, /wrong number of arguments \(2 for 0\)/
|
189
|
+
end
|
204
190
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
191
|
+
it 'does not raise arity errors, when there is no default block and the value is overridden' do
|
192
|
+
mocked_class.define :meth
|
193
|
+
mocked = mocked_class.new
|
194
|
+
mocked.instance_variable_set :@meth, "abc"
|
195
|
+
mocked.meth 1, 2, 3
|
196
|
+
end
|
209
197
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
Surrogate.endow self
|
214
|
-
def initialize(a) @a = a end
|
215
|
-
end
|
216
|
-
klass.new(1).should have_been_initialized_with 1
|
217
|
-
klass.new(1).instance_variable_get(:@a).should == 1
|
218
|
-
end
|
219
|
-
|
220
|
-
specify 'even when initialize is defined before surrogate block' do
|
221
|
-
klass = Class.new do
|
222
|
-
def initialize(a) @a = a end
|
223
|
-
Surrogate.endow self
|
224
|
-
end
|
225
|
-
klass.new(1).should have_been_initialized_with 1
|
226
|
-
klass.new(1).instance_variable_get(:@a).should == 1
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
198
|
+
it 'can make #initialize an api method' do
|
199
|
+
mocked_class.define(:initialize) { @abc = 123 }
|
200
|
+
mocked_class.new.instance_variable_get(:@abc).should == 123
|
230
201
|
end
|
231
202
|
|
203
|
+
|
232
204
|
describe 'it takes a block whos return value will be used as the default' do
|
233
205
|
specify 'the block is instance evaled' do
|
234
206
|
mocked_class.define(:meth) { self }
|
@@ -245,32 +217,32 @@ describe 'define' do
|
|
245
217
|
end
|
246
218
|
|
247
219
|
it 'remembers what it was invoked with' do
|
248
|
-
mocked_class.define(:meth) { nil }
|
220
|
+
mocked_class.define(:meth) { |*| nil }
|
249
221
|
mock = mocked_class.new
|
250
222
|
mock.meth 1
|
251
223
|
mock.meth 1, 2
|
252
|
-
|
224
|
+
invocations(mock, :meth).should == [Surrogate::Invocation.new([1]), Surrogate::Invocation.new([1, 2])]
|
253
225
|
|
254
226
|
val = 0
|
255
227
|
mock.meth(1, 2) { val = 3 }
|
256
|
-
expect {
|
228
|
+
expect { invocations(mock, :meth).last.block.call }.to change { val }.from(0).to(3)
|
257
229
|
end
|
258
230
|
|
259
231
|
it 'raises an error if asked about invocations for api methods it does not know' do
|
260
232
|
mocked_class.define :meth1
|
261
233
|
mocked_class.define :meth2
|
262
234
|
mock = mocked_class.new
|
263
|
-
expect { mock
|
264
|
-
expect { mock
|
235
|
+
expect { invocations mock, :meth1 }.to_not raise_error
|
236
|
+
expect { invocations mock, :meth3 }.to raise_error Surrogate::UnknownMethod, /doesn't know "meth3", only knows "meth1", "meth2"/
|
265
237
|
end
|
266
238
|
end
|
267
239
|
|
268
240
|
|
269
241
|
describe 'clone' do
|
270
|
-
|
242
|
+
example 'acceptance spec' do
|
271
243
|
pristine_klass = Class.new do
|
272
244
|
Surrogate.endow self do
|
273
|
-
define(:find) { 123 }
|
245
|
+
define(:find) { |n| 123 }
|
274
246
|
define(:bind) { 'abc' }
|
275
247
|
end
|
276
248
|
define(:peat) { true }
|
@@ -286,7 +258,7 @@ describe 'define' do
|
|
286
258
|
klass2 = pristine_klass.clone
|
287
259
|
klass2.will_find 456
|
288
260
|
klass2.find(2).should == 456
|
289
|
-
klass1.find.should == 123
|
261
|
+
klass1.find(3).should == 123
|
290
262
|
|
291
263
|
klass1.should have_been_told_to(:find).with(1)
|
292
264
|
klass2.should have_been_told_to(:find).with(2)
|
@@ -301,15 +273,17 @@ describe 'define' do
|
|
301
273
|
superclass = Surrogate.endow Class.new
|
302
274
|
superclass.clone.new.should be_a_kind_of superclass
|
303
275
|
end
|
304
|
-
end
|
305
276
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
277
|
+
describe '.name' do
|
278
|
+
it 'is nil for anonymous classes' do
|
279
|
+
Surrogate.endow(Class.new).clone.name.should be_nil
|
280
|
+
end
|
281
|
+
|
282
|
+
it "is the class's name suffixed with '.clone' for named classes" do
|
283
|
+
klass = Surrogate.endow(Class.new)
|
284
|
+
self.class.const_set 'Xyz', klass
|
285
|
+
klass.clone.name.should == self.class.name + '::Xyz.clone'
|
311
286
|
end
|
312
|
-
mocked_class.api_method_names.should == [:abc]
|
313
287
|
end
|
314
288
|
end
|
315
289
|
end
|
data/spec/other_shit_spec.rb
CHANGED
@@ -52,3 +52,134 @@ describe '.last_instance' do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
+
|
56
|
+
|
57
|
+
describe 'inspect methods' do
|
58
|
+
context 'on the class' do
|
59
|
+
context 'when anonymous identifies itself as an anonymous surrogate and lists three each of class and instance methods, alphabetically' do
|
60
|
+
example 'no class methods' do
|
61
|
+
Surrogate.endow(Class.new).inspect.should == 'AnonymousSurrogate(no api)'
|
62
|
+
end
|
63
|
+
|
64
|
+
example 'one class method' do
|
65
|
+
klass = Surrogate.endow(Class.new) { define :cmeth }
|
66
|
+
klass.inspect.should == 'AnonymousSurrogate(Class: cmeth)'
|
67
|
+
end
|
68
|
+
|
69
|
+
example 'more than three class methods' do
|
70
|
+
Surrogate.endow(Class.new) do
|
71
|
+
define :cmethd
|
72
|
+
define :cmethc
|
73
|
+
define :cmetha
|
74
|
+
define :cmethb
|
75
|
+
end.inspect.should == 'AnonymousSurrogate(Class: cmetha cmethb cmethc ...)'
|
76
|
+
end
|
77
|
+
|
78
|
+
example 'one instance method' do
|
79
|
+
Surrogate.endow(Class.new).define(:imeth).inspect.should == 'AnonymousSurrogate(Instance: imeth)'
|
80
|
+
end
|
81
|
+
|
82
|
+
example 'more than three instance methods' do
|
83
|
+
klass = Surrogate.endow Class.new
|
84
|
+
klass.define :imethd
|
85
|
+
klass.define :imethc
|
86
|
+
klass.define :imetha
|
87
|
+
klass.define :imethb
|
88
|
+
klass.inspect.should == 'AnonymousSurrogate(Instance: imetha imethb imethc ...)'
|
89
|
+
end
|
90
|
+
|
91
|
+
example 'one of each' do
|
92
|
+
Surrogate.endow(Class.new) { define :cmeth }.define(:imeth).inspect.should == 'AnonymousSurrogate(Class: cmeth, Instance: imeth)'
|
93
|
+
end
|
94
|
+
|
95
|
+
example 'more than three of each' do
|
96
|
+
klass = Surrogate.endow Class.new do
|
97
|
+
define :cmethd
|
98
|
+
define :cmethc
|
99
|
+
define :cmetha
|
100
|
+
define :cmethb
|
101
|
+
end
|
102
|
+
klass.define :imethd
|
103
|
+
klass.define :imethc
|
104
|
+
klass.define :imetha
|
105
|
+
klass.define :imethb
|
106
|
+
klass.inspect.should == 'AnonymousSurrogate(Class: cmetha cmethb cmethc ..., Instance: imetha imethb imethc ...)'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when the surrogate has a name (e.g. assigned to a constant)' do
|
111
|
+
it 'inspects to the name of the constant' do
|
112
|
+
klass = Surrogate.endow(Class.new)
|
113
|
+
self.class.const_set 'Abc', klass
|
114
|
+
klass.inspect.should == self.class.name << '::Abc'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'on a clone of the surrogate' do
|
120
|
+
context 'when the surrogate has a name (e.g. assigned to a constant)' do
|
121
|
+
it 'inspects to the name of the constant, cloned' do
|
122
|
+
klass = Surrogate.endow(Class.new)
|
123
|
+
self.class.const_set 'Abc', klass
|
124
|
+
klass.clone.inspect.should == self.class.name << '::Abc.clone'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# eventually these should maybe show unique state (expectations/invocations) but for now, this is better than what was there before
|
130
|
+
context 'on the instance' do
|
131
|
+
context 'when anonymous' do
|
132
|
+
it 'identifies itself as an anonymous surrogate and lists three of its methods, alphabetically' do
|
133
|
+
klass = Surrogate.endow Class.new
|
134
|
+
klass.new.inspect.should == '#<AnonymousSurrogate: no api>'
|
135
|
+
klass.define :imethb
|
136
|
+
klass.new.inspect.should == '#<AnonymousSurrogate: imethb>'
|
137
|
+
klass.define :imetha
|
138
|
+
klass.define :imethd
|
139
|
+
klass.new.inspect.should == '#<AnonymousSurrogate: imetha imethb imethd>'
|
140
|
+
klass.define :imethc
|
141
|
+
klass.new.inspect.should == '#<AnonymousSurrogate: imetha imethb imethc ...>'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when a clone of an anonymous surrogate' do
|
146
|
+
it 'looks the same as any other anonymous surrogate' do
|
147
|
+
klass = Surrogate.endow Class.new
|
148
|
+
klass.new.inspect.should == '#<AnonymousSurrogate: no api>'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when its class has a name (e.g. for a constant)' do
|
153
|
+
it 'identifies itself as an instance of the constant and lists three of its methods, alphabetically' do
|
154
|
+
klass = Surrogate.endow Class.new
|
155
|
+
self.class.const_set 'Abc', klass
|
156
|
+
klass.stub name: 'Abc'
|
157
|
+
klass.new.inspect.should == "#<Abc: no api>"
|
158
|
+
klass.define :imethb
|
159
|
+
klass.new.inspect.should == "#<Abc: imethb>"
|
160
|
+
klass.define :imetha
|
161
|
+
klass.define :imethd
|
162
|
+
klass.new.inspect.should == "#<Abc: imetha imethb imethd>"
|
163
|
+
klass.define :imethc
|
164
|
+
klass.new.inspect.should == "#<Abc: imetha imethb imethc ...>"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'on an instance of a surrogate clone' do
|
170
|
+
context 'when its class has a name (e.g. for a constant)' do
|
171
|
+
it 'identifies itself as an instance of the clone of the constant and lists three of its methods, alphabetically' do
|
172
|
+
klass = Surrogate.endow Class.new
|
173
|
+
self.class.const_set 'Abc', klass
|
174
|
+
klass.clone.new.inspect.should == "#<#{self.class}::Abc.clone: no api>"
|
175
|
+
klass.define :imethb
|
176
|
+
klass.clone.new.inspect.should == "#<#{self.class}::Abc.clone: imethb>"
|
177
|
+
klass.define :imetha
|
178
|
+
klass.define :imethd
|
179
|
+
klass.clone.new.inspect.should == "#<#{self.class}::Abc.clone: imetha imethb imethd>"
|
180
|
+
klass.define :imethc
|
181
|
+
klass.clone.new.inspect.should == "#<#{self.class}::Abc.clone: imetha imethb imethc ...>"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|