mutant 0.2.20 → 0.3.0.beta2

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.
Files changed (201) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +10 -11
  3. data/Changelog.md +93 -38
  4. data/Gemfile +3 -1
  5. data/Gemfile.devtools +16 -20
  6. data/Guardfile +1 -1
  7. data/README.md +36 -16
  8. data/Rakefile +21 -2
  9. data/TODO +11 -7
  10. data/bin/mutant +4 -0
  11. data/bin/zombie +4 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +2 -2
  14. data/config/flog.yml +1 -1
  15. data/config/{site.reek → reek.yml} +94 -70
  16. data/lib/mutant/cli/classifier/method.rb +100 -0
  17. data/lib/mutant/cli/classifier/namespace.rb +47 -0
  18. data/lib/mutant/cli/classifier/scope.rb +35 -0
  19. data/lib/mutant/cli/classifier.rb +141 -0
  20. data/lib/mutant/cli.rb +115 -162
  21. data/lib/mutant/color.rb +2 -2
  22. data/lib/mutant/config.rb +27 -0
  23. data/lib/mutant/constants.rb +32 -17
  24. data/lib/mutant/context/scope.rb +33 -51
  25. data/lib/mutant/context.rb +8 -19
  26. data/lib/mutant/differ.rb +5 -5
  27. data/lib/mutant/helper.rb +2 -17
  28. data/lib/mutant/killer/forked.rb +44 -0
  29. data/lib/mutant/killer/forking.rb +3 -57
  30. data/lib/mutant/killer/rspec.rb +16 -20
  31. data/lib/mutant/killer/static.rb +6 -7
  32. data/lib/mutant/killer.rb +48 -74
  33. data/lib/mutant/loader.rb +6 -6
  34. data/lib/mutant/matcher/chain.rb +4 -25
  35. data/lib/mutant/matcher/method/instance.rb +14 -24
  36. data/lib/mutant/matcher/method/singleton.rb +35 -46
  37. data/lib/mutant/matcher/method.rb +95 -83
  38. data/lib/mutant/matcher/{scope_methods.rb → methods.rb} +53 -76
  39. data/lib/mutant/matcher/namespace.rb +71 -0
  40. data/lib/mutant/matcher/scope.rb +34 -0
  41. data/lib/mutant/matcher.rb +24 -34
  42. data/lib/mutant/mutation/evil.rb +35 -0
  43. data/lib/mutant/mutation/filter/code.rb +7 -28
  44. data/lib/mutant/mutation/filter/regexp.rb +6 -18
  45. data/lib/mutant/mutation/filter/whitelist.rb +5 -4
  46. data/lib/mutant/mutation/filter.rb +10 -9
  47. data/lib/mutant/mutation/neutral.rb +35 -0
  48. data/lib/mutant/mutation.rb +21 -61
  49. data/lib/mutant/mutator/node/argument.rb +88 -0
  50. data/lib/mutant/mutator/node/arguments.rb +52 -0
  51. data/lib/mutant/mutator/node/assignment.rb +34 -38
  52. data/lib/mutant/mutator/node/begin.rb +33 -0
  53. data/lib/mutant/mutator/node/block.rb +14 -14
  54. data/lib/mutant/mutator/node/case.rb +59 -0
  55. data/lib/mutant/mutator/node/define.rb +26 -22
  56. data/lib/mutant/mutator/node/if.rb +31 -71
  57. data/lib/mutant/mutator/node/literal/array.rb +25 -9
  58. data/lib/mutant/mutator/node/literal/boolean.rb +13 -30
  59. data/lib/mutant/mutator/node/literal/dynamic.rb +6 -5
  60. data/lib/mutant/mutator/node/literal/fixnum.rb +18 -7
  61. data/lib/mutant/mutator/node/literal/float.rb +15 -8
  62. data/lib/mutant/mutator/node/literal/hash.rb +33 -52
  63. data/lib/mutant/mutator/node/literal/nil.rb +8 -7
  64. data/lib/mutant/mutator/node/literal/range.rb +25 -50
  65. data/lib/mutant/mutator/node/literal/regex.rb +15 -23
  66. data/lib/mutant/mutator/node/literal/string.rb +7 -6
  67. data/lib/mutant/mutator/node/literal/symbol.rb +7 -6
  68. data/lib/mutant/mutator/node/literal.rb +4 -46
  69. data/lib/mutant/mutator/node/mlhs.rb +27 -0
  70. data/lib/mutant/mutator/node/noop.rb +18 -43
  71. data/lib/mutant/mutator/node/return.rb +8 -8
  72. data/lib/mutant/mutator/node/send/binary.rb +31 -0
  73. data/lib/mutant/mutator/node/send.rb +106 -72
  74. data/lib/mutant/mutator/node/super.rb +15 -20
  75. data/lib/mutant/mutator/node/when.rb +32 -7
  76. data/lib/mutant/mutator/node/while.rb +9 -7
  77. data/lib/mutant/mutator/node.rb +116 -66
  78. data/lib/mutant/mutator/registry.rb +14 -11
  79. data/lib/mutant/mutator/util/array.rb +9 -9
  80. data/lib/mutant/mutator/util/symbol.rb +6 -20
  81. data/lib/mutant/mutator/util.rb +6 -3
  82. data/lib/mutant/mutator.rb +12 -28
  83. data/lib/mutant/node_helpers.rb +28 -0
  84. data/lib/mutant/random.rb +3 -2
  85. data/lib/mutant/reporter/cli/printer/config.rb +174 -0
  86. data/lib/mutant/reporter/cli/printer/killer.rb +42 -0
  87. data/lib/mutant/reporter/cli/printer/mutation.rb +55 -0
  88. data/lib/mutant/reporter/cli/printer/subject.rb +147 -0
  89. data/lib/mutant/reporter/cli/printer.rb +165 -0
  90. data/lib/mutant/reporter/cli.rb +9 -277
  91. data/lib/mutant/reporter/null.rb +6 -30
  92. data/lib/mutant/reporter.rb +6 -73
  93. data/lib/mutant/runner/config.rb +82 -0
  94. data/lib/mutant/runner/mutation.rb +58 -0
  95. data/lib/mutant/runner/subject.rb +81 -0
  96. data/lib/mutant/runner.rb +42 -92
  97. data/lib/mutant/singleton_methods.rb +2 -2
  98. data/lib/mutant/strategy/method_expansion.rb +51 -0
  99. data/lib/mutant/strategy/rspec/dm2/lookup/method.rb +142 -0
  100. data/lib/mutant/strategy/rspec/dm2/lookup.rb +61 -0
  101. data/lib/mutant/strategy/rspec/dm2.rb +22 -0
  102. data/lib/mutant/strategy/rspec.rb +20 -22
  103. data/lib/mutant/strategy/static.rb +18 -0
  104. data/lib/mutant/strategy.rb +15 -50
  105. data/lib/mutant/subject/method.rb +100 -0
  106. data/lib/mutant/subject.rb +18 -49
  107. data/lib/mutant/support/method_object.rb +4 -2
  108. data/lib/mutant.rb +40 -35
  109. data/mutant.gemspec +9 -8
  110. data/spec/integration/mutant/rspec_killer_spec.rb +3 -3
  111. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +9 -0
  112. data/spec/integration/mutant/zombie_spec.rb +1 -1
  113. data/spec/shared/method_matcher_behavior.rb +35 -0
  114. data/spec/shared/mutator_behavior.rb +63 -32
  115. data/spec/spec_helper.rb +13 -3
  116. data/spec/support/ice_nine_config.rb +8 -0
  117. data/spec/support/rspec.rb +1 -1
  118. data/spec/support/zombie.rb +1 -1
  119. data/spec/unit/mutant/cli/class_methods/new_spec.rb +42 -28
  120. data/spec/unit/mutant/cli/class_methods/run_spec.rb +15 -13
  121. data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +44 -0
  122. data/spec/unit/mutant/context/scope/root_spec.rb +4 -4
  123. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +6 -5
  124. data/spec/unit/mutant/killer/success_predicate_spec.rb +28 -0
  125. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +1 -1
  126. data/spec/unit/mutant/matcher/chain/each_spec.rb +1 -1
  127. data/spec/unit/mutant/matcher/chain/matchers_spec.rb +1 -1
  128. data/spec/unit/mutant/matcher/method/instance/each_spec.rb +112 -0
  129. data/spec/unit/mutant/matcher/method/singleton/each_spec.rb +93 -0
  130. data/spec/unit/mutant/matcher/methods/instance/each_spec.rb +59 -0
  131. data/spec/unit/mutant/matcher/methods/singleton/each_spec.rb +53 -0
  132. data/spec/unit/mutant/matcher/namespace/each_spec.rb +37 -0
  133. data/spec/unit/mutant/mutator/node/begin/mutation_spec.rb +33 -0
  134. data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +42 -14
  135. data/spec/unit/mutant/mutator/node/case/mutation_spec.rb +319 -0
  136. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +31 -27
  137. data/spec/unit/mutant/mutator/node/if/mutation_spec.rb +75 -0
  138. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -1
  139. data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +2 -2
  140. data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +1 -3
  141. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +1 -9
  142. data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +6 -2
  143. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +111 -108
  144. data/spec/unit/mutant/mutator/node/super/mutation_spec.rb +0 -33
  145. data/spec/unit/mutant/mutator/node/while/mutation_spec.rb +2 -2
  146. data/spec/unit/mutant/runner/config/subjects_spec.rb +38 -0
  147. data/spec/unit/mutant/runner/config/success_predicate_spec.rb +53 -0
  148. data/spec/unit/mutant/runner/failed_predicte_spec.rb +33 -0
  149. data/spec/unit/mutant/runner/mutation/killer_spec.rb +39 -0
  150. data/spec/unit/mutant/runner/subject/success_predicate_spec.rb +49 -0
  151. data/spec/unit/mutant/strategy/method_expansion/class_methods/run_spec.rb +49 -0
  152. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +52 -0
  153. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +42 -0
  154. data/spec/unit/mutant/subject/context_spec.rb +6 -3
  155. data/spec/unit/mutant/subject/each_spec.rb +11 -8
  156. data/spec/unit/mutant/subject/node_spec.rb +6 -2
  157. data/test_app/spec/shared/method_filter_parse_behavior.rb +0 -2
  158. data/test_app/spec/shared/method_match_behavior.rb +1 -1
  159. data/test_app/spec/spec_helper.rb +4 -2
  160. metadata +101 -109
  161. data/config/roodi.yml +0 -26
  162. data/lib/mutant/matcher/method/classifier.rb +0 -141
  163. data/lib/mutant/matcher/object_space.rb +0 -114
  164. data/lib/mutant/mutator/node/actual_arguments.rb +0 -25
  165. data/lib/mutant/mutator/node/default_arguments.rb +0 -25
  166. data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +0 -33
  167. data/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb +0 -35
  168. data/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb +0 -37
  169. data/lib/mutant/mutator/node/formal_arguments_19.rb +0 -41
  170. data/lib/mutant/mutator/node/iter_19.rb +0 -27
  171. data/lib/mutant/mutator/node/literal/empty_array.rb +0 -26
  172. data/lib/mutant/mutator/node/pattern_arguments.rb +0 -41
  173. data/lib/mutant/mutator/node/pattern_variable.rb +0 -23
  174. data/lib/mutant/mutator/node/receiver_case.rb +0 -122
  175. data/lib/mutant/mutator/node/send/binary_operator_method.rb +0 -61
  176. data/lib/mutant/mutator/node/send/with_arguments.rb +0 -81
  177. data/lib/mutant/reporter/stats.rb +0 -120
  178. data/lib/mutant/strategy/rspec/example_lookup.rb +0 -163
  179. data/spec/integration/mutant/method_matching_spec.rb +0 -269
  180. data/spec/shared/method_match_behavior.rb +0 -39
  181. data/spec/unit/mutant/killer/fail_ques_spec.rb +0 -39
  182. data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +0 -49
  183. data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +0 -12
  184. data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +0 -21
  185. data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +0 -52
  186. data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +0 -24
  187. data/spec/unit/mutant/matcher/object_space/each_spec.rb +0 -31
  188. data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +0 -60
  189. data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +0 -27
  190. data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +0 -236
  191. data/spec/unit/mutant/subject/class_methods/new_spec.rb +0 -13
  192. data/tasks/metrics/ci.rake +0 -7
  193. data/tasks/metrics/flay.rake +0 -41
  194. data/tasks/metrics/flog.rake +0 -43
  195. data/tasks/metrics/heckle.rake +0 -216
  196. data/tasks/metrics/metric_fu.rake +0 -31
  197. data/tasks/metrics/reek.rake +0 -15
  198. data/tasks/metrics/roodi.rake +0 -15
  199. data/tasks/metrics/yardstick.rake +0 -23
  200. data/tasks/spec.rake +0 -45
  201. data/tasks/yard.rake +0 -9
