reek 4.0.5 → 4.1.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: ed3e33db32a0d7b3de163a1ac8d9f042d0123e00
4
- data.tar.gz: 6316cc39b5ed860bbca0b4f47aca9f3547709eca
3
+ metadata.gz: fb4b9a8e91de50f96e7fc83d96e72f92af778dfc
4
+ data.tar.gz: f33a99fd21ffa8d82eb48d4e1cde69a0a01e0468
5
5
  SHA512:
6
- metadata.gz: 5f81127feca9c714604fdb3e80cdb1a83e071359e953f902de08295b14ebf6942f345613b1f6b9b90e259364aa194a1704038c8a8e1f03c4beabab51b7e0319e
7
- data.tar.gz: bbbbe01a9d539dd9b50a1c883fdbbe519d2258abedaaa16e59cb481a7576048be7743163f8bad3cabc867a9b4fb8353e07220e29b6cbae7f82f6cdf403fe3722
6
+ metadata.gz: 82da2cb804ea9c2fe37665072c0666350c7a22ba0e857f3629589ca7f91e87a55ee84ae198cd01e06d51e7e00d16b1aabde1b5d7f3cd59523435118e6692d954
7
+ data.tar.gz: 5177823be762c3c3354144a57abee5c1fea661d2ed9f947e63233a16de0aeae3dd8ac443bd3beb17cb7f3d22ac1d0d12f106a8e8eab568a934b878d8bf550cdd
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 4.1.0 (2016-06-23)
4
+
5
+ * (waldyr) Add SubclassedFromCoreClass detector
6
+
3
7
  ## 4.0.5 (2016-06-16)
4
8
 
5
9
  * (mvz) Handle new lambda syntax in NestedIterators
data/README.md CHANGED
@@ -440,11 +440,34 @@ Or just run the whole test suite:
440
440
  bundle exec rake
441
441
  ```
442
442
 
443
+ This will run the tests (RSpec and Cucumber), RuboCop and Reek itself.
444
+
445
+ You can also run:
446
+
447
+ ```
448
+ bundle exec rake ci
449
+ ```
450
+
451
+ This will run everything the default task runs and also [Ataru](https://github.com/CodePadawans/ataru) and [Mutant](https://github.com/mbj/mutant). This is the task that we run on Travis as well and that determines if your pull request is green or red.
452
+
453
+ Another useful Rake task is the `console` task. This will throw you right into an environment where you can play around with Reeks modules and classes:
454
+
455
+ ```
456
+ bundle exec rake console
457
+
458
+ [3] pry(main)> require_relative 'lib/reek/examiner'
459
+ => true
460
+ [4] pry(main)> Reek::Examiner
461
+ => Reek::Examiner
462
+ ```
463
+
464
+ Have a look at our [Developer API](docs/API.md) for more inspiration.
465
+
443
466
  From then on you should check out:
467
+
444
468
  * [How Reek works internally](docs/How-reek-works-internally.md)
445
469
  * [the contributing guide](CONTRIBUTING.md)
446
470
 
447
-
448
471
  If you don't feel like getting your hands dirty with code there are still other ways you can help us:
449
472
 
450
473
  * Open up an [issue](https://github.com/troessner/reek/issues) and report bugs
@@ -57,6 +57,9 @@ RepeatedConditional:
57
57
  enabled: true
58
58
  exclude: []
59
59
  max_ifs: 2
60
+ SubclassedFromCoreClass:
61
+ enabled: true
62
+ exclude: []
60
63
  TooManyInstanceVariables:
61
64
  enabled: true
62
65
  exclude: []
@@ -0,0 +1,79 @@
1
+ # Subclassed From Core Class
2
+
3
+ ## Introduction
4
+
5
+ Candidate classes for the _Subclassed From Core Class_ smell are classes which inherit from Core Classes like Hash, String and Array.
6
+
7
+ Inheriting from Core Classes means that you are going to have a bad time debugging (the explanation below is taken from [here](http://words.steveklabnik.com/beware-subclassing-ruby-core-classes)):
8
+
9
+ > What do you think this code should do?
10
+
11
+ ```Ruby
12
+ List = Class.new(Array)
13
+
14
+ l = List.new
15
+ l << 1
16
+ l << 2
17
+ puts l.reverse.class # => Array
18
+ ```
19
+
20
+ > If you said “it prints Array” you’d be right.
21
+ > Let’s talk about a more pernicious issue: Strings.
22
+
23
+ ```Ruby
24
+ class MyString < String
25
+ def to_s
26
+ "lol"
27
+ end
28
+ end
29
+
30
+ s = MyString.new
31
+ s.concat "Hey"
32
+
33
+ puts s # => Hey
34
+ puts s.to_s # => lol
35
+ puts "#{s}" # => Hey
36
+ ```
37
+
38
+ > That’s right! With Strings, Ruby doesn’t call #to_s: it puts the value in directly.
39
+ > Generally speaking, subclassing isn’t the right idea here.
40
+
41
+ ## Example
42
+
43
+ Given
44
+
45
+ ```Ruby
46
+ class Ary < Array
47
+ end
48
+
49
+ class Str < String
50
+ end
51
+ ```
52
+
53
+ Reek would report the _Subclassed From Core Class_ smell for both classes. Instead of subclassing them you want a data structure that uses one of these core classes internally, but isn’t exactly like one. For instance:
54
+
55
+ ```Ruby
56
+ require 'forwardable'
57
+
58
+ class List
59
+ extend Forwardable
60
+ def_delegators :@list, :<<, :length # and anything else
61
+
62
+ def initialize(list = [])
63
+ @list = list
64
+ end
65
+
66
+ def reverse
67
+ List.new(@list.reverse)
68
+ end
69
+ end
70
+
71
+ l = List.new
72
+ l << 1
73
+ l << 2
74
+ puts l.reverse.class # => List
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ _Subclassed From Core Class_ offers the [Basic Smell Options](Basic-Smell-Options.md).
@@ -59,7 +59,7 @@ Feature: Basic smell detection
59
59
  UncommunicativeVariableName: Inline::C#module_name has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
