reek 4.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd5651b2cc5c8b32a2eb8d1297c190b67ebf3f6b
4
- data.tar.gz: 948781b5c00280ff13499c1a31cf634f495e5736
3
+ metadata.gz: 300e8430e0cb4dcd0ca01529abb40921ed54f29f
4
+ data.tar.gz: c844e886e736762ddb4d61cdcf5bc5931aabc041
5
5
  SHA512:
6
- metadata.gz: 16653eda1cae7ac27cf6f439f45b8d2f77e8c954bd9e989cd70fdfa0c5c553cebb3caa97bb6a8696312a5029120455a7e7c45e20a46b42b3823d77c25abc3a2d
7
- data.tar.gz: ecc7e1105e00ae9e6f284889ad5fc58e19eee73d79fde9f6c6ae9194ed6ad2b29c06006e3eee8e74356a4f629b04cfb001ab6e8ea9c7ad1dc4e263051a1edb83
6
+ metadata.gz: 287000e19154d5d0fff6a0996a175eeb4ff091506b19487dd126d3b858a2513d21afa284a2a603791b8c1e7a8e4538c0c3d75c213f3e04264e6e59deedcb3837
7
+ data.tar.gz: 7e67159cc8a61f025a1d36f25fba7fefd0b1abda631d7f590c44242292a179804c26604ae4c72dc2af012a9b4c7e359be169f1c29a3d88868a7470810eebe8db
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 4.2.0 (2016-07-16)
4
+
5
+ * (waldyr) Add Too Many Constants smell detector
6
+
3
7
  ## 4.1.1 (2016-07-05)
4
8
 
5
9
  * (waldyr) Checking lambda should not raise error
data/Gemfile CHANGED
@@ -11,9 +11,9 @@ group :development do
11
11
  gem 'rake', '~> 11.1'
12
12
  gem 'rspec', '~> 3.0'
13
13
  gem 'rubocop', '~> 0.41.1'
14
- gem 'simplecov', '~> 0.11.1'
14
+ gem 'simplecov', '~> 0.12.0'
15
15
  gem 'yard', '~> 0.8.7'
16
- gem 'activesupport', '~> 4.2.7.rc1'
16
+ gem 'activesupport', '~> 4.2'
17
17
 
18
18
  platforms :mri do
19
19
  gem 'redcarpet', '~> 3.3.1'
data/README.md CHANGED
@@ -461,6 +461,23 @@ bundle exec rake console
461
461
  => Reek::Examiner