@@ -1,104 +1,128 @@
1
1
  ---
2
- UncommunicativeParameterName:
3
- accept: []
2
+ Attribute:
3
+ enabled: false
4
4
  exclude: []
5
+ BooleanParameter:
5
6
  enabled: true
6
- reject:
7
- - !ruby/regexp /^.$/
8
- - !ruby/regexp /[0-9]$/
9
- - !ruby/regexp /[A-Z]/
10
- LargeClass:
11
- max_methods: 10
12
- exclude:
13
- - "Mutant::Reporter::CLI" # 16 methods TODO Reduce!
14
- - "Mutant::Reporter::Stats" # 6 ivars TODO Reduce!
15
- - "Mutant::CLI" # 19 methods and 7 ivars, TODO Reduce!
16
- enabled: true
17
- max_instance_variables: 3
18
- UncommunicativeMethodName:
19
- accept:
20
- - sha1
21
7
  exclude: []
8
+ ClassVariable:
22
9
  enabled: true
23
- reject:
24
- - !ruby/regexp /^[a-z]$/
25
- - !ruby/regexp /[0-9]$/
26
- - !ruby/regexp /[A-Z]/
27
- LongParameterList:
28
- max_params: 2
29
- exclude:
30
- - "Mutant::Mutator::Node#new_send_with_arguments" # 3 params
31
- - "Mutant::Context::Constant#initialize" # 3 params
32
- - "Mutant::Subject#initialize" # 3 params
33
- enabled: true
34
- overrides: {}
35
- FeatureEnvy:
36
10
  exclude: []