60
60
  UncommunicativeVariableName: Inline::C#parse_signature has the variable name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
61
61
  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 -- 117 warnings:
62
+ optparse.rb -- 119 warnings:
63
63
  Attribute: OptionParser#banner is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
64
64
  Attribute: OptionParser#default_argv is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
65
65
  Attribute: OptionParser#program_name is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
@@ -129,6 +129,8 @@ Feature: Basic smell detection
129
129
  RepeatedConditional: OptionParser tests default_pattern at least 7 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
130
130
  RepeatedConditional: OptionParser tests not_style at least 3 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
131
131
  RepeatedConditional: OptionParser tests s at least 7 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
132
+ SubclassedFromCoreClass: OptionParser::CompletingHash inherits from a core class (Hash) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
133
+ SubclassedFromCoreClass: OptionParser::OptionMap inherits from a core class (Hash) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
132
134
  TooManyInstanceVariables: OptionParser has at least 6 instance variables [https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md]
133
135
  TooManyInstanceVariables: OptionParser::Switch has at least 7 instance variables [https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md]
134
136
  TooManyMethods: OptionParser has at least 42 methods [https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md]
@@ -177,7 +179,7 @@ Feature: Basic smell detection
177
179
  UnusedParameters: OptionParser::Completion#convert has unused parameter 'opt' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
178
180
  UnusedParameters: OptionParser::Switch::NoArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
179
181
  UnusedParameters: OptionParser::Switch::OptionalArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
180
- redcloth.rb -- 102 warnings:
182
+ redcloth.rb -- 103 warnings:
181
183
  Attribute: RedCloth#filter_html is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
182
184
  Attribute: RedCloth#filter_styles is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
183
185
  Attribute: RedCloth#hard_breaks is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
@@ -226,6 +228,7 @@ Feature: Basic smell detection
226
228
  RepeatedConditional: RedCloth tests codepre.zero? at least 3 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
227
229
  RepeatedConditional: RedCloth tests href at least 3 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
228
230
  RepeatedConditional: RedCloth tests title at least 4 times [https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md]
231
+ SubclassedFromCoreClass: RedCloth inherits from a core class (String) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
229
232
  TooManyMethods: RedCloth has at least 44 methods [https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md]
230
233
  TooManyStatements: RedCloth#block_markdown_bq has approx 6 statements [https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md]
231
234
  TooManyStatements: RedCloth#block_textile_lists has approx 21 statements [https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md]
