fix 1.0.0.beta4 → 1.0.0.beta8
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 +4 -4
- data/LICENSE.md +1 -1
- data/README.md +71 -49
- data/lib/fix.rb +12 -15
- data/lib/fix/doc.rb +24 -0
- data/lib/fix/dsl.rb +113 -0
- data/lib/fix/matcher.rb +241 -0
- data/lib/fix/requirement.rb +162 -0
- data/lib/fix/run.rb +55 -0
- data/lib/fix/set.rb +92 -0
- data/lib/kernel.rb +29 -2
- metadata +82 -35
- data/lib/fix/context.rb +0 -160
- data/lib/fix/expectation_result_not_found_error.rb +0 -5
- data/lib/fix/it.rb +0 -31
- data/lib/fix/suspicious_success_error.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d095777befeb78f0e16a218f3f14c0f8b39955265db3848cd26a0afb0b224f3
|
4
|
+
data.tar.gz: d918e8bfd693765450dfb14477f4fe9285642e22a35995446b89acac78662837
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c063f295f4d133635d70da15659c613018b51931db21d40c3bedb7db9b442fa756e8ed52833fc49333a816749df99c73956d43c237159f7bd6e728ea36e7791d
|
7
|
+
data.tar.gz: 1e4317121e610e67d0d396571b86e0a82b07c16122cc0be5c2c7ee5514136d9ec349282b643f06fe6d7f7b33cd04584048cf72ec3d8d88345e93f1135e7d5dee
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,91 +1,120 @@
|
|
1
1
|
# Fix
|
2
2
|
|
3
|
-
[](https://fixrb.dev/)
|
4
|
+
[](https://github.com/fixrb/fix/releases)
|
5
|
+
[](https://rubydoc.info/github/fixrb/fix/main)
|
6
|
+
[](https://github.com/fixrb/fix/actions?query=workflow%3Aci+branch%3Amain)
|
7
|
+
[](https://github.com/fixrb/fix/actions?query=workflow%3Arubocop+branch%3Amain)
|
8
|
+
[](https://github.com/fixrb/fix/raw/main/LICENSE.md)
|
9
9
|
|
10
|
-
|
10
|
+
⚠️ This project is still in the experimental phase. May be used at your own risk.
|
11
|
+
|
12
|
+

|
13
|
+
|
14
|
+
## Project goals
|
15
|
+
|
16
|
+
* Extract specs from the tests.
|
17
|
+
* Look like English documents.
|
18
|
+
* Be minimalist and easy to use.
|
19
|
+
* Run tests quickly.
|
11
20
|
|
12
21
|
## Installation
|
13
22
|
|
14
23
|
Add this line to your application's Gemfile:
|
15
24
|
|
16
25
|
```ruby
|
17
|
-
gem
|
26
|
+
gem "fix", ">= 1.0.0.beta8"
|
18
27
|
```
|
19
28
|
|
20
29
|
And then execute:
|
21
30
|
|
22
|
-
|
31
|
+
```sh
|
32
|
+
bundle
|
33
|
+
```
|
23
34
|
|
24
35
|
Or install it yourself as:
|
25
36
|
|
26
|
-
|
37
|
+
```sh
|
38
|
+
gem install fix --pre
|
39
|
+
```
|
27
40
|
|
28
41
|
## Example
|
29
42
|
|
30
|
-
Given
|
43
|
+
Given these specifications:
|
31
44
|
|
32
45
|
```ruby
|
33
|
-
# examples/duck/
|
34
|
-
|
35
|
-
|
36
|
-
|
46
|
+
# examples/duck/fix.rb
|
47
|
+
|
48
|
+
require "fix"
|
49
|
+
|
50
|
+
Fix :Duck do
|
51
|
+
it SHOULD be_an_instance_of :Duck
|
52
|
+
|
53
|
+
on :swims do
|
54
|
+
it MUST be_an_instance_of :String
|
55
|
+
it MUST eql "Swoosh..."
|
37
56
|
end
|
38
57
|
|
39
|
-
|
40
|
-
|
58
|
+
on :speaks do
|
59
|
+
it MUST raise_exception NoMethodError
|
41
60
|
end
|
42
61
|
|
43
|
-
|
44
|
-
|
62
|
+
on :sings do
|
63
|
+
it MAY eql "♪... ♫..."
|
45
64
|
end
|
46
65
|
end
|
47
66
|
```
|
48
67
|
|
49
|
-
When
|
68
|
+
When we load this `Duck` application:
|
50
69
|
|
51
70
|
```ruby
|
52
|
-
# examples/duck/
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@bird = Duck.new
|
57
|
-
|
58
|
-
Fix(@bird) do
|
59
|
-
on :swims do
|
60
|
-
it { MUST eql 'Swoosh...' }
|
71
|
+
# examples/duck/app.rb
|
72
|
+
class Duck
|
73
|
+
def walks
|
74
|
+
"Klop klop!"
|
61
75
|
end
|
62
76
|
|
63
|
-
|
64
|
-
|
77
|
+
def swims
|
78
|
+
"Swoosh..."
|
65
79
|
end
|
66
80
|
|
67
|
-
|
68
|
-
|
81
|
+
def quacks
|
82
|
+
puts "Quaaaaaack!"
|
69
83
|
end
|
70
84
|
end
|
71
85
|
```
|
72
86
|
|
73
|
-
|
87
|
+
And we run this test:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
# examples/duck/test.rb
|
91
|
+
|
92
|
+
require_relative "app"
|
93
|
+
require_relative "fix"
|
94
|
+
|
95
|
+
Fix[:Duck].against { Duck.new }
|
96
|
+
```
|
74
97
|
|
75
98
|
```sh
|
76
|
-
ruby examples/duck/
|
99
|
+
ruby examples/duck/test.rb
|
77
100
|
```
|
78
101
|
|
102
|
+
We should see this output:
|
103
|
+
|
79
104
|
```txt
|
80
|
-
|
81
|
-
|
82
|
-
|
105
|
+
(irb):3 Success: expected #<Duck:0x00007fb2fa208708> to be an instance of Duck.
|
106
|
+
(irb):7 Success: expected to eq "Swoosh...".
|
107
|
+
(irb):15 NoMethodError: undefined method `sings' for #<Duck:0x00007fb2fd8371d0>.
|
108
|
+
(irb):6 Success: expected "Swoosh..." to be an instance of String.
|
109
|
+
(irb):11 Success: undefined method `speaks' for #<Duck:0x00007fb2fcc79258>.
|
83
110
|
```
|
84
111
|
|
85
112
|
## Contact
|
86
113
|
|
87
|
-
* Home page: https://fixrb.dev/
|
88
|
-
* Source code: https://github.com/fixrb/fix
|
114
|
+
* Home page: [https://fixrb.dev/](https://fixrb.dev/)
|
115
|
+
* Source code: [https://github.com/fixrb/fix](https://github.com/fixrb/fix)
|
116
|
+
* API Doc: [https://rubydoc.info/gems/fix](https://rubydoc.info/gems/fix)
|
117
|
+
* Twitter: [https://twitter.com/fix\_rb](https://twitter.com/fix\_rb)
|
89
118
|
|
90
119
|
## Versioning
|
91
120
|
|
@@ -93,20 +122,13 @@ __Fix__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
93
122
|
|
94
123
|
## License
|
95
124
|
|
96
|
-
The gem is available as open source under the terms of the [MIT License](https://
|
125
|
+
The [gem](https://rubygems.org/gems/fix) is available as open source under the terms of the [MIT License](https://github.com/fixrb/fix/raw/main/LICENSE.md).
|
97
126
|
|
98
127
|
***
|
99
128
|
|
100
129
|
<p>
|
101
130
|
This project is sponsored by:<br />
|
102
131
|
<a href="https://sashite.com/"><img
|
103
|
-
src="https://github.com/fixrb/fix/raw/
|
132
|
+
src="https://github.com/fixrb/fix/raw/main/img/sashite.png"
|
104
133
|
alt="Sashite" /></a>
|
105
134
|
</p>
|
106
|
-
|
107
|
-
[travis]: https://travis-ci.org/fixrb/fix
|
108
|
-
[codeclimate]: https://codeclimate.com/github/fixrb/fix
|
109
|
-
[gem]: https://rubygems.org/gems/fix
|
110
|
-
[inchpages]: https://inch-ci.org/github/fixrb/fix
|
111
|
-
[rubydoc]: https://rubydoc.info/gems/fix/frames
|
112
|
-
[gitter]: https://gitter.im/fixrb/fix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
data/lib/fix.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("fix", "set")
|
4
|
+
|
5
|
+
require_relative "kernel"
|
6
|
+
|
3
7
|
# Namespace for the Fix framework.
|
4
8
|
#
|
9
|
+
# @api public
|
5
10
|
module Fix
|
6
|
-
#
|
11
|
+
# Test a built specification.
|
7
12
|
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# it { MUST equal 42 }
|
11
|
-
# end
|
13
|
+
# @example Run _Answer_ specification against `42`.
|
14
|
+
# Fix[:Answer].against(42)
|
12
15
|
#
|
13
|
-
# @param
|
14
|
-
# @param options [Hash] Some options.
|
15
|
-
# @param specs [Proc] The set of specs.
|
16
|
+
# @param name [String, Symbol] The constant name of the specifications.
|
16
17
|
#
|
17
|
-
# @
|
18
|
-
def self.
|
19
|
-
|
20
|
-
c.instance_eval(&block)
|
18
|
+
# @return [::Fix::Test] The specification document.
|
19
|
+
def self.[](name)
|
20
|
+
::Fix::Set.load(name)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
require_relative 'kernel'
|
25
|
-
require_relative File.join('fix', 'context')
|
data/lib/fix/doc.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fix
|
4
|
+
# Module for storing spec documents.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
module Doc
|
8
|
+
# @param name [String, Symbol] The constant name of the specifications.
|
9
|
+
def self.fetch(name)
|
10
|
+
const_get("#{name}::CONTEXTS")
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param contexts [Array<::Fix::Dsl>] The list of contexts document.
|
14
|
+
def self.specs(*contexts)
|
15
|
+
contexts.flat_map do |context|
|
16
|
+
env = context.new
|
17
|
+
|
18
|
+
env.public_methods(false).map do |public_method|
|
19
|
+
[env] + env.public_send(public_method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/fix/dsl.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "defi"
|
4
|
+
|
5
|
+
require_relative "matcher"
|
6
|
+
require_relative "requirement"
|
7
|
+
|
8
|
+
module Fix
|
9
|
+
# Abstract class for handling the domain-specific language.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Dsl
|
13
|
+
extend Matcher
|
14
|
+
extend Requirement
|
15
|
+
|
16
|
+
# Sets a user-defined property.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# require "fix"
|
20
|
+
#
|
21
|
+
# Fix do
|
22
|
+
# let(:name) { "Bob" }
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @param name [String, Symbol] The name of the property.
|
26
|
+
# @param block [Proc] The content of the method to define.
|
27
|
+
#
|
28
|
+
# @return [Symbol] A private method that define the block content.
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def self.let(name, &block)
|
32
|
+
private define_method(name, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Defines an example group with user-defined properties that describes a
|
36
|
+
# unit to be tested.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# require "fix"
|
40
|
+
#
|
41
|
+
# Fix do
|
42
|
+
# with :password, "secret" do
|
43
|
+
# it MUST be true
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @param kwargs [Hash] The list of propreties.
|
48
|
+
# @param block [Proc] The block to define the specs.
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def self.with(**kwargs, &block)
|
52
|
+
klass = ::Class.new(self)
|
53
|
+
klass.const_get(:CONTEXTS) << klass
|
54
|
+
kwargs.each { |name, value| klass.let(name) { value } }
|
55
|
+
klass.instance_eval(&block)
|
56
|
+
klass
|
57
|
+
end
|
58
|
+
|
59
|
+
# Defines an example group that describes a unit to be tested.
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# require "fix"
|
63
|
+
#
|
64
|
+
# Fix do
|
65
|
+
# on :+, 2 do
|
66
|
+
# it MUST be 42
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# @param method_name [String, Symbol] The method to send to the subject.
|
71
|
+
# @param block [Proc] The block to define the specs.
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
def self.on(method_name, *args, **kwargs, &block)
|
75
|
+
klass = ::Class.new(self)
|
76
|
+
klass.const_get(:CONTEXTS) << klass
|
77
|
+
|
78
|
+
const_set("Child#{block.object_id}", klass)
|
79
|
+
|
80
|
+
klass.define_singleton_method(:challenges) do
|
81
|
+
challenge = ::Defi.send(method_name, *args, **kwargs)
|
82
|
+
super() + [challenge]
|
83
|
+
end
|
84
|
+
|
85
|
+
klass.instance_eval(&block)
|
86
|
+
klass
|
87
|
+
end
|
88
|
+
|
89
|
+
# Defines a concrete spec definition.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# require "fix"
|
93
|
+
#
|
94
|
+
# Fix { it MUST be 42 }
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def self.it(requirement)
|
98
|
+
location = caller_locations(1, 1).fetch(0)
|
99
|
+
location = [location.path, location.lineno].join(":")
|
100
|
+
|
101
|
+
define_method("test_#{requirement.object_id}") do
|
102
|
+
[location, requirement, self.class.challenges]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# The list of challenges to be addressed to the object to be tested.
|
107
|
+
#
|
108
|
+
# @return [Array<Defi::Challenge>] A list of challenges.
|
109
|
+
def self.challenges
|
110
|
+
[]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/fix/matcher.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "matchi"
|
4
|
+
|
5
|
+
module Fix
|
6
|
+
# Collection of expectation matchers.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
module Matcher
|
10
|
+
# Equivalence matcher
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# matcher = eq("foo")
|
14
|
+
# matcher.matches? { "foo" } # => true
|
15
|
+
# matcher.matches? { "bar" } # => false
|
16
|
+
#
|
17
|
+
# @param expected [#eql?] An expected equivalent object.
|
18
|
+
#
|
19
|
+
# @return [#matches?] An equivalence matcher.
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def eq(expected)
|
23
|
+
::Matchi::Eq.new(expected)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias eql eq
|
27
|
+
|
28
|
+
# Identity matcher
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# object = "foo"
|
32
|
+
# matcher = be(object)
|
33
|
+
# matcher.matches? { object } # => true
|
34
|
+
# matcher.matches? { "foo" } # => false
|
35
|
+
#
|
36
|
+
# @param expected [#equal?] The expected identical object.
|
37
|
+
#
|
38
|
+
# @return [#matches?] An identity matcher.
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def be(expected)
|
42
|
+
::Matchi::Be.new(expected)
|
43
|
+
end
|
44
|
+
|
45
|
+
alias equal be
|
46
|
+
|
47
|
+
# Comparisons matcher
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# matcher = be_within(1).of(41)
|
51
|
+
# matcher.matches? { 42 } # => true
|
52
|
+
# matcher.matches? { 43 } # => false
|
53
|
+
#
|
54
|
+
# @param delta [Numeric] A numeric value.
|
55
|
+
#
|
56
|
+
# @return [#matches?] A comparison matcher.
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def be_within(delta)
|
60
|
+
::Matchi::BeWithin.new(delta)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Regular expressions matcher
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# matcher = match(/^foo$/)
|
67
|
+
# matcher.matches? { "foo" } # => true
|
68
|
+
# matcher.matches? { "bar" } # => false
|
69
|
+
#
|
70
|
+
# @param expected [#match] A regular expression.
|
71
|
+
#
|
72
|
+
# @return [#matches?] A regular expression matcher.
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def match(expected)
|
76
|
+
::Matchi::Match.new(expected)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Expecting errors matcher
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# matcher = raise_exception(NameError)
|
83
|
+
# matcher.matches? { Boom } # => true
|
84
|
+
# matcher.matches? { true } # => false
|
85
|
+
#
|
86
|
+
# @param expected [Exception, #to_s] The expected exception name.
|
87
|
+
#
|
88
|
+
# @return [#matches?] An error matcher.
|
89
|
+
#
|
90
|
+
# @api public
|
91
|
+
def raise_exception(expected)
|
92
|
+
::Matchi::RaiseException.new(expected)
|
93
|
+
end
|
94
|
+
|
95
|
+
# True matcher
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# matcher = be_true
|
99
|
+
# matcher.matches? { true } # => true
|
100
|
+
# matcher.matches? { false } # => false
|
101
|
+
# matcher.matches? { nil } # => false
|
102
|
+
# matcher.matches? { 4 } # => false
|
103
|
+
#
|
104
|
+
# @return [#matches?] A `true` matcher.
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def be_true
|
108
|
+
be(true)
|
109
|
+
end
|
110
|
+
|
111
|
+
# False matcher
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# matcher = be_false
|
115
|
+
# matcher.matches? { false } # => true
|
116
|
+
# matcher.matches? { true } # => false
|
117
|
+
# matcher.matches? { nil } # => false
|
118
|
+
# matcher.matches? { 4 } # => false
|
119
|
+
#
|
120
|
+
# @return [#matches?] A `false` matcher.
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def be_false
|
124
|
+
be(false)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Nil matcher
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# matcher = be_nil
|
131
|
+
# matcher.matches? { nil } # => true
|
132
|
+
# matcher.matches? { false } # => false
|
133
|
+
# matcher.matches? { true } # => false
|
134
|
+
# matcher.matches? { 4 } # => false
|
135
|
+
#
|
136
|
+
# @return [#matches?] A `nil` matcher.
|
137
|
+
#
|
138
|
+
# @api public
|
139
|
+
def be_nil
|
140
|
+
be(nil)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Type/class matcher
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# matcher = be_an_instance_of(String)
|
147
|
+
# matcher.matches? { "foo" } # => true
|
148
|
+
# matcher.matches? { 4 } # => false
|
149
|
+
#
|
150
|
+
# @param expected [Class, #to_s] The expected class name.
|
151
|
+
#
|
152
|
+
# @return [#matches?] A type/class matcher.
|
153
|
+
#
|
154
|
+
# @api public
|
155
|
+
def be_an_instance_of(expected)
|
156
|
+
::Matchi::BeAnInstanceOf.new(expected)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Change matcher
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
# object = []
|
163
|
+
# matcher = change(object, :length).by(1)
|
164
|
+
# matcher.matches? { object << 1 } # => true
|
165
|
+
#
|
166
|
+
# object = []
|
167
|
+
# matcher = change(object, :length).by_at_least(1)
|
168
|
+
# matcher.matches? { object << 1 } # => true
|
169
|
+
#
|
170
|
+
# object = []
|
171
|
+
# matcher = change(object, :length).by_at_most(1)
|
172
|
+
# matcher.matches? { object << 1 } # => true
|
173
|
+
#
|
174
|
+
# object = "foo"
|
175
|
+
# matcher = change(object, :to_s).from("foo").to("FOO")
|
176
|
+
# matcher.matches? { object.upcase! } # => true
|
177
|
+
#
|
178
|
+
# object = "foo"
|
179
|
+
# matcher = change(object, :to_s).to("FOO")
|
180
|
+
# matcher.matches? { object.upcase! } # => true
|
181
|
+
#
|
182
|
+
# @param object [#object_id] An object.
|
183
|
+
# @param method [Symbol] The name of a method.
|
184
|
+
# @param args [Array] A list of arguments.
|
185
|
+
# @param kwargs [Hash] A list of keyword arguments.
|
186
|
+
#
|
187
|
+
# @return [#matches?] A change matcher.
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def change(object, method, *args, **kwargs, &block)
|
191
|
+
::Matchi::Change.new(object, method, *args, **kwargs, &block)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Satisfy matcher
|
195
|
+
#
|
196
|
+
# @example
|
197
|
+
# matcher = satisfy { |value| value == 42 }
|
198
|
+
# matcher.matches? { 42 } # => true
|
199
|
+
#
|
200
|
+
# @param expected [Proc] A block of code.
|
201
|
+
#
|
202
|
+
# @return [#matches?] A satisfy matcher.
|
203
|
+
#
|
204
|
+
# @api public
|
205
|
+
def satisfy(&expected)
|
206
|
+
::Matchi::Satisfy.new(&expected)
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
# Predicate matcher, or default method missing behavior.
|
212
|
+
#
|
213
|
+
# @example Empty predicate matcher
|
214
|
+
# matcher = be_empty
|
215
|
+
# matcher.matches? { [] } # => true
|
216
|
+
# matcher.matches? { [4] } # => false
|
217
|
+
def method_missing(name, *args, **kwargs, &block)
|
218
|
+
return super unless predicate_matcher_name?(name)
|
219
|
+
|
220
|
+
::Matchi::Predicate.new(name, *args, **kwargs, &block)
|
221
|
+
end
|
222
|
+
|
223
|
+
# :nocov:
|
224
|
+
|
225
|
+
# Hook method to return whether the obj can respond to id method or not.
|
226
|
+
def respond_to_missing?(name, include_private = false)
|
227
|
+
predicate_matcher_name?(name) || super
|
228
|
+
end
|
229
|
+
|
230
|
+
# :nocov:
|
231
|
+
|
232
|
+
# Predicate matcher name detector.
|
233
|
+
#
|
234
|
+
# @param name [Array, Symbol] The name of a potential predicate matcher.
|
235
|
+
#
|
236
|
+
# @return [Boolean] Indicates if it is a predicate matcher name or not.
|
237
|
+
def predicate_matcher_name?(name)
|
238
|
+
name.start_with?("be_", "have_") && !name.end_with?("!", "?")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|