11
+ ControlParameter:
37
12
  enabled: true
38
- ClassVariable:
39
13
  exclude: []
14
+ DataClump:
40
15
  enabled: true
41
- BooleanParameter:
42
16
  exclude: []
17
+ max_copies: 2
18
+ min_clump_size: 2
19
+ DuplicateMethodCall:
20
+ enabled: false
21
+ exclude: []
22
+ max_calls: 1
23
+ allow_calls: []
24
+ FeatureEnvy:
43
25
  enabled: true
26
+ exclude:
27
+ - Mutant::Matcher::Method::Singleton#receiver?
28
+ - Mutant::Matcher::Method::Instance#match?
29
+ - Mutant::CLI#parse
30
+ - Mutant::Mutation::Evil#success?
31
+ - Mutant::Mutation::Neutral#success?
32
+ - Mutant::Reporter::CLI#subject_results
44
33
  IrresponsibleModule:
34
+ enabled: true
45
35
  exclude: []
36
+ LongParameterList:
46
37
  enabled: true
47
- UncommunicativeModuleName:
48
- accept:
49
- - Mutant::Mutator::Node::Iter19
50
- - Mutant::Strategy::Rspec::DM2
51
38
  exclude: []
39
+ max_params: 2
40
+ LongYieldList:
52
41
  enabled: true
