rspec-expectations 3.6.0.beta2 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4334ca25737c438a7a9266a95fb2cd4182853299
4
- data.tar.gz: 810d8162263ea539f45652f84c0bf9deec512e6b
3
+ metadata.gz: aa835825e71953a32260e80c58d892f5517ed692
4
+ data.tar.gz: 3e66073a9dbefb71be893d00692df5162826105f
5
5
  SHA512:
6
- metadata.gz: b9d714c6f1f8ee892a133df714e7ffcc5afca4aa0f73735eda1478591a4aeebe3601c61d96d1ceab68c60e9f023b1f629107619ecd7150a74efa7c5ce276992d
7
- data.tar.gz: 45c51f595123a44b52c02058939248bf53eb3d73e7e6305a79936250d31208e191817ff60862856accc5f34e0bc5090204617d122e9cb0dd1841143976a21237
6
+ metadata.gz: e967e93899006e4a6c9b419b217f6cac32aee74d60d006724db428e4d574fea2be76499759348808e50f9ba69a7e34d9472aa02eb7b23cca04e56ef9beab6743
7
+ data.tar.gz: 96f5ea4360e26175df7dfaae04a9f76c0d1d273ccca0e8939722947e59c7d09fd3a0c3e29eb27035101060cd80d540b90cfb6abcb4b7a40d60d5f8ae5a307a16
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/Changelog.md CHANGED
@@ -1,3 +1,23 @@
1
+ ### 3.6.0 / 2017-05-04
2
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta2...v3.6.0)
3
+
4
+ Enhancements:
5
+
6
+ * Treat NoMethodError as a failure for comparison matchers. (Jon Rowe, #972)
7
+ * Allow for scoped aliased and negated matchers--just call
8
+ `alias_matcher` or `define_negated_matcher` from within an example
9
+ group. (Markus Reiter, #974)
10
+ * Improve failure message of `change` matcher with block and `satisfy` matcher
11
+ by including the block snippet instead of just describing it as `result` or
12
+ `block` when Ripper is available. (Yuji Nakayama, #987)
13
+
14
+ Bug Fixes:
15
+
16
+ * Fix `yield_with_args` and `yield_successive_args` matchers so that
17
+ they compare expected to actual args at the time the args are yielded
18
+ instead of at the end, in case the method that is yielding mutates the
19
+ arguments after yielding. (Alyssa Ross, #965)
20
+
1
21
  ### 3.6.0.beta2 / 2016-12-12
2
22
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta1...v3.6.0.beta2)
3
23
 
@@ -92,7 +112,7 @@ Bug Fixes:
92
112
 
93
113
  * Fix failure message from dynamic predicate matchers when the object
94
114
  does not respond to the predicate so that it is inspected rather
95
- than relying upon it's `to_s` -- that way for `nil`, `"nil"` is
115
+ than relying upon its `to_s` -- that way for `nil`, `"nil"` is
96
116
  printed rather than an empty string. (Myron Marston, #841)
97
117
  * Fix SystemStackError raised when diffing an Enumerable object
98
118
  whose `#each` includes the object itself. (Yuji Nakayama, #857)
data/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  RSpec::Expectations lets you express expected outcomes on an object in an
4
4
  example.
5
5
 
6
- expect(account.balance).to eq(Money.new(37.42, :USD))
6
+ ```ruby
7
+ expect(account.balance).to eq(Money.new(37.42, :USD))
8
+ ```
7
9
 
8
10
  ## Install
9
11
 
@@ -150,7 +152,7 @@ expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args
150
152
  expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded
151
153
 
152
154
  expect { |b| 5.tap(&b) }.to yield_with_args(5)
153
- expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum)
155
+ expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
154
156
  expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)
155
157
 
156
158
  expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
@@ -263,7 +265,7 @@ expect(hash).to match(
263
265
  :a => {
264
266
  :b => a_collection_containing_exactly(
265
267
  a_string_starting_with("f"),
266
- an_instance_of(Fixnum)
268
+ an_instance_of(Integer)
267
269
  ),
268
270
  :c => { :d => (a_value < 3) }
269
271
  }
@@ -76,6 +76,7 @@ module RSpec
76
76
  class MultipleExpectationsNotMetError < ExpectationNotMetError
77
77
  end
78
78
 
79
- autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
79
+ autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
80
+ autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
80
81
  end
81
82
  end
@@ -0,0 +1,253 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class BlockSnippetExtractor # rubocop:disable Style/ClassLength
5
+ # rubocop should properly handle `Struct.new {}` as an inner class definition.
6
+
7
+ attr_reader :proc, :method_name
8
+
9
+ def self.try_extracting_single_line_body_of(proc, method_name)
10
+ lines = new(proc, method_name).body_content_lines
11
+ return nil unless lines.count == 1
12
+ lines.first
13
+ rescue Error
14
+ nil
15
+ end
16
+
17
+ def initialize(proc, method_name)
18
+ @proc = proc
19
+ @method_name = method_name.to_s.freeze
20
+ end
21
+
22
+ # Ideally we should properly handle indentations of multiline snippet,
23
+ # but it's not implemented yet since because we use result of this method only when it's a
24
+ # single line and implementing the logic introduces additional complexity.
25
+ def body_content_lines
26
+ raw_body_lines.map(&:strip).reject(&:empty?)
27
+ end
28
+
29
+ private
30
+
31
+ def raw_body_lines
32
+ raw_body_snippet.split("\n")
33
+ end
34
+
35
+ def raw_body_snippet
36
+ block_token_extractor.body_tokens.map(&:string).join
37
+ end
38
+
39
+ def block_token_extractor
40
+ @block_token_extractor ||= BlockTokenExtractor.new(method_name, source, beginning_line_number)
41
+ end
42
+
43
+ if RSpec.respond_to?(:world)
44
+ def source
45
+ raise TargetNotFoundError unless File.exist?(file_path)
46
+ RSpec.world.source_from_file(file_path)
47
+ end
48
+ else
49
+ RSpec::Support.require_rspec_support 'source'
50
+ def source
51
+ raise TargetNotFoundError unless File.exist?(file_path)
52
+ @source ||= RSpec::Support::Source.from_file(file_path)
53
+ end
54
+ end
55
+
56
+ def file_path
57
+ source_location.first
58
+ end
59
+
60
+ def beginning_line_number
61
+ source_location.last
62
+ end
63
+
64
+ def source_location
65
+ proc.source_location || raise(TargetNotFoundError)
66
+ end
67
+
68
+ Error = Class.new(StandardError)
69
+ TargetNotFoundError = Class.new(Error)
70
+ AmbiguousTargetError = Class.new(Error)
71
+
72
+ # @private
73
+ # Performs extraction of block body snippet using tokens,
74
+ # which cannot be done with node information.
75
+ BlockTokenExtractor = Struct.new(:method_name, :source, :beginning_line_number) do
76
+ attr_reader :state, :body_tokens
77
+
78
+ def initialize(*)
79
+ super
80
+ parse!
81
+ end
82
+
83
+ private
84
+
85
+ def parse!
86
+ @state = :initial
87
+
88
+ catch(:finish) do
89
+ source.tokens.each do |token|
90
+ invoke_state_handler(token)
91
+ end
92
+ end
93
+ end
94
+
95
+ def finish!
96
+ throw :finish
97
+ end
98
+
99
+ def invoke_state_handler(token)
100
+ __send__("#{state}_state", token)
101
+ end
102
+
103
+ def initial_state(token)
104
+ @state = :after_method_call if token.location == block_locator.method_call_location
105
+ end
106
+
107
+ def after_method_call_state(token)
108
+ @state = :after_opener if handle_opener_token(token)
109
+ end
110
+
111
+ def after_opener_state(token)
112
+ if handle_closer_token(token)
113
+ finish_or_find_next_block_if_incorrect!
114
+ elsif pipe_token?(token)
115
+ finalize_pending_tokens!
116
+ @state = :after_beginning_of_args
117
+ else
118
+ pending_tokens << token
119
+ handle_opener_token(token)
120
+ @state = :after_beginning_of_body unless token.type == :on_sp
121
+ end
122
+ end
123
+
124
+ def after_beginning_of_args_state(token)
125
+ @state = :after_beginning_of_body if pipe_token?(token)
126
+ end
127
+
128
+ def after_beginning_of_body_state(token)
129
+ if handle_closer_token(token)
130
+ finish_or_find_next_block_if_incorrect!
131
+ else
132
+ pending_tokens << token
133
+ handle_opener_token(token)
134
+ end
135
+ end
136
+
137
+ def pending_tokens
138
+ @pending_tokens ||= []
139
+ end
140
+
141
+ def finalize_pending_tokens!
142
+ pending_tokens.freeze.tap do
143
+ @pending_tokens = nil
144
+ end
145
+ end
146
+
147
+ def finish_or_find_next_block_if_incorrect!
148
+ body_tokens = finalize_pending_tokens!
149
+
150
+ if correct_block?(body_tokens)
151
+ @body_tokens = body_tokens
152
+ finish!
153
+ else
154
+ @state = :after_method_call
155
+ end
156
+ end
157
+
158
+ def handle_opener_token(token)
159
+ opener_token?(token).tap do |boolean|
160
+ opener_token_stack.push(token) if boolean
161
+ end
162
+ end
163
+
164
+ def opener_token?(token)
165
+ token.type == :on_lbrace || (token.type == :on_kw && token.string == 'do')
166
+ end
167
+
168
+ def handle_closer_token(token)
169
+ if opener_token_stack.last.closed_by?(token)
170
+ opener_token_stack.pop
171
+ opener_token_stack.empty?
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ def opener_token_stack
178
+ @opener_token_stack ||= []
179
+ end
180
+
181
+ def pipe_token?(token)
182
+ token.type == :on_op && token.string == '|'
183
+ end
184
+
185
+ def correct_block?(body_tokens)
186
+ return true if block_locator.body_content_locations.empty?
187
+ content_location = block_locator.body_content_locations.first
188
+ content_location.between?(body_tokens.first.location, body_tokens.last.location)
189
+ end
190
+
191
+ def block_locator
192
+ @block_locator ||= BlockLocator.new(method_name, source, beginning_line_number)
193
+ end
194
+ end
195
+
196
+ # @private
197
+ # Locates target block with node information (semantics), which tokens don't have.
198
+ BlockLocator = Struct.new(:method_name, :source, :beginning_line_number) do
199
+ def method_call_location
200
+ @method_call_location ||= method_ident_node.location
201
+ end
202
+
203
+ def body_content_locations
204
+ @body_content_locations ||= block_body_node.map(&:location).compact
205
+ end
206
+
207
+ private
208
+
209
+ def method_ident_node
210
+ method_call_node = block_wrapper_node.children.first
211
+ method_call_node.find do |node|
212
+ method_ident_node?(node)
213
+ end
214
+ end
215
+
216
+ def block_body_node
217
+ block_node = block_wrapper_node.children[1]
218
+ block_node.children.last
219
+ end
220
+
221
+ def block_wrapper_node
222
+ case candidate_block_wrapper_nodes.size
223
+ when 1
224
+ candidate_block_wrapper_nodes.first
225
+ when 0
226
+ raise TargetNotFoundError
227
+ else
228
+ raise AmbiguousTargetError
229
+ end
230
+ end
231
+
232
+ def candidate_block_wrapper_nodes
233
+ @candidate_block_wrapper_nodes ||= candidate_method_ident_nodes.map do |method_ident_node|
234
+ block_wrapper_node = method_ident_node.each_ancestor.find { |node| node.type == :method_add_block }
235
+ next nil unless block_wrapper_node
236
+ method_call_node = block_wrapper_node.children.first
237
+ method_call_node.include?(method_ident_node) ? block_wrapper_node : nil
238
+ end.compact
239
+ end
240
+
241
+ def candidate_method_ident_nodes
242
+ source.nodes_by_line_number[beginning_line_number].select do |node|
243
+ method_ident_node?(node)
244
+ end
245
+ end
246
+
247
+ def method_ident_node?(node)
248
+ node.type == :@ident && node.args.first == method_name
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '3.6.0.beta2'
5
+ STRING = '3.6.0'
6
6
  end
7
7
  end
8
8
  end
@@ -41,9 +41,9 @@ module RSpec
41
41
  #
42
42
  # expect("a string").to be_an_instance_of(String) # =>"a string".instance_of?(String) # passes
43
43
  #
44
- # expect(3).to be_a_kind_of(Fixnum) # => 3.kind_of?(Numeric) | passes
45
- # expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
46
- # expect(3).to be_an_instance_of(Fixnum) # => 3.instance_of?(Fixnum) | passes
44
+ # expect(3).to be_a_kind_of(Integer) # => 3.kind_of?(Numeric) | passes
45
+ # expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
46
+ # expect(3).to be_an_instance_of(Integer) # => 3.instance_of?(Integer) | passes
47
47
  # expect(3).not_to be_an_instance_of(Numeric) # => 3.instance_of?(Numeric) | fails
48
48
  #
49
49
  # RSpec will also create custom matchers for predicates like `has_key?`. To
@@ -62,6 +62,26 @@ module RSpec
62
62
  # RSpec::Matchers.alias_matcher :a_user_who_is_an_admin, :be_an_admin
63
63
  # expect(user_list).to include(a_user_who_is_an_admin)
64
64
  #
65
+ # ## Alias Matchers
66
+ #
67
+ # With {RSpec::Matchers.alias_matcher}, you can easily create an
68
+ # alternate name for a given matcher.
69
+ #
70
+ # The description will also change according to the new name:
71
+ #
72
+ # RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
73
+ # sum_to(3).description # => "sum to 3"
74
+ # a_list_that_sums_to(3).description # => "a list that sums to 3"
75
+ #
76
+ # or you can specify a custom description like this:
77
+ #
78
+ # RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
79
+ # description.sub("be sorted by", "a list sorted by")
80
+ # end
81
+ #
82
+ # be_sorted_by(:age).description # => "be sorted by age"
83
+ # a_list_sorted_by(:age).description # => "a list sorted by age"
84
+ #
65
85
  # ## Custom Matchers
66
86
  #
67
87
  # When you find that none of the stock matchers provide a natural feeling
@@ -202,7 +222,34 @@ module RSpec
202
222
  # expressions, and also uses the noun-phrase wording in the matcher's `description`,
203
223
  # for readable failure messages. You can alias your custom matchers in similar fashion
204
224
  # using {RSpec::Matchers.alias_matcher}.
225
+ #
226
+ # ## Negated Matchers
227
+ #
228
+ # Sometimes if you want to test for the opposite using a more descriptive name
229
+ # instead of using `not_to`, you can use {RSpec::Matchers.define_negated_matcher}:
230
+ #
231
+ # RSpec::Matchers.define_negated_matcher :exclude, :include
232
+ # include(1, 2).description # => "include 1 and 2"
233
+ # exclude(1, 2).description # => "exclude 1 and 2"
234
+ #
235
+ # While the most obvious negated form may be to add a `not_` prefix,
236
+ # the failure messages you get with that form can be confusing (e.g.
237
+ # "expected [actual] to not [verb], but did not"). We've found it works
238
+ # best to find a more positive name for the negated form, such as
239
+ # `avoid_changing` rather than `not_change`.
240
+ #
205
241
  module Matchers
242
+ extend ::RSpec::Matchers::DSL
243
+
244
+ # @!method self.alias_matcher(new_name, old_name, options={}, &description_override)
245
+ # Extended from {RSpec::Matchers::DSL#alias_matcher}.
246
+
247
+ # @!method self.define(name, &declarations)
248
+ # Extended from {RSpec::Matchers::DSL#define}.
249
+
250
+ # @!method self.define_negated_matcher(negated_name, base_name, &description_override)
251
+ # Extended from {RSpec::Matchers::DSL#define_negated_matcher}.
252
+
206
253
  # @method expect
207
254
  # Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
208
255
  # `ExpectationTarget`.
@@ -213,70 +260,6 @@ module RSpec
213
260
  # @see ExpectationTarget#to
214
261
  # @see ExpectationTarget#not_to
215
262
 
216
- # Defines a matcher alias. The returned matcher's `description` will be overriden
217
- # to reflect the phrasing of the new name, which will be used in failure messages
218
- # when passed as an argument to another matcher in a composed matcher expression.
219
- #
220
- # @param new_name [Symbol] the new name for the matcher
221
- # @param old_name [Symbol] the original name for the matcher
222
- # @param options [Hash] options for the aliased matcher
223
- # @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
224
- # @yield [String] optional block that, when given, is used to define the overriden
225
- # logic. The yielded arg is the original description or failure message. If no
226
- # block is provided, a default override is used based on the old and new names.
227
- #
228
- # @example
229
- # RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
230
- # sum_to(3).description # => "sum to 3"
231
- # a_list_that_sums_to(3).description # => "a list that sums to 3"
232
- #
233
- # @example
234
- # RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
235
- # description.sub("be sorted by", "a list sorted by")
236
- # end
237
- #
238
- # be_sorted_by(:age).description # => "be sorted by age"
239
- # a_list_sorted_by(:age).description # => "a list sorted by age"
240
- #
241
- # @!macro [attach] alias_matcher
242
- # @!parse
243
- # alias $1 $2
244
- def self.alias_matcher(new_name, old_name, options={}, &description_override)
245
- description_override ||= lambda do |old_desc|
246
- old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
247
- end
248
- klass = options.fetch(:klass) { AliasedMatcher }
249
-
250
- define_method(new_name) do |*args, &block|
251
- matcher = __send__(old_name, *args, &block)
252
- klass.new(matcher, description_override)
253
- end
254
- end
255
-
256
- # Defines a negated matcher. The returned matcher's `description` and `failure_message`
257
- # will be overriden to reflect the phrasing of the new name, and the match logic will
258
- # be based on the original matcher but negated.
259
- #
260
- # @param negated_name [Symbol] the name for the negated matcher
261
- # @param base_name [Symbol] the name of the original matcher that will be negated
262
- # @yield [String] optional block that, when given, is used to define the overriden
263
- # logic. The yielded arg is the original description or failure message. If no
264
- # block is provided, a default override is used based on the old and new names.
265
- #
266
- # @example
267
- # RSpec::Matchers.define_negated_matcher :exclude, :include
268
- # include(1, 2).description # => "include 1 and 2"
269
- # exclude(1, 2).description # => "exclude 1 and 2"
270
- #
271
- # @note While the most obvious negated form may be to add a `not_` prefix,
272
- # the failure messages you get with that form can be confusing (e.g.
273
- # "expected [actual] to not [verb], but did not"). We've found it works
274
- # best to find a more positive name for the negated form, such as
275
- # `avoid_changing` rather than `not_change`.
276
- def self.define_negated_matcher(negated_name, base_name, &description_override)
277
- alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
278
- end
279
-
280
263
  # Allows multiple expectations in the provided block to fail, and then
281
264
  # aggregates them into a single exception, rather than aborting on the
282
265
  # first expectation failure like normal. This allows you to see all
@@ -313,6 +296,10 @@ module RSpec
313
296
  Expectations::FailureAggregator.new(label, metadata).aggregate(&block)
314
297
  end
315
298
 
299
+ # @!macro [attach] alias_matcher
300
+ # @!parse
301
+ # alias $1 $2
302
+
316
303
  # Passes if actual is truthy (anything but false or nil)
317
304
  def be_truthy
318
305
  BuiltIn::BeTruthy.new
@@ -367,7 +354,7 @@ module RSpec
367
354
  # Passes if actual.instance_of?(expected)
368
355
  #
369
356
  # @example
370
- # expect(5).to be_an_instance_of(Fixnum)
357
+ # expect(5).to be_an_instance_of(Integer)
371
358
  # expect(5).not_to be_an_instance_of(Numeric)
372
359
  # expect(5).not_to be_an_instance_of(Float)
373
360
  def be_an_instance_of(expected)
@@ -379,7 +366,7 @@ module RSpec
379
366
  # Passes if actual.kind_of?(expected)
380
367
  #
381
368
  # @example
382
- # expect(5).to be_a_kind_of(Fixnum)
369
+ # expect(5).to be_a_kind_of(Integer)
383
370
  # expect(5).to be_a_kind_of(Numeric)
384
371
  # expect(5).not_to be_a_kind_of(Float)
385
372
  def be_a_kind_of(expected)
@@ -585,7 +572,7 @@ module RSpec
585
572
  # information about equality in Ruby.
586
573
  #
587
574
  # @example
588
- # expect(5).to equal(5) # Fixnums are equal
575
+ # expect(5).to equal(5) # Integers are equal
589
576
  # expect("5").not_to equal("5") # Strings that look the same are not the same object
590
577
  def equal(expected)
591
578
  BuiltIn::Equal.new(expected)
@@ -688,7 +675,7 @@ module RSpec
688
675
  # :a => {
689
676
  # :b => a_collection_containing_exactly(
690
677
  # a_string_starting_with("f"),
691
- # an_instance_of(Fixnum)
678
+ # an_instance_of(Integer)
692
679
  # ),
693
680
  # :c => { :d => (a_value < 3) }
694
681
  # }
@@ -809,7 +796,7 @@ module RSpec
809
796
  # @example
810
797
  # expect(5).to satisfy { |n| n > 3 }
811
798
  # expect(5).to satisfy("be greater than 3") { |n| n > 3 }
812
- def satisfy(description="satisfy block", &block)
799
+ def satisfy(description=nil, &block)
813
800
  BuiltIn::Satisfy.new(description, &block)
814
801
  end
815
802
  alias_matcher :an_object_satisfying, :satisfy
@@ -905,7 +892,7 @@ module RSpec
905
892
  # @example
906
893
  # expect { |b| 5.tap(&b) }.to yield_with_args # because #tap yields an arg
907
894
  # expect { |b| 5.tap(&b) }.to yield_with_args(5) # because 5 == 5
908
- # expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum) # because Fixnum === 5
895
+ # expect { |b| 5.tap(&b) }.to yield_with_args(Integer) # because Integer === 5
909
896
  # expect { |b| File.open("f.txt", &b) }.to yield_with_args(/txt/) # because /txt/ === "f.txt"
910
897
  #
911
898
  # expect { |b| User.transaction(&b) }.not_to yield_with_args # because it yields no args
@@ -22,6 +22,9 @@ module RSpec
22
22
  # @private
23
23
  attr_reader :actual, :expected, :rescued_exception
24
24
 
25
+ # @private
26
+ attr_writer :matcher_name
27
+
25
28
  def initialize(expected=UNDEFINED)
26
29
  @expected = expected unless UNDEFINED.equal?(expected)
27
30
  end
@@ -95,6 +98,15 @@ module RSpec
95
98
  @matcher_name ||= underscore(name.split('::').last)
96
99
  end
97
100
 
101
+ # @private
102
+ def matcher_name
103
+ if defined?(@matcher_name)
104
+ @matcher_name
105
+ else
106
+ self.class.matcher_name
107
+ end
108
+ end
109
+
98
110
  # @private
99
111
  # Borrowed from ActiveSupport.
100
112
  def self.underscore(camel_cased_word)
@@ -145,7 +145,7 @@ module RSpec
145
145
  def matches?(actual)
146
146
  @actual = actual
147
147
  @actual.__send__ @operator, @expected
148
- rescue ArgumentError
148
+ rescue ArgumentError, NoMethodError
149
149
  false
150
150
  end
151
151
 
@@ -8,7 +8,7 @@ module RSpec
8
8
  # @api public
9
9
  # Specifies the delta of the expected change.
10
10
  def by(expected_delta)
11
- ChangeRelatively.new(@change_details, expected_delta, :by) do |actual_delta|
11
+ ChangeRelatively.new(change_details, expected_delta, :by) do |actual_delta|
12
12
  values_match?(expected_delta, actual_delta)
13
13
  end
14
14
  end
@@ -16,7 +16,7 @@ module RSpec
16
16
  # @api public
17
17
  # Specifies a minimum delta of the expected change.
18
18
  def by_at_least(minimum)
19
- ChangeRelatively.new(@change_details, minimum, :by_at_least) do |actual_delta|
19
+ ChangeRelatively.new(change_details, minimum, :by_at_least) do |actual_delta|
20
20
  actual_delta >= minimum
21
21
  end
22
22
  end
@@ -24,7 +24,7 @@ module RSpec
24
24
  # @api public
25
25
  # Specifies a maximum delta of the expected change.
26
26
  def by_at_most(maximum)
27
- ChangeRelatively.new(@change_details, maximum, :by_at_most) do |actual_delta|
27
+ ChangeRelatively.new(change_details, maximum, :by_at_most) do |actual_delta|
28
28
  actual_delta <= maximum
29
29
  end
30
30
  end
@@ -32,13 +32,13 @@ module RSpec
32
32
  # @api public
33
33
  # Specifies the new value you expect.
34
34
  def to(value)
35
- ChangeToValue.new(@change_details, value)
35
+ ChangeToValue.new(change_details, value)
36
36
  end
37
37
 
38
38
  # @api public
39
39
  # Specifies the original value.
40
40
  def from(value)
41
- ChangeFromValue.new(@change_details, value)
41
+ ChangeFromValue.new(change_details, value)
42
42
  end
43
43
 
44
44
  # @private
@@ -46,8 +46,8 @@ module RSpec
46
46
  @event_proc = event_proc
47
47
  return false unless Proc === event_proc
48
48
  raise_block_syntax_error if block_given?
49
- @change_details.perform_change(event_proc)
50
- @change_details.changed?
49
+ change_details.perform_change(event_proc)
50
+ change_details.changed?
51
51
  end
52
52
 
53
53
  def does_not_match?(event_proc)
@@ -58,21 +58,21 @@ module RSpec
58
58
  # @api private
59
59
  # @return [String]
60
60
  def failure_message
61
- "expected #{@change_details.message} to have changed, " \
61
+ "expected #{change_details.value_representation} to have changed, " \
62
62
  "but #{positive_failure_reason}"
63
63
  end
64
64
 
65
65
  # @api private
66
66
  # @return [String]
67
67
  def failure_message_when_negated
68
- "expected #{@change_details.message} not to have changed, " \
68
+ "expected #{change_details.value_representation} not to have changed, " \
69
69
  "but #{negative_failure_reason}"
70
70
  end
71
71
 
72
72
  # @api private
73
73
  # @return [String]
74
74
  def description
75
- "change #{@change_details.message}"
75
+ "change #{change_details.value_representation}"
76
76
  end
77
77
 
78
78
  # @private
@@ -83,7 +83,13 @@ module RSpec
83
83
  private
84
84
 
85
85
  def initialize(receiver=nil, message=nil, &block)
86
- @change_details = ChangeDetails.new(receiver, message, &block)
86
+ @receiver = receiver
87
+ @message = message
88
+ @block = block
89
+ end
90
+
91
+ def change_details
92
+ @change_details ||= ChangeDetails.new(matcher_name, @receiver, @message, &@block)
87
93
  end
88
94
 
89
95
  def raise_block_syntax_error
@@ -93,13 +99,13 @@ module RSpec
93
99
 
94
100
  def positive_failure_reason
95
101
  return "was not given a block" unless Proc === @event_proc
96
- "is still #{description_of @change_details.actual_before}"
102
+ "is still #{description_of change_details.actual_before}"
97
103
  end
98
104
 
99
105
  def negative_failure_reason
100
106
  return "was not given a block" unless Proc === @event_proc
101
- "did change from #{description_of @change_details.actual_before} " \
102
- "to #{description_of @change_details.actual_after}"
107
+ "did change from #{description_of change_details.actual_before} " \
108
+ "to #{description_of change_details.actual_after}"
103
109
  end
104
110
  end
105
111
 
@@ -115,7 +121,7 @@ module RSpec
115
121
 
116
122
  # @private
117
123
  def failure_message
118
- "expected #{@change_details.message} to have changed " \
124
+ "expected #{@change_details.value_representation} to have changed " \
119
125
  "#{@relativity.to_s.tr('_', ' ')} " \
120
126
  "#{description_of @expected_delta}, but #{failure_reason}"
121
127
  end
@@ -136,7 +142,7 @@ module RSpec
136
142
 
137
143
  # @private
138
144
  def description
139
- "change #{@change_details.message} " \
145
+ "change #{@change_details.value_representation} " \
140
146
  "#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
141
147
  end
142
148
 
@@ -175,7 +181,7 @@ module RSpec
175
181
 
176
182
  # @private
177
183
  def description
178
- "change #{@change_details.message} #{change_description}"
184
+ "change #{@change_details.value_representation} #{change_description}"
179
185
  end
180
186
 
181
187
  # @private
@@ -202,30 +208,30 @@ module RSpec
202
208
  end
203
209
 
204
210
  def before_value_failure
205
- "expected #{@change_details.message} " \
211
+ "expected #{@change_details.value_representation} " \
206
212
  "to have initially been #{description_of @expected_before}, " \
207
213
  "but was #{description_of @change_details.actual_before}"
208
214
  end
209
215
 
210
216
  def after_value_failure
211
- "expected #{@change_details.message} " \
217
+ "expected #{@change_details.value_representation} " \
212
218
  "to have changed to #{description_of @expected_after}, " \
213
219
  "but is now #{description_of @change_details.actual_after}"
214
220
  end
215
221
 
216
222
  def did_not_change_failure
217
- "expected #{@change_details.message} " \
223
+ "expected #{@change_details.value_representation} " \
218
224
  "to have changed #{change_description}, but did not change"
219
225
  end
220
226
 
221
227
  def did_change_failure
222
- "expected #{@change_details.message} not to have changed, but " \
228
+ "expected #{@change_details.value_representation} not to have changed, but " \
223
229
  "did change from #{description_of @change_details.actual_before} " \
224
230
  "to #{description_of @change_details.actual_after}"
225
231
  end
226
232
 
227
233
  def not_given_a_block_failure
228
- "expected #{@change_details.message} to have changed " \
234
+ "expected #{@change_details.value_representation} to have changed " \
229
235
  "#{change_description}, but was not given a block"
230
236
  end
231
237
  end
@@ -307,9 +313,9 @@ module RSpec
307
313
  # @private
308
314
  # Encapsulates the details of the before/after values.
309
315
  class ChangeDetails
310
- attr_reader :message, :actual_before, :actual_after
316
+ attr_reader :actual_before, :actual_after
311
317
 
312
- def initialize(receiver=nil, message=nil, &block)
318
+ def initialize(matcher_name, receiver=nil, message=nil, &block)
313
319
  if receiver && !message
314
320
  raise(
315
321
  ArgumentError,
@@ -318,8 +324,22 @@ module RSpec
318
324
  "You passed an object but no message."
319
325
  )
320
326
  end
321
- @message = message ? "##{message}" : "result"
322
- @value_proc = block || lambda { receiver.__send__(message) }
327
+
328
+ @matcher_name = matcher_name
329
+ @receiver = receiver
330
+ @message = message
331
+ @value_proc = block
332
+ end
333
+
334
+ def value_representation
335
+ @value_representation ||=
336
+ if @message
337
+ "##{@message}"
338
+ elsif (value_block_snippet = extract_value_block_snippet)
339
+ "`#{value_block_snippet}`"
340
+ else
341
+ 'result'
342
+ end
323
343
  end
324
344
 
325
345
  def perform_change(event_proc)
@@ -339,7 +359,9 @@ module RSpec
339
359
  private
340
360
 
341
361
  def evaluate_value_proc
342
- case val = @value_proc.call
362
+ value_proc = @value_proc || lambda { @receiver.__send__(@message) }
363
+
364
+ case val = value_proc.call
343
365
  when IO # enumerable, but we don't want to dup it.
344
366
  val
345
367
  when Enumerable, String
@@ -348,6 +370,17 @@ module RSpec
348
370
  val
349
371
  end
350
372
  end
373
+
374
+ if RSpec::Support::RubyFeatures.ripper_supported?
375
+ def extract_value_block_snippet
376
+ return nil unless @value_proc
377
+ Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
378
+ end
379
+ else
380
+ def extract_value_block_snippet
381
+ nil
382
+ end
383
+ end
351
384
  end
352
385
  end
353
386
  end
@@ -172,7 +172,7 @@ module RSpec
172
172
  "including those raised by Ruby (e.g. NoMethodError, NameError " \
173
173
  "and ArgumentError), meaning the code you are intending to test " \
174
174
  "may not even get reached. Instead consider using " \
175
- "`expect {}.not_to raise_error` or `expect { }.to raise_error" \
175
+ "`expect { }.not_to raise_error` or `expect { }.to raise_error" \
176
176
  "(DifferentSpecificErrorClass)`. This message can be suppressed by " \
177
177
  "setting: `RSpec::Expectations.configuration.on_potential_false" \
178
178
  "_positives = :nothing`")
@@ -5,10 +5,7 @@ module RSpec
5
5
  # Provides the implementation for `satisfy`.
6
6
  # Not intended to be instantiated directly.
7
7
  class Satisfy < BaseMatcher
8
- # @private
9
- attr_reader :description
10
-
11
- def initialize(description="satisfy block", &block)
8
+ def initialize(description=nil, &block)
12
9
  @description = description
13
10
  @block = block
14
11
  end
@@ -20,6 +17,11 @@ module RSpec
20
17
  @block.call(actual)
21
18
  end
22
19
 
20
+ # @private
21
+ def description
22
+ @description ||= "satisfy #{block_representation}"
23
+ end
24
+
23
25
  # @api private
24
26
  # @return [String]
25
27
  def failure_message
@@ -31,6 +33,27 @@ module RSpec
31
33
  def failure_message_when_negated
32
34
  "expected #{actual_formatted} not to #{description}"
33
35
  end
36
+
37
+ private # rubocop:disable Lint/UselessAccessModifier
38
+
39
+ if RSpec::Support::RubyFeatures.ripper_supported?
40
+ def block_representation
41
+ if (block_snippet = extract_block_snippet)
42
+ "expression `#{block_snippet}`"
43
+ else
44
+ 'block'
45
+ end
46
+ end
47
+
48
+ def extract_block_snippet
49
+ return nil unless @block
50
+ Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@block, matcher_name)
51
+ end
52
+ else
53
+ def block_representation
54
+ 'block'
55
+ end
56
+ end
34
57
  end
35
58
  end
36
59
  end
@@ -8,19 +8,17 @@ module RSpec
8
8
  # yield matchers is used. Provides information about
9
9
  # the yield behavior of the object-under-test.
10
10
  class YieldProbe
11
- def self.probe(block)
12
- probe = new(block)
11
+ def self.probe(block, &callback)
12
+ probe = new(block, &callback)
13
13
  return probe unless probe.has_block?
14
- probe.assert_valid_expect_block!
15
- block.call(probe)
16
- probe.assert_used!
17
- probe
14
+ probe.probe
18
15
  end
19
16
 
20
17
  attr_accessor :num_yields, :yielded_args
21
18
 
22
- def initialize(block)
19
+ def initialize(block, &callback)
23
20
  @block = block
21
+ @callback = callback || Proc.new {}
24
22
  @used = false
25
23
  self.num_yields = 0
26
24
  self.yielded_args = []
@@ -30,13 +28,22 @@ module RSpec
30
28
  Proc === @block
31
29
  end
32
30
 
31
+ def probe
32
+ assert_valid_expect_block!
33
+ @block.call(self)
34
+ assert_used!
35
+ self
36
+ end
37
+
33
38
  def to_proc
34
39
  @used = true
35
40
 
36
41
  probe = self
42
+ callback = @callback
37
43
  Proc.new do |*args|
38
44
  probe.num_yields += 1
39
45
  probe.yielded_args << args
46
+ callback.call(*args)
40
47
  nil # to indicate the block does not return a meaningful value
41
48
  end
42
49
  end
@@ -56,12 +63,6 @@ module RSpec
56
63
  end
57
64
  end
58
65
 
59
- def successive_yield_args
60
- yielded_args.map do |arg_array|
61
- arg_array.size == 1 ? arg_array.first : arg_array
62
- end
63
- end
64
-
65
66
  def assert_used!
66
67
  return if @used
67
68
  raise 'You must pass the argument yielded to your expect block on ' \
@@ -269,10 +270,15 @@ module RSpec
269
270
 
270
271
  # @private
271
272
  def matches?(block)
272
- @probe = YieldProbe.probe(block)
273
+ @args_matched_when_yielded = true
274
+ @probe = YieldProbe.new(block) do
275
+ @actual = @probe.single_yield_args
276
+ @actual_formatted = actual_formatted
277
+ @args_matched_when_yielded &&= args_currently_match?
278
+ end
273
279
  return false unless @probe.has_block?
274
- @actual = @probe.single_yield_args
275
- @probe.yielded_once?(:yield_with_args) && args_match?
280
+ @probe.probe
281
+ @probe.yielded_once?(:yield_with_args) && @args_matched_when_yielded
276
282
  end
277
283
 
278
284
  # @private
@@ -317,16 +323,16 @@ module RSpec
317
323
  def negative_failure_reason
318
324
  if !@probe.has_block?
319
325
  'was not a block'
320
- elsif all_args_match?
326
+ elsif @args_matched_when_yielded && !@expected.empty?
321
327
  'yielded with expected arguments' \
322
328
  "\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
323
- "\n got: #{actual_formatted}"
329
+ "\n got: #{@actual_formatted}"
324
330
  else
325
331
  'did'
326
332
  end
327
333
  end
328
334
 
329
- def args_match?
335
+ def args_currently_match?
330
336
  if @expected.empty? # expect {...}.to yield_with_args
331
337
  @positive_args_failure = 'yielded with no arguments' if @actual.empty?
332
338
  return !@actual.empty?
@@ -335,7 +341,7 @@ module RSpec
335
341
  unless (match = all_args_match?)
336
342
  @positive_args_failure = 'yielded with unexpected arguments' \
337
343
  "\nexpected: #{surface_descriptions_in(@expected).inspect}" \
338
- "\n got: #{actual_formatted}"
344
+ "\n got: #{@actual_formatted}"
339
345
  end
340
346
 
341
347
  match
@@ -356,10 +362,21 @@ module RSpec
356
362
 
357
363
  # @private
358
364
  def matches?(block)
359
- @probe = YieldProbe.probe(block)
365
+ @actual_formatted = []
366
+ @actual = []
367
+ args_matched_when_yielded = true
368
+ yield_count = 0
369
+
370
+ @probe = YieldProbe.probe(block) do |*arg_array|
371
+ arg_or_args = arg_array.size == 1 ? arg_array.first : arg_array
372
+ @actual_formatted << RSpec::Support::ObjectFormatter.format(arg_or_args)
373
+ @actual << arg_or_args
374
+ args_matched_when_yielded &&= values_match?(@expected[yield_count], arg_or_args)
375
+ yield_count += 1
376
+ end
377
+
360
378
  return false unless @probe.has_block?
361
- @actual = @probe.successive_yield_args
362
- args_match?
379
+ args_matched_when_yielded && yield_count == @expected.length
363
380
  end
364
381
 
365
382
  def does_not_match?(block)
@@ -390,10 +407,6 @@ module RSpec
390
407
 
391
408
  private
392
409
 
393
- def args_match?
394
- values_match?(@expected, @actual)
395
- end
396
-
397
410
  def expected_arg_description
398
411
  @expected.map { |e| description_of e }.join(', ')
399
412
  end
@@ -403,7 +416,7 @@ module RSpec
403
416
 
404
417
  'yielded with unexpected arguments' \
405
418
  "\nexpected: #{surface_descriptions_in(@expected).inspect}" \
406
- "\n got: #{actual_formatted}"
419
+ "\n got: [#{@actual_formatted.join(", ")}]"
407
420
  end
408
421
 
409
422
  def negative_failure_reason
@@ -411,7 +424,7 @@ module RSpec
411
424
 
412
425
  'yielded with expected arguments' \
413
426
  "\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
414
- "\n got: #{actual_formatted}"
427
+ "\n got: [#{@actual_formatted.join(", ")}]"
415
428
  end
416
429
  end
417
430
  end
@@ -2,7 +2,70 @@ module RSpec
2
2
  module Matchers
3
3
  # Defines the custom matcher DSL.
4
4
  module DSL
5
+ # Defines a matcher alias. The returned matcher's `description` will be overriden
6
+ # to reflect the phrasing of the new name, which will be used in failure messages
7
+ # when passed as an argument to another matcher in a composed matcher expression.
8
+ #
9
+ # @example
10
+ # RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
11
+ # sum_to(3).description # => "sum to 3"
12
+ # a_list_that_sums_to(3).description # => "a list that sums to 3"
13
+ #
14
+ # @example
15
+ # RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
16
+ # description.sub("be sorted by", "a list sorted by")
17
+ # end
18
+ #
19
+ # be_sorted_by(:age).description # => "be sorted by age"
20
+ # a_list_sorted_by(:age).description # => "a list sorted by age"
21
+ #
22
+ # @param new_name [Symbol] the new name for the matcher
23
+ # @param old_name [Symbol] the original name for the matcher
24
+ # @param options [Hash] options for the aliased matcher
25
+ # @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
26
+ # @yield [String] optional block that, when given, is used to define the overriden
27
+ # logic. The yielded arg is the original description or failure message. If no
28
+ # block is provided, a default override is used based on the old and new names.
29
+ # @see RSpec::Matchers
30
+ def alias_matcher(new_name, old_name, options={}, &description_override)
31
+ description_override ||= lambda do |old_desc|
32
+ old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
33
+ end
34
+ klass = options.fetch(:klass) { AliasedMatcher }
35
+
36
+ define_method(new_name) do |*args, &block|
37
+ matcher = __send__(old_name, *args, &block)
38
+ matcher.matcher_name = new_name if matcher.respond_to?(:matcher_name=)
39
+ klass.new(matcher, description_override)
40
+ end
41
+ end
42
+
43
+ # Defines a negated matcher. The returned matcher's `description` and `failure_message`
44
+ # will be overriden to reflect the phrasing of the new name, and the match logic will
45
+ # be based on the original matcher but negated.
46
+ #
47
+ # @example
48
+ # RSpec::Matchers.define_negated_matcher :exclude, :include
49
+ # include(1, 2).description # => "include 1 and 2"
50
+ # exclude(1, 2).description # => "exclude 1 and 2"
51
+ #
52
+ # @param negated_name [Symbol] the name for the negated matcher
53
+ # @param base_name [Symbol] the name of the original matcher that will be negated
54
+ # @yield [String] optional block that, when given, is used to define the overriden
55
+ # logic. The yielded arg is the original description or failure message. If no
56
+ # block is provided, a default override is used based on the old and new names.
57
+ # @see RSpec::Matchers
58
+ def define_negated_matcher(negated_name, base_name, &description_override)
59
+ alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
60
+ end
61
+
5
62
  # Defines a custom matcher.
63
+ #
64
+ # @param name [Symbol] the name for the matcher
65
+ # @yield [Object] block that is used to define the matcher.
66
+ # The block is evaluated in the context of your custom matcher class.
67
+ # When args are passed to your matcher, they will be yielded here,
68
+ # usually representing the expected value(s).
6
69
  # @see RSpec::Matchers
7
70
  def define(name, &declarations)
8
71
  warn_about_block_args(name, declarations)
@@ -266,7 +329,7 @@ module RSpec
266
329
  #
267
330
  # This compiles the user block into an actual method, allowing
268
331
  # them to use normal method constructs like `return`
269
- # (e.g. for a early guard statement), while allowing us to define
332
+ # (e.g. for an early guard statement), while allowing us to define
270
333
  # an override that can provide the wrapped handling
271
334
  # (e.g. assigning `@actual`, rescueing errors, etc) and
272
335
  # can `super` to the user's definition.
@@ -462,5 +525,3 @@ module RSpec
462
525
  end
463
526
  end
464
527
  end
465
-
466
- RSpec::Matchers.extend RSpec::Matchers::DSL
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-expectations
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0.beta2
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Baker
@@ -45,22 +45,22 @@ cert_chain:
45
45
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
46
46
  F3MdtaDehhjC
47
47
  -----END CERTIFICATE-----
48
- date: 2016-12-12 00:00:00.000000000 Z
48
+ date: 2017-05-04 00:00:00.000000000 Z
49
49
  dependencies:
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: rspec-support
52
52
  requirement: !ruby/object:Gem::Requirement
53
53
  requirements:
54
- - - '='
54
+ - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 3.6.0.beta2
56
+ version: 3.6.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - '='
61
+ - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 3.6.0.beta2
63
+ version: 3.6.0
64
64
  - !ruby/object:Gem::Dependency
65
65
  name: diff-lcs
66
66
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,7 @@ files:
150
150
  - LICENSE.md
151
151
  - README.md
152
152
  - lib/rspec/expectations.rb
153
+ - lib/rspec/expectations/block_snippet_extractor.rb
153
154
  - lib/rspec/expectations/configuration.rb
154
155
  - lib/rspec/expectations/expectation_target.rb
155
156
  - lib/rspec/expectations/fail_with.rb
@@ -212,14 +213,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
213
  version: 1.8.7
213
214
  required_rubygems_version: !ruby/object:Gem::Requirement
214
215
  requirements:
215
- - - ">"
216
+ - - ">="
216
217
  - !ruby/object:Gem::Version
217
- version: 1.3.1
218
+ version: '0'
218
219
  requirements: []
219
220
  rubyforge_project:
220
- rubygems_version: 2.2.2
221
+ rubygems_version: 2.4.5.2
221
222
  signing_key:
222
223
  specification_version: 4
223
- summary: rspec-expectations-3.6.0.beta2
224
+ summary: rspec-expectations-3.6.0
224
225
  test_files: []
225
226
  has_rdoc:
metadata.gz.sig CHANGED
Binary file