462
462
  ```
463
463
 
464
+ You can also use Pry while running the tests by adding the following at the
465
+ point where you want to start debugging:
466
+
467
+ ```Ruby
468
+ require 'pry'
469
+ binding.pry
470
+ ```
471
+
472
+ If you do this, you need to also run the specs with `DEBUG=1` set, e.g.:
473
+
474
+ ```
475
+ DEBUG=1 bundle exec rspec spec/your/file_spec.rb:23
476
+ ```
477
+
478
+ This is necessary because normally all specs run with a timeout of 5 seconds,
479
+ which isn't much if you're busy using Pry.
480
+
464
481
  Have a look at our [Developer API](docs/API.md) for more inspiration.
465
482
 
466
483
  From then on you should check out:
@@ -545,6 +562,7 @@ The Reek core team consists of:
545
562
 
546
563
  * [Matijs van Zuijlen](https://github.com/mvz)
547
564
  * [Piotr Szotkowski](https://github.com/chastell)
565
+ * [Waldyr Souza](https://github.com/waldyr)
548
566
  * [Timo Rößner](https://github.com/troessner)
549
567
 
550
568
  The original author of Reek is [Kevin Rutherford](https://github.com/kevinrutherford).
@@ -60,6 +60,10 @@ RepeatedConditional:
60
60
  SubclassedFromCoreClass:
61
61
  enabled: true
62
62
  exclude: []
63
+ TooManyConstants:
64
+ enabled: true
65
+ exclude: []
66
+ max_constants: 5
63
67
  TooManyInstanceVariables:
64
68
  enabled: true
65
69
  exclude: []
@@ -115,4 +119,3 @@ UnusedPrivateMethod:
115
119
  UtilityFunction:
116
120
  enabled: true
117
121
  exclude: []
118
- public_methods_only: false
@@ -0,0 +1,37 @@
1
+ ## Introduction
2
+
3
+ _Too Many Constants_ is a case of [Large Class](Large-Class.md).
4
+
5
+ ## Example
6
+
7
+ Given this configuration
8
+
9
+ ```yaml
10
+ TooManyConstants:
11
+ max_constants: 3
12
+ ```
13
+
14
+ and this code:
15
+
16
+ ```Ruby
17
+ class Smelly
18
+ CONST_1 = :dummy
19
+ CONST_2 = :dummy
20
+ CONST_3 = :dummy
21
+ CONST_4 = :dummy
22
+ end
23
+ ```
24
+
25
+ Reek would emit the following warning:
26
+
27
+ ```
28
+ test.rb -- 1 warning:
29
+ [1]:TooManyConstants: Smelly has 4 constants
30
+ ```
31
+ ## Configuration
32
+
33
+ Reek's _Too Many Constants_ detector offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
34
+
35
+ | Option | Value | Effect |
36
+ | -------------------------|---------|---------|
37
+ | `max_constants` | integer | The maximum number of constants that are permitted. Defaults to 5 |
@@ -11,7 +11,7 @@ Feature: Basic smell detection
11
11
  Then the exit status indicates smells
12
12
  And it reports:
13
13
  """
14
- inline.rb -- 47 warnings:
14
+ inline.rb -- 48 warnings:
15
15
  BooleanParameter: Inline::C#parse_signature has boolean parameter 'raw' [https://github.com/troessner/reek/blob/master/docs/Boolean-Parameter.md]
16
16
  ClassVariable: Inline declares the class variable @@directory [https://github.com/troessner/reek/blob/master/docs/Class-Variable.md]
17
17
  ClassVariable: Inline declares the class variable @@rootdir [https://github.com/troessner/reek/blob/master/docs/Class-Variable.md]
@@ -41,6 +41,7 @@ Feature: Basic smell detection
41
41
  RepeatedConditional: Inline::C tests $DEBUG at least 7 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
42
42
  RepeatedConditional: Inline::C tests $TESTING at least 4 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
43
43
  RepeatedConditional: Inline::C tests @@type_map.has_key? type at least 3 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
44
+ TooManyConstants: Inline has 6 constants [https://github.com/troessner/reek/blob/master/docs/Too-Many-Constants.md]
44
45
  TooManyInstanceVariables: Inline::C has at least 13 instance variables [https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md]
45
46
  TooManyMethods: Inline::C has at least 25 methods [https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md]
46
47
  TooManyStatements: File#self.write_with_backup has approx 6 statements [https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md]
@@ -59,7 +60,7 @@ Feature: Basic smell detection
59
60
  UncommunicativeVariableName: Inline::C#module_name has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
60
61
  UncommunicativeVariableName: Inline::C#parse_signature has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
61
62
  UtilityFunction: Inline::C#strip_comments doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
62
- optparse.rb -- 119 warnings:
63
+ optparse.rb -- 120 warnings:
63
64
  Attribute: OptionParser#banner is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
64
65
  Attribute: OptionParser#default_argv is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
65
66
  Attribute: OptionParser#program_name is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
@@ -131,6 +132,7 @@ Feature: Basic smell detection
131
132
  RepeatedConditional: OptionParser tests s at least 7 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
132
133
  SubclassedFromCoreClass: OptionParser::CompletingHash inherits from a core class (Hash) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
133
134
  SubclassedFromCoreClass: OptionParser::OptionMap inherits from a core class (Hash) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
135
+ TooManyConstants: OptionParser has 16 constants [https://github.com/troessner/reek/blob/master/docs/Too-Many-Constants.md]
134
136
  TooManyInstanceVariables: OptionParser has at least 6 instance variables [https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md]
135
137
  TooManyInstanceVariables: OptionParser::Switch has at least 7 instance variables [https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md]
136
138
  TooManyMethods: OptionParser has at least 42 methods [https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md]
@@ -179,7 +181,7 @@ Feature: Basic smell detection
179
181
  UnusedParameters: OptionParser::Completion#convert has unused parameter 'opt' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
180
182
  UnusedParameters: OptionParser::Switch::NoArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
181
183
  UnusedParameters: OptionParser::Switch::OptionalArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
182
- redcloth.rb -- 103 warnings:
184
+ redcloth.rb -- 104 warnings:
183
185
  Attribute: RedCloth#filter_html is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
184
186
  Attribute: RedCloth#filter_styles is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
185
187
  Attribute: RedCloth#hard_breaks is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
@@ -229,6 +231,7 @@ Feature: Basic smell detection
229
231
  RepeatedConditional: RedCloth tests href at least 3 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
230
232
  RepeatedConditional: RedCloth tests title at least 4 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
231
233
  SubclassedFromCoreClass: RedCloth inherits from a core class (String) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
234
+ TooManyConstants: RedCloth has 45 constants [https://github.com/troessner/reek/blob/master/docs/Too-Many-Constants.md]
232
235
  TooManyMethods: RedCloth has at least 44 methods [https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md]
233
236
  TooManyStatements: RedCloth#block_markdown_bq has approx 6 statements [https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md]
234
237
  TooManyStatements: RedCloth#block_textile_lists has approx 21 statements [https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md]
@@ -283,5 +286,5 @@ Feature: Basic smell detection
283
286
  UtilityFunction: RedCloth#lT doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
284
287
  UtilityFunction: RedCloth#no_textile doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
285
288
  UtilityFunction: RedCloth#v_align doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
286
- 269 total warnings
289
+ 272 total warnings
287
290
  """
@@ -0,0 +1,19 @@
1
+ Feature: Smell - TooManyConstants
2
+ Scenario: Reports smell
3
+ Given a file named "my_class.rb" with:
4
+ """
5
+ class MyClass
6
+ A = 1
7
+ B = 2
8
+ C = 3
9
+ D = 4
10
+ E = 5
11
+ F = 6
12
+ end
13
+ """
14
+ When I run `reek my_class.rb`
15
+ Then it reports:
16
+ """
17
+ my_class.rb -- 1 warning:
18
+ [1]:TooManyConstants: MyClass has 6 constants [https://github.com/troessner/reek/blob/master/docs/Too-Many-Constants.md]
19
+ """
@@ -101,6 +101,11 @@ module Reek
101
101
  end
102
102
  end
103
103
 
104
+ # Provide length for statement counting. A sexp counts as one statement.
105
+ def length
106
+ 1
107
+ end
108
+
104
109
  protected
105
110
 
106
111
  # See ".each_node" for documentation.
@@ -7,10 +7,6 @@ module Reek
7
7
  def name
8
8
  'lambda'
9
9
  end
10
-
11
- def module_creation_call?
12
- false
13
- end
14
10
  end
15
11
  end
16
12
  end
@@ -58,6 +58,47 @@ module Reek
58
58
  call && call.module_creation_call?
59
59
  end
60
60
 
61
+ # Sometimes we assign classes like:
62
+ #
63
+ # Foo = Class.new(Bar)
64
+ #
65
+ # This is mapped into the following expression:
66
+ #
67
+ # s(:casgn, nil :Foo,
68
+ # s(:send,
69
+ # s(:const, nil, :Class), :new,
70
+ # s(:const, nil, :Bar)
71
+ # )
72
+ # )
73
+ #
74
+ # And we are only looking for s(:const, nil, :Bar)
75
+ #
76
+ def superclass
77
+ return nil unless defines_module?
78
+
79
+ constant_definition.args.first if constant_definition.receiver.name == 'Class'
80
+ end
81
+
82
+ def name
83
+ children[1].to_s
84
+ end
85
+
86
+ # there are two valid forms of the casgn sexp
87
+ # (casgn <namespace> <name> <value>) and
88
+ # (casgn <namespace> <name>) used in or-asgn and mlhs
89
+ #
90
+ # source = "class Hi; THIS ||= 3; end"
91
+ # (class
92
+ # (const nil :Hi) nil
93
+ # (or-asgn
94
+ # (casgn nil :THIS)
95
+ # (int 3)))
96
+ def value
97
+ children[2]
98
+ end
99
+
100
+ private
101
+
61
102
  # This is the right hand side of a constant
62
103
  # assignment.
63
104
  #
@@ -92,50 +133,12 @@ module Reek
92
133
 
93
134
  case value.type
94
135
  when :block
95
- value.call
136
+ call = value.call
137
+ call if call.type == :send
96
138
  when :send
97
139
  value
98
140
  end
99
141
  end
100
-
101
- # Sometimes we assign classes like:
102
- #
103
- # Foo = Class.new(Bar)
104
- #
105
- # This is mapped into the following expression:
106
- #
107
- # s(:casgn, nil :Foo,
108
- # s(:send,
109
- # s(:const, nil, :Class), :new,
110
- # s(:const, nil, :Bar)
111
- # )
112
- # )
113
- #
114
- # And we are only looking for s(:const, nil, :Bar)
115
- #
116
- def superclass
117
- return nil unless constant_definition
118
-
119
- constant_definition.args.first
120
- end
121
-
122
- def name
123
- children[1].to_s
124
- end
125
-
126
- # there are two valid forms of the casgn sexp
127
- # (casgn <namespace> <name> <value>) and
128
- # (casgn <namespace> <name>) used in or-asgn and mlhs
129
- #
130
- # source = "class Hi; THIS ||= 3; end"
131
- # (class
132
- # (const nil :Hi) nil
133
- # (or-asgn
134
- # (casgn nil :THIS)
135
- # (int 3)))
136
- def value
137
- children[2]
138
- end
139
142
  end
140
143
  end
141
144
  end
@@ -89,7 +89,6 @@ module Reek
89
89
  def set_configuration_options
90
90
  parser.separator 'Configuration:'
91
91
  parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
92
- raise ArgumentError, "Config file #{file} doesn't exist" unless File.exist?(file)
93
92
  self.config_file = Pathname.new(file)
94
93
  end
95
94
  parser.on('--smell SMELL', 'Detect smell SMELL (default: all enabled smells)') do |smell|
@@ -47,12 +47,7 @@ module Reek
47
47
  end
48
48
 
49
49
  def self.default
50
- from_path nil
51
- end
52
-
53
- def self.new(*)
54
- raise NotImplementedError,
55
- 'Calling `new` is not supported, please use one of the factory methods'
50
+ new
56
51
  end
57
52
 
58
53
  # Returns the directive for a given directory.
@@ -12,15 +12,7 @@ module Reek
12
12
  end
13
13
 
14
14
  def increase_by(sexp)
15
- return unless sexp
16
- case sexp
17
- when Reek::AST::Node
18
- self.value = value + 1
19
- when Array
20
- self.value = value + sexp.length
21
- else
22
- raise ArgumentError, "Invalid type #{sexp} given"
23
- end
15
+ self.value = value + sexp.length if sexp
24
16
  end
25
17
 
26
18
  def decrease_by(number)
@@ -6,8 +6,6 @@ require_relative 'report/heading_formatter'
6
6
  module Reek
7
7
  # Reek reporting functionality.
8
8
  module Report
9
- module_function
10
-
11
9
  REPORT_CLASSES = {
12
10
  yaml: YAMLReport,
13
11
  json: JSONReport,
@@ -39,28 +37,38 @@ module Reek
39
37
  #
40
38
  # @return The mapped report class
41
39
  #
42
- def report_class(report_format)
43
- REPORT_CLASSES.fetch(report_format) do
44
- raise "Unknown report format: #{report_format}"
45
- end
40
+ def self.report_class(report_format)
41
+ REPORT_CLASSES.fetch(report_format)
46
42
  end
47
43
 
48
- def location_formatter(location_format)
49
- LOCATION_FORMATTERS.fetch(location_format) do
50
- raise "Unknown location format: #{location_format}"
51
- end
44
+ # Map location format symbol to a report class.
45
+ #
46
+ # @param [Symbol] location_format The format to map
47
+ #
48
+ # @return The mapped location class
49
+ #
50
+ def self.location_formatter(location_format)
51
+ LOCATION_FORMATTERS.fetch(location_format)
52
52
  end
53
53
 
54
- def heading_formatter(heading_format)
55
- HEADING_FORMATTERS.fetch(heading_format) do
56
- raise "Unknown heading format: #{heading_format}"
57
- end
54
+ # Map heading format symbol to a report class.
55
+ #
56
+ # @param [Symbol] heading_format The format to map
57
+ #
58
+ # @return The mapped heading class
59
+ #
60
+ def self.heading_formatter(heading_format)
61
+ HEADING_FORMATTERS.fetch(heading_format)
58
62
  end
59
63
 
60
- def warning_formatter_class(warning_format)
61
- WARNING_FORMATTER_CLASSES.fetch(warning_format) do
62
- raise "Unknown warning format: #{warning_format}"
63
- end
64
+ # Map warning format symbol to a report class.
65
+ #
66
+ # @param [Symbol] warning_format The format to map
67
+ #
68
+ # @return The mapped warning class
69
+ #
70
+ def self.warning_formatter_class(warning_format)
71
+ WARNING_FORMATTER_CLASSES.fetch(warning_format)
64
72
  end
65
73
  end
66
74
  end
@@ -525,6 +525,37 @@ TooManyInstanceVariables:
525
525
  test.rb -- 5 warnings:
526
526
  [1]:TooManyInstanceVariables has at least 4 instance variables (TooManyInstanceVariables)
527
527
  ```
