reek 4.0.5 → 4.1.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: 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