matchi 4.1.0 → 4.2.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.
data/lib/matchi/change.rb CHANGED
@@ -12,97 +12,171 @@ module Matchi
12
12
  # Initialize a wrapper of the change matcher with an object and the name of
13
13
  # one of its methods.
14
14
  #
15
- # @example
16
- # require "matchi/change"
15
+ # @api public
16
+ #
17
+ # @param object [#object_id] The object whose state will be monitored
18
+ # @param method [Symbol] The name of the method to track
19
+ # @param args [Array] Additional positional arguments to pass to the method
20
+ # @param kwargs [Hash] Additional keyword arguments to pass to the method
21
+ # @param block [Proc] Optional block to pass to the method
22
+ #
23
+ # @raise [ArgumentError] if method is not a Symbol
24
+ # @raise [ArgumentError] if object doesn't respond to method
25
+ #
26
+ # @return [Change] a new instance of the change wrapper
27
+ #
28
+ # @example Basic initialization
29
+ # array = []
30
+ # Change.new(array, :length) # Track array length changes
17
31
  #
18
- # Matchi::Change.new("foo", :to_s)
32
+ # @example With positional arguments
33
+ # hash = { key: "value" }
34
+ # Change.new(hash, :fetch, :key) # Track specific key value
19
35
  #
20
- # @param object [#object_id] An object.
21
- # @param method [Symbol] The name of a method.
22
- def initialize(object, method, ...)
36
+ # @example With keyword arguments
37
+ # hash = { a: 1, b: 2 }
38
+ # Change.new(hash, :fetch, default: 0) # Track with default value
39
+ #
40
+ # @example With block
41
+ # hash = { a: 1 }
42
+ # Change.new(hash, :fetch, :b) { |k| k.to_s } # Track with block default
43
+ def initialize(object, method, *args, **kwargs, &block)
23
44
  raise ::ArgumentError, "method must be a Symbol" unless method.is_a?(::Symbol)
24
45
  raise ::ArgumentError, "object must respond to method" unless object.respond_to?(method)
25
46
 
26
- @state = -> { object.send(method, ...) }
47
+ @state = -> { object.send(method, *args, **kwargs, &block) }
27
48
  end
28
49
 
29
- # Specifies a minimum delta of the expected change.
50
+ # Checks if the tracked method's return value changes when executing the block.
51
+ #
52
+ # This method verifies that the value changes in any way between the start and end
53
+ # of the block execution. It doesn't care about the type or magnitude of the change,
54
+ # only that it's different.
55
+ #
56
+ # @api public
57
+ #
58
+ # @yield [] Block during which the change should occur
59
+ # @yieldreturn [Object] Result of the block execution (not used)
60
+ #
61
+ # @return [Boolean] true if the value changed, false otherwise
62
+ #
63
+ # @raise [ArgumentError] if no block is provided
64
+ #
65
+ # @example Basic usage with array length
66
+ # array = []
67
+ # matcher = Change.new(array, :length)
68
+ # matcher.match? { array << "item" } # => true
69
+ # matcher.match? { array.clear } # => true (from 1 to 0)
70
+ # matcher.match? { array.dup } # => false (no change)
71
+ #
72
+ # @example With method parameters
73
+ # hash = { key: "old" }
74
+ # matcher = Change.new(hash, :fetch, :key)
75
+ # matcher.match? { hash[:key] = "new" } # => true
76
+ # matcher.match? { hash[:key] = "new" } # => false (same value)
77
+ #
78
+ # @example With computed values
79
+ # text = "hello"
80
+ # matcher = Change.new(text, :upcase)
81
+ # matcher.match? { text.upcase! } # => true
82
+ # matcher.match? { text.upcase! } # => false (already uppercase)
83
+ def match?
84
+ raise ::ArgumentError, "a block must be provided" unless block_given?
85
+
86
+ value_before = @state.call
87
+ yield
88
+ value_after = @state.call
89
+
90
+ !value_before.eql?(value_after)
91
+ end
92
+
93
+ # Returns a human-readable description of the matcher.
94
+ #
95
+ # @api public
96
+ #
97
+ # @return [String] A string describing what this matcher verifies
30
98
  #
31
99
  # @example