53
- reject:
54
- - !ruby/regexp /^.$/
55
- - !ruby/regexp /[0-9]$/
42
+ exclude: []
43
+ max_params: 2
56
44
  NestedIterators:
57
- ignore_iterators: []
58
- exclude:
59
- - Mutant#self.define_singleton_subclass
60
- - Mutant::Mutator::Util::Array::Element#dispatch
61
- - Mutant::Mutator::Node::ReceiverCase#emit_when_branch_mutation
62
45
  enabled: true
46
+ exclude:
47
+ - Mutant#self.singleton_subclass_instance
48
+ - Mutant::Mutator::Util::Array::Element#dispatch
49
+ - Mutant::CLI#parse
63
50
  max_allowed_nesting: 1
64
- LongMethod:
65
- max_statements: 6
66
- exclude: []
51
+ ignore_iterators: []
52
+ NilCheck:
67
53
  enabled: true
68
- Duplication:
69
- allow_calls: []
70
- exclude:
71
- - Mutant::Mutator::Node::Literal::Hash#emit_element_presence
54
+ exclude:
55
+ - Mutant::Matcher::Method#skip?
56
+ RepeatedConditional:
72
57
  enabled: true
73
- max_calls: 1
74
- UtilityFunction:
75
- max_helper_calls: 0
76
- exclude: []
58
+ exclude:
59
+ - Mutant::Mutator
60
+ - Mutant::Reporter::CLI
61
+ - Mutant::Mutator::Node::Send
62
+ - Mutant::Mutator::Node::If
63
+ max_ifs: 1
64
+ TooManyInstanceVariables:
77
65
  enabled: true
