mutant 0.3.0.rc1 → 0.3.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.travis.yml +5 -4
  4. data/Gemfile.devtools +4 -4
  5. data/Guardfile +7 -5
  6. data/README.md +8 -8
  7. data/config/flay.yml +2 -2
  8. data/config/reek.yml +1 -0
  9. data/config/rubocop.yml +0 -4
  10. data/lib/mutant/cli/builder.rb +177 -0
  11. data/lib/mutant/cli/classifier/method.rb +10 -14
  12. data/lib/mutant/cli/classifier/namespace.rb +7 -9
  13. data/lib/mutant/cli/classifier.rb +29 -31
  14. data/lib/mutant/cli.rb +34 -96
  15. data/lib/mutant/color.rb +1 -1
  16. data/lib/mutant/config.rb +9 -3
  17. data/lib/mutant/constants.rb +1 -1
  18. data/lib/mutant/context/scope.rb +2 -2
  19. data/lib/mutant/differ.rb +0 -1
  20. data/lib/mutant/killer/rspec.rb +3 -8
  21. data/lib/mutant/killer.rb +1 -22
  22. data/lib/mutant/matcher/chain.rb +2 -2
  23. data/lib/mutant/matcher/filter.rb +32 -0
  24. data/lib/mutant/matcher/method/instance.rb +2 -2
  25. data/lib/mutant/matcher/method.rb +3 -3
  26. data/lib/mutant/matcher/methods.rb +1 -1
  27. data/lib/mutant/matcher.rb +1 -0
  28. data/lib/mutant/mutation/evil.rb +0 -10
  29. data/lib/mutant/mutation/neutral.rb +0 -23
  30. data/lib/mutant/mutation.rb +13 -23
  31. data/lib/mutant/mutator/node/begin.rb +12 -10
  32. data/lib/mutant/mutator/node/block.rb +1 -0
  33. data/lib/mutant/mutator/node/blockarg.rb +15 -0
  34. data/lib/mutant/mutator/node/case.rb +1 -0
  35. data/lib/mutant/mutator/node/connective/binary.rb +2 -2
  36. data/lib/mutant/mutator/node/const.rb +2 -1
  37. data/lib/mutant/mutator/node/dstr.rb +28 -0
  38. data/lib/mutant/mutator/node/dsym.rb +28 -0
  39. data/lib/mutant/mutator/node/generic.rb +3 -4
  40. data/lib/mutant/mutator/node/if.rb +1 -0
  41. data/lib/mutant/mutator/node/literal/boolean.rb +2 -2
  42. data/lib/mutant/mutator/node/literal/range.rb +8 -6
  43. data/lib/mutant/mutator/node/literal.rb +0 -17
  44. data/lib/mutant/mutator/node/masgn.rb +1 -1
  45. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +5 -4
  46. data/lib/mutant/mutator/node/op_asgn.rb +30 -0
  47. data/lib/mutant/mutator/node/restarg.rb +15 -0
  48. data/lib/mutant/mutator/node/return.rb +1 -2
  49. data/lib/mutant/mutator/node/send.rb +6 -5
  50. data/lib/mutant/mutator/node/super.rb +1 -0
  51. data/lib/mutant/mutator/node/while.rb +1 -0
  52. data/lib/mutant/mutator/node.rb +11 -1
  53. data/lib/mutant/node_helpers.rb +3 -3
  54. data/lib/mutant/predicate/attribute.rb +85 -0
  55. data/lib/mutant/predicate/blacklist.rb +27 -0
  56. data/lib/mutant/predicate/matcher.rb +36 -0
  57. data/lib/mutant/predicate/whitelist.rb +28 -0
  58. data/lib/mutant/predicate.rb +91 -0
  59. data/lib/mutant/reporter/cli/printer/config.rb +3 -3
  60. data/lib/mutant/reporter/cli/printer/killer.rb +1 -1
  61. data/lib/mutant/reporter/cli/printer/mutation.rb +12 -14
  62. data/lib/mutant/reporter/cli/printer/subject.rb +1 -3
  63. data/lib/mutant/reporter/cli/printer.rb +31 -7
  64. data/lib/mutant/runner/mutation.rb +9 -1
  65. data/lib/mutant/runner/subject.rb +9 -1
  66. data/lib/mutant/runner.rb +5 -6
  67. data/lib/mutant/strategy/rspec.rb +4 -4
  68. data/lib/mutant/strategy.rb +2 -0
  69. data/lib/mutant/support/method_object.rb +0 -1
  70. data/lib/mutant/version.rb +1 -2
  71. data/lib/mutant/zombifier.rb +6 -3
  72. data/lib/mutant.rb +12 -4
  73. data/mutant.gemspec +7 -7
  74. data/spec/integration/mutant/rspec_spec.rb +12 -5
  75. data/spec/shared/mutator_behavior.rb +2 -1
  76. data/spec/spec_helper.rb +11 -9
  77. data/spec/unit/mutant/cli/builder/rspec_spec.rb +38 -0
  78. data/spec/unit/mutant/cli/classifier/{method/each_spec.rb → method_spec.rb} +2 -14
  79. data/spec/unit/mutant/cli/classifier/namespace/{flat/each_spec.rb → flat_spec.rb} +1 -1
  80. data/spec/unit/mutant/cli/classifier/namespace/{recursive/each_spec.rb → recursive_spec.rb} +1 -1
  81. data/spec/unit/mutant/cli/classifier_spec.rb +59 -0
  82. data/spec/unit/mutant/{cli/class_methods/new_spec.rb → cli_new_spec.rb} +22 -23
  83. data/spec/unit/mutant/{cli/class_methods/run_spec.rb → cli_run_spec.rb} +8 -8
  84. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +6 -6
  85. data/spec/unit/mutant/killer/success_predicate_spec.rb +5 -5
  86. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +1 -1
  87. data/spec/unit/mutant/matcher/filter_spec.rb +19 -0
  88. data/spec/unit/mutant/matcher/namespace/each_spec.rb +5 -5
  89. data/spec/unit/mutant/mutation_spec.rb +42 -0
  90. data/spec/unit/mutant/mutator/each_spec.rb +1 -1
  91. data/spec/unit/mutant/mutator/node/and_asgn/mutation_spec.rb +3 -2
  92. data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +7 -2
  93. data/spec/unit/mutant/mutator/node/block_pass/mutation_spec.rb +1 -0
  94. data/spec/unit/mutant/mutator/node/blockarg/mutation_spec.rb +17 -0
  95. data/spec/unit/mutant/mutator/node/case/mutation_spec.rb +5 -1
  96. data/spec/unit/mutant/mutator/node/cbase/mutation_spec.rb +1 -0
  97. data/spec/unit/mutant/mutator/node/const/mutation_spec.rb +3 -2
  98. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +3 -3
  99. data/spec/unit/mutant/mutator/node/defined_predicate/mutation_spec.rb +6 -2
  100. data/spec/unit/mutant/mutator/node/dstr/mutation_spec.rb +4 -2
  101. data/spec/unit/mutant/mutator/node/dsym/mutation_spec.rb +4 -2
  102. data/spec/unit/mutant/mutator/node/if/mutation_spec.rb +6 -1
  103. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -1
  104. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +1 -1
  105. data/spec/unit/mutant/mutator/node/literal/range_spec.rb +31 -0
  106. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +4 -2
  107. data/spec/unit/mutant/mutator/node/literal/string_spec.rb +1 -1
  108. data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +1 -1
  109. data/spec/unit/mutant/mutator/node/masgn/mutation_spec.rb +6 -2
  110. data/spec/unit/mutant/mutator/node/match_current_line/mutation_spec.rb +1 -0
  111. data/spec/unit/mutant/mutator/node/named_value/access/mutation_spec.rb +5 -1
  112. data/spec/unit/mutant/mutator/node/named_value/constant_assignment/mutation_spec.rb +1 -2
  113. data/spec/unit/mutant/mutator/node/named_value/variable_assignment/mutation_spec.rb +5 -5
  114. data/spec/unit/mutant/mutator/node/op_assgn/mutation_spec.rb +3 -2
  115. data/spec/unit/mutant/mutator/node/or_asgn/mutation_spec.rb +3 -2
  116. data/spec/unit/mutant/mutator/node/rescue/mutation_spec.rb +1 -1
  117. data/spec/unit/mutant/mutator/node/restarg/mutation_spec.rb +3 -1
  118. data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +7 -3
  119. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +37 -2
  120. data/spec/unit/mutant/mutator/node/super/mutation_spec.rb +4 -0
  121. data/spec/unit/mutant/mutator/node/while/mutation_spec.rb +3 -0
  122. data/spec/unit/mutant/predicate_spec.rb +135 -0
  123. data/spec/unit/mutant/runner/config/subjects_spec.rb +11 -11
  124. data/spec/unit/mutant/runner/config/success_predicate_spec.rb +6 -6
  125. data/spec/unit/mutant/runner/mutation/killer_spec.rb +11 -11
  126. data/spec/unit/mutant/runner/subject/success_predicate_spec.rb +9 -9
  127. data/spec/unit/mutant/strategy_spec.rb +21 -0
  128. data/spec/unit/mutant/subject_spec.rb +39 -0
  129. data/test_app/spec/shared/method_filter_parse_behavior.rb +1 -1
  130. metadata +54 -36
  131. data/lib/mutant/cli/classifier/scope.rb +0 -37
  132. data/lib/mutant/mutation/filter/code.rb +0 -49
  133. data/lib/mutant/mutation/filter/regexp.rb +0 -29
  134. data/lib/mutant/mutation/filter/whitelist.rb +0 -52
  135. data/lib/mutant/mutation/filter.rb +0 -78
  136. data/lib/mutant/strategy/static.rb +0 -20
  137. data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +0 -48
  138. data/spec/unit/mutant/cli/classifier/scope/each_spec.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd04beb38330a250653e83799b208a0eb92a8656