32
- # require "matchi/change"
100
+ # Change.new("test", :upcase).to_s # => 'eq "test"'
101
+ def to_s
102
+ "change #{@state.inspect}"
103
+ end
104
+
105
+ # Specifies a minimum delta of the expected change.
33
106
  #
34
- # object = []
107
+ # @api public
35
108
  #
36
- # change_wrapper = Matchi::Change.new(object, :length)
37
- # change_wrapper.by_at_least(1)
109
+ # @param minimum_delta [#object_id] The minimum expected change amount
38
110
  #
39
- # @param minimum_delta [#object_id] The minimum delta of the expected change.
111
+ # @return [#match?] A matcher that verifies the minimum change
40
112
  #
41
- # @return [#match?] A *change by at least* matcher.
113
+ # @example
114
+ # counter = 0
115
+ # matcher = Change.new(counter, :to_i).by_at_least(5)
116
+ # matcher.match? { counter += 6 } # => true
42
117
  def by_at_least(minimum_delta)
43
118
  ByAtLeast.new(minimum_delta, &@state)
44
119
  end
45
120
 
46
121
  # Specifies a maximum delta of the expected change.
47
122
  #
48
- # @example
49
- # require "matchi/change"
50
- #
51
- # object = []
123
+ # @api public
52
124
  #
53
- # change_wrapper = Matchi::Change.new(object, :length)
54
- # change_wrapper.by_at_most(1)
125
+ # @param maximum_delta [#object_id] The maximum allowed change amount
55
126
  #
56
- # @param maximum_delta [#object_id] The maximum delta of the expected change.
127
+ # @return [#match?] A matcher that verifies the maximum change
57
128
  #
58
- # @return [#match?] A *change by at most* matcher.
129
+ # @example
130
+ # counter = 0
131
+ # matcher = Change.new(counter, :to_i).by_at_most(5)
132
+ # matcher.match? { counter += 3 } # => true
59
133
  def by_at_most(maximum_delta)
60
134
  ByAtMost.new(maximum_delta, &@state)
61
135
  end
62
136
 
63
- # Specifies the delta of the expected change.
64
- #
65
- # @example
66
- # require "matchi/change"
137
+ # Specifies the exact delta of the expected change.
67
138
  #
68
- # object = []
139
+ # @api public
69
140
  #
70
- # change_wrapper = Matchi::Change.new(object, :length)
71
- # change_wrapper.by(1)
141
+ # @param delta [#object_id] The exact expected change amount
72
142
  #
73
- # @param delta [#object_id] The delta of the expected change.
143
+ # @return [#match?] A matcher that verifies the exact change
74
144
  #
75
- # @return [#match?] A *change by* matcher.
145
+ # @example
146
+ # counter = 0
147
+ # matcher = Change.new(counter, :to_i).by(5)
148
+ # matcher.match? { counter += 5 } # => true
76
149
  def by(delta)
77
150
  By.new(delta, &@state)
78
151
  end
79
152
 
80
- # Specifies the original value.
153
+ # Specifies the original value in a value transition check.
81
154
  #
82
- # @example
83
- # require "matchi/change"
155
+ # @api public
84
156
  #
85
- # change_wrapper = Matchi::Change.new("foo", :to_s)
86
- # change_wrapper.from("foo")
157
+ # @param old_value [#object_id] The expected initial value
87
158
  #
88
- # @param old_value [#object_id] The original value.
159
+ # @return [#to] A wrapper for creating a from/to matcher
89
160
  #
90
- # @return [#match?] A *change from* wrapper.
161
+ # @example
162
+ # string = "foo"
163
+ # Change.new(string, :to_s).from("foo").to("FOO")
91
164
  def from(old_value)
92
165
  From.new(old_value, &@state)
93
166
  end
94
167
 
95
- # Specifies the new value to expect.
168
+ # Specifies the final value to expect.
96
169
  #
97
- # @example
98
- # require "matchi/change"
170
+ # @api public
99
171
  #
100
- # change_wrapper = Matchi::Change.new("foo", :to_s)
101
- # change_wrapper.to("FOO")
172
+ # @param new_value [#object_id] The expected final value
102
173
  #
103
- # @param new_value [#object_id] The new value to expect.
174
+ # @return [#match?] A matcher that verifies the final state
104
175
  #
