flexmock 2.3.0 → 2.3.1

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
  SHA1:
3
- metadata.gz: 471129d58809ac2dc9a53b22254f76e352f5b253
4
- data.tar.gz: 3ac4914a786029b8cb1c10750b28f7de602c984b
3
+ metadata.gz: 16f8df99b548903abdd25fef26a98df2906448bf
4
+ data.tar.gz: 7ce30d1e5ada1a4d679e53db575bd3d942b8e836
5
5
  SHA512:
6
- metadata.gz: dcab0eed825f701d642cce8286b9816b087b765beff23fe39205841e1289ecdfc80284a14d59cbbfb90a27d5050c50b25618fd5f27a6f9db4367e2b0b739360c
7
- data.tar.gz: 8e97cc8946bb2fac86b6b69cb01dec2c42a2cb3aa67d0307dc696e94228bccd66f82cabe1a8c6bdb92b096ba91b3a6889b898e40af714cbc7c7d071b0496a4aa
6
+ metadata.gz: 7ff6a1a9172b9df71a8d5d3c8717304cad88d7947ea212c45417f53106001b71587879bfcd9684301440fc766db0a48552f5fc80b81dbd55e4d65c81acc55a23
7
+ data.tar.gz: e93acaa91ace46df1fd14ead3c4790b857a654fb901d6a4d08193eae607abf65eeefa78e199b5e034855c72752d5b7bb824aa2d88455b229c740842dc2e8871a
data/lib/flexmock/core.rb CHANGED
@@ -45,7 +45,7 @@ class FlexMock
45
45
  include Ordering
46
46
 
47
47
  attr_reader :flexmock_name
48
- attr_accessor :flexmock_container
48
+ attr_reader :flexmock_container_stack
49
49
 
50
50
  class << self
51
51
  attr_accessor :partials_are_based
@@ -54,21 +54,50 @@ class FlexMock
54
54
  self.partials_are_based = false
55
55
  self.partials_verify_signatures = false
56
56
 
57
+ # Null object for {#parent_mock}
58
+ class NullParentMock
59
+ def flexmock_expectations_for(sym)
60
+ end
61
+ end
62
+
57
63
  # Create a FlexMock object with the given name. The name is used in
58
64
  # error messages. If no container is given, create a new, one-off
59
65
  # container for this mock.
60
- def initialize(name="unknown", container=nil)
66
+ def initialize(name="unknown", container=nil, parent: nil)
61
67
  @flexmock_name = name
62
68
  @flexmock_closed = false
69
+ @flexmock_container_stack = Array.new
63
70
  @expectations = Hash.new
64
- @ignore_missing = false
65
71
  @verified = false
66
72
  @calls = []
67
73
  @base_class = nil
74
+ if parent
75
+ @ignore_missing = parent.ignore_missing?
76
+ @parent_mock = parent
77
+ else
78
+ @ignore_missing = false
79
+ @parent_mock = NullParentMock.new
80
+ end
68
81
  container = UseContainer.new if container.nil?
69
82
  container.flexmock_remember(self)
70
83
  end
71
84
 
85
+ def ignore_missing?
86
+ @ignore_missing
87
+ end
88
+
89
+ def flexmock_container
90
+ flexmock_container_stack.last
91
+ end
92
+
93
+ def push_flexmock_container(container)
94
+ flexmock_container_stack.push(container)
95
+ end
96
+
97
+ def pop_flexmock_container
98
+ flexmock_container_stack.pop
99
+ end
100
+
72
101
  # Return the inspection string for a mock.
73
102
  def inspect
74
103
  "<FlexMock:#{flexmock_name}>"
@@ -117,8 +146,8 @@ class FlexMock
117
146
  flexmock_wrap do
118
147
  if flexmock_closed?
119
148
  FlexMock.undefined
120
- elsif handler = @expectations[sym]
121
- handler.call(enhanced_args, call_record)
149
+ elsif exp = flexmock_expectations_for(sym)
150
+ exp.call(enhanced_args, call_record)
122
151
  elsif @base_class && @base_class.flexmock_defined?(sym)
123
152
  FlexMock.undefined
124
153
  elsif @ignore_missing
@@ -139,13 +168,14 @@ class FlexMock
139
168
 