528
+ TooManyConstants:
529
+ remediation_points: 500_000
530
+ content: |
531
+ `Too Many Constants` is a special case of `LargeClass`.
532
+
533
+ ## Example
534
+
535
+ Given this configuration
536
+
537
+ ```yaml
538
+ TooManyConstants:
539
+ max_constants: 3
540
+ ```
541
+
542
+ and this code:
543
+
544
+ ```Ruby
545
+ class TooManyConstants
546
+ CONST_1 = :dummy
547
+ CONST_2 = :dummy
548
+ CONST_3 = :dummy
549
+ CONST_4 = :dummy
550
+ end
551
+ ```
552
+
553
+ Reek would emit the following warning:
554
+
555
+ ```
556
+ test.rb -- 1 warnings:
557
+ [1]:TooManyConstants has 4 constants (TooManyConstants)
558
+ ```
528
559
  TooManyMethods:
529
560
  remediation_points: 500_000
530
561
  content: |
@@ -16,6 +16,7 @@ require_relative 'smells/prima_donna_method'
16
16
  require_relative 'smells/repeated_conditional'
17
17
  require_relative 'smells/subclassed_from_core_class'
18
18
  require_relative 'smells/too_many_instance_variables'
19
+ require_relative 'smells/too_many_constants'
19
20
  require_relative 'smells/too_many_methods'