4
- data.tar.gz: 4b6512fbb3f2e31fcbd90411a1ea1c0c9f10c261
3
+ metadata.gz: 5aa8cc60b0b68e67f1c9a253bfce5c7bd7aa7920
4
+ data.tar.gz: 60323d0b1d1d2230a62a3f9b7a28d50eb8014d7d
5
5
  SHA512:
6
- metadata.gz: 8b11f4a25768e953373c1ce9a854051c9ffa28758a139d369568089a520eb90893585242dcc4586c276e112b886157083ac0165fcb0c83e94fcf768a97c613c8
7
- data.tar.gz: 8d358b32f4c915ab85779f43106e607576250fa8f8badf1a94ff23cfc84845e919411bba73a6380b869ce7f8525eeca4174e38a313d864c81c3e636e31eb0ea7
6
+ metadata.gz: 21ce19cfa4bfddf01eb471a3c8685d3e3468c1c5dd1c9504ecc00f6e43f5a096e64573d3ff6ebb13ed0720b224ba51b3c9588a03d127255a43494e4517bdf755
7
+ data.tar.gz: 91157f59a4f81ff1957168ee0300fa4b33869fcd975ed9b3461a5e50fc9e7c7a629d2e1aa6ee83d47c48b21c824b3e8a20a5f7fb2feefd67fe8e1bf6d7e8d605
data/.rspec CHANGED
@@ -1,5 +1,4 @@
1
1
  --color