140
169
  # Find the mock expectation for method sym and arguments.
141
170
  def flexmock_find_expectation(method_name, *args) # :nodoc:
142
- exp = @expectations[method_name]
143
- exp ? exp.find_expectation(*args) : nil
171
+ if exp = flexmock_expectations_for(method_name)
172
+ exp.find_expectation(*args)
173
+ end
144
174
  end
145
175
 
146
176
  # Return the expectation director for a method name.
147
177
  def flexmock_expectations_for(method_name) # :nodoc:
148
- @expectations[method_name]
178
+ @expectations[method_name] || @parent_mock.flexmock_expectations_for(method_name)
149
179
  end
150
180
 
151
181
  def flexmock_base_class
@@ -183,9 +213,9 @@ class FlexMock
183
213
 
184
214
  # Override the built-in +method+ to include the mocked methods.
185
215
  def method(method_name)
186
- @expectations[method_name] || super
216
+ flexmock_expectations_for(method_name) || super
187
217
  rescue NameError => ex
188
- if @ignore_missing
218
+ if ignore_missing?
189
219
  proc { FlexMock.undefined }
190
220
  else
191
221
  raise ex
@@ -218,10 +248,11 @@ class FlexMock
218
248
  # Using +location+, define the expectations specified by +args+.
219
249
  def flexmock_define_expectation(location, *args)
220
250
  @last_expectation = EXP_BUILDER.parse_should_args(self, args) do |method_name|
221
- @expectations[method_name] ||= ExpectationDirector.new(method_name)
251
+ exp = flexmock_expectations_for(method_name) || ExpectationDirector.new(method_name)
252
+ @expectations[method_name] = exp
222
253
  result = Expectation.new(self, method_name, location)
223
- @expectations[method_name] << result
224
- override_existing_method(method_name) if flexmock_respond_to?(method_name)
254
+ exp << result
255
+ override_existing_method(method_name) if flexmock_respond_to?(method_name, true)
225
256
  result = ExplicitNeeded.new(result, method_name, @base_class) if
226
257
  @base_class && ! @base_class.flexmock_defined?(method_name)
227
258
  result
@@ -143,11 +143,6 @@ class FlexMock
143
143
  @count_validators.all? { |v| v.eligible?(@actual_count) }
144
144
  end
145
145
 
146
- # Is this expectation constrained by any call counts?
147
- def call_count_constrained?
148
- ! @count_validators.empty?
149
- end
150
-
151
146
  # Validate that the order
152
147
  def validate_order
153
148
  if @order_number
@@ -55,7 +55,7 @@ class FlexMock
55
55
  elsif names.size > 1
56
56
  create_demeter_chain(mock, names)
57
57
  else
58
- fail "Empty list of names"
58
+ fail ArgumentError, "Empty list of names"
59
59
  end
60
60
  end
61
61
 
@@ -52,6 +52,7 @@ class FlexMock
52
52
  def flexmock_close
53
53
  flexmock_created_mocks.each do |m|
54
54
  m.flexmock_teardown
55
+ m.pop_flexmock_container
55
56
  end
56
57
  @flexmock_created_mocks = []
57
58
  end
@@ -127,7 +128,7 @@ class FlexMock
127
128
  def flexmock_remember(mocking_object)
128
129
  @flexmock_created_mocks ||= []
129
130
  @flexmock_created_mocks << mocking_object
130
- mocking_object.flexmock_container = self
131
+ mocking_object.push_flexmock_container(self)
131
132
  mocking_object
132
133
  end
133
134
 
@@ -28,27 +28,64 @@ class FlexMock
28
28
 
29
29
  attr_reader :mock
30
30
 
31
- ProxyBox = Struct.new :proxy
31
+ # Boxing of the flexmock proxy
32
+ #
33
+ # It is managed as a stack in order to allow to setup containers recursively
34
+ # (as e.g. FlexMock.use( ... ) checks)
35
+ class ProxyBox
36
+ attr_reader :stack
37
+
38
+ Element = Struct.new :proxy, :container
39
+
40
+ def initialize
41
+ @stack = [Element.new]
42
+ end
43
+
44
+ # Tests whether the given container is the one on which the current proxy
45
+ # acts
46
+ def container
47
+ stack.last.container
48
+ end
49
+
50
+ def proxy
51
+ stack.last.proxy
52
+ end
53
+
54
+ def push(proxy, container)
55
+ stack.push(Element.new(proxy, container))
56
+ end
57
+
58
+ def pop
59
+ if !stack.empty?
60
+ stack.pop
61
+ end
62
+ end
63
+
64
+ def empty?
65
+ stack.size == 1
66
+ end
67
+ end
32
68
 
