mutant 0.8.7 → 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -0
  3. data/README.md +64 -3
  4. data/config/flay.yml +1 -1
  5. data/lib/mutant.rb +2 -0
  6. data/lib/mutant/cli.rb +1 -1
  7. data/lib/mutant/env/bootstrap.rb +36 -8
  8. data/lib/mutant/expression/method.rb +3 -5
  9. data/lib/mutant/expression/methods.rb +2 -4
  10. data/lib/mutant/expression/namespace.rb +4 -8
  11. data/lib/mutant/matcher.rb +3 -18
  12. data/lib/mutant/matcher/chain.rb +7 -13
  13. data/lib/mutant/matcher/compiler.rb +2 -13
  14. data/lib/mutant/matcher/filter.rb +6 -19
  15. data/lib/mutant/matcher/method.rb +124 -104
  16. data/lib/mutant/matcher/method/instance.rb +40 -34
  17. data/lib/mutant/matcher/method/singleton.rb +80 -61
  18. data/lib/mutant/matcher/methods.rb +19 -29
  19. data/lib/mutant/matcher/namespace.rb +22 -16
  20. data/lib/mutant/matcher/null.rb +4 -7
  21. data/lib/mutant/matcher/scope.rb +23 -13
  22. data/lib/mutant/matcher/static.rb +17 -0
  23. data/lib/mutant/mutation.rb +0 -5
  24. data/lib/mutant/reporter/cli/format.rb +2 -3
  25. data/lib/mutant/reporter/cli/printer/env_progress.rb +37 -11
  26. data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -1
  27. data/lib/mutant/scope.rb +6 -0
  28. data/lib/mutant/subject/method.rb +0 -7
  29. data/lib/mutant/subject/method/instance.rb +0 -10
  30. data/lib/mutant/subject/method/singleton.rb +0 -10
  31. data/lib/mutant/version.rb +1 -1
  32. data/lib/mutant/zombifier.rb +2 -1
  33. data/mutant-rspec.gemspec +1 -1
  34. data/spec/integration/mutant/rspec_spec.rb +1 -1
  35. data/spec/shared/method_matcher_behavior.rb +21 -14
  36. data/spec/spec_helper.rb +6 -0
  37. data/spec/unit/mutant/env/boostrap_spec.rb +88 -26
  38. data/spec/unit/mutant/env_spec.rb +0 -1
  39. data/spec/unit/mutant/expression/method_spec.rb +3 -3
  40. data/spec/unit/mutant/expression/methods_spec.rb +3 -4
  41. data/spec/unit/mutant/expression/namespace/flat_spec.rb +2 -3
  42. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +2 -4
  43. data/spec/unit/mutant/matcher/chain_spec.rb +21 -29
  44. data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +16 -13
  45. data/spec/unit/mutant/matcher/compiler_spec.rb +49 -60
  46. data/spec/unit/mutant/matcher/filter_spec.rb +15 -31
  47. data/spec/unit/mutant/matcher/method/instance_spec.rb +84 -128
  48. data/spec/unit/mutant/matcher/method/singleton_spec.rb +48 -52
  49. data/spec/unit/mutant/matcher/methods/instance_spec.rb +21 -24
  50. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +18 -21
  51. data/spec/unit/mutant/matcher/namespace_spec.rb +30 -38
  52. data/spec/unit/mutant/matcher/null_spec.rb +5 -20
  53. data/spec/unit/mutant/matcher/scope_spec.rb +33 -0
  54. data/spec/unit/mutant/matcher/static_spec.rb +11 -0
  55. data/spec/unit/mutant/mutation_spec.rb +30 -10
  56. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +6 -0
  57. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -0
  58. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +10 -0
  59. data/spec/unit/mutant/reporter/cli_spec.rb +4 -0
  60. data/spec/unit/mutant/subject/method/instance_spec.rb +0 -28
  61. data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -28
  62. data/test_app/Gemfile.rspec3.4 +7 -0
  63. data/test_app/lib/test_app.rb +16 -12
  64. data/test_app/lib/test_app/literal.rb +3 -0
  65. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f0d162e90a523f4f62c1f3541b1157fb78d6080