78
- Attribute:
79
- exclude: []
80
- enabled: false
81
- UncommunicativeVariableName:
66
+ exclude:
67
+ - Mutant::CLI # 4 vars
68
+ - Mutant::Killer # 4 vars
69
+ max_instance_variables: 3
70
+ TooManyMethods:
71
+ enabled: true
72
+ exclude:
73
+ - Mutant::CLI
74
+ - Mutant::Reporter::CLI
75
+ max_methods: 10
76
+ TooManyStatements:
77
+ enabled: true
78
+ exclude:
79
+ - Mutant::CLI#parse
80
+ - Mutant::Reporter::CLI#colorized_diff
81
+ - Mutant::Reporter::CLI::Printer::Config::Runner#run
82
+ - Mutant#self.singleton_subclass_instance
83
+ max_statements: 6
84
+ UncommunicativeMethodName:
85
+ enabled: true
86
+ exclude:
87
+ - Mutant::Mutation#sha1
88
+ - Mutant::NodeHelpers#s
89
+ reject:
90
+ - !ruby/regexp /^[a-z]$/
91
+ - !ruby/regexp /[0-9]$/
92
+ - !ruby/regexp /[A-Z]/
82
93
  accept: []
83
- exclude: []
94
+ UncommunicativeModuleName:
84
95
  enabled: true
96
+ exclude:
97
+ - Mutant::Strategy::Rspec::DM2
85
98
  reject:
86
99
  - !ruby/regexp /^.$/
87
100
  - !ruby/regexp /[0-9]$/
88
- - !ruby/regexp /[A-Z]/
89
- SimulatedPolymorphism:
90
- exclude: []
101
+ accept: []
102
+ UncommunicativeParameterName:
91
103
  enabled: true
92
- max_ifs: 1
93
- DataClump:
94
104
  exclude: []
105
+ reject:
106
+ - !ruby/regexp /^.$/
107
+ - !ruby/regexp /[0-9]$/
108
+ - !ruby/regexp /[A-Z]/
109
+ accept: []
110
+ UncommunicativeVariableName:
95
111
  enabled: true
96
- max_copies: 2
97
- min_clump_size: 2
98
- ControlCouple:
99
112
  exclude: []
113
+ reject:
114
+ - !ruby/regexp /^.$/
115
+ - !ruby/regexp /[0-9]$/
116
+ - !ruby/regexp /[A-Z]/
117
+ accept: []
118
+ UnusedParameters:
100
119
  enabled: true
101
- LongYieldList:
102
- max_params: 1
103
120
  exclude: []
121
+ UtilityFunction:
104
122
  enabled: true
