surrogate 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|