4
- data.tar.gz: fa8dcc8abc2f6960ebd74a53711bf8e87ec0f32c
3
+ metadata.gz: d83617d43f73b49c391d1b7e8f3317a666297c83
4
+ data.tar.gz: 186b8ce58089718ba3cafa1c08f609b89941541e
5
5
  SHA512:
6
- metadata.gz: 4c672d952620acf28cff4a26127a03facbc32c13bece89934b487433dc1fd2a918a76188fa0efe86588c7a480e8bf54383baa66eed869eeaf27752dce5ab313e
7
- data.tar.gz: f3e533b36d8f52544523fbd81cda480a0653861106cf841420e11043a953c41a043e7a3b88fd8838629272d3bd960e76d22494bce9b7f014b10c67601d7a2c6e
6
+ metadata.gz: 3d4ee1e94a96ba909cf327bde6afdca8ff5e50f80603ca8835115fdba2abbfa05ad89897366908f8b64bca2e9302605e7accddf1167260c187cf4e40417d0df5
7
+ data.tar.gz: 7b599e22fa7ddd03e9f9ac91af9ce0e730b155c8df2b5ab1b5a67fc8cd62f8bf8972ca8df64b45fb5114a556e7416f3c86baf666ea62f5de5201e8b068406afc
data/Changelog.md CHANGED
@@ -1,3 +1,8 @@
1
+ # v0.8.8 2015-11-15
2
+
3
+ * Add support for rspec-3.3.4
4
+ * Add mutations/s performance metric to report
5
+
1
6
  # v0.8.7 2015-10-30
2
7
 
3
8
  * Fix blackliting regexp to correctly match the String `(eval)` absolutely.
data/README.md CHANGED
@@ -73,6 +73,54 @@ To mutation test Rails models with rspec comment out ```require 'rspec/autorun'`
73
73
  RAILS_ENV=test bundle exec mutant -r ./config/environment --use rspec User