33
69
  # Make a partial mock proxy and install it on the target +obj+.
34
70
  def self.make_proxy_for(obj, container, name, safe_mode)
35
71
  name ||= "flexmock(#{obj.class.to_s})"
36
- if ! proxy_defined_on?(obj)
37
- mock = FlexMock.new(name, container)
38
- proxy = PartialMockProxy.new(obj, mock, safe_mode)
39
- if obj.instance_variable_defined?("@flexmock_proxy")
40
- obj.instance_variable_get("@flexmock_proxy").proxy = proxy
41
- else
42
- obj.instance_variable_set("@flexmock_proxy", ProxyBox.new(proxy))
43
- end
72
+ if !obj.instance_variable_defined?("@flexmock_proxy")
73
+ proxy_box = obj.instance_variable_set("@flexmock_proxy", ProxyBox.new)
74
+ else
75
+ proxy_box = obj.instance_variable_get("@flexmock_proxy")
44
76
  end
45
- obj.instance_variable_get("@flexmock_proxy").proxy
46
- end
47
77
 
48
- # Is there a mock proxy defined on the domain object?
49
- def self.proxy_defined_on?(obj)
50
- obj.instance_variable_defined?("@flexmock_proxy") &&
51
- obj.instance_variable_get("@flexmock_proxy").proxy
78
+ if proxy_box.container != container
79
+ if !proxy_box.empty?
80
+ parent_proxy, _ = proxy_box.proxy
81
+ parent_mock = parent_proxy.mock
82
+ end
83
+
84
+ mock = FlexMock.new(name, container, parent: parent_mock)
85
+ proxy = PartialMockProxy.new(obj, mock, safe_mode, parent: parent_proxy)
86
+ proxy_box.push(proxy, container)
87
+ end
88
+ proxy_box.proxy
52
89
  end
53
90
 
54
91
  # The following methods are added to partial mocks so that they
@@ -62,14 +99,21 @@ class FlexMock
62
99
  :invoke_original
63
100
  ]
64
101
 
102
+ attr_reader :method_definitions
103
+
65
104
  # Initialize a PartialMockProxy object.
66
- def initialize(obj, mock, safe_mode)
105
+ def initialize(obj, mock, safe_mode, parent: nil)
67
106
  @obj = obj
68
107
  @mock = mock
69
- @method_definitions = {}
70
- @methods_proxied = []
71
108
  @proxy_definition_module = nil
72
109
  @initialize_override = nil
110
+
111
+ if parent
112
+ @method_definitions = parent.method_definitions.dup
113
+ else
114
+ @method_definitions = {}
115
+ end
116
+
73
117
  unless safe_mode
74
118
  add_mock_method(:should_receive)
75
119
  MOCK_METHODS.each do |sym|
@@ -85,6 +129,14 @@ class FlexMock
85
129
  @mock
86
130
  end
87
131
 
132
+ def push_flexmock_container(container)
133
+ @mock.push_flexmock_container(container)
134
+ end
135
+
136
+ def pop_flexmock_container
137
+ @mock.pop_flexmock_container
138
+ end
139
+
88
140
  # :call-seq:
89
141
  # should_receive(:method_name)
90
142
  # should_receive(:method1, method2, ...)
@@ -119,9 +171,20 @@ class FlexMock
119
171
  flexmock_invoke_original(m, args)
120
172
  end
121
173
 
174
+ # Whether the given method's original definition has been stored
175
+ def has_original_method?(m)
176
+ @method_definitions.has_key?(m)
177
+ end
178
+
179
+ # Whether the given method is already being proxied
180
+ def has_proxied_method?(m)
181
+ @proxy_definition_module &&
182
+ @proxy_definition_module.method_defined?(m)
183
+ end
184
+
122
185
  def flexmock_define_expectation(location, *args)
123
186
  EXP_BUILDER.parse_should_args(self, args) do |method_name|