105
- # @return [#match?] A *change to* matcher.
176
+ # @example
177
+ # string = "foo"
178
+ # matcher = Change.new(string, :to_s).to("FOO")
179
+ # matcher.match? { string.upcase! } # => true
106
180
  def to(new_value)
107
181
  To.new(new_value, &@state)
108
182
  end
data/lib/matchi/eq.rb CHANGED
@@ -1,41 +1,83 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Matchi
4
- # *Equivalence* matcher.
4
+ # Value equivalence matcher that checks if two objects have identical values.
5
+ #
6
+ # This matcher verifies value equality using Ruby's Object#eql? method, which
7
+ # compares the values of objects rather than their identity. This is different
8
+ # from identity comparison (equal?) which checks if objects are the same instance.
9
+ #
10
+ # @example Basic usage with strings
11
+ # matcher = Matchi::Eq.new("test")
12
+ # matcher.match? { "test" } # => true
13
+ # matcher.match? { "test".dup } # => true
14
+ # matcher.match? { "other" } # => false
15
+ #
16
+ # @example With numbers
17
+ # matcher = Matchi::Eq.new(42)
18
+ # matcher.match? { 42 } # => true
19
+ # matcher.match? { 42.0 } # => false # Different types
20
+ # matcher.match? { 43 } # => false
21
+ #
22
+ # @example With collections
23
+ # array = [1, 2, 3]
24
+ # matcher = Matchi::Eq.new(array)
25
+ # matcher.match? { array.dup } # => true # Same values
26
+ # matcher.match? { array } # => true # Same object
27
+ # matcher.match? { [1, 2, 3] } # => true # Same values
28
+ # matcher.match? { [1, 2] } # => false # Different values
29
+ #
30
+ # @see https://ruby-doc.org/core/Object.html#method-i-eql-3F
31
+ # @see Matchi::Be
5
32
  class Eq
6
- # Initialize the matcher with an object.
33
+ # Initialize the matcher with a reference value.
7
34
  #
8
- # @example
9
- # require "matchi/eq"
35
+ # @api public
36
+ #
37
+ # @param expected [#eql?] The expected equivalent value
10
38
  #
11
- # Matchi::Eq.new("foo")
39
+ # @return [Eq] a new instance of the matcher
12
40
  #
13
- # @param expected [#eql?] An expected equivalent object.
41
+ # @example
42
+ # Eq.new("test") # Match strings with same value
43
+ # Eq.new([1, 2, 3]) # Match arrays with same elements
14
44
  def initialize(expected)
15
45
  @expected = expected
16
46
  end
17
47
 
18
- # Boolean comparison between the actual value and the expected value.
48
+ # Checks if the yielded object has a value equivalent to the expected object.
19
49
  #
20
- # @example
21
- # require "matchi/eq"
50
+ # This method uses Ruby's Object#eql? method, which performs value comparison.
51
+ # Two objects are considered equivalent if they have the same value, even if
52
+ # they are different instances.
53
+ #
54
+ # @api public
22
55
  #
23
- # matcher = Matchi::Eq.new("foo")
24
- # matcher.match? { "foo" } # => true
56
+ # @yield [] Block that returns the object to check
57
+ # @yieldreturn [Object] The object to verify equivalence with
25
58
  #
26
- # @yieldreturn [#object_id] The actual value to compare to the expected
27
- # one.
59
+ # @return [Boolean] true if both objects have equivalent values
28
60
  #
29
- # @return [Boolean] Comparison between actual and expected values.
61
+ # @raise [ArgumentError] if no block is provided
62
+ #
63
+ # @example
64
+ # matcher = Eq.new([1, 2, 3])
65
+ # matcher.match? { [1, 2, 3] } # => true
66
+ # matcher.match? { [1, 2, 3].dup } # => true
30
67
  def match?
31
68
  raise ::ArgumentError, "a block must be provided" unless block_given?
32
69
 
33
70
  @expected.eql?(yield)
34
71
  end
35
72
 
36
- # Returns a string representing the matcher.
73
+ # Returns a human-readable description of the matcher.
74
+ #
75
+ # @api public
37
76
  #
38
- # @return [String] a human-readable description of the matcher
77
+ # @return [String] A string describing what this matcher verifies
78
+ #
79
+ # @example
80
+ # Eq.new("test").to_s # => 'eq "test"'
39
81
  def to_s