20
21
  require_relative 'smells/too_many_statements'
21
22
  require_relative 'smells/uncommunicative_method_name'
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'smell_detector'
3
+ require_relative 'smell_warning'
4
+
5
+ module Reek
6
+ module Smells
7
+ #
8
+ # A Large Class is a class or module that has a large number of
9
+ # instance variables, methods, constants or lines of code.
10
+ #
11
+ # +TooManyConstants' reports classes having more than a
12
+ # configurable number of constants.
13
+ #
14
+ # See {file:docs/Too-Many-Constants.md} for details.
15
+ class TooManyConstants < SmellDetector
16
+ # The name of the config field that sets the maximum number
17
+ # of constants permitted in a class.
18
+ MAX_ALLOWED_CONSTANTS_KEY = 'max_constants'.freeze
19
+ DEFAULT_MAX_CONSTANTS = 5
20
+
21
+ def self.contexts
22
+ [:class, :module]
23
+ end
24
+
25
+ def self.default_config
26
+ super.merge(
27
+ MAX_ALLOWED_CONSTANTS_KEY => DEFAULT_MAX_CONSTANTS,
28
+ EXCLUDE_KEY => [])
29
+ end
30
+
31
+ #
32
+ # Checks +klass+ for too many constants.
33
+ #
34
+ # @return [Array<SmellWarning>]
35
+ #
36
+ def inspect(ctx)
37
+ max_allowed_constants = value(MAX_ALLOWED_CONSTANTS_KEY, ctx)
38
+
39
+ count = ctx.each_node(:casgn, [:class]).delete_if(&:defines_module?).length
40
+
41
+ return [] if count <= max_allowed_constants
42
+
43
+ build_smell_warning(ctx, count)
44
+ end
45
+
46
+ private
47
+
48
+ def build_smell_warning(ctx, count)
49
+ [smell_warning(
50
+ context: ctx,
51
+ lines: [ctx.exp.line],
52
+ message: "has #{count} constants",
53
+ parameters: { count: count })]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -7,6 +7,6 @@ module Reek
7
7
  # @public