74
74
  ```
75
75
 
76
+ Limitations
77
+ -----------
78
+
79
+ Mutant cannot emit mutations for...
80
+
81
+ * methods defined within a closure. For example, methods defined using `module_eval`, `class_eval`,
82
+ `define_method`, or `define_singleton_method`:
83
+
84
+ ```ruby
85
+ class Example
86
+ class_eval do
87
+ def example1
88
+ end
89
+ end
90
+
91
+ module_eval do
92
+ def example2
93
+ end
94
+ end
95
+
96
+ define_method(:example3) do
97
+ end
98
+
99
+ define_singleton_method(:example4) do
100
+ end
101
+ end
102
+ ```
103
+
104
+ * singleton methods not defined on a constant or `self`
105
+
106
+ ```ruby
107
+ class Foo
108
+ def self.bar; end # ok
109
+ def Foo.baz; end # ok
110
+
111
+ myself = self
112
+ def myself.qux; end # cannot mutate
113
+ end
114
+ ```
115
+
116
+ * methods defined with eval:
117
+
118
+ ```ruby
119
+ class Foo
120
+ class_eval('def bar; end') # cannot mutate
121
+ end
122
+ ```
123
+
76
124
  Mutation-Operators:
77
125
  -------------------
78
126
 
@@ -182,11 +230,24 @@ There are some presentations about mutant in the wild:
182
230
  * [eurucamp 2013](http://2013.eurucamp.org/) / FrOSCon-2013 http://slid.es/markusschirp/mutation-testing
183
231
  * [Cologne.rb](http://www.colognerb.de/topics/mutation-testing-mit-mutant) / https://github.com/DonSchado/colognerb-on-mutant/blob/master/mutation_testing_slides.pdf
184
232
 
185
- Blog-Posts
233
+ Blog posts
186
234
  ----------
187
235
 
188
- * http://www.sitepoint.com/mutation-testing-mutant/
189
- * http://solnic.eu/2013/01/23/mutation-testing-with-mutant.html
236
+ Sorted by recency:
237
+
238
+ * [How to write better code using mutation testing (November 2015)][blockscore]
239
+ * [How good are your Ruby tests? Testing your tests with mutant (June 2015)][arkency1]
240
+ * [Mutation testing and continuous integration (May 2015)][arkency2]
241
+ * [Why I want to introduce mutation testing to the `rails_event_store` gem (April 2015)][arkency3]
242
+ * [Mutation testing with mutant (April 2014)][sitepoint]
243
+ * [Mutation testing with mutant (January 2013)][solnic]
244
+
245
+ [blockscore]: https://blog.blockscore.com/how-to-write-better-code-using-mutation-testing/
246
+ [sitepoint]: http://www.sitepoint.com/mutation-testing-mutant/
247
+ [arkency1]: http://blog.arkency.com/2015/06/how-good-are-your-ruby-tests-testing-your-tests-with-mutant/
248
+ [arkency2]: http://blog.arkency.com/2015/05/mutation-testing-and-continuous-integration/
249
+ [arkency3]: http://blog.arkency.com/2015/04/why-i-want-to-introduce-mutation-testing-to-the-rails-event-store-gem/
250
+ [solnic]: http://solnic.eu/2013/01/23/mutation-testing-with-mutant.html
190
251
 
191
252
  The Crash / Stuck Problem (MRI)
192
253
  -------------------------------
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 1253
3
+ total_score: 1232
data/lib/mutant.rb CHANGED
@@ -132,6 +132,7 @@ require 'mutant/mutator/node/match_current_line'
132
132
  require 'mutant/loader'
133
133
  require 'mutant/context'
134
134
  require 'mutant/context/scope'
135
+ require 'mutant/scope'
135
136
  require 'mutant/subject'
136
137
  require 'mutant/subject/method'
137
138
  require 'mutant/subject/method/instance'
@@ -148,6 +149,7 @@ require 'mutant/matcher/namespace'
148
149
  require 'mutant/matcher/scope'
149
150
  require 'mutant/matcher/filter'
150
151
  require 'mutant/matcher/null'
152
+ require 'mutant/matcher/static'
151
153
  require 'mutant/expression'
152
154
  require 'mutant/expression/parser'
153
155
  require 'mutant/expression/method'
data/lib/mutant/cli.rb CHANGED
@@ -192,7 +192,7 @@ module Mutant
192
192
  #
193
193
  # @api private
194
194
  def with(attributes)
195
- @config = @config.with(attributes)
195
+ @config = config.with(attributes)
196
196
  end
197
197
 
198
198
  # Add configuration
@@ -4,10 +4,18 @@ module Mutant
4
4
  class Bootstrap
5
5
  include Adamantium::Flat, Concord::Public.new(:config, :cache), Procto.call(:env)
6
6
 
7
- SEMANTICS_MESSAGE =
8
- "Fix your lib to follow normal ruby semantics!\n" \
7
+ SEMANTICS_MESSAGE_FORMAT =
8
+ "%<message>s. Fix your lib to follow normal ruby semantics!\n" \
9
9
  '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
10
10
 
11
+ CLASS_NAME_RAISED_EXCEPTION =
12
+ '%<scope_class>s#name from: %<scope>s raised an error: %<exception>s'.freeze
13
+
14
+ CLASS_NAME_TYPE_MISMATCH_FORMAT =
15
+ '%<scope_class>s#name from: %<scope>s returned %<name>s'.freeze
16
+
17
+ private_constant(*constants(false))
18
+
11
19
  # Scopes that are eligible for matching
12
20
  #
13
21
  # @return [Enumerable<Matcher::Scope>]
@@ -84,7 +92,12 @@ module Mutant
84
92
  def scope_name(scope)
85
93
  scope.name
86
94
  rescue => exception
87
- warn("#{scope.class}#name from: #{scope.inspect} raised an error: #{exception.inspect}. #{SEMANTICS_MESSAGE}")
95
+ semantics_warning(
96
+ CLASS_NAME_RAISED_EXCEPTION,
97
+ scope_class: scope.class,
98
+ scope: scope,
99
+ exception: exception.inspect
100
+ )
88
101
  nil
89
102
  end
90
103
 
@@ -95,7 +108,7 @@ module Mutant
95
108
  # @api private
96
109
  def infect
97
110
  config.includes.each(&$LOAD_PATH.method(:<<))
98
- config.requires.each(&method(:require))
111
+ config.requires.each(&Kernel.method(:require))
99
112
  @integration = config.integration.new(config).setup
100
113
  end
101
114
 
@@ -105,7 +118,7 @@ module Mutant
105
118
  #
106
119
  # @api private
107
120
  def matched_subjects
108
- Matcher::Compiler.call(self, config.matcher).to_a
121
+ Matcher::Compiler.call(config.matcher).call(self)
109
122
  end
110
123
 
111
124
  # Initialize matchable scopes
@@ -115,8 +128,8 @@ module Mutant
115
128
  # @api private
116
129
  def initialize_matchable_scopes
117
130
  scopes = ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
118
- expression = expression(scope)
119
- aggregate << Matcher::Scope.new(self, scope, expression) if expression
131
+ expression = expression(scope) || next
132
+ aggregate << Scope.new(scope, expression)
120
133
  end
121
134
 
122
135
  @matchable_scopes = scopes.sort_by { |scope| scope.expression.syntax }
@@ -137,12 +150,27 @@ module Mutant
137
150
  name = scope_name(scope) or return
138
151
 
139
152
  unless name.instance_of?(String)
140
- warn("#{scope.class}#name from: #{scope.inspect} returned #{name.inspect}. #{SEMANTICS_MESSAGE}")
153
+ semantics_warning(
154
+ CLASS_NAME_TYPE_MISMATCH_FORMAT,
155
+ scope_class: scope.class,
156
+ scope: scope,
157
+ name: name
158
+ )
141
159
  return
142
160
  end
143
161
 
144
162
  config.expression_parser.try_parse(name)
145
163
  end
164
+
165
+ # Write a semantics warning
166
+ #
167
+ # @return [undefined]
168
+ #
169
+ # @api private
170
+ def semantics_warning(format, options)
171
+ message = format % options
172
+ warn(SEMANTICS_MESSAGE_FORMAT % { message: message })
173
+ end
146
174
  end # Boostrap
147
175
  end # Env
148
176
  end # Mutant
@@ -32,15 +32,13 @@ module Mutant
32
32
 
33
33
  # Matcher for expression
34
34
  #
35
- # @param [Env] env
36
- #
37
35
  # @return [Matcher]
38
36
  #
39
37
  # @api private
40
- def matcher(env)
41
- methods_matcher = MATCHERS.fetch(scope_symbol).new(env, scope)
38
+ def matcher
39
+ methods_matcher = MATCHERS.fetch(scope_symbol).new(scope)
42
40
 
43
- Matcher::Filter.build(methods_matcher) { |subject| subject.expression.eql?(self) }
41
+ Matcher::Filter.new(methods_matcher, ->(subject) { subject.expression.eql?(self) })
44
42
  end
45
43
 
46
44
  private
@@ -26,13 +26,11 @@ module Mutant
26
26
 
27
27
  # Matcher on expression
28
28
  #
29
- # @param [Env] env
30
- #
31
29
  # @return [Matcher::Method]
32
30
  #
33
31
  # @api private
34
- def matcher(env)
35
- MATCHERS.fetch(scope_symbol).new(env, scope)
32
+ def matcher
33
+ MATCHERS.fetch(scope_symbol).new(scope)
36
34
  end
37
35
 
38
36
  # Length of match with other expression
@@ -35,13 +35,11 @@ module Mutant
35
35
 
36
36
  # Matcher for expression
37
37
  #
38
- # @param [Env::Bootstrap] env
39
- #
40
38
  # @return [Matcher]
41
39
  #
42
40
  # @api private
43
- def matcher(env)
44
- Matcher::Namespace.new(env, self)
41
+ def matcher
42
+ Matcher::Namespace.new(self)
45
43
  end
46
44
 
47
45
  # Length of match with other expression
@@ -71,13 +69,11 @@ module Mutant
71
69
 
72
70
  # Matcher matcher on expression
73
71
  #
74
- # @param [Env::Bootstrap] env
75
- #
76
72
  # @return [Matcher]
77
73
  #
78
74
  # @api private
79
- def matcher(env)
80
- Matcher::Scope.new(env, Object.const_get(scope_name), self)
75
+ def matcher
76
+ Matcher::Scope.new(Object.const_get(scope_name))
81
77
  end
82
78
 
83
79
  # Syntax for expression
@@ -1,30 +1,15 @@
1
1
  module Mutant
2
2
  # Abstract matcher to find subjects to mutate
3
3
  class Matcher
4
- include Adamantium::Flat, Enumerable, AbstractType
4
+ include Adamantium::Flat, AbstractType
5
5
 
6
- # Default matcher build implementation
6
+ # Call matcher
7
7
  #
8
8
  # @param [Env] env
9
- # @param [Object] input
10
- #
11
- # @return [undefined]
12
- #
13
- # @api private
14
- def self.build(*arguments)
15
- new(*arguments)
16
- end
17
-
18
- # Enumerate subjects
19
- #
20
- # @return [self]
21
- # if block given
22
9
  #
23
10
  # @return [Enumerable<Subject>]
24
- # otherwise
25
11
  #
26
- # @api private
27
- abstract_method :each
12
+ abstract_method :call
28
13
 
29
14
  end # Matcher
30
15
  end # Mutant
@@ -1,26 +1,20 @@
1
1
  module Mutant
2
2
  class Matcher
3
- # A chain of matchers
3
+ # Matcher chaining results of other matchers together
4
4
  class Chain < self
5
5
  include Concord.new(:matchers)
6
6
 
7
- # Enumerate subjects
7
+ # Call matcher
8
8
  #
9
- # @return [Enumerator<Subject]
10
- # if no block given
9
+ # @param [Env] env
11
10
  #
12
- # @return [self]
13
- # otherwise
11
+ # @return [Enumerable<Subject>]
14
12
  #
15
13
  # @api private
16
- def each(&block)
17
- return to_enum unless block_given?
18
-
19
- matchers.each do |matcher|
20
- matcher.each(&block)
14
+ def call(env)
15
+ matchers.flat_map do |matcher|
16
+ matcher.call(env)
21
17
  end
22
-
23
- self
24
18
  end
25
19
 
26
20
  end # Chain
@@ -3,7 +3,7 @@ module Mutant
3
3
 
4
4
  # Compiler for complex matchers
5
5
  class Compiler
6
- include Concord.new(:env, :config), AST::Sexp, Procto.call(:result)
6
+ include Concord.new(:config), AST::Sexp, Procto.call(:result)
7
7
 
8
8
  # Generated matcher
9
9
  #
@@ -12,7 +12,7 @@ module Mutant
12
12
  # @api private
13
13
  def result
14
14
  Filter.new(
15
- Chain.build(config.match_expressions.map(&method(:matcher))),
15
+ Chain.new(config.match_expressions.map(&:matcher)),
16
16
  Morpher::Evaluator::Predicate::Boolean::And.new(
17
17
  [
18
18
  ignored_subjects,
@@ -61,17 +61,6 @@ module Mutant
61
61
  Morpher::Evaluator::Predicate::Boolean::And.new(config.subject_filters)
62
62
  end
63
63
 
64
- # Matcher for expression on env
65
- #
66
- # @param [Mutant::Expression] expression
67
- #
68
- # @return [Matcher]
69
- #
70
- # @api private
71
- def matcher(expression)
72
- expression.matcher(env)
73
- end
74
-
75
64
  end # Compiler
76
65
  end # Matcher
77
66
  end # Mutant
@@ -4,30 +4,17 @@ module Mutant
4
4
  class Filter < self
5
5
  include Concord.new(:matcher, :predicate)
6
6
 
7
- # New matcher
8
- #
9
- # @param [Matcher] matcher
10
- #
11
- # @return [Matcher]
12
- #
13
- # @api private
14
- def self.build(matcher, &predicate)
15
- new(matcher, predicate)
16
- end
17
-
18
7
  # Enumerate matches
19
8
  #
20
- # @return [self]
21
- # if block given
9
+ # @param [Env] env
22
10
  #
23
- # @return [Enumerator<Subject>]
24
- # otherwise
11
+ # @return [Enumerable<Subject>]
25
12
  #
26
13
  # @api private
27
- def each(&block)
28
- return to_enum unless block_given?
29
- matcher.select(&predicate.method(:call)).each(&block)
30
- self
14
+ def call(env)
15
+ matcher
16
+ .call(env)
17
+ .select(&predicate.method(:call))
31
18
  end
32
19
 
33
20
  end # Filter
@@ -1,133 +1,153 @@
1
1
  module Mutant
2
2
  class Matcher
3
- # Matcher for subjects that are a specific method
3
+ # Abstract base class for method matchers
4
4
  class Method < self
5
- include Adamantium::Flat, Concord::Public.new(:env, :scope, :target_method)
6
- include AST::NodePredicates
5
+ include AbstractType,
6
+ Adamantium::Flat,
7
+ Concord::Public.new(:scope, :target_method, :evaluator)
7
8
 
8
9
  # Methods within rbx kernel directory are precompiled and their source
9
10
  # cannot be accessed via reading source location. Same for methods created by eval.
10
11
  BLACKLIST = %r{\A(kernel/|\(eval\)\z)}.freeze
11
12
 
12
- # Enumerate matches
13
+ SOURCE_LOCATION_WARNING_FORMAT =
14
+ '%s does not have a valid source location, unable to emit subject'.freeze
15
+
16
+ CLOSURE_WARNING_FORMAT =
17
+ '%s is dynamically defined in a closure, unable to emit subject'.freeze
18
+
19
+ # Matched subjects
13
20
  #
14
- # @return [Enumerable<Subject>]
15
- # if no block given
21
+ # @param [Env] env
16
22
  #
17
- # @return [self]
18
- # otherwise
23
+ # @return [Enumerable<Subject>]
19
24
  #
20
25
  # @api private
21
- def each
22
- return to_enum unless block_given?
26
+ def call(env)
27
+ evaluator.call(scope, target_method, env)
28
+ end
29
+
30
+ # Abstract method match evaluator
31
+ #
32
+ # Present to avoid passing the env argument around in case the
33
+ # logic would be implemnented directly on the Matcher::Method
34
+ # instance
35
+ class Evaluator
36
+ include AbstractType,
37
+ Adamantium,
38
+ Concord.new(:scope, :target_method, :env),
39
+ Procto.call,
40
+ AST::NodePredicates
41
+
42
+ # Matched subjects
43
+ #
44
+ # @return [Enumerable<Subject>]
45
+ #
46
+ # @api private
47
+ def call
48
+ return EMPTY_ARRAY if skip?
23
49
 
24
- if !skip? && subject
25
- yield subject
50
+ [subject].compact
26
51
  end
27
52
 
28
- self
29
- end
53
+ private
30
54
 
31
- private
55
+ # Test if method should be skipped
56
+ #
57
+ # @return [Truthy]
58
+ #
59
+ # @api private
60
+ def skip?
61
+ location = source_location
62
+ if location.nil? || BLACKLIST.match(location.first)
63
+ env.warn(SOURCE_LOCATION_WARNING_FORMAT % target_method)
64
+ elsif matched_node_path.any?(&method(:n_block?))
65
+ env.warn(CLOSURE_WARNING_FORMAT % target_method)
66
+ end
67
+ end
32
68
 
33
- # Test if method should be skipped
34
- #
35
- # @return [Boolean]
36
- #
37
- # @api private
38
- def skip?
39
- location = source_location
40
- if location.nil? || BLACKLIST.match(location.first)
41
- env.warn(format('%s does not have valid source location unable to emit subject', target_method.inspect))
42
- true
43
- elsif matched_node_path.any?(&method(:n_block?))
44
- env.warn(format('%s is defined from a 3rd party lib unable to emit subject', target_method.inspect))
45
- true
46
- else
47
- false
69
+ # Target method name
70
+ #
71
+ # @return [String]
72
+ #
73
+ # @api private
74
+ def method_name
75
+ target_method.name
48
76
  end
49
- end
50
77
 
51
- # Target method name
52
- #
53
- # @return [String]
54
- #
55
- # @api private
56
- def method_name
57
- target_method.name
58
- end
78
+ # Target context
79
+ #
80
+ # @return [Context::Scope]
81
+ #
82
+ # @api private
83
+ def context
84
+ Context::Scope.new(scope, source_path)
85
+ end
59
86
 
60
- # Target context
61
- #
62
- # @return [Context::Scope]
63
- #
64
- # @api private
65
- def context
66
- Context::Scope.new(scope, source_path)
67
- end
87
+ # Root source node
88
+ #
89
+ # @return [Parser::AST::Node]
90
+ #
91
+ # @api private
92
+ def ast
93
+ env.cache.parse(source_path)
94
+ end
68
95
 
69
- # Root source node
70
- #
71
- # @return [Parser::AST::Node]
72
- #
73
- # @api private
74
- def ast
75
- env.cache.parse(source_path)
76
- end
96
+ # Path to source
97
+ #
98
+ # @return [Pathname]
99
+ #
100
+ # @api private
101
+ def source_path
102
+ Pathname.new(source_location.first)
103
+ end
104
+ memoize :source_path
77
105
 
78
- # Path to source
79
- #
80
- # @return [Pathname]
81
- #
82
- # @api private
83
- def source_path
84
- Pathname.new(source_location.first)
85
- end
86
- memoize :source_path
106
+ # Source file line
107
+ #
108
+ # @return [Fixnum]
109
+ #
110
+ # @api private
111
+ def source_line
112
+ source_location.last
113
+ end
87
114
 
88
- # Source file line
89
- #
90
- # @return [Fixnum]
91
- #
92
- # @api private
93
- def source_line
94
- source_location.last
95
- end
115
+ # Full source location
116
+ #
117
+ # @return [Array{String,Fixnum}]
118
+ #
119
+ # @api private
120
+ def source_location
121
+ target_method.source_location
122
+ end
96
123
 
97
- # Full source location
98
- #
99
- # @return [Array{String,Fixnum}]
100
- #
101
- # @api private
102
- def source_location
103
- target_method.source_location
104
- end
124
+ # Matched subject
125
+ #
126
+ # @return [Subject]
127
+ # if there is a matched node
128
+ #
129
+ # @return [nil]
130
+ # otherwise
131
+ #
132
+ # @api private
133
+ def subject
134
+ node = matched_node_path.last || return
135
+ self.class::SUBJECT_CLASS.new(context, node)
136
+ end
137
+ memoize :subject
105
138
 
106
- # Matched subject
107
- #
108
- # @return [Subject]
109
- # if there is a matched node
110
- #
111
- # @return [nil]
112
- # otherwise
113
- #
114
- # @api private
115
- def subject
116
- node = matched_node_path.last
117
- return unless node
118
- self.class::SUBJECT_CLASS.new(context, node)
119
- end
120
- memoize :subject
139
+ # Matched node path
140
+ #
141
+ # @return [Array<Parser::AST::Node>]
142
+ #
143
+ # @api private
144
+ def matched_node_path
145
+ AST.find_last_path(ast, &method(:match?))
146
+ end
147
+ memoize :matched_node_path
148
+ end # Evaluator
121
149
 
122
- # Matched node path
123
- #
124
- # @return [Array<Parser::AST::Node>]
125
- #
126
- # @api private
127
- def matched_node_path
128
- AST.find_last_path(ast, &method(:match?))
129
- end
130
- memoize :matched_node_path
150
+ private_constant(*constants(false))
131
151
 
132
152
  end # Method
133
153
  end # Matcher