40
82
  "eq #{@expected.inspect}"
41
83
  end
data/lib/matchi/match.rb CHANGED
@@ -1,43 +1,83 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Matchi
4
- # *Regular expressions* matcher.
4
+ # Pattern matching matcher that checks if a value matches a regular expression.
5
+ #
6
+ # This matcher verifies that a value matches a pattern using Ruby's Regexp#match? method.
7
+ # It's particularly useful for string validation, pattern matching, and text analysis.
8
+ # The matcher ensures secure pattern matching by requiring the pattern to respond to match?.
9
+ #
10
+ # @example Basic usage
11
+ # matcher = Matchi::Match.new(/^test/)
12
+ # matcher.match? { "test_string" } # => true
13
+ # matcher.match? { "other_string" } # => false
14
+ #
15
+ # @example Case sensitivity
16
+ # matcher = Matchi::Match.new(/^test$/i)
17
+ # matcher.match? { "TEST" } # => true
18
+ # matcher.match? { "Test" } # => true
19
+ # matcher.match? { "testing" } # => false
20
+ #
21
+ # @example Multiline patterns
22
+ # matcher = Matchi::Match.new(/\A\d+\Z/m)
23
+ # matcher.match? { "123" } # => true
24
+ # matcher.match? { "12.3" } # => false
25
+ #
26
+ # @see https://ruby-doc.org/core/Regexp.html#method-i-match-3F
5
27
  class Match
6
- # Initialize the matcher with an instance of Regexp.
28
+ # Initialize the matcher with a pattern.
7
29
  #
8
- # @example
9
- # require "matchi/match"
30
+ # @api public
31
+ #
32
+ # @param expected [#match?] A pattern that responds to match?
33
+ #
34
+ # @raise [ArgumentError] if the pattern doesn't respond to match?
10
35
  #
11
- # Matchi::Match.new(/^foo$/)
36
+ # @return [Match] a new instance of the matcher
12
37
  #
13
- # @param expected [#match] A regular expression.
38
+ # @example
39
+ # Match.new(/\d+/) # Match digits
40
+ # Match.new(/^test$/i) # Case-insensitive match
41
+ # Match.new(/\A\w+\Z/) # Full string word characters
14
42
  def initialize(expected)
15
43
  raise ::ArgumentError, "expected must respond to match?" unless expected.respond_to?(:match?)
16
44
 
17
45
  @expected = expected
18
46
  end
19
47
 
20
- # Boolean comparison between the actual value and the expected value.
48
+ # Checks if the yielded value matches the expected pattern.
21
49
  #
22
- # @example
23
- # require "matchi/match"
50
+ # This method uses the pattern's match? method to perform the comparison.
51
+ # The match is performed on the entire string unless the pattern specifically
52
+ # allows partial matches.
53
+ #
54
+ # @api public
24
55
  #
25
- # matcher = Matchi::Match.new(/^foo$/)
26
- # matcher.match? { "foo" } # => true
56
+ # @yield [] Block that returns the value to check
57
+ # @yieldreturn [#to_s] The value to match against the pattern
27
58
  #
28
- # @yieldreturn [#object_id] The actual value to compare to the expected
29
- # one.
59
+ # @return [Boolean] true if the value matches the pattern
30
60
  #
31
- # @return [Boolean] Comparison between actual and expected values.
61
+ # @raise [ArgumentError] if no block is provided
62
+ #
63
+ # @example
64
+ # matcher = Match.new(/^\d{3}-\d{2}-\d{4}$/)
65
+ # matcher.match? { "123-45-6789" } # => true
66
+ # matcher.match? { "123456789" } # => false
32
67
  def match?
33
68
  raise ::ArgumentError, "a block must be provided" unless block_given?
34
69
 
35
70
  @expected.match?(yield)
36
71
  end
37
72
 
38
- # Returns a string representing the matcher.
73
+ # Returns a human-readable description of the matcher.
74
+ #
75
+ # @api public
39
76
  #
40
- # @return [String] a human-readable description of the matcher
77
+ # @return [String] A string describing what this matcher verifies
78
+ #
79
+ # @example
80
+ # Match.new(/^test/).to_s # => "match /^test/"
41
81
  def to_s