123
+ exclude:
124
+ - Mutant::NodeHelpers#s
125
+ - Mutant::CLI#reporter
126
+ - Mutant::Mutation::Evil#success?
127
+ - Mutant::Mutation::Neutral#success?
128
+ max_helper_calls: 0
@@ -0,0 +1,100 @@
1
+ module Mutant
2
+ class CLI
3
+ class Classifier
4
+ # Explicit method classifier
5
+ class Method < self
6
+ register
7
+
8
+ TABLE = {
9
+ '.' => Matcher::Methods::Singleton,
10
+ '#' => Matcher::Methods::Instance
11
+ }.freeze
12
+
13
+ REGEXP = %r(\A(#{SCOPE_PATTERN})([.#])(#{METHOD_NAME_PATTERN}\z)).freeze
14
+
15
+ # Positions of captured regexp groups
16
+ SCOPE_NAME_POSITION = 1
17
+ SCOPE_SYMBOL_POSITION = 2
18
+ METHOD_NAME_POSITION = 3
19
+
20
+ # Return method matcher
21
+ #
22
+ # @return [Matcher::Method]
23
+ #
24
+ # @api private
25
+ #
26
+ def matcher
27
+ methods_matcher.matcher.new(scope, method)
28
+ end
29
+ memoize :matcher
30
+
31
+ private
32
+
33
+ # Return method
34
+ #
35
+ # @return [Method, UnboundMethod]
36
+ #
37
+ # @api private
38
+ #
39
+ def method
40
+ methods_matcher.methods.detect do |method|
41
+ method.name == method_name
42
+ end or raise("Cannot find method #{identification}")
43
+ end
44
+ memoize :method, :freezer => :noop
45
+
46
+ # Return scope name
47
+ #
48
+ # @return [String]
49
+ #
50
+ # @api private
51
+ #
52
+ def scope_name
53
+ match[SCOPE_NAME_POSITION]
54
+ end
55
+
56
+ # Return scope
57
+ #
58
+ # @return [Class, Method]
59
+ #
60
+ # @api private
61
+ #
62
+ def scope
63
+ Classifier.constant_lookup(scope_name)
64
+ end
65
+
66
+ # Return method name
67
+ #
68
+ # @return [String]
69
+ #
70
+ # @api private
71
+ #
72
+ def method_name
73
+ match[METHOD_NAME_POSITION].to_sym
74
+ end
75
+
76
+ # Return scope symbol
77
+ #
78
+ # @return [Symbol]
79
+ #
80
+ # @api private
81
+ #
82
+ def scope_symbol
83
+ match[SCOPE_SYMBOL_POSITION]
84
+ end
85
+
86
+ # Return matcher class
87
+ #
88
+ # @return [Class:Mutant::Matcher]
89
+ #
90
+ # @api private
91
+ #
92
+ def methods_matcher
93
+ TABLE.fetch(scope_symbol).new(scope)
94
+ end
95
+ memoize :methods_matcher
96
+
97
+ end # Method
98
+ end # Classifier
99
+ end # CLI
100
+ end # Mutant
@@ -0,0 +1,47 @@
1
+ module Mutant
2
+ class CLI
3
+ class Classifier
4
+
5
+ # Namespace classifier
6
+ class Namespace < self
7
+
8
+ private
9
+
10
+ # Return matcher
11
+ #
12
+ # @return [Matcher]
13
+ #
14
+ # @api private
15
+ #
16
+ def matcher
17
+ self.class::MATCHER.new(namespace)
18
+ end
19
+
20
+ # Return namespace
21
+ #
22
+ # @return [Class, Module]
23
+ #
24
+ # @api private
25
+ #
26
+ def namespace
27
+ Classifier.constant_lookup(match[1].to_s)
28
+ end
29
+
30
+ # Recursive namespace classifier
31
+ class Recursive < self
32
+ REGEXP = %r(\A(#{SCOPE_PATTERN})\*\z).freeze
33
+ MATCHER = Matcher::Namespace
34
+ register
35
+ end # Recursive
36
+
37
+ # Recursive namespace classifier
38
+ class Flat < self
39
+ REGEXP = %r(\A(#{SCOPE_PATTERN})\z).freeze
40
+ MATCHER = Matcher::Scope
41
+ register
42
+ end # Flat
43
+
44
+ end # Namespace
45
+ end # Classifier
46
+ end # CLI
47
+ end # Mutant
@@ -0,0 +1,35 @@
1
+ module Mutant
2
+ class CLI
3
+ class Classifier
4
+
5
+ # Scope classifier
6
+ class Scope < self
7
+
8
+ REGEXP = %r(\A(#{SCOPE_PATTERN})\z).freeze
9
+
10
+ private
11
+
12
+ # Return matcher
13
+ #
14
+ # @return [Matcher]
15
+ #
16
+ # @api private
17
+ #
18
+ def matcher
19
+ Matcher::Scope.new(scope)
20
+ end
21
+
22
+ # Return namespace
23
+ #
24
+ # @return [Class, Module]
25
+ #
26
+ # @api private
27
+ #
28
+ def scope
29
+ Classifier.constant_lookup(match[1].to_s)
30
+ end
31
+
32
+ end # Scope
33
+ end # Classifier
34
+ end # CLI
35
+ end # Mutant
@@ -0,0 +1,141 @@
1
+ module Mutant
2
+ class CLI
3
+ # A classifier for input strings
4
+ class Classifier < Matcher
5
+ include AbstractType, Adamantium::Flat, Equalizer.new(:identification)
6
+
7
+ SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z_0-9]*/.freeze
8
+ METHOD_NAME_PATTERN = /[_A-Za-z][A-Za-z0-9_]*[!?=]?/.freeze
9
+ SCOPE_PATTERN = /(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_PATTERN})*/.freeze
10
+
11
+ SINGLETON_PATTERN = %r(\A(#{SCOPE_PATTERN})\z).freeze
12
+
13
+ REGISTRY = []
14
+
15
+ # Register classifier
16
+ #
17
+ # @return [undefined]
18
+ #
19
+ # @api private
20
+ #
21
+ def self.register
22
+ REGISTRY << self
23
+ end
24
+ private_class_method :register
25
+
26
+ # Return constant
27
+ #
28
+ # @param [String] location
29
+ #
30
+ # @return [Class|Module]
31
+ #
32
+ # @api private
33
+ #
34
+ def self.constant_lookup(location)
35
+ location.gsub(%r(\A::), '').split('::').inject(::Object) do |parent, name|
36
+ parent.const_get(name)
37
+ end
38
+ end
39
+
40
+ # Return matchers for input
41
+ #
42
+ # @param [String] input
43
+ #
44
+ # @return [Classifier]
45
+ # if a classifier handles the input
46
+ #
47
+ # @return [nil]
48
+ # otherwise
49
+ #
50
+ # @api private
51
+ #
52
+ def self.build(input)
53
+ classifiers = REGISTRY.map do |descendant|
54
+ descendant.run(input)
55
+ end.compact
56
+
57
+ raise if classifiers.length > 1
58
+
59
+ classifiers.first
60
+ end
61
+
62
+ # Run classifier
63
+ #
64
+ # @return [Classifier]
65
+ # if input is handled by classifier
66
+ #
67
+ # @return [nil]
68
+ # otherwise
69
+ #
70
+ # @api private
71
+ #
72
+ def self.run(input)
73
+ match = self::REGEXP.match(input)
74
+ return unless match
75
+
76
+ new(match)
77
+ end
78
+
79
+ # No protected_class_method in ruby :(
80
+ class << self; protected :run; end
81
+
82
+ # Enumerate subjects
83
+ #
84
+ # @return [self]
85
+ # if block given
86
+ #
87
+ # @return [Enumerator<Subject>]
88
+ # otherwise
89
+ #
90
+ # @api private
91
+ #
92
+ def each(&block)
93
+ return to_enum unless block_given?
94
+ matcher.each(&block)
95
+ self
96
+ end
97
+
98
+ # Return identification
99
+ #
100
+ # @return [String]
101
+ #
102
+ # @api private
103
+ #
104
+ def identification
105
+ match.to_s
106
+ end
107
+ memoize :identification
108
+
109
+ private
110
+
111
+ # Initialize object
112
+ #
113
+ # @param [MatchData] match
114
+ #
115
+ # @return [undefined]
116
+ #
117
+ # @api private
118
+ #
119
+ def initialize(match)
120
+ @match = match
121
+ end
122
+
123
+ # Return match
124
+ #
125
+ # @return [MatchData]
126
+ #
127
+ # @api private
128
+ #
129
+ attr_reader :match
130
+
131
+ # Return matcher
132
+ #
133
+ # @return [Matcher]
134
+ #
135
+ # @api private
136
+ #
137
+ abstract_method :matcher
138
+
139
+ end # Classifier
140
+ end # CLI
141
+ end # Mutant