reek 4.1.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
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