rspec-expectations 3.8.5 → 3.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Changelog.md +56 -1
- data/README.md +35 -20
- data/lib/rspec/expectations/expectation_target.rb +7 -43
- data/lib/rspec/expectations/handler.rb +2 -2
- data/lib/rspec/expectations/syntax.rb +1 -1
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +49 -30
- data/lib/rspec/matchers/built_in/base_matcher.rb +0 -5
- data/lib/rspec/matchers/built_in/be.rb +45 -9
- data/lib/rspec/matchers/built_in/be_within.rb +2 -2
- data/lib/rspec/matchers/built_in/change.rb +10 -15
- data/lib/rspec/matchers/built_in/compound.rb +6 -15
- data/lib/rspec/matchers/built_in/has.rb +24 -4
- data/lib/rspec/matchers/built_in/have_attributes.rb +1 -1
- data/lib/rspec/matchers/built_in/output.rb +2 -7
- data/lib/rspec/matchers/built_in/raise_error.rb +1 -6
- data/lib/rspec/matchers/built_in/respond_to.rb +37 -3
- data/lib/rspec/matchers/built_in/throw_symbol.rb +3 -6
- data/lib/rspec/matchers/built_in/yield.rb +49 -40
- data/lib/rspec/matchers/dsl.rb +16 -7
- data/lib/rspec/matchers/english_phrasing.rb +1 -1
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +16 -7
- data/lib/rspec/matchers/matcher_protocol.rb +0 -6
- metadata +24 -10
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba515bcbb264eef62d6948840e73b71ef3a68ed947d2e711df04fdf1b04fa4ec
|
4
|
+
data.tar.gz: ba8501df256831e843786ca4f78cc7825e8ad6dca125191beda504de0c1d16e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac2ff1267b689ba5daf08898a11ff97f1f2554db817f43135a6b1d96bcada94f03d728ddda4050158fcf8a483b8295644457aea810d18b37686d5aa253007917
|
7
|
+
data.tar.gz: 6527bed095e09131e0e8a50f12784d017953f073b4099d710df7fff638f632d03bedba40836adeba6aa33f51d9d4c501f795c364c6e1304ddc8c41225df2c18d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/Changelog.md
CHANGED
@@ -1,3 +1,58 @@
|
|
1
|
+
### 3.9.3 / 2020-10-23
|
2
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.2...v3.9.3)
|
3
|
+
|
4
|
+
Bug Fixes:
|
5
|
+
|
6
|
+
* Swap the comparison of the delta vs the expected for the `be_within` matcher allowing
|
7
|
+
more complicated oobjects to be compared providing they provide `abs` and other
|
8
|
+
comparison methods. (Kelly Stannard, #1182)
|
9
|
+
* Properly format expected in the description of the `be_within` matcher. (Jon Rowe, #1185)
|
10
|
+
* Remove warning when using keyword arguments with `be_` and `have_` matchers on 2.7.x
|
11
|
+
(Jon Rowe, #1187)
|
12
|
+
* Prevent formatting a single hash as a list of key value pairs in default failure messages
|
13
|
+
for custom matches (fixes formatting in `EnglishPhrasing#list`). (Robert Eshleman, #1193)
|
14
|
+
* Prevent errors from causing false positives when using `be <operator>` comparison, e.g.
|
15
|
+
`expect(1).not_to be < 'a'` will now correctly fail rather than pass. (Jon Rowe, #1208)
|
16
|
+
|
17
|
+
### 3.9.2 / 2020-05-08
|
18
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.1...v3.9.2)
|
19
|
+
|
20
|
+
Bug Fixes:
|
21
|
+
|
22
|
+
* Issue a proper `ArgumentError` when invalid arguments are given to `yield_control`
|
23
|
+
modifiers such as `at_least` et al. (Marc-André Lafortune, #1167)
|
24
|
+
* Prevent Ruby 2.7 keyword arguments warning from being issued by custom
|
25
|
+
matcher definitions. (Jon Rowe, #1176)
|
26
|
+
|
27
|
+
### 3.9.1 / 2020-03-13
|
28
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.0...v3.9.1)
|
29
|
+
|
30
|
+
Bug Fixes:
|
31
|
+
|
32
|
+
* Issue an improved warning when using `respond_to(...).with(n).arguments` and ignore
|
33
|
+
the warning when using with `have_attributes(...)`. (Jon Rowe, #1164)
|
34
|
+
|
35
|
+
### 3.9.0 / 2019-10-08
|
36
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.6...v3.9.0)
|
37
|
+
|
38
|
+
Enhancements:
|
39
|
+
|
40
|
+
* The `respond_to` matcher now uses the signature from `initialize` to validate checks
|
41
|
+
for `new` (unless `new` is non standard). (Jon Rowe, #1072)
|
42
|
+
* Generated descriptions for matchers now use `is expected to` rather than `should` in
|
43
|
+
line with our preferred DSL. (Pete Johns, #1080, rspec/rspec-core#2572)
|
44
|
+
* Add the ability to re-raise expectation errors when matching
|
45
|
+
with `match_when_negated` blocks. (Jon Rowe, #1130)
|
46
|
+
* Add a warning when an empty diff is produce due to identical inspect output.
|
47
|
+
(Benoit Tigeot, #1126)
|
48
|
+
|
49
|
+
### 3.8.6 / 2019-10-07
|
50
|
+
|
51
|
+
Bug Fixes:
|
52
|
+
|
53
|
+
* Revert #1125 due to the change being incompatible with our semantic versioning
|
54
|
+
policy.
|
55
|
+
|
1
56
|
### 3.8.5 / 2019-10-02
|
2
57
|
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.8.4...v3.8.5)
|
3
58
|
|
@@ -22,7 +77,7 @@ Bug Fixes:
|
|
22
77
|
* Prevent composed `all` matchers from leaking into their siblings leading to duplicate
|
23
78
|
failures. (Jamie English, #1086)
|
24
79
|
* Prevent objects which change their hash on comparison from failing change checks.
|
25
|
-
(Phil Pirozhkov, #
|
80
|
+
(Phil Pirozhkov, #1110)
|
26
81
|
* Issue an `ArgumentError` rather than a `NoMethodError` when `be_an_instance_of` and
|
27
82
|
`be_kind_of` matchers encounter objects not supporting those methods.
|
28
83
|
(Taichi Ishitani, #1107)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# RSpec Expectations [![Build Status](https://secure.travis-ci.org/rspec/rspec-expectations.svg?branch=
|
1
|
+
# RSpec Expectations [![Build Status](https://secure.travis-ci.org/rspec/rspec-expectations.svg?branch=main)](http://travis-ci.org/rspec/rspec-expectations) [![Code Climate](https://codeclimate.com/github/rspec/rspec-expectations.svg)](https://codeclimate.com/github/rspec/rspec-expectations)
|
2
2
|
|
3
3
|
RSpec::Expectations lets you express expected outcomes on an object in an
|
4
4
|
example.
|
@@ -15,12 +15,12 @@ rspec-core and rspec-mocks):
|
|
15
15
|
|
16
16
|
gem install rspec
|
17
17
|
|
18
|
-
Want to run against the `
|
18
|
+
Want to run against the `main` branch? You'll need to include the dependent
|
19
19
|
RSpec repos as well. Add the following to your `Gemfile`:
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
|
23
|
-
gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => '
|
23
|
+
gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
|
24
24
|
end
|
25
25
|
```
|
26
26
|
|
@@ -175,30 +175,45 @@ expect(1..10).to cover(3)
|
|
175
175
|
### Collection membership
|
176
176
|
|
177
177
|
```ruby
|
178
|
-
|
178
|
+
# exact order, entire collection
|
179
|
+
expect(actual).to eq(expected)
|
180
|
+
|
181
|
+
# exact order, partial collection (based on an exact position)
|
179
182
|
expect(actual).to start_with(expected)
|
180
183
|
expect(actual).to end_with(expected)
|
181
184
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
+
# any order, entire collection
|
186
|
+
expect(actual).to match_array(expected)
|
187
|
+
|
188
|
+
# You can also express this by passing the expected elements
|
189
|
+
# as individual arguments
|
190
|
+
expect(actual).to contain_exactly(expected_element1, expected_element2)
|
191
|
+
|
192
|
+
# any order, partial collection
|
193
|
+
expect(actual).to include(expected)
|
185
194
|
```
|
186
195
|
|
187
196
|
#### Examples
|
188
197
|
|
189
198
|
```ruby
|
190
|
-
expect([1, 2, 3]).to
|
191
|
-
expect([1, 2, 3]).to include(1,
|
192
|
-
expect([1, 2, 3]).to
|
193
|
-
expect([1, 2, 3]).to start_with(1,
|
194
|
-
expect([1, 2, 3]).to
|
195
|
-
expect([1, 2, 3]).to end_with(
|
196
|
-
expect(
|
197
|
-
expect(
|
198
|
-
expect("this string").to
|
199
|
-
expect("this string").to
|
200
|
-
expect(
|
201
|
-
expect([1, 2, 3]).to
|
199
|
+
expect([1, 2, 3]).to eq([1, 2, 3]) # Order dependent equality check
|
200
|
+
expect([1, 2, 3]).to include(1) # Exact ordering, partial collection matches
|
201
|
+
expect([1, 2, 3]).to include(2, 3) #
|
202
|
+
expect([1, 2, 3]).to start_with(1) # As above, but from the start of the collection
|
203
|
+
expect([1, 2, 3]).to start_with(1, 2) #
|
204
|
+
expect([1, 2, 3]).to end_with(3) # As above but from the end of the collection
|
205
|
+
expect([1, 2, 3]).to end_with(2, 3) #
|
206
|
+
expect({:a => 'b'}).to include(:a => 'b') # Matching within hashes
|
207
|
+
expect("this string").to include("is str") # Matching within strings
|
208
|
+
expect("this string").to start_with("this") #
|
209
|
+
expect("this string").to end_with("ring") #
|
210
|
+
expect([1, 2, 3]).to contain_exactly(2, 3, 1) # Order independent matches
|
211
|
+
expect([1, 2, 3]).to match_array([3, 2, 1]) #
|
212
|
+
|
213
|
+
# Order dependent compound matchers
|
214
|
+
expect(
|
215
|
+
[{:a => 'hash'},{:a => 'another'}]
|
216
|
+
).to match([a_hash_including(:a => 'hash'), a_hash_including(:a => 'another')])
|
202
217
|
```
|
203
218
|
|
204
219
|
## `should` syntax
|
@@ -212,7 +227,7 @@ actual.should be > 3
|
|
212
227
|
[1, 2, 3].should_not include 4
|
213
228
|
```
|
214
229
|
|
215
|
-
See [detailed information on the `should` syntax and its usage.](https://github.com/rspec/rspec-expectations/blob/
|
230
|
+
See [detailed information on the `should` syntax and its usage.](https://github.com/rspec/rspec-expectations/blob/main/Should.md)
|
216
231
|
|
217
232
|
## Compound Matcher Expressions
|
218
233
|
|
@@ -33,16 +33,16 @@ module RSpec
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# @private
|
36
|
-
def self.for(value,
|
36
|
+
def self.for(value, block)
|
37
37
|
if UndefinedValue.equal?(value)
|
38
|
-
unless
|
38
|
+
unless block
|
39
39
|
raise ArgumentError, "You must pass either an argument or a block to `expect`."
|
40
40
|
end
|
41
41
|
BlockExpectationTarget.new(block)
|
42
|
-
elsif
|
42
|
+
elsif block
|
43
43
|
raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
|
44
44
|
else
|
45
|
-
|
45
|
+
new(value)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -90,40 +90,6 @@ module RSpec
|
|
90
90
|
include InstanceMethods
|
91
91
|
end
|
92
92
|
|
93
|
-
# @private
|
94
|
-
# Validates the provided matcher to ensure it supports block
|
95
|
-
# expectations, in order to avoid user confusion when they
|
96
|
-
# use a block thinking the expectation will be on the return
|
97
|
-
# value of the block rather than the block itself.
|
98
|
-
class ValueExpectationTarget < ExpectationTarget
|
99
|
-
def to(matcher=nil, message=nil, &block)
|
100
|
-
enforce_value_expectation(matcher)
|
101
|
-
super
|
102
|
-
end
|
103
|
-
|
104
|
-
def not_to(matcher=nil, message=nil, &block)
|
105
|
-
enforce_value_expectation(matcher)
|
106
|
-
super
|
107
|
-
end
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def enforce_value_expectation(matcher)
|
112
|
-
return if supports_value_expectations?(matcher)
|
113
|
-
|
114
|
-
raise ExpectationNotMetError, "You must pass a block rather than an argument to `expect` to use the provided " \
|
115
|
-
"block expectation matcher (#{RSpec::Support::ObjectFormatter.format(matcher)})."
|
116
|
-
end
|
117
|
-
|
118
|
-
def supports_value_expectations?(matcher)
|
119
|
-
if matcher.respond_to?(:supports_value_expectations?)
|
120
|
-
matcher.supports_value_expectations?
|
121
|
-
else
|
122
|
-
true
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
93
|
# @private
|
128
94
|
# Validates the provided matcher to ensure it supports block
|
129
95
|
# expectations, in order to avoid user confusion when they
|
@@ -152,11 +118,9 @@ module RSpec
|
|
152
118
|
end
|
153
119
|
|
154
120
|
def supports_block_expectations?(matcher)
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
false
|
159
|
-
end
|
121
|
+
matcher.supports_block_expectations?
|
122
|
+
rescue NoMethodError
|
123
|
+
false
|
160
124
|
end
|
161
125
|
end
|
162
126
|
end
|
@@ -52,7 +52,7 @@ module RSpec
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.verb
|
55
|
-
|
55
|
+
'is expected to'
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.should_method
|
@@ -82,7 +82,7 @@ module RSpec
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def self.verb
|
85
|
-
|
85
|
+
'is expected not to'
|
86
86
|
end
|
87
87
|
|
88
88
|
def self.should_method
|
@@ -70,7 +70,7 @@ module RSpec
|
|
70
70
|
|
71
71
|
syntax_host.module_exec do
|
72
72
|
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
|
73
|
-
::RSpec::Expectations::ExpectationTarget.for(value,
|
73
|
+
::RSpec::Expectations::ExpectationTarget.for(value, block)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
data/lib/rspec/matchers.rb
CHANGED
@@ -238,7 +238,7 @@ module RSpec
|
|
238
238
|
# best to find a more positive name for the negated form, such as
|
239
239
|
# `avoid_changing` rather than `not_change`.
|
240
240
|
#
|
241
|
-
module Matchers
|
241
|
+
module Matchers # rubocop:disable Metrics/ModuleLength
|
242
242
|
extend ::RSpec::Matchers::DSL
|
243
243
|
|
244
244
|
# @!macro [attach] alias_matcher
|
@@ -953,14 +953,29 @@ module RSpec
|
|
953
953
|
HAS_REGEX = /^(?:have_)(.*)/
|
954
954
|
DYNAMIC_MATCHER_REGEX = Regexp.union(BE_PREDICATE_REGEX, HAS_REGEX)
|
955
955
|
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
956
|
+
if RSpec::Support::RubyFeatures.kw_args_supported?
|
957
|
+
binding.eval(<<-CODE, __FILE__, __LINE__)
|
958
|
+
def method_missing(method, *args, **kwargs, &block)
|
959
|
+
case method.to_s
|
960
|
+
when BE_PREDICATE_REGEX
|
961
|
+
BuiltIn::BePredicate.new(method, *args, **kwargs, &block)
|
962
|
+
when HAS_REGEX
|
963
|
+
BuiltIn::Has.new(method, *args, **kwargs, &block)
|
964
|
+
else
|
965
|
+
super
|
966
|
+
end
|
967
|
+
end
|
968
|
+
CODE
|
969
|
+
else
|
970
|
+
def method_missing(method, *args, &block)
|
971
|
+
case method.to_s
|
972
|
+
when BE_PREDICATE_REGEX
|
973
|
+
BuiltIn::BePredicate.new(method, *args, &block)
|
974
|
+
when HAS_REGEX
|
975
|
+
BuiltIn::Has.new(method, *args, &block)
|
976
|
+
else
|
977
|
+
super
|
978
|
+
end
|
964
979
|
end
|
965
980
|
end
|
966
981
|
|
@@ -1003,31 +1018,35 @@ module RSpec
|
|
1003
1018
|
is_a_matcher?(obj) && obj.respond_to?(:description)
|
1004
1019
|
end
|
1005
1020
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1021
|
+
class << self
|
1022
|
+
private
|
1023
|
+
|
1024
|
+
if RSpec::Support::Ruby.mri? && RUBY_VERSION[0, 3] == '1.9'
|
1025
|
+
# Note that `included` doesn't work for this because it is triggered
|
1026
|
+
# _after_ `RSpec::Matchers` is an ancestor of the inclusion host, rather
|
1027
|
+
# than _before_, like `append_features`. It's important we check this before
|
1028
|
+
# in order to find the cases where it was already previously included.
|
1029
|
+
# @api private
|
1030
|
+
def append_features(mod)
|
1031
|
+
return super if mod < self # `mod < self` indicates a re-inclusion.
|
1014
1032
|
|
1015
|
-
|
1016
|
-
|
1033
|
+
subclasses = ObjectSpace.each_object(Class).select { |c| c < mod && c < self }
|
1034
|
+
return super unless subclasses.any?
|
1017
1035
|
|
1018
|
-
|
1019
|
-
|
1036
|
+
subclasses.reject! { |s| subclasses.any? { |s2| s < s2 } } # Filter to the root ancestor.
|
1037
|
+
subclasses = subclasses.map { |s| "`#{s}`" }.join(", ")
|
1020
1038
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1039
|
+
RSpec.warning "`#{self}` has been included in a superclass (`#{mod}`) " \
|
1040
|
+
"after previously being included in subclasses (#{subclasses}), " \
|
1041
|
+
"which can trigger infinite recursion from `super` due to an MRI 1.9 bug " \
|
1042
|
+
"(https://redmine.ruby-lang.org/issues/3351). To work around this, " \
|
1043
|
+
"either upgrade to MRI 2.0+, include a dup of the module (e.g. " \
|
1044
|
+
"`include #{self}.dup`), or find a way to include `#{self}` in `#{mod}` " \
|
1045
|
+
"before it is included in subclasses (#{subclasses}). See " \
|
1046
|
+
"https://github.com/rspec/rspec-expectations/issues/814 for more info"
|
1029
1047
|
|
1030
|
-
|
1048
|
+
super
|
1049
|
+
end
|
1031
1050
|
end
|
1032
1051
|
end
|
1033
1052
|
end
|
@@ -143,8 +143,13 @@ module RSpec
|
|
143
143
|
end
|
144
144
|
|
145
145
|
def matches?(actual)
|
146
|
-
|
147
|
-
|
146
|
+
perform_match(actual)
|
147
|
+
rescue ArgumentError, NoMethodError
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
def does_not_match?(actual)
|
152
|
+
!perform_match(actual)
|
148
153
|
rescue ArgumentError, NoMethodError
|
149
154
|
false
|
150
155
|
end
|
@@ -173,6 +178,13 @@ module RSpec
|
|
173
178
|
def description
|
174
179
|
"be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
|
175
180
|
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def perform_match(actual)
|
185
|
+
@actual = actual
|
186
|
+
@actual.__send__ @operator, @expected
|
187
|
+
end
|
176
188
|
end
|
177
189
|
|
178
190
|
# @api private
|
@@ -181,10 +193,21 @@ module RSpec
|
|
181
193
|
class BePredicate < BaseMatcher
|
182
194
|
include BeHelpers
|
183
195
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
196
|
+
if RSpec::Support::RubyFeatures.kw_args_supported?
|
197
|
+
binding.eval(<<-CODE, __FILE__, __LINE__)
|
198
|
+
def initialize(*args, **kwargs, &block)
|
199
|
+
@expected = parse_expected(args.shift)
|
200
|
+
@args = args
|
201
|
+
@kwargs = kwargs
|
202
|
+
@block = block
|
203
|
+
end
|
204
|
+
CODE
|
205
|
+
else
|
206
|
+
def initialize(*args, &block)
|
207
|
+
@expected = parse_expected(args.shift)
|
208
|
+
@args = args
|
209
|
+
@block = block
|
210
|
+
end
|
188
211
|
end
|
189
212
|
|
190
213
|
def matches?(actual, &block)
|
@@ -236,9 +259,22 @@ module RSpec
|
|
236
259
|
end
|
237
260
|
end
|
238
261
|
|
239
|
-
|
240
|
-
|
241
|
-
|
262
|
+
if RSpec::Support::RubyFeatures.kw_args_supported?
|
263
|
+
binding.eval(<<-CODE, __FILE__, __LINE__)
|
264
|
+
def predicate_matches?
|
265
|
+
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
|
266
|
+
if @kwargs.empty?
|
267
|
+
@predicate_matches = actual.__send__(method_name, *@args, &@block)
|
268
|
+
else
|
269
|
+
@predicate_matches = actual.__send__(method_name, *@args, **@kwargs, &@block)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
CODE
|
273
|
+
else
|
274
|
+
def predicate_matches?
|
275
|
+
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
|
276
|
+
@predicate_matches = actual.__send__(method_name, *@args, &@block)
|
277
|
+
end
|
242
278
|
end
|
243
279
|
|
244
280
|
def predicate
|