8
8
  module Version
9
9
  # @public
10
- STRING = '4.1.1'.freeze
10
+ STRING = '4.2.0'.freeze
11
11
  end
12
12
  end
@@ -69,4 +69,10 @@ RSpec.describe Reek::AST::Node do
69
69
  expect(node1.hash).not_to eq(node2.hash)
70
70
  end
71
71
  end
72
+
73
+ describe '#length' do
74
+ it 'counts itself as representing one statement' do
75
+ expect(sexp(:foo).length).to eq 1
76
+ end
77
+ end
72
78
  end
@@ -441,7 +441,7 @@ RSpec.describe Reek::AST::SexpExtensions::ModuleNode do
441
441
  end
442
442
 
443
443
  RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
444
- context '#defines_module' do
444
+ describe '#defines_module?' do
445
445
  context 'with single assignment' do
446
446
  subject do
447
447
  sexp(:casgn, nil, :Foo)
@@ -467,16 +467,24 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
467
467
  end
468
468
  end
469
469
 
470
- context 'when assign a lambda to a constant' do
470
+ context 'when assigning a lambda to a constant' do
471
471
  it 'does not define a module' do
472
472
  exp = Reek::Source::SourceCode.from('C = ->{}').syntax_tree
473
473
 
474
474
  expect(exp.defines_module?).to be_falsey
475
475
  end
476
476
  end
477
+
478
+ context 'when assigning a string to a constant' do
479
+ it 'does not define a module' do
480
+ exp = Reek::Source::SourceCode.from('C = "hello"').syntax_tree
481
+
482
+ expect(exp.defines_module?).to be_falsey
483
+ end
484
+ end
477
485
  end
478
486
 
479
- context '#superclass' do
487
+ describe '#superclass' do
480
488
  it 'returns the superclass from the class definition' do
481
489
  exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
482
490
 
@@ -494,33 +502,17 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
494
502
 
495
503
  expect(exp.superclass).to be_nil
496
504
  end
497
- end
498
505
 
499
- context '#constant_definition' do
500
- context 'simple constant assignment' do
501
- it 'is not important' do
502
- exp = sexp(:casgn, nil, :Foo, sexp(:int, 23))
506
+ it 'returns nothing for a class definition using Struct.new' do
507
+ exp = Reek::Source::SourceCode.from('Foo = Struct.new("Bar")').syntax_tree
503
508
 
504
- expect(exp.constant_definition).to be_nil
505
- end
509
+ expect(exp.superclass).to be_nil
506
510
  end
507
511
 
508
- context 'complicated constant assignments' do
509
- it 'calls the block and returns what is being sent' do
510
- exp = Reek::Source::SourceCode.from('Iterator = Struct.new(:exp) { def p; end; }').syntax_tree
512
+ it 'returns nothing for a constant assigned with a bare method call' do
513
+ exp = Reek::Source::SourceCode.from('Foo = foo("Bar")').syntax_tree
511
514
 
512
- struct_exp = sexp(:send, sexp(:const, nil, :Struct), :new, sexp(:sym, :exp))
513
-
514
- expect(exp.constant_definition).to eq struct_exp
515
- end
516
-
517
- it 'returns the expression responsible for class creation' do
518
- exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
519
-
520
- class_exp = sexp(:send, sexp(:const, nil, :Class), :new, sexp(:const, nil, :Bar))
521
-
522
- expect(exp.constant_definition).to eq class_exp
523
- end
515
+ expect(exp.superclass).to be_nil
524
516
  end