2
2
  --format progress
3
- --profile
4
3
  --warnings
5
4
  --order random
data/.travis.yml CHANGED
@@ -2,12 +2,13 @@ language: ruby
2
2
  before_install: gem install bundler
3
3
  bundler_args: --without yard guard benchmarks
4
4
  script: "bundle exec rake ci"
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - ruby-head
9
+ - rbx-19mode
5
10
  matrix:
6
11
  include:
7
- - rvm: 1.9.3
8
- - rvm: 2.0.0
9
- - rvm: ruby-head
10
- - rvm: rbx-19mode
11
12
  - rvm: jruby-19mode
12
13
  env: JRUBY_OPTS="$JRUBY_OPTS --debug"
13
14
  - rvm: jruby-head
data/Gemfile.devtools CHANGED
@@ -18,7 +18,7 @@ group :guard do
18
18
  gem 'guard-mutant', '~> 0.0.1'
19
19
 
20
20
  # file system change event handling
21
- gem 'listen', '~> 1.2.2'
21
+ gem 'listen', '~> 1.3.0'
22
22
  gem 'rb-fchange', '~> 0.0.6', require: false
23
23
  gem 'rb-fsevent', '~> 0.9.3', require: false
24
24
  gem 'rb-inotify', '~> 0.9.0', require: false