42
82
  "match #{@expected.inspect}"
43
83
  end
@@ -1,45 +1,109 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Matchi
4
- # *Predicate* matcher.
4
+ # Predicate matcher that checks if an object responds to a predicate method with a truthy value.
5
+ #
6
+ # This matcher converts a predicate name (starting with 'be_' or 'have_') into a method call
7
+ # ending with '?' and verifies that calling this method returns a boolean value. It's useful
8
+ # for testing state-checking methods and collection properties. The matcher supports two types
9
+ # of predicate formats: 'be_*' which converts to '*?' and 'have_*' which converts to 'has_*?'.
10
+ #
11
+ # @example Basic empty check
12
+ # matcher = Matchi::Predicate.new(:be_empty)
13
+ # matcher.match? { [] } # => true
14
+ # matcher.match? { [1, 2] } # => false
15
+ #
16
+ # @example Object property check with arguments
17
+ # matcher = Matchi::Predicate.new(:have_key, :name)
18
+ # matcher.match? { { name: "Alice" } } # => true
19
+ # matcher.match? { { age: 30 } } # => false
20
+ #
21
+ # @example Using keyword arguments
22
+ # class Record
23
+ # def complete?(status: nil)
24
+ # status.nil? || status == :validated
25
+ # end
26
+ # end
27
+ #
28
+ # matcher = Matchi::Predicate.new(:be_complete, status: :validated)
29
+ # matcher.match? { Record.new } # => true
30
+ #
31
+ # @example With block arguments
32
+ # class List
33
+ # def all?(&block)
34
+ # block ? super : empty?
35
+ # end
36
+ # end
37
+ #
38
+ # matcher = Matchi::Predicate.new(:be_all) { |x| x.positive? }
39
+ # matcher.match? { [1, 2, 3] } # => true
40
+ # matcher.match? { [-1, 2, 3] } # => false
41
+ #
42
+ # @see https://ruby-doc.org/core/Object.html#method-i-respond_to-3F
5
43
  class Predicate
6
- # Initialize the matcher with a name and arguments.
44
+ # Mapping of predicate prefixes to their method name transformations.
45
+ # Each entry defines how a prefix should be converted to its method form.
7
46
  #
8
- # @example
9
- # require "matchi/predicate"
47
+ # @api private
48
+ PREFIXES = {
49
+ "be_" => ->(name) { "#{name.gsub(/\A(?:be_)/, "")}?" },
50
+ "have_" => ->(name) { "#{name.gsub(/\A(?:have_)/, "has_")}?" }
51
+ }.freeze
52
+
53
+ # Initialize the matcher with a predicate name and optional arguments.
54
+ #
55
+ # @api public
56
+ #
57
+ # @param name [#to_s] A matcher name starting with 'be_' or 'have_'
58
+ # @param args [Array] Optional positional arguments to pass to the predicate method
59
+ # @param kwargs [Hash] Optional keyword arguments to pass to the predicate method
60
+ # @param block [Proc] Optional block to pass to the predicate method
61
+ #
62
+ # @raise [ArgumentError] if the predicate name format is invalid
63
+ #
64
+ # @return [Predicate] a new instance of the matcher
10
65
  #
11
- # Matchi::Predicate.new(:be_empty)
66
+ # @example With simple predicate
67
+ # Predicate.new(:be_empty) # Empty check
12
68
  #
13
- # @param name [#to_s] A matcher name.
14
- # @param args [Array] A list of parameters.
15
- # @param kwargs [Hash] A list of keyword parameters.
16
- # @param block [Proc] A block of code.
69
+ # @example With arguments
70
+ # Predicate.new(:have_key, :id) # Key presence check
71
+ #
72
+ # @example With keyword arguments
73
+ # Predicate.new(:be_valid, status: true) # Conditional validation
17
74
  def initialize(name, *args, **kwargs, &block)
18
75
  @name = String(name)
19
76
  raise ::ArgumentError, "invalid predicate name format" unless valid_name?
20
77
 
21
- @args = args
78
+ @args = args
22
79
  @kwargs = kwargs
23
- @block = block
80
+ @block = block
24
81
  end
25
82
 