525
517
  end
526
518
  end
@@ -68,8 +68,6 @@ RSpec.describe Reek::CLI::Application do
68
68
  let(:app) { Reek::CLI::Application.new ['--config', 'some_file.reek'] }
69
69
 
70
70
  before do
71
- allow(File).to receive(:exist?).and_call_original
72
- allow(File).to receive(:exist?).with('some_file.reek').and_return true
73
71
  allow(Reek::Configuration::AppConfiguration).
74
72
  to receive(:from_path).
75
73
  with(Pathname.new('some_file.reek')).
@@ -6,12 +6,6 @@ require_lib 'reek/configuration/default_directive'
6
6
  require_lib 'reek/configuration/excluded_paths'
7
7
 
8
8
  RSpec.describe Reek::Configuration::AppConfiguration do
9
- describe '#new' do
10
- it 'raises NotImplementedError' do
11
- expect { subject }.to raise_error(NotImplementedError)
12
- end
13
- end
14
-
15
9
  describe 'factory methods' do
16
10
  let(:expected_excluded_paths) do
17
11
  [SAMPLES_PATH.join('two_smelly_files'),
@@ -68,6 +62,16 @@ RSpec.describe Reek::Configuration::AppConfiguration do
68
62
  end
69
63
  end
70
64
 
65
+ describe '#default' do
66
+ it 'returns a blank AppConfiguration' do
67
+ config = described_class.default
68
+ expect(config).to be_instance_of described_class
69
+ expect(config.send(:excluded_paths)).to eq([])
70
+ expect(config.send(:default_directive)).to eq({})
71
+ expect(config.send(:directory_directives)).to eq({})
72
+ end
73
+ end
74
+
71
75
  describe '#directive_for' do
72
76
  context 'multiple directory directives and no default directive present' do
73
77
  let(:source_via) { 'spec/samples/three_clean_files/dummy.rb' }
@@ -0,0 +1,23 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/context/statement_counter'
3
+
4
+ RSpec.describe Reek::Context::StatementCounter do
5
+ let(:counter) { described_class.new }
6
+ describe '#increase_by' do
7
+ it 'does not increase if passed a falsy value' do
8
+ counter.increase_by(nil)
9
+ expect(counter.value).to eq 0
10
+ end
11
+
12
+ it 'increase by the lengh of the passed in argument' do
13
+ counter.increase_by([1, 2, 3])
14
+ expect(counter.value).to eq 3
15
+ end
16
+
17
+ it 'accumulates increases' do
18
+ counter.increase_by([1, 2, 3])
19
+ counter.increase_by([1, 2, 3])
20
+ expect(counter.value).to eq 6
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/report'
3
+
4
+ RSpec.describe Reek::Report do
5
+ describe '.report_class' do
6
+ it 'returns the correct class' do
7
+ expect(Reek::Report.report_class(:text)).to eq Reek::Report::TextReport
8
+ end
9
+ end
10
+
11
+ describe '.location_formatter' do
12
+ it 'returns the correct class' do
13
+ expect(Reek::Report.location_formatter(:plain)).to eq Reek::Report::BlankLocationFormatter
14
+ end
15
+ end
16
+
17
+ describe '.heading_formatter' do
18
+ it 'returns the correct class' do
19
+ expect(Reek::Report.heading_formatter(:quiet)).to eq Reek::Report::HeadingFormatter::Quiet
20
+ end
21
+ end
22
+
23
+ describe '.warning_formatter_class' do
24
+ it 'returns the correct class' do
25
+ expect(Reek::Report.warning_formatter_class(:simple)).to eq Reek::Report::SimpleWarningFormatter
26
+ end
27
+ end
28
+ end
@@ -119,7 +119,7 @@ RSpec.describe Reek::Smells::SubclassedFromCoreClass do
119
119
  end
120
120
  end
121
121
  EOS
122
- expect(src).to_not reek_of(:SubclassedFromCoreClass, ancestor: 'Array')
122
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
123
123
  end
124
124
 
125
125
  it 'should not report if inner class inherits from allowed classes' do
@@ -130,6 +130,28 @@ RSpec.describe Reek::Smells::SubclassedFromCoreClass do
130
130
  end
131
131
  end
132
132
  EOS
133
- expect(src).to_not reek_of(:SubclassedFromCoreClass, ancestor: 'StandardError')
133
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
134
+ end
135
+
136
+ it 'should not report if class is created with Struct.new' do
137
+ src = <<-EOS
138
+ module Namespace
139
+ class Dummy
140
+ Dummiest = Struct.new('Array')
141
+ end
142
+ end
143
+ EOS
144
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
145
+ end
146
+
147
+ it 'should only report classes created with Class.new' do
148
+ src = <<-EOS
149
+ module Namespace
150
+ class Dummy
151
+ Dummiest = Foo.new(Array)
152
+ end
153
+ end
154
+ EOS
155
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
134
156
  end
135
157
  end
@@ -0,0 +1,216 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/smells/too_many_constants'
3
+ require_relative 'smell_detector_shared'
4
+
5
+ RSpec.describe Reek::Smells::TooManyConstants do
6
+ context 'counting constants' do
7
+ it 'should not report for non-excessive constants' do
8
+ src = <<-EOS
9
+ class Dummy
10
+ A = B = C = D = E = 1
11
+ end
12
+ EOS
13
+
14
+ expect(src).not_to reek_of(:TooManyConstants)
15
+ end
16
+
17
+ it 'should not report when increasing default' do
18
+ src = <<-EOS
19
+ # :reek:TooManyConstants: { max_constants: 6 }
20
+ class Dummy
21
+ A = B = C = D = E = 1
22
+ F = 1
23
+ end
24
+ EOS
25
+
26
+ expect(src).not_to reek_of(:TooManyConstants)
27
+ end
28
+
29
+ it 'should not report when disabled' do
30
+ src = <<-EOS
31
+ # :reek:TooManyConstants: { enabled: false }
32
+ class Dummy
33
+ A = B = C = D = E = 1
34
+ F = 1
35
+ end
36
+ EOS
37
+
38
+ expect(src).not_to reek_of(:TooManyConstants)
39
+ end
40
+
41
+ it 'should not account class definition' do
42
+ src = <<-EOS
43
+ module Dummiest
44
+ class Dummy
45
+ A = B = C = D = E = 1
46
+
47
+ Error = Class.new(StandardError)
48
+ end
49
+ end
50
+ EOS
51
+
52
+ expect(src).not_to reek_of(:TooManyConstants)
53
+ end
54
+
55
+ it 'should not account struct definition' do
56
+ src = <<-EOS
57
+ module Dummiest
58
+ class Dummy
59
+ A = B = C = D = E = 1
60
+
61
+ Struct = Struct.new
62
+ end
63
+ end
64
+ EOS
65
+
66
+ expect(src).to_not reek_of(:TooManyConstants)
67
+ end
68
+
69
+ it 'should count each constant only once' do
70
+ src = <<-EOS
71
+ class Good
72
+ A = B = C = D = E = 1
73
+ end
74
+
75
+ class Bad
76
+ A = B = C = D = E = 1
77
+ end
78
+
79
+ class Ugly
80
+ A = B = C = D = E = 1
81
+ end
82
+ EOS
83
+
84
+ expect(src).not_to reek_of(:TooManyConstants)
85
+ end
86
+
87
+ it 'should count each constant only once for each class' do
88
+ src = <<-EOS
89
+ module Movie
90
+ class Good
91
+ A = B = C = D = E = 1
92
+ end
93
+
94
+ class Bad
95
+ F = G = H = I = J = 1
96
+ end
97
+
98
+ class Ugly
99
+ K = L = M = N = O = 1
100
+ end
101
+ end
102
+ EOS
103
+
104
+ expect(src).not_to reek_of(:TooManyConstants)
105
+ end
106
+
107
+ it 'should count each constant only once for each namespace' do
108
+ src = <<-EOS
109
+ module Movie
110
+ A = B = C = D = E = 1
111
+
112
+ class Good
113
+ F = 1
114
+ end
115
+ end
116
+ EOS
117
+
118
+ expect(src).not_to reek_of(:TooManyConstants)
119
+ end
120
+
121
+ it 'should report for excessive constants inside a class' do
122
+ src = <<-EOS
123
+ class Dummy
124
+ A = B = C = D = E = 1
125
+ F = 1
126
+ end
127
+ EOS
128
+
129
+ expect(src).to reek_of(:TooManyConstants)
130
+ end
131
+
132
+ it 'should report for excessive constants inside a module' do
133
+ src = <<-EOS
134
+ module Dummiest
135
+ A = B = C = D = E = 1
136
+ F = 1
137
+
138
+ class Dummy
139
+ end
140
+ end
141
+ EOS
142
+
143
+ expect(src).to reek_of(:TooManyConstants, context: 'Dummiest')
144
+ end
145
+ end
146
+
147
+ context 'smell report' do
148
+ it 'reports the number of constants' do
149
+ src = <<-EOS
150
+ module Moduly
151
+ class Klass
152
+ A = B = C = D = E = 1
153
+ F = 1
154
+ end
155
+ end
156
+ EOS
157
+
158
+ expect(src).to reek_of(:TooManyConstants, count: 6)
159
+ end
160
+
161
+ it 'reports the line where it occurs' do
162
+ src = <<-EOS
163
+ module Moduly
164
+ class Klass
165
+ A = B = C = D = E = 1
166
+ F = 1
167
+ end
168
+ end
169
+ EOS
170
+
171
+ expect(src).to reek_of(:TooManyConstants, lines: [2])
172
+ end
173
+
174
+ it 'reports a readable message' do
175
+ src = <<-EOS
176
+ module Moduly
177
+ class Klass
178
+ A = B = C = D = E = 1
179
+ F = 1
180
+ end
181
+ end
182
+ EOS
183
+
184
+ expect(src).to reek_of(:TooManyConstants, message: 'has 6 constants')
185
+ end
186
+
187
+ context 'context' do
188
+ it 'reports the full class name' do
189
+ src = <<-EOS
190
+ module Moduly
191
+ class Klass
192
+ A = B = C = D = E = 1
193
+ F = 1
194
+ end
195
+ end
196
+ EOS
197
+
198
+ expect(src).to reek_of(:TooManyConstants, context: 'Moduly::Klass')
199
+ end
200
+
201
+ it 'reports the module name' do
202
+ src = <<-EOS
203
+ module Moduly
204
+ A = B = C = D = E = 1
205
+ F = 1
206
+
207
+ class Klass
208
+ end
209
+ end
210
+ EOS
211
+
212
+ expect(src).to reek_of(:TooManyConstants, context: 'Moduly')
213
+ end
214
+ end
215
+ end
216
+ end
@@ -84,8 +84,12 @@ RSpec.configure do |config|
84
84
  end
85
85
 
86
86
  # Avoid infinitely running tests. This is mainly useful when running mutant.
87
- config.around(:each) do |example|
88
- Timeout.timeout(5, &example)
87
+ # Set the DEBUG environment variable to something truthy like '1' to disable
88
+ # this and allow using pry without specs failing.
89
+ unless ENV['DEBUG']
90
+ config.around(:each) do |example|
91
+ Timeout.timeout(5, &example)
92
+ end
89
93
  end
90
94
  end
91
95
 
@@ -1,4 +1,5 @@
1
1
  desc 'Runs ataru to check if our docs are still in sync with our code'
2
2
  task :ataru do
3
3
  system 'bundle exec ataru check'
4
+ abort unless $CHILD_STATUS.success?
4
5
  end
@@ -15,4 +15,5 @@ task :mutant do
15
15
  fi
16
16
  EOS
17
17
  system command
18
+ abort unless $CHILD_STATUS.success?
18
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.1
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-07-05 00:00:00.000000000 Z
14
+ date: 2016-07-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -119,6 +119,7 @@ files:
119
119
  - docs/Simulated-Polymorphism.md
120
120
  - docs/Smell-Suppression.md
121
121
  - docs/Subclassed-From-Core-Class.md
122
+ - docs/Too-Many-Constants.md
122
123
  - docs/Too-Many-Instance-Variables.md
123
124
  - docs/Too-Many-Methods.md
124
125
  - docs/Too-Many-Statements.md
@@ -158,6 +159,7 @@ files:
158
159
  - features/reports/yaml.feature
159
160
  - features/samples.feature
160
161
  - features/smells/subclassed_from_core_class.feature
162
+ - features/smells/too_many_constants.feature
161
163
  - features/step_definitions/.rubocop.yml
162
164
  - features/step_definitions/reek_steps.rb
163
165
  - features/step_definitions/sample_file_steps.rb
@@ -246,6 +248,7 @@ files:
246
248
  - lib/reek/smells/smell_repository.rb
247
249
  - lib/reek/smells/smell_warning.rb
248
250
  - lib/reek/smells/subclassed_from_core_class.rb
251
+ - lib/reek/smells/too_many_constants.rb
249
252
  - lib/reek/smells/too_many_instance_variables.rb
250
253
  - lib/reek/smells/too_many_methods.rb
251
254
  - lib/reek/smells/too_many_statements.rb
@@ -294,6 +297,7 @@ files:
294
297
  - spec/reek/context/method_context_spec.rb
295
298
  - spec/reek/context/module_context_spec.rb
296
299
  - spec/reek/context/root_context_spec.rb
300
+ - spec/reek/context/statement_counter_spec.rb
297
301
  - spec/reek/context_builder_spec.rb
298
302
  - spec/reek/examiner_spec.rb
299
303
  - spec/reek/rake/task_spec.rb
@@ -305,6 +309,7 @@ files:
305
309
  - spec/reek/report/text_report_spec.rb
306
310
  - spec/reek/report/xml_report_spec.rb
307
311
  - spec/reek/report/yaml_report_spec.rb
312
+ - spec/reek/report_spec.rb
308
313
  - spec/reek/smells/attribute_spec.rb
309
314
  - spec/reek/smells/boolean_parameter_spec.rb
310
315
  - spec/reek/smells/class_variable_spec.rb
@@ -326,6 +331,7 @@ files:
326
331
  - spec/reek/smells/smell_repository_spec.rb
327
332
  - spec/reek/smells/smell_warning_spec.rb
328
333
  - spec/reek/smells/subclassed_from_core_class_spec.rb
334
+ - spec/reek/smells/too_many_constants_spec.rb
329
335
  - spec/reek/smells/too_many_instance_variables_spec.rb
330
336
  - spec/reek/smells/too_many_methods_spec.rb
331
337
  - spec/reek/smells/too_many_statements_spec.rb