@@ -33,10 +33,10 @@ group :metrics do
33
33
  gem 'coveralls', '~> 0.6.7'
34
34
  gem 'flay', '~> 2.4.0'
35
35
  gem 'flog', '~> 4.1.1'
36
- gem 'reek', '~> 1.3.1', git: 'https://github.com/troessner/reek.git'
37
- gem 'rubocop', '~> 0.10.0', git: 'https://github.com/bbatsov/rubocop.git'
36
+ gem 'reek', '~> 1.3.2'
37
+ gem 'rubocop', '~> 0.11.0'
38
38
  gem 'simplecov', '~> 0.7.1'
39
- gem 'yardstick', '~> 0.9.6', git: 'https://github.com/dkubb/yardstick.git'
39
+ gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
40
40
 
41
41
  platforms :ruby_19, :ruby_20 do
42
42
  gem 'yard-spellcheck', '~> 0.1.5'
data/Guardfile CHANGED
@@ -25,8 +25,10 @@ guard :rspec, cli: File.read('.rspec').split.push('--fail-fast').join(' '), keep
25
25
  watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
26
26
  end
27
27
 
28
- guard :rubocop, cli: %w[--config config/rubocop.yml] do
29
- watch(%r{.+\.(?:rb|rake)\z})
30
- watch(%r{\Aconfig/rubocop\.yml\z}) { |m| File.dirname(m[0]) }
31
- watch(%r{(?:.+/)?\.rubocop\.yml\z}) { |m| File.dirname(m[0]) }
32
- end
28
+ # Deactivated for now. Somehow it disables the rspec guard.
29
+ #
30
+ # guard :rubocop, cli: %w[--config config/rubocop.yml] do
31
+ # watch(%r{.+\.(?:rb|rake)\z})
32
+ # watch(%r{\Aconfig/rubocop\.yml\z}) { |m| File.dirname(m[0]) }
33
+ # watch(%r{(?:.+/)?\.rubocop\.yml\z}) { |m| File.dirname(m[0]) }
34
+ # end
data/README.md CHANGED
@@ -62,14 +62,14 @@ Currently mutant covers the majority of ruby's complex nodes that often occur in
62
62
  Some stats from the [axiom](https://github.com/dkubb/axiom) library:
63
63
 
64
64
  ```
65
- Subjects: 417 # Amount of subjects being mutated (currently only methods)
66
- Mutations: 5442 # Amount of mutations mutant generated (~13 mutations per method)
67
- Kills: 5385 # Amount of successfully killed mutations
68
- Runtime: 1898.11s # Total runtime
69
- Killtime: 1884.17s # Time spend killing mutations
70
- Overhead: 0.73%
71
- Coverage: 98.95% # Coverage score
72
- Alive: 57 # Amount of alive mutations.
65
+ Subjects: 424 # Amount of subjects being mutated (currently only methods)
66
+ Mutations: 6760 # Amount of mutations mutant generated (~13 mutations per method)
67
+ Kills: 6664 # Amount of successfully killed mutations
68
+ Runtime: 5123.13s # Total runtime
69
+ Killtime: 5092.63s # Time spend killing mutations
70
+ Overhead: 0.60%
71
+ Coverage: 98.58% # Coverage score
72
+ Alive: 96 # Amount of alive mutations.
73
73
  ```
74
74
 
75
75
 
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
- threshold: 16
3
- total_score: 738
2
+ threshold: 18
3
+ total_score: 748
data/config/reek.yml CHANGED
@@ -73,6 +73,7 @@ TooManyMethods:
73
73
  enabled: true
74
74
  exclude:
75
75
  - Mutant::CLI
76
+ - Mutant::Mutator::Node
76
77
  - Mutant::Reporter::CLI
77
78
  max_methods: 10
78
79
  TooManyStatements:
data/config/rubocop.yml CHANGED
@@ -80,10 +80,6 @@ ConstantName:
80
80
  TrivialAccessors:
81
81
  Enabled: false
82
82
 
83
- # I like 1.8 syntax:
84
- HashSyntax:
85
- Enabled: false
86
-
87
83
  # And also have a differend opinion here
88
84
  AndOr:
89
85
  Enabled: false
@@ -0,0 +1,177 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class CLI
5
+ # Abstract base class for strategy builders
6
+ class Builder
7
+ include AbstractType
8
+
9
+ # Return cache
10
+ #
11
+ # @return [Cache]
12
+ #
13
+ # @api private
14
+ #
15
+ attr_reader :cache
16
+ private :cache
17
+
18
+ # Return parser
19
+ #
20
+ # @return [OptionParser]
21
+ #
22
+ # @api private
23
+ #
24
+ attr_reader :parser
25
+ private :parser
26
+
27
+ # Initialize builder
28
+ #
29
+ # @param [OptionParser] parser
30
+ #
31
+ # @api privateo
32
+ #
33
+ def initialize(cache, parser)
34
+ @cache, @parser = cache, parser
35
+ add_options
36
+ end
37
+
38
+ # Add cli options
39
+ #
40
+ # @param [OptionParser]
41
+ #
42
+ # @return [self]
43
+ #
44
+ # @api private
45
+ #
46
+ abstract_method :add_options
47
+
48
+ # Return build output
49
+ #
50
+ # @return [Object]
51
+ #
52
+ # @api private
53
+ #
54
+ abstract_method :output
55
+
56
+ # Rspec strategy builder
57
+ class Rspec < self
58
+
59
+ # Initialize object
60
+ #
61
+ # @return [undefined]
62
+ #
63
+ # @api private
64
+ #
65
+ def initialize(*)
66
+ @level = 0
67
+ @rspec = false
68
+ super
69
+ end
70
+
71
+ # Return strategy
72
+ #
73
+ # @return [Strategy::Rspec]
74
+ #
75
+ # @api private
76
+ #
77
+ def output
78
+ unless @rspec
79
+ raise Error, 'No strategy given'
80
+ end
81
+
82
+ Strategy::Rspec.new(@level)
83
+ end
84
+
85
+ private
86
+
87
+ # Set rspec level
88
+ #
89
+ # @return [self]
90
+ #
91
+ # @api private
92
+ #
93
+ def set_level(level)
94
+ @level = level
95
+ self
96
+ end
97
+
98
+ # Add cli options
99
+ #
100
+ # @param [OptionParser] parser
101
+ #
102
+ # @return [undefined]
103
+ #
104
+ # @api private
105
+ #
106
+ def add_options
107
+ parser.on('--rspec', 'kills mutations with rspec') do
108
+ @rspec = true
109
+ end
110
+
111
+ parser.on('--rspec-level LEVEL', 'set rspec expansion level') do |level|
112
+ @level = level.to_i
113
+ end
114
+ end
115
+
116
+ end # Rspec
117
+
118
+ # Abstract predicate builder
119
+ class Predicate < self
120
+
121
+ class Subject < self
122
+
123
+ # Initialize object
124
+ #
125
+ # @api private
126
+ #
127
+ # @return [undefined]
128
+ #
129
+ def initialize(*)
130
+ super
131
+ @predicates = []
132
+ end
133
+
134
+ # Return predicate
135
+ #
136
+ # @api private
137
+ #
138
+ def output
139
+ if @predicates.empty?
140
+ Mutant::Predicate::CONTRADICTION
141
+ else
142
+ Mutant::Predicate::Whitelist.new(@predicates)
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ # Add cli options
149
+ #
150
+ # @return [undefined]
151
+ #
152
+ # @api private
153
+ #
154
+ def add_options
155
+ parser.on('--ignore-subject MATCHER', 'ignores subjects that matches MATCHER') do |pattern|
156
+ add_pattern(pattern)
157
+ end
158
+ end
159
+
160
+ # Add matcher to predicates
161
+ #
162
+ # @param [String] pattern
163
+ #
164
+ # @api private
165
+ #
166
+ def add_pattern(pattern)
167
+ matcher = Classifier.run(@cache, pattern)
168
+ @predicates << Mutant::Predicate::Matcher.new(matcher)
169
+ end
170
+
171
+ end
172
+
173
+ end # Predicate
174
+
175
+ end # Builder
176
+ end # CLI
177
+ end # Mutant
@@ -6,7 +6,6 @@ module Mutant
6
6
 
7
7
  # Explicit method classifier
8
8
  class Method < self
9
- register
10
9
 
11
10
  TABLE = {
12
11
  '.' => Matcher::Methods::Singleton,
@@ -15,18 +14,13 @@ module Mutant
15
14
 
16
15
  REGEXP = /
17
16
  \A
18
- (#{SCOPE_PATTERN})
19
- ([.#])
20
- (#{METHOD_NAME_PATTERN})
17
+ (?<scope_name>#{SCOPE_PATTERN})
18
+ (?<scope_symbol>[.#])
19
+ (?<method_name>#{METHOD_NAME_PATTERN})
21
20
  \z
22
21
  /x.freeze
23
22
 
24
- # Positions of captured regexp groups
25
- SCOPE_NAME_POSITION = 1
26
- SCOPE_SYMBOL_POSITION = 2
27
- METHOD_NAME_POSITION = 3
28
-
29
- private
23
+ register(REGEXP)
30
24
 
31
25
  # Return method matcher
32
26
  #
@@ -39,6 +33,8 @@ module Mutant
39
33
  end
40
34
  memoize :matcher
41
35
 
36
+ private
37
+
42
38
  # Return method
43
39
  #
44
40
  # @return [Method, UnboundMethod]
@@ -50,7 +46,7 @@ module Mutant
50
46
  method.name == method_name
51
47
  end or raise NameError, "Cannot find method #{identifier}"
52
48
  end
53
- memoize :method, :freezer => :noop
49
+ memoize :method, freezer: :noop
54
50
 
55
51
  # Return scope name
56
52
  #
@@ -59,7 +55,7 @@ module Mutant
59
55
  # @api private
60
56
  #
61
57
  def scope_name
62
- match[SCOPE_NAME_POSITION]
58
+ match[__method__]
63
59
  end
64
60
 
65
61
  # Return scope
@@ -79,7 +75,7 @@ module Mutant
79
75
  # @api private
80
76
  #
81
77
  def method_name
82
- match[METHOD_NAME_POSITION].to_sym
78
+ match[__method__].to_sym
83
79
  end
84
80
 
85
81
  # Return scope symbol
@@ -89,7 +85,7 @@ module Mutant
89
85
  # @api private
90
86
  #
91
87
  def scope_symbol
92
- match[SCOPE_SYMBOL_POSITION]
88
+ match[__method__]
93
89
  end
94
90
 
95
91
  # Return matcher class
@@ -7,8 +7,6 @@ module Mutant
7
7
  # Namespace classifier
8
8
  class Namespace < self
9
9
 
10
- private
11
-
12
10
  # Return matcher
13
11
  #
14
12
  # @return [Matcher]
@@ -19,6 +17,8 @@ module Mutant
19
17
  self.class::MATCHER.new(cache, namespace)
20
18
  end
21
19
 
20
+ private
21
+
22
22
  # Return namespace
23
23
  #
24
24
  # @return [Class, Module]
@@ -26,23 +26,21 @@ module Mutant
26
26
  # @api private
27
27
  #
28
28
  def namespace
29
- Classifier.constant_lookup(match[1].to_s)
29
+ Classifier.constant_lookup(match[__method__].to_s)
30
30
  end
31
31
 
32
32
  # Recursive namespace classifier
33
33
  class Recursive < self
34
- REGEXP = /\A(#{SCOPE_PATTERN})\*\z/.freeze
34
+ REGEXP = /\A(?<namespace>#{SCOPE_PATTERN})\*\z/.freeze
35
35
  MATCHER = Matcher::Namespace
36
-
37
- register
36
+ register(REGEXP)
38
37
  end # Recursive
39
38
 
40
39
  # Recursive namespace classifier
41
40
  class Flat < self
42
- REGEXP = /\A(#{SCOPE_PATTERN})\z/.freeze
41
+ REGEXP = /\A(?<namespace>#{SCOPE_PATTERN})\z/.freeze
43
42
  MATCHER = Matcher::Scope
44
-
45
- register
43
+ register(REGEXP)
46
44
  end # Flat
47
45
 
48
46
  end # Namespace
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  class CLI
5
5
  # A classifier for input strings
6
- class Classifier < Matcher
6
+ class Classifier
7
7
  include AbstractType, Adamantium::Flat, Concord.new(:cache, :match)
8
8
 
9
9
  include Equalizer.new(:identifier)
@@ -22,7 +22,7 @@ module Mutant
22
22
  (?:#{SCOPE_OPERATOR}#{SCOPE_NAME_PATTERN})*
23
23
  /x.freeze
24
24
 
25
- REGISTRY = []
25
+ REGISTRY = {}
26
26
 
27
27
  # Register classifier
28
28
  #
@@ -30,8 +30,8 @@ module Mutant
30
30
  #
31
31
  # @api private
32
32
  #
33
- def self.register
34
- REGISTRY << self
33
+ def self.register(regexp)
34
+ REGISTRY[regexp] = self
35
35
  end
36
36
  private_class_method :register
37
37
 
@@ -54,47 +54,45 @@ module Mutant
54
54
 
55
55
  # Return matchers for input
56
56
  #
57
- # @return [Classifier]
57
+ # @param [Cache] cache
58
+ # @param [String] pattern
59
+ #
60
+ # @return [Matcher]
58
61
  # if a classifier handles the input
59
62
  #
60
- # @return [nil]
63
+ # @raise [RuntimeError]
61
64
  # otherwise
62
65
  #
63
66
  # @api private
64
67
  #
65
- def self.build(*arguments)
66
- classifiers = REGISTRY.map do |descendant|
67
- descendant.run(*arguments)
68
- end.compact
69
-
70
- raise if classifiers.length > 1
71
-
72
- classifiers.first
68
+ def self.run(cache, pattern)
69
+ matches = find(pattern)
70
+ case matches.length
71
+ when 0
72
+ raise Error, "No matcher handles: #{pattern.inspect}"
73
+ when 1
74
+ klass, match = matches.first
75
+ klass.new(cache, match).matcher
76
+ else
77
+ raise Error, "More than one matcher found for: #{pattern.inspect}"
78
+ end
73
79
  end
74
80
 
75
- # Run classifier
76
- #
77
- # @param [Cache] cache
81
+ # Find classifiers
78
82
  #
79
83
  # @param [String] input
80
84
  #
81
- # @return [Classifier]
82
- # if input is handled by classifier
83
- #
84
- # @return [nil]
85
- # otherwise
86
- #
87
85
  # @api private
88
86
  #
89
- def self.run(cache, input)
90
- match = self::REGEXP.match(input)
91
- return unless match
92
-
93
- new(cache, match)
87
+ def self.find(input)
88
+ REGISTRY.each_with_object([]) do |(regexp, klass), matches|
89
+ match = regexp.match(input)
90
+ if match
91
+ matches << [klass, match]
92
+ end
93
+ end
94
94
  end
95
-
96
- # No protected_class_method in ruby :(
97
- class << self; protected :run; end
95
+ private_class_method :find
98
96
 
99
97
  # Enumerate subjects
100
98
  #