26
- # Boolean comparison between the actual value and the expected value.
83
+ # Checks if the yielded object responds to and returns true for the predicate.
27
84
  #
28
- # @example
29
- # require "matchi/predicate"
85
+ # This method converts the predicate name into a method name according to the prefix
86
+ # mapping and calls it on the yielded object with any provided arguments. The method
87
+ # must return a boolean value, or a TypeError will be raised.
30
88
  #
31
- # matcher = Matchi::Predicate.new(:be_empty)
32
- # matcher.match? { [] } # => true
89
+ # @api public
33
90
  #
34
- # @example
35
- # require "matchi/predicate"
91
+ # @yield [] Block that returns the object to check
92
+ # @yieldreturn [Object] The object to call the predicate method on
36
93
  #
37
- # matcher = Matchi::Predicate.new(:have_key, :foo)
38
- # matcher.match? { { foo: 42 } } # => true
94
+ # @return [Boolean] true if the predicate method returns true
39
95
  #
40
- # @yieldreturn [#object_id] The actual value to receive the method request.
96
+ # @raise [ArgumentError] if no block is provided
97
+ # @raise [TypeError] if predicate method returns non-boolean value
41
98
  #
42
- # @return [Boolean] A boolean returned by the actual value being tested.
99
+ # @example Basic usage
100
+ # matcher = Predicate.new(:be_empty)
101
+ # matcher.match? { [] } # => true
102
+ # matcher.match? { [1] } # => false
103
+ #
104
+ # @example With arguments
105
+ # matcher = Predicate.new(:have_key, :id)
106
+ # matcher.match? { { id: 1 } } # => true
43
107
  def match?
44
108
  raise ::ArgumentError, "a block must be provided" unless block_given?
45
109
 
@@ -49,9 +113,20 @@ module Matchi
49
113
  raise ::TypeError, "Boolean expected, but #{value.class} instance returned."
50
114
  end
51
115
 
52
- # Returns a string representing the matcher.
116
+ # Returns a human-readable description of the matcher.
117
+ #
118
+ # @api public
53
119
  #
54
- # @return [String] a human-readable description of the matcher
120
+ # @return [String] A string describing what this matcher verifies
121
+ #
122
+ # @example Simple predicate
123
+ # Predicate.new(:be_empty).to_s # => "be empty"
124
+ #
125
+ # @example With arguments
126
+ # Predicate.new(:have_key, :id).to_s # => "have key :id"
127
+ #
128
+ # @example With keyword arguments
129
+ # Predicate.new(:be_valid, active: true).to_s # => "be valid active: true"
55
130
  def to_s
56
131
  (
57
132
  "#{@name.tr("_", " ")} " + [
@@ -64,20 +139,34 @@ module Matchi
64
139
 
65
140
  private
66
141
 
67
- # The name of the method to send to the object.
142
+ # Converts the predicate name into the actual method name to call.
143
+ #
144
+ # @api private
145
+ #
146
+ # @return [Symbol] The method name to call on the object
147
+ # @raise [ArgumentError] if the predicate prefix is unknown
148
+ #
149
+ # @example
150
+ # # With be_ prefix
151
+ # method_name # => :empty? (from be_empty)
152
+ # # With have_ prefix
153
+ # method_name # => :has_key? (from have_key)
68
154
  def method_name
69
- if @name.start_with?("be_")
70
- :"#{@name.gsub("be_", "")}?"
71
- else
72
- :"#{@name.gsub("have_", "has_")}?"
73
- end
155
+ _, transform = PREFIXES.find { |prefix, _| @name.start_with?(prefix) }
156
+ return transform.call(@name) if transform
157
+
158
+ raise ::ArgumentError, "unknown prefix in predicate name: #{@name}"
74
159
  end
75
160
 
76
- # Verify the matcher name structure.
161
+ # Verifies that the predicate name follows the required format.
162
+ #
163
+ # @api private
164
+ #
165
+ # @return [Boolean] true if the name follows the required format
77
166
  def valid_name?
78
- return false if @name.end_with?("?", "!")
167
+ return false if @name.match?(/[?!]\z/)
79
168
 
80
- @name.start_with?("be_", "have_")
169
+ PREFIXES.keys.any? { |prefix| @name.start_with?(prefix) }
81
170
  end
82
171
  end
83
172
  end