@@ -280,5 +283,5 @@ Feature: Basic smell detection
280
283
  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]
281
284
  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]
282
285
  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]
283
- 266 total warnings
286
+ 269 total warnings
284
287
  """
@@ -0,0 +1,14 @@
1
+ Feature: Smell - SubclassedFromCoreClass
2
+ Scenario: Reports smell
3
+ Given a file named "my_hash.rb" with:
4
+ """
5
+ # The MyHash class
6
+ class MyHash < Hash
7
+ end
8
+ """
9
+ When I run `reek my_hash.rb`
10
+ Then it reports:
11
+ """
12
+ my_hash.rb -- 1 warning:
13
+ [2]:SubclassedFromCoreClass: MyHash inherits from a core class (Hash) [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
14
+ """
@@ -5,7 +5,6 @@ module Reek
5
5
  # Utility methods for :const nodes.
6
6
  module ConstNode
7
7
  def name
8
- namespace = children.first
9
8
  if namespace
10
9
  "#{namespace.format_to_ruby}::#{simple_name}"
11
10
  else
@@ -16,6 +15,10 @@ module Reek
16
15
  def simple_name
17
16
  children.last
18
17
  end
18
+
19
+ def namespace
20
+ children.first
21
+ end
19
22
  end
20
23
  end
21
24
  end
@@ -45,6 +45,7 @@ module Reek
45
45
  # Utility methods for :class nodes.
46
46
  module ClassNode
47
47
  include ModuleNodeBase
48
+
48
49
  def superclass() children[1] end
49
50
  end
50
51
 
@@ -53,16 +54,71 @@ module Reek
53
54
  include ModuleNodeBase
54
55
 
55
56
  def defines_module?
56
- return false unless value
57
- call = case value.type
58
- when :block
59
- value.call
60
- when :send
61
- value
62
- end
57
+ call = constant_definition
63
58
  call && call.module_creation_call?
64
59
  end
65
60
 
61
+ # This is the right hand side of a constant
62
+ # assignment.
63
+ #
64
+ # This can be simple:
65
+ #
66
+ # Foo = 23
67
+ #
68
+ # s(:casgn, nil, :Foo,
69
+ # s(:int, 23))
70
+ #
71
+ # In this cases we do not care and return nil.
72
+ #
73
+ # Or complicated:
74
+ #
75
+ # Iterator = Struct.new :exp do ... end
76
+ #
77
+ # s(:casgn, nil, :Iterator,
78
+ # s(:block,
79
+ # s(:send,
80
+ # s(:const, nil, :Struct), :new,
81
+ # s(:sym, :exp)
82
+ # ),
83
+ # s(:args),
84
+ # ...
85
+ # )
86
+ # )
87
+ #
88
+ # In this cases we return the Struct.new part
89
+ #
90
+ def constant_definition
91
+ return nil unless value
92
+
93
+ case value.type
94
+ when :block
95
+ value.call
96
+ when :send
97
+ value
98
+ end
99
+ 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
+
66
122
  def name
67
123
  children[1].to_s
68
124
  end
@@ -662,3 +662,14 @@ UtilityFunction:
662
662
  remediation_points: 250_000
663
663
  content: |
664
664
  A _Utility Function_ is any instance method that has no dependency on the state of the instance.
665
+ SubclassedFromCoreClass:
666
+ remediation_points: 250_000
667
+ content: |
668
+ Subclassing core classes in Ruby can lead to unexpected side effects.
669
+
670
+ Knowing that Ruby has a core library, which is written in C,
671
+ and a standard library, which is written in Ruby,
672
+ if you do not know exactly how these core classes operate at the C level,
673
+ you are gonna have a bad time.
674
+
675
+ Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
@@ -14,6 +14,7 @@ require_relative 'smells/nested_iterators'
14
14
  require_relative 'smells/nil_check'
15
15
  require_relative 'smells/prima_donna_method'
16
16
  require_relative 'smells/repeated_conditional'
17
+ require_relative 'smells/subclassed_from_core_class'
17
18
  require_relative 'smells/too_many_instance_variables'
18
19
  require_relative 'smells/too_many_methods'
19
20
  require_relative 'smells/too_many_statements'
@@ -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
+ # Subclassing core classes in Ruby can lead to unexpected side effects.
9
+ # Knowing that Ruby has a core library, which is written in C, and a standard
10
+ # library, which is written in Ruby, if you do not know exactly how these core
11
+ # classes operate at the C level, you are gonna have a bad time.
12
+ #
13
+ # Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
14
+ #
15
+ class SubclassedFromCoreClass < SmellDetector
16
+ CORE_CLASSES = ['Array', 'Hash', 'String'].freeze
17
+
18
+ def self.contexts
19
+ [:class, :casgn]
20
+ end
21
+
22
+ # Checks +ctx+ for either expressions:
23
+ #
24
+ # Foo = Class.new(Bar)
25
+ #
26
+ # class Foo < Bar; end;
27
+ #
28
+ # @return [Array<SmellWarning>]
29
+ def inspect(ctx)
30
+ superclass = ctx.exp.superclass
31
+
32
+ return [] unless superclass
33
+
34
+ inspect_superclass(ctx, superclass.name)
35
+ end
36
+
37
+ private
38
+
39
+ def inspect_superclass(ctx, superclass_name)
40
+ return [] unless CORE_CLASSES.include?(superclass_name)
41
+
42
+ [build_smell_warning(ctx, superclass_name)]
43
+ end
44
+
45
+ def build_smell_warning(ctx, ancestor_name)
46
+ smell_attributes = {
47
+ context: ctx,
48
+ lines: [ctx.exp.line],
49
+ message: "inherits from a core class (#{ancestor_name})",
50
+ parameters: { ancestor: ancestor_name }
51
+ }
52
+
53
+ smell_warning(smell_attributes)
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.0.5'.freeze
10
+ STRING = '4.1.0'.freeze
11
11
  end
12
12
  end
@@ -240,6 +240,44 @@ RSpec.describe Reek::AST::SexpExtensions::LvarNode do
240
240
  end
241
241
  end
242
242
 
243
+ RSpec.describe Reek::AST::SexpExtensions::ConstNode do
244
+ describe '#name' do
245
+ it 'returns the fully qualified name' do
246
+ node = sexp(:const, sexp(:const, sexp(:cbase), :Parser), :AST)
247
+
248
+ expect(node.name).to eq "#{sexp(:const, sexp(:cbase), :Parser)}::AST"
249
+ end
250
+
251
+ it 'returns only the name in case of no namespace' do
252
+ node = sexp(:const, nil, :AST)
253
+
254
+ expect(node.name).to eq 'AST'
255
+ end
256
+ end
257
+
258
+ describe '#simple_name' do
259
+ it 'returns the name' do
260
+ node = sexp(:const, sexp(:const, nil, :Rake), :TaskLib)
261
+
262
+ expect(node.simple_name).to eq :TaskLib
263
+ end
264
+ end
265
+
266
+ describe '#namespace' do
267
+ it 'returns the namespace' do
268
+ node = sexp(:const, :Parser, :AST)
269
+
270
+ expect(node.namespace).to eq :Parser
271
+ end
272
+
273
+ it 'returns nil in case of no namespace' do
274
+ node = sexp(:const, nil, :AST)
275
+
276
+ expect(node.namespace).to be_nil
277
+ end
278
+ end
279
+ end
280
+
243
281
  RSpec.describe Reek::AST::SexpExtensions::SendNode do
244
282
  context 'with no parameters' do
245
283
  let(:node) { sexp(:send, nil, :hello) }
@@ -403,20 +441,78 @@ RSpec.describe Reek::AST::SexpExtensions::ModuleNode do
403
441
  end
404
442
 
405
443
  RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
406
- context 'with single assignment' do
407
- subject do
408
- sexp(:casgn, nil, :Foo)
444
+ context '#defines_module' do
445
+ context 'with single assignment' do
446
+ subject do
447
+ sexp(:casgn, nil, :Foo)
448
+ end
449
+
450
+ it 'does not define a module' do
451
+ expect(subject.defines_module?).to be_falsey
452
+ end
409
453
  end
410
454
 
411
- it 'does not define a module' do
412
- expect(subject.defines_module?).to be_falsey
455
+ context 'with implicit receiver to new' do
456
+ it 'does not define a module' do
457
+ exp = sexp(:casgn, nil, :Foo, sexp(:send, nil, :new))
458
+ expect(exp.defines_module?).to be_falsey
459
+ end
460
+ end
461
+
462
+ context 'with implicit receiver to new' do
463
+ it 'does not define a module' do
464
+ exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
465
+
466
+ expect(exp.defines_module?).to be_truthy
467
+ end
413
468
  end
414
469
  end
415
470
 
416
- context 'with implicit receiver to new' do
417
- it 'does not define a module' do
418
- exp = sexp(:casgn, nil, :Foo, sexp(:send, nil, :new))
419
- expect(exp.defines_module?).to be_falsey
471
+ context '#superclass' do
472
+ it 'returns the superclass from the class definition' do
473
+ exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
474
+
475
+ expect(exp.superclass).to eq sexp(:const, nil, :Bar)
476
+ end
477
+
478
+ it 'returns nil in case of no class definition' do
479
+ exp = Reek::Source::SourceCode.from('Foo = 23').syntax_tree
480
+
481
+ expect(exp.superclass).to be_nil
482
+ end
483
+
484
+ it 'returns nil in case of no superclass' do
485
+ exp = Reek::Source::SourceCode.from('Foo = Class.new').syntax_tree
486
+
487
+ expect(exp.superclass).to be_nil
488
+ end
489
+ end
490
+
491
+ context '#constant_definition' do
492
+ context 'simple constant assignment' do
493
+ it 'is not important' do
494
+ exp = sexp(:casgn, nil, :Foo, sexp(:int, 23))
495
+
496
+ expect(exp.constant_definition).to be_nil
497
+ end
498
+ end
499
+
500
+ context 'complicated constant assignments' do
501
+ it 'calls the block and returns what is being sent' do
502
+ exp = Reek::Source::SourceCode.from('Iterator = Struct.new(:exp) { def p; end; }').syntax_tree
503
+
504
+ struct_exp = sexp(:send, sexp(:const, nil, :Struct), :new, sexp(:sym, :exp))
505
+
506
+ expect(exp.constant_definition).to eq struct_exp
507
+ end
508
+
509
+ it 'returns the expression responsible for class creation' do
510
+ exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
511
+
512
+ class_exp = sexp(:send, sexp(:const, nil, :Class), :new, sexp(:const, nil, :Bar))
513
+
514
+ expect(exp.constant_definition).to eq class_exp
515
+ end
420
516
  end
421
517
  end
422
518
  end
@@ -0,0 +1,135 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/smells/subclassed_from_core_class'
3
+ require_relative 'smell_detector_shared'
4
+
5
+ RSpec.describe Reek::Smells::SubclassedFromCoreClass do
6
+ let(:detector) { described_class.new }
7
+
8
+ it_should_behave_like 'SmellDetector'
9
+
10
+ context 'report' do
11
+ context 'smell line' do
12
+ context 'single class' do
13
+ it 'should report the core class in the message' do
14
+ src = <<-EOS
15
+ class Dummy < Hash
16
+ end
17
+ EOS
18
+
19
+ expect(src).to reek_of(:SubclassedFromCoreClass, lines: [1])
20
+ end
21
+ end
22
+
23
+ context 'class inside a module' do
24
+ it 'should report the core class in the message' do
25
+ src = <<-EOS
26
+ module Namespace
27
+ class Dummy < Hash
28
+ end
29
+ end
30
+ EOS
31
+
32
+ expect(src).to reek_of(:SubclassedFromCoreClass, lines: [2])
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'smell message' do
38
+ context 'Array' do
39
+ it 'should report the core class in the message' do
40
+ src = <<-EOS
41
+ class Dummy < Array
42
+ end
43
+ EOS
44
+
45
+ expect(src).to reek_of(:SubclassedFromCoreClass, message: 'inherits from a core class (Array)')
46
+ end
47
+ end
48
+
49
+ context 'Hash' do
50
+ it 'should report the core class in the message' do
51
+ src = <<-EOS
52
+ class Dummy < Hash
53
+ end
54
+ EOS
55
+
56
+ expect(src).to reek_of(:SubclassedFromCoreClass, message: 'inherits from a core class (Hash)')
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ it 'does not inherit from a core class' do
63
+ src = <<-EOS
64
+ class Dummy
65
+ end
66
+ EOS
67
+
68
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
69
+ end
70
+
71
+ it 'should report if we inherit from a core class' do
72
+ src = <<-EOS
73
+ class Dummy < Array
74
+ end
75
+ EOS
76
+
77
+ expect(src).to reek_of(:SubclassedFromCoreClass, ancestor: 'Array', message: 'inherits from a core class (Array)')
78
+ end
79
+
80
+ it 'should not report on coincidental core class names in other namespaces' do
81
+ src = <<-EOS
82
+ class Dummy < My::Array
83
+ end
84
+ EOS
85
+ expect(src).to_not reek_of(:SubclassedFromCoreClass)
86
+ end
87
+
88
+ it 'should report if we inherit from a core class from within a namespaced class' do
89
+ src = <<-EOS
90
+ module Namespace
91
+ class Dummy < Array
92
+ end
93
+ end
94
+ EOS
95
+ expect(src).to reek_of(:SubclassedFromCoreClass, ancestor: 'Array')
96
+ end
97
+
98
+ it 'should report if we inherit from a core class using Class#new' do
99
+ src = 'Dummy = Class.new(Array)'
100
+ expect(src).to reek_of(:SubclassedFromCoreClass, ancestor: 'Array')
101
+ end
102
+
103
+ it 'should report if inner class inherit from a core class' do
104
+ src = <<-EOS
105
+ module Namespace
106
+ class Dummy
107
+ Dummiest = Class.new(Array)
108
+ end
109
+ end
110
+ EOS
111
+ expect(src).to reek_of(:SubclassedFromCoreClass, ancestor: 'Array')
112
+ end
113
+
114
+ it 'should not report on coincidental core class names in other namespaces' do
115
+ src = <<-EOS
116
+ module Namespace
117
+ class Dummy
118
+ Dummiest = Class.new(My::Array)
119
+ end
120
+ end
121
+ EOS
122
+ expect(src).to_not reek_of(:SubclassedFromCoreClass, ancestor: 'Array')
123
+ end
124
+
125
+ it 'should not report if inner class inherits from allowed classes' do
126
+ src = <<-EOS
127
+ module Namespace
128
+ class Dummy
129
+ Dummiest = Class.new(StandardError)
130
+ end
131
+ end
132
+ EOS
133
+ expect(src).to_not reek_of(:SubclassedFromCoreClass, ancestor: 'StandardError')
134
+ end
135
+ 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.0.5
4
+ version: 4.1.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-06-16 00:00:00.000000000 Z
14
+ date: 2016-06-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -118,6 +118,7 @@ files:
118
118
  - docs/Repeated-Conditional.md
119
119
  - docs/Simulated-Polymorphism.md
120
120
  - docs/Smell-Suppression.md
121
+ - docs/Subclassed-From-Core-Class.md
121
122
  - docs/Too-Many-Instance-Variables.md
122
123
  - docs/Too-Many-Methods.md
123
124
  - docs/Too-Many-Statements.md
@@ -156,6 +157,7 @@ files:
156
157
  - features/reports/reports.feature
157
158
  - features/reports/yaml.feature
158
159
  - features/samples.feature
160
+ - features/smells/subclassed_from_core_class.feature
159
161
  - features/step_definitions/.rubocop.yml
160
162
  - features/step_definitions/reek_steps.rb
161
163
  - features/step_definitions/sample_file_steps.rb
@@ -243,6 +245,7 @@ files:
243
245
  - lib/reek/smells/smell_detector.rb
244
246
  - lib/reek/smells/smell_repository.rb
245
247
  - lib/reek/smells/smell_warning.rb
248
+ - lib/reek/smells/subclassed_from_core_class.rb
246
249
  - lib/reek/smells/too_many_instance_variables.rb
247
250
  - lib/reek/smells/too_many_methods.rb
248
251
  - lib/reek/smells/too_many_statements.rb
@@ -322,6 +325,7 @@ files:
322
325
  - spec/reek/smells/smell_detector_spec.rb
323
326
  - spec/reek/smells/smell_repository_spec.rb
324
327
  - spec/reek/smells/smell_warning_spec.rb
328
+ - spec/reek/smells/subclassed_from_core_class_spec.rb
325
329
  - spec/reek/smells/too_many_instance_variables_spec.rb
326
330
  - spec/reek/smells/too_many_methods_spec.rb
327
331
  - spec/reek/smells/too_many_statements_spec.rb