rubycritic 0.0.5 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -0
  3. data/README.md +72 -11
  4. data/bin/rubycritic +2 -0
  5. data/lib/rubycritic/adapters/complexity/flog.rb +22 -0
  6. data/lib/rubycritic/adapters/smell/flay.rb +68 -0
  7. data/lib/rubycritic/adapters/smell/flog.rb +62 -0
  8. data/lib/rubycritic/adapters/smell/reek.rb +44 -0
  9. data/lib/rubycritic/analysers/churn.rb +19 -0
  10. data/lib/rubycritic/analysers/config.reek +8 -0
  11. data/lib/rubycritic/analysers/flay.rb +15 -0
  12. data/lib/rubycritic/analysers/flog.rb +1 -2
  13. data/lib/rubycritic/analysers/stats.rb +31 -0
  14. data/lib/rubycritic/analysers_runner.rb +15 -14
  15. data/lib/rubycritic/cli.rb +35 -14
  16. data/lib/rubycritic/configuration.rb +24 -0
  17. data/lib/rubycritic/core/analysed_file.rb +50 -0
  18. data/lib/rubycritic/{location.rb → core/location.rb} +6 -2
  19. data/lib/rubycritic/core/rating.rb +22 -0
  20. data/lib/rubycritic/{smell.rb → core/smell.rb} +5 -12
  21. data/lib/rubycritic/files_initializer.rb +15 -0
  22. data/lib/rubycritic/orchestrator.rb +23 -0
  23. data/lib/rubycritic/report_generators/assets/javascripts/application.js +87 -8
  24. data/lib/rubycritic/report_generators/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
  25. data/lib/rubycritic/report_generators/assets/javascripts/jquery.floatThead-v1.2.7.js +754 -0
  26. data/lib/rubycritic/report_generators/assets/javascripts/jquery.scrollTo-1.4.11.js +186 -0
  27. data/lib/rubycritic/report_generators/assets/javascripts/jquery.tablesorter-2.0.js +1031 -0
  28. data/lib/rubycritic/report_generators/assets/javascripts/jquery.timeago-v1.4.1.js +214 -0
  29. data/lib/rubycritic/report_generators/assets/stylesheets/application.css +204 -1
  30. data/lib/rubycritic/report_generators/assets/stylesheets/prettify.custom_theme.css +1 -4
  31. data/lib/rubycritic/report_generators/base.rb +49 -0
  32. data/lib/rubycritic/report_generators/code_file.rb +38 -0
  33. data/lib/rubycritic/report_generators/code_index.rb +24 -0
  34. data/lib/rubycritic/report_generators/current_code_file.rb +17 -0
  35. data/lib/rubycritic/report_generators/line.rb +31 -0
  36. data/lib/rubycritic/report_generators/overview.rb +25 -0
  37. data/lib/rubycritic/report_generators/smells_index.rb +24 -0
  38. data/lib/rubycritic/report_generators/templates/code_file.html.erb +36 -0
  39. data/lib/rubycritic/report_generators/templates/code_index.html.erb +26 -0
  40. data/lib/rubycritic/report_generators/templates/layouts/application.html.erb +27 -16
  41. data/lib/rubycritic/report_generators/templates/overview.html.erb +5 -0
  42. data/lib/rubycritic/report_generators/templates/smells_index.html.erb +20 -0
  43. data/lib/rubycritic/report_generators/view_helpers.rb +28 -9
  44. data/lib/rubycritic/reporters/base.rb +24 -0
  45. data/lib/rubycritic/reporters/main.rb +52 -0
  46. data/lib/rubycritic/reporters/mini.rb +30 -0
  47. data/lib/rubycritic/revision_comparator.rb +21 -28
  48. data/lib/rubycritic/serializer.rb +32 -0
  49. data/lib/rubycritic/smells_status_setter.rb +7 -16
  50. data/lib/rubycritic/source_control_systems/base.rb +60 -0
  51. data/lib/rubycritic/source_control_systems/double.rb +19 -0
  52. data/lib/rubycritic/source_control_systems/git.rb +47 -37
  53. data/lib/rubycritic/source_locator.rb +8 -12
  54. data/lib/rubycritic/turbulence.rb +17 -0
  55. data/lib/rubycritic/version.rb +1 -1
  56. data/lib/rubycritic.rb +2 -27
  57. data/rubycritic.gemspec +3 -0
  58. data/test/lib/rubycritic/adapters/complexity/flog_test.rb +18 -0
  59. data/test/lib/rubycritic/adapters/smell/flay_test.rb +34 -0
  60. data/test/lib/rubycritic/adapters/smell/flog_test.rb +26 -0
  61. data/test/lib/rubycritic/adapters/smell/reek_test.rb +35 -0
  62. data/test/lib/rubycritic/analysers/churn_test.rb +38 -0
  63. data/test/lib/rubycritic/configuration_test.rb +17 -0
  64. data/test/lib/rubycritic/core/analysed_file_test.rb +71 -0
  65. data/test/lib/rubycritic/{location_test.rb → core/location_test.rb} +8 -4
  66. data/test/lib/rubycritic/core/smell_test.rb +73 -0
  67. data/test/lib/rubycritic/{smells_array_test.rb → core/smells_array_test.rb} +1 -1
  68. data/test/lib/rubycritic/smells_status_setter_test.rb +5 -5
  69. data/test/lib/rubycritic/source_control_systems/source_control_system_test.rb +5 -11
  70. data/test/lib/rubycritic/source_locator_test.rb +26 -17
  71. data/test/lib/rubycritic/turbulence_test.rb +17 -0
  72. data/test/lib/rubycritic/version_test.rb +1 -0
  73. data/test/samples/flay/smelly.rb +17 -0
  74. data/test/samples/flog/complex.rb +11 -0
  75. data/test/samples/flog/smelly.rb +7 -2
  76. data/test/samples/reek/not_smelly.rb +31 -3
  77. data/test/test_helper.rb +1 -0
  78. metadata +97 -32
  79. data/SPEC.md +0 -58
  80. data/lib/rubycritic/report_generators/base_generator.rb +0 -42
  81. data/lib/rubycritic/report_generators/file_generator.rb +0 -42
  82. data/lib/rubycritic/report_generators/index_generator.rb +0 -28
  83. data/lib/rubycritic/report_generators/line_generator.rb +0 -27
  84. data/lib/rubycritic/report_generators/reporter.rb +0 -45
  85. data/lib/rubycritic/report_generators/templates/file.html.erb +0 -3
  86. data/lib/rubycritic/report_generators/templates/index.html.erb +0 -14
  87. data/lib/rubycritic/smell_adapters/flog.rb +0 -41
  88. data/lib/rubycritic/smell_adapters/reek.rb +0 -35
  89. data/lib/rubycritic/smells_aggregator.rb +0 -29
  90. data/lib/rubycritic/smelly_pathnames_serializer.rb +0 -34
  91. data/lib/rubycritic/source_control_systems/source_control_system.rb +0 -42
  92. data/test/lib/rubycritic/metric_adapters/flog_adapter_test.rb +0 -25
  93. data/test/lib/rubycritic/metric_adapters/reek_adapter_test.rb +0 -34
  94. data/test/lib/rubycritic/smell_test.rb +0 -71
  95. data/test/lib/rubycritic/smells_aggregator_test.rb +0 -47
  96. /data/lib/rubycritic/report_generators/assets/javascripts/{prettify.js → prettify-4-Mar-2013.js} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e3de50932159be5b26bc5a2cf4afe9eeec17e8dc
4
- data.tar.gz: c7f9620aae8d000fe5f674844158d48296c706c3
3
+ metadata.gz: 5a43f9d486a902fa7eda73e1df3e492cdb6ab085
4
+ data.tar.gz: a29c16803a4e83f4b559065630b024b76b45a69c
5
5
  SHA512:
6
- metadata.gz: 28d945ef0e7036d298e1c8ff54b909c7c4c67b777e6ccfad20c548905ccb515fe849c351866b38bbba6765dfd31af610d41ae6c6cede57e469db3b54ee4f44bb
7
- data.tar.gz: 509b8ffc4b0cdcb85cccacbbe242a5a5bc5f0e06e2975d12ac3827d9f168c0426793bcde26d4c48cf953e49ff41ba0039dce216428e8a94f009ba0e964f21522
6
+ metadata.gz: d0720b07a319110c0cb31dc6c5ca1ec69fef4eb60d7759069ba1e1e869a16f537d1e7d49553aa5ca544ccc91993c481f16f6fe7da2abe8917358499f952e2436
7
+ data.tar.gz: 05e48772690e0230b7e8d5788751204e754ccaaf0c4e40d72a4e5520fc0bad81ddc3f87a09f141d9a494372de94ae9c1e6df5adf808d2b765de22a3ae23294d1
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.1
7
+ - rbx-2
8
+ - jruby
9
+
10
+ script: "bundle exec rake"
data/README.md CHANGED
@@ -1,33 +1,73 @@
1
- Ruby Critic
1
+ RubyCritic
2
2
  ===========
3
3
 
4
- Ruby Critic is a tool that detects and reports smells in Ruby code.
4
+ [![Gem Version](https://badge.fury.io/rb/rubycritic.svg)](http://badge.fury.io/rb/rubycritic)
5
+ [![Build Status](https://travis-ci.org/whitesmith/rubycritic.svg?branch=master)](https://travis-ci.org/whitesmith/rubycritic)
6
+ [![Code Climate](http://img.shields.io/codeclimate/github/whitesmith/rubycritic.svg)](https://codeclimate.com/github/whitesmith/rubycritic)
7
+
8
+ RubyCritic is a gem that wraps around static analysis gems such as [Reek][1]
9
+ and [Flay][2] to provide a quality report of your Ruby code.
10
+
11
+ This gem provides features such as:
12
+
13
+ 1. An overview of your project:
14
+
15
+ ![RubyCritic overview screenshot](http://i.imgur.com/OrOflfj.png)
16
+
17
+ 2. An index of the project files with their respective number of smells:
18
+
19
+ ![RubyCritic code index screenshot](http://i.imgur.com/0ETNrX7.png)
20
+
21
+ 3. An index of the smells detected:
22
+
23
+ ![RubyCritic smells index screenshot](http://i.imgur.com/5CpPt9v.png)
24
+
25
+ 4. Finally, when analysing code like the following:
26
+
27
+ ```ruby
28
+ class Dirty
29
+ def awful(x, y)
30
+ if y
31
+ @screen = widgets.map {|w| w.each {|key| key += 3}}
32
+ end
33
+ end
34
+ end
35
+ ```
36
+
37
+ It basically turns something like this:
38
+
39
+ ![Reek output screenshot](http://i.imgur.com/tCgZX9I.png)
40
+
41
+ Into something like this:
42
+
43
+ ![RubyCritic file code screenshot](http://i.imgur.com/KLVrhMm.png)
5
44
 
6
45
  Installation
7
46
  ------------
8
47
 
9
- Add this line to your application's Gemfile:
48
+ RubyCritic can be installed with the following command:
10
49
 
11
- ```ruby
12
- gem "rubycritic"
50
+ ```bash
51
+ $ gem install rubycritic
13
52
  ```
14
53
 
15
- And then execute:
54
+ If you'd rather install RubyCritic using Bundler, add this line to your
55
+ application's Gemfile:
16
56
 
17
- ```bash
18
- $ bundle
57
+ ```ruby
58
+ gem "rubycritic", :require => false
19
59
  ```
20
60
 
21
- Or just install it yourself:
61
+ And then execute:
22
62
 
23
63
  ```bash
24
- $ gem install rubycritic
64
+ $ bundle
25
65
  ```
26
66
 
27
67
  Usage
28
68
  -----
29
69
 
30
- Running `rubycritic` with no arguments will check all Ruby source files in the
70
+ Running `rubycritic` with no arguments will analyse all the Ruby files in the
31
71
  current directory:
32
72
 
33
73
  ```bash
@@ -39,3 +79,24 @@ Alternatively you can pass `rubycritic` a list of files and directories to check
39
79
  ```bash
40
80
  $ rubycritic app lib/foo.rb
41
81
  ```
82
+
83
+ By default, RubyCritic generates its report in `tmp/rubycritic`. You can customize the output directory using the `path` option. You can use relative paths:
84
+
85
+ ```bash
86
+ rubycritic --path tmp/custom_dir # outputs to tmp/custom_dir
87
+ ```
88
+
89
+ Or full paths:
90
+
91
+ ```bash
92
+ rubycritic --path $HOME/tmp/custom_dir # outputs to $HOME/tmp/custom_dir
93
+ ```
94
+
95
+ For a full list of the command-line options run:
96
+
97
+ ```bash
98
+ $ rubycritic --help
99
+ ```
100
+
101
+ [1]: https://github.com/troessner/reek
102
+ [2]: https://github.com/seattlerb/flay
data/bin/rubycritic CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubycritic/cli"
4
+
5
+ exit Rubycritic::Cli.new(ARGV).execute
@@ -0,0 +1,22 @@
1
+ require "rubycritic/analysers/flog"
2
+
3
+ module Rubycritic
4
+ module ComplexityAdapter
5
+
6
+ class Flog
7
+ def initialize(analysed_files)
8
+ @flog = Analyser::Flog.new
9
+ @analysed_files = analysed_files
10
+ end
11
+
12
+ def complexity
13
+ @analysed_files.each do |analysed_file|
14
+ @flog.reset
15
+ @flog.flog(analysed_file.path)
16
+ analysed_file.complexity = @flog.total_score.round
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ require "rubycritic/analysers/flay"
2
+ require "rubycritic/core/smell"
3
+
4
+ module Rubycritic
5
+ module SmellAdapter
6
+
7
+ class Flay
8
+ def initialize(analysed_files)
9
+ @analysed_files = paths_to_analysed_files(analysed_files)
10
+ @flay = Analyser::Flay.new(@analysed_files.keys)
11
+ end
12
+
13
+ def smells
14
+ @flay.hashes.each do |structural_hash, nodes|
15
+ smell = create_smell(structural_hash, nodes)
16
+ nodes.map(&:file).uniq.each do |file|
17
+ @analysed_files[file].smells << smell
18
+ end
19
+
20
+ nodes.each do |node|
21
+ @analysed_files[node.file].duplication += node.mass
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def paths_to_analysed_files(analysed_files)
29
+ paths = {}
30
+ analysed_files.each do |analysed_file|
31
+ paths[analysed_file.path] = analysed_file
32
+ end
33
+ paths
34
+ end
35
+
36
+ def create_smell(structural_hash, nodes)
37
+ mass = @flay.masses[structural_hash]
38
+ Smell.new(
39
+ :locations => smell_locations(nodes),
40
+ :context => similarity(structural_hash),
41
+ :message => "found in #{nodes.size} nodes",
42
+ :score => mass,
43
+ :type => "DuplicateCode",
44
+ :cost => cost(mass)
45
+ )
46
+ end
47
+
48
+ def smell_locations(nodes)
49
+ nodes.map do |node|
50
+ Location.new(node.file, node.line)
51
+ end.sort
52
+ end
53
+
54
+ def similarity(structural_hash)
55
+ if @flay.identical[structural_hash]
56
+ "Identical code"
57
+ else
58
+ "Similar code"
59
+ end
60
+ end
61
+
62
+ def cost(mass)
63
+ mass / 25
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,62 @@
1
+ require "rubycritic/analysers/flog"
2
+ require "rubycritic/core/smell"
3
+
4
+ module Rubycritic
5
+ module SmellAdapter
6
+
7
+ class Flog
8
+ HIGH_COMPLEXITY_SCORE_THRESHOLD = 25
9
+ VERY_HIGH_COMPLEXITY_SCORE_THRESHOLD = 60
10
+
11
+ def initialize(analysed_files)
12
+ @flog = Analyser::Flog.new
13
+ @analysed_files = analysed_files
14
+ end
15
+
16
+ def smells
17
+ @analysed_files.each do |analysed_file|
18
+ add_smells_to(analysed_file)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def add_smells_to(analysed_file)
25
+ @flog.reset
26
+ @flog.flog(analysed_file.path)
27
+ @flog.each_by_score do |class_method, original_score|
28
+ score = original_score.round
29
+ if score >= HIGH_COMPLEXITY_SCORE_THRESHOLD
30
+ analysed_file.smells << create_smell(class_method, score)
31
+ end
32
+ end
33
+ end
34
+
35
+ def create_smell(context, score)
36
+ Smell.new(
37
+ :locations => smell_locations(context),
38
+ :context => context,
39
+ :message => "has a flog score of #{score}",
40
+ :score => score,
41
+ :type => type(score),
42
+ :cost => 0
43
+ )
44
+ end
45
+
46
+ def smell_locations(context)
47
+ line = @flog.method_locations[context]
48
+ file_path, file_line = line.split(":")
49
+ [Location.new(file_path, file_line)]
50
+ end
51
+
52
+ def type(score)
53
+ if score >= VERY_HIGH_COMPLEXITY_SCORE_THRESHOLD
54
+ "VeryHighComplexity"
55
+ else
56
+ "HighComplexity"
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,44 @@
1
+ require "rubycritic/analysers/reek"
2
+ require "rubycritic/core/smell"
3
+
4
+ module Rubycritic
5
+ module SmellAdapter
6
+
7
+ class Reek
8
+ def initialize(analysed_files)
9
+ @analysed_files = analysed_files
10
+ end
11
+
12
+ def smells
13
+ @analysed_files.each do |analysed_file|
14
+ add_smells_to(analysed_file)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def add_smells_to(analysed_file)
21
+ Analyser::Reek.new(analysed_file.path).smells.each do |smell|
22
+ analysed_file.smells << create_smell(smell)
23
+ end
24
+ end
25
+
26
+ def create_smell(smell)
27
+ Smell.new(
28
+ :locations => smell_locations(smell.source, smell.lines),
29
+ :context => smell.context,
30
+ :message => smell.message,
31
+ :type => smell.subclass,
32
+ :cost => 0
33
+ )
34
+ end
35
+
36
+ def smell_locations(file_path, file_lines)
37
+ file_lines.uniq.map do |file_line|
38
+ Location.new(file_path, file_line)
39
+ end.sort
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ module Rubycritic
2
+ module Analyser
3
+
4
+ class Churn
5
+ def initialize(analysed_files, source_control_system)
6
+ @analysed_files = analysed_files
7
+ @source_control_system = source_control_system
8
+ end
9
+
10
+ def churn
11
+ @analysed_files.each do |analysed_file|
12
+ analysed_file.churn = @source_control_system.revisions_count(analysed_file.path)
13
+ analysed_file.committed_at = @source_control_system.date_of_last_commit(analysed_file.path)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -1,8 +1,14 @@
1
1
  ---
2
2
  Attribute:
3
3
  enabled: false
4
+ DuplicateMethodCall:
5
+ max_calls: 2
4
6
  IrresponsibleModule:
5
7
  enabled: false
8
+ NestedIterators:
9
+ max_allowed_nesting: 2
10
+ TooManyStatements:
11
+ enabled: false
6
12
  UncommunicativeMethodName:
7
13
  reject:
8
14
  - !ruby/regexp /^[a-z]$/
@@ -16,3 +22,5 @@ UncommunicativeVariableName:
16
22
  reject:
17
23
  - !ruby/regexp /^.$/
18
24
  - !ruby/regexp /[0-9]$/
25
+ UtilityFunction:
26
+ enabled: false
@@ -0,0 +1,15 @@
1
+ require "flay"
2
+
3
+ module Rubycritic
4
+ module Analyser
5
+
6
+ class Flay < ::Flay
7
+ def initialize(paths)
8
+ super()
9
+ process(*paths)
10
+ analyze
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -10,9 +10,8 @@ module Rubycritic
10
10
  :methods => true
11
11
  }
12
12
 
13
- def initialize(paths)
13
+ def initialize
14
14
  super(DEFAULT_OPTIONS)
15
- flog(*paths)
16
15
  end
17
16
  end
18
17
 
@@ -0,0 +1,31 @@
1
+ require "code_analyzer"
2
+
3
+ module Rubycritic
4
+ module Analyser
5
+
6
+ class Stats
7
+ def initialize(analysed_files)
8
+ @analysed_files = analysed_files
9
+ end
10
+
11
+ def stats
12
+ @analysed_files.each do |analysed_file|
13
+ analysed_file.methods_count = methods_count(analysed_file.path)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def methods_count(path)
20
+ content = File.read(path)
21
+ node = parse_content(content)
22
+ node.grep_nodes_count(sexp_type: [:def, :defs])
23
+ end
24
+
25
+ def parse_content(content)
26
+ Sexp.from_array(Ripper::SexpBuilder.new(content).parse)[1]
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -1,26 +1,27 @@
1
- require "rubycritic/analysers/reek"
2
- require "rubycritic/smell_adapters/reek"
1
+ require "rubycritic/adapters/smell/flay"
2
+ require "rubycritic/adapters/smell/flog"
3
+ require "rubycritic/adapters/smell/reek"
4
+ require "rubycritic/adapters/complexity/flog"
5
+ require "rubycritic/analysers/churn"
6
+ require "rubycritic/analysers/stats"
3
7
 
4
8
  module Rubycritic
5
9
 
6
10
  class AnalysersRunner
7
- ANALYSERS = ["Reek"]
11
+ SMELL_ANALYSERS = [SmellAdapter::Flay, SmellAdapter::Flog, SmellAdapter::Reek]
8
12
 
9
- def initialize(paths)
10
- @paths = paths
13
+ def initialize(analysed_files, source_control_system)
14
+ @analysed_files = analysed_files
15
+ @source_control_system = source_control_system
11
16
  end
12
17
 
13
18
  def run
14
- smell_adapters
15
- end
16
-
17
- private
18
-
19
- def smell_adapters
20
- ANALYSERS.map do |analyser_name|
21
- analyser = Object.const_get("Rubycritic::Analyser::#{analyser_name}").new(@paths)
22
- Object.const_get("Rubycritic::SmellAdapter::#{analyser_name}").new(analyser)
19
+ SMELL_ANALYSERS.map do |analyser|
20
+ analyser.new(@analysed_files).smells
23
21
  end
22
+ ComplexityAdapter::Flog.new(@analysed_files).complexity
23
+ Analyser::Churn.new(@analysed_files, @source_control_system).churn
24
+ Analyser::Stats.new(@analysed_files).stats
24
25
  end
25
26
  end
26
27
 
@@ -1,25 +1,46 @@
1
1
  require "optparse"
2
2
  require "rubycritic"
3
+ require "rubycritic/reporters/main"
3
4
 
4
5
  module Rubycritic
5
6
 
6
- OptionParser.new do |opts|
7
- opts.banner = "Usage: rubycritic [options] [paths]"
7
+ class Cli
8
+ STATUS_SUCCESS = 0
8
9
 
9
- opts.on_tail("-v", "--version", "Show this version") do
10
- require "rubycritic/version"
11
- puts VERSION
12
- exit 0
10
+ def initialize(argv)
11
+ @argv = argv
12
+ @argv << "." if @argv.empty?
13
+ @main_command = true
13
14
  end
14
15
 
15
- opts.on_tail("-h", "--help", "Show this message") do
16
- puts opts
17
- exit 0
18
- end
19
- end.parse!
16
+ def execute
17
+ OptionParser.new do |opts|
18
+ opts.banner = "Usage: rubycritic [options] [paths]"
19
+
20
+ opts.on("-p", "--path [PATH]", "Set path where report will be saved (tmp/rubycritic by default)") do |path|
21
+ ::Rubycritic.configuration.root = path
22
+ end
23
+
24
+ opts.on_tail("-v", "--version", "Show gem's version") do
25
+ require "rubycritic/version"
26
+ puts "RubyCritic #{VERSION}"
27
+ @main_command = false
28
+ end
20
29
 
21
- ARGV << "." if ARGV.empty?
22
- puts "New critique at #{Rubycritic.new.critique(ARGV)}"
23
- exit 0
30
+ opts.on_tail("-h", "--help", "Show this message") do
31
+ puts opts
32
+ @main_command = false
33
+ end
34
+ end.parse!(@argv)
35
+
36
+ if @main_command
37
+ analysed_files = Orchestrator.new.critique(@argv)
38
+ report_location = Reporter::Main.new(analysed_files).generate_report
39
+ puts "New critique at #{report_location}"
40
+ end
41
+
42
+ STATUS_SUCCESS
43
+ end
44
+ end
24
45
 
25
46
  end
@@ -0,0 +1,24 @@
1
+ require "pathname"
2
+
3
+ module Rubycritic
4
+ def self.configuration
5
+ @configuration ||= Configuration.new
6
+ end
7
+
8
+ class Configuration
9
+ attr_reader :root
10
+
11
+ def initialize
12
+ self.root = "tmp/rubycritic"
13
+ end
14
+
15
+ def root=(path)
16
+ @root =
17
+ if Pathname(path).relative?
18
+ File.expand_path(path)
19
+ else
20
+ path
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ require "virtus"
2
+ require "rubycritic/core/rating"
3
+
4
+ module Rubycritic
5
+
6
+ class AnalysedFile
7
+ include Virtus.model
8
+
9
+ attribute :pathname
10
+ attribute :smells
11
+ attribute :churn
12
+ attribute :committed_at
13
+ attribute :complexity
14
+ attribute :duplication
15
+ attribute :methods_count
16
+
17
+ def name
18
+ @name ||= pathname.basename.sub_ext("").to_s
19
+ end
20
+
21
+ def path
22
+ @path ||= pathname.to_s
23
+ end
24
+
25
+ def cost
26
+ @cost ||= smells.map(&:cost).inject(0, :+) + (complexity / 25)
27
+ end
28
+
29
+ def rating
30
+ @rating ||= Rating.from_cost(cost)
31
+ end
32
+
33
+ def complexity_per_method
34
+ if methods_count == 0
35
+ "N/A"
36
+ else
37
+ complexity.fdiv(methods_count).round(1)
38
+ end
39
+ end
40
+
41
+ def has_smells?
42
+ !smells.empty?
43
+ end
44
+
45
+ def smells_at_location(location)
46
+ smells.select { |smell| smell.at_location?(location) }
47
+ end
48
+ end
49
+
50
+ end
@@ -7,7 +7,11 @@ module Rubycritic
7
7
 
8
8
  def initialize(path, line)
9
9
  @pathname = Pathname.new(path)
10
- @line = line
10
+ @line = line.to_i
11
+ end
12
+
13
+ def file_name
14
+ @pathname.basename.sub_ext("").to_s
11
15
  end
12
16
 
13
17
  def to_s
@@ -15,7 +19,7 @@ module Rubycritic
15
19
  end
16
20
 
17
21
  def ==(other)
18
- self.class == other.class && state == other.state
22
+ state == other.state
19
23
  end
20
24
 
21
25
  def <=>(other)
@@ -0,0 +1,22 @@
1
+ module Rubycritic
2
+
3
+ class Rating
4
+ def self.from_cost(cost)
5
+ if cost <= 2 then new("A")
6
+ elsif cost <= 4 then new("B")
7
+ elsif cost <= 8 then new("C")
8
+ elsif cost <= 16 then new("D")
9
+ else new("F")
10
+ end
11
+ end
12
+
13
+ def initialize(letter)
14
+ @letter = letter
15
+ end
16
+
17
+ def to_s
18
+ @letter
19
+ end
20
+ end
21
+
22
+ end