124
- if !@methods_proxied.include?(method_name)
187
+ if !has_proxied_method?(method_name) && !has_original_method?(method_name)
125
188
  hide_existing_method(method_name)
126
189
  end
127
190
  ex = @mock.flexmock_define_expectation(location, method_name)
@@ -278,7 +341,7 @@ class FlexMock
278
341
  end
279
342
  if @obj.instance_variable_defined?(:@flexmock_proxy) &&
280
343
  (box = @obj.instance_variable_get(:@flexmock_proxy))
281
- box.proxy = nil
344
+ box.pop
282
345
  end
283
346
  @obj = nil
284
347
  end
@@ -358,9 +421,8 @@ class FlexMock
358
421
  # Stow the existing method definition so that it can be recovered
359
422
  # later.
360
423
  def stow_existing_definition(method_name)
361
- if !@methods_proxied.include?(method_name)
424
+ if !@method_definitions.has_key?(method_name)
362
425
  @method_definitions[method_name] = target_class_eval { instance_method(method_name) }
363
- @methods_proxied << method_name
364
426
  end
365
427
  @method_definitions[method_name]
366
428
  rescue NameError
@@ -390,6 +452,5 @@ class FlexMock
390
452
  def detached?
391
453
  @obj.nil?
392
454
  end
393
-
394
455
  end
395
456
  end
@@ -213,18 +213,28 @@ class FlexMock
213
213
  # @param [Array] args
214
214
  # @raise ValidationFailed
215
215
  def validate(args)
216
+ args = args.dup
216
217
  kw_args = Hash.new
218
+
219
+ last_is_proc = false
220
+ begin
221
+ if args.last.kind_of?(Proc)
222
+ args.pop
223
+ last_is_proc = true
224
+ end
225
+ rescue NoMethodError
226
+ end
227
+
228
+ last_is_kw_hash = false
217
229
  if expects_keyword_arguments?
218
230
  last_is_kw_hash =
219
- begin
220
- args.last.kind_of?(Hash)
221
- rescue NoMethodError
222
- false
223
- end
231
+ begin
232
+ args.last.kind_of?(Hash)
233
+ rescue NoMethodError
234
+ end
224
235
 
225
236
  if last_is_kw_hash
226
- kw_args = args[-1]
227
- args = args[0..-2]
237
+ kw_args = args.pop
228
238
  elsif requires_keyword_arguments?
229
239
  raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided"
230
240
  end
@@ -232,12 +242,34 @@ class FlexMock
232
242
 
233
243
  # There is currently no way to disambiguate "given a block" from "given a
234
244
  # proc as last argument" ... give some leeway in this case
245
+ positional_count = args.size
246
+
247
+ if required_arguments > positional_count
248
+ if requires_keyword_arguments?
249
+ raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
250
+ end
251
+
252
+ if (required_arguments - positional_count) == 1 && (last_is_kw_hash || last_is_proc)
253
+ if last_is_kw_hash
254
+ last_is_kw_hash = false
255
+ kw_args = Hash.new
256
+ else
257
+ last_is_proc = false
258
+ end
259
+ positional_count += 1
260
+ elsif (required_arguments - positional_count) == 2 && (last_is_kw_hash && last_is_proc)
261
+ last_is_kw_hash = false
262
+ kw_args = Hash.new
263
+ last_is_proc = false
264
+ positional_count += 2
265
+ else
266
+ raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
267
+ end
268
+ end
235
269
 
236
- if required_arguments > args.size
237
- raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{args.size}"
238
- elsif !splat? && (required_arguments + optional_arguments) < args.size
239
- if !args.last.kind_of?(Proc) || (required_arguments + optional_arguments) < args.size - 1
240
- raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{args.size}"
270
+ if !splat? && (required_arguments + optional_arguments) < positional_count
271
+ if !last_is_proc || (required_arguments + optional_arguments) < positional_count - 1
272
+ raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{positional_count}"
241
273
  end
242
274
  end
243
275
 
@@ -1,3 +1,3 @@
1
1
  class FlexMock
2
- VERSION = "2.3.0"
2
+ VERSION = "2.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flexmock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Weirich
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-11 00:00:00.000000000 Z
12
+ date: 2016-10-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest