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.
- checksums.yaml +4 -4
- data/.travis.yml +10 -0
- data/README.md +72 -11
- data/bin/rubycritic +2 -0
- data/lib/rubycritic/adapters/complexity/flog.rb +22 -0
- data/lib/rubycritic/adapters/smell/flay.rb +68 -0
- data/lib/rubycritic/adapters/smell/flog.rb +62 -0
- data/lib/rubycritic/adapters/smell/reek.rb +44 -0
- data/lib/rubycritic/analysers/churn.rb +19 -0
- data/lib/rubycritic/analysers/config.reek +8 -0
- data/lib/rubycritic/analysers/flay.rb +15 -0
- data/lib/rubycritic/analysers/flog.rb +1 -2
- data/lib/rubycritic/analysers/stats.rb +31 -0
- data/lib/rubycritic/analysers_runner.rb +15 -14
- data/lib/rubycritic/cli.rb +35 -14
- data/lib/rubycritic/configuration.rb +24 -0
- data/lib/rubycritic/core/analysed_file.rb +50 -0
- data/lib/rubycritic/{location.rb → core/location.rb} +6 -2
- data/lib/rubycritic/core/rating.rb +22 -0
- data/lib/rubycritic/{smell.rb → core/smell.rb} +5 -12
- data/lib/rubycritic/files_initializer.rb +15 -0
- data/lib/rubycritic/orchestrator.rb +23 -0
- data/lib/rubycritic/report_generators/assets/javascripts/application.js +87 -8
- data/lib/rubycritic/report_generators/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
- data/lib/rubycritic/report_generators/assets/javascripts/jquery.floatThead-v1.2.7.js +754 -0
- data/lib/rubycritic/report_generators/assets/javascripts/jquery.scrollTo-1.4.11.js +186 -0
- data/lib/rubycritic/report_generators/assets/javascripts/jquery.tablesorter-2.0.js +1031 -0
- data/lib/rubycritic/report_generators/assets/javascripts/jquery.timeago-v1.4.1.js +214 -0
- data/lib/rubycritic/report_generators/assets/stylesheets/application.css +204 -1
- data/lib/rubycritic/report_generators/assets/stylesheets/prettify.custom_theme.css +1 -4
- data/lib/rubycritic/report_generators/base.rb +49 -0
- data/lib/rubycritic/report_generators/code_file.rb +38 -0
- data/lib/rubycritic/report_generators/code_index.rb +24 -0
- data/lib/rubycritic/report_generators/current_code_file.rb +17 -0
- data/lib/rubycritic/report_generators/line.rb +31 -0
- data/lib/rubycritic/report_generators/overview.rb +25 -0
- data/lib/rubycritic/report_generators/smells_index.rb +24 -0
- data/lib/rubycritic/report_generators/templates/code_file.html.erb +36 -0
- data/lib/rubycritic/report_generators/templates/code_index.html.erb +26 -0
- data/lib/rubycritic/report_generators/templates/layouts/application.html.erb +27 -16
- data/lib/rubycritic/report_generators/templates/overview.html.erb +5 -0
- data/lib/rubycritic/report_generators/templates/smells_index.html.erb +20 -0
- data/lib/rubycritic/report_generators/view_helpers.rb +28 -9
- data/lib/rubycritic/reporters/base.rb +24 -0
- data/lib/rubycritic/reporters/main.rb +52 -0
- data/lib/rubycritic/reporters/mini.rb +30 -0
- data/lib/rubycritic/revision_comparator.rb +21 -28
- data/lib/rubycritic/serializer.rb +32 -0
- data/lib/rubycritic/smells_status_setter.rb +7 -16
- data/lib/rubycritic/source_control_systems/base.rb +60 -0
- data/lib/rubycritic/source_control_systems/double.rb +19 -0
- data/lib/rubycritic/source_control_systems/git.rb +47 -37
- data/lib/rubycritic/source_locator.rb +8 -12
- data/lib/rubycritic/turbulence.rb +17 -0
- data/lib/rubycritic/version.rb +1 -1
- data/lib/rubycritic.rb +2 -27
- data/rubycritic.gemspec +3 -0
- data/test/lib/rubycritic/adapters/complexity/flog_test.rb +18 -0
- data/test/lib/rubycritic/adapters/smell/flay_test.rb +34 -0
- data/test/lib/rubycritic/adapters/smell/flog_test.rb +26 -0
- data/test/lib/rubycritic/adapters/smell/reek_test.rb +35 -0
- data/test/lib/rubycritic/analysers/churn_test.rb +38 -0
- data/test/lib/rubycritic/configuration_test.rb +17 -0
- data/test/lib/rubycritic/core/analysed_file_test.rb +71 -0
- data/test/lib/rubycritic/{location_test.rb → core/location_test.rb} +8 -4
- data/test/lib/rubycritic/core/smell_test.rb +73 -0
- data/test/lib/rubycritic/{smells_array_test.rb → core/smells_array_test.rb} +1 -1
- data/test/lib/rubycritic/smells_status_setter_test.rb +5 -5
- data/test/lib/rubycritic/source_control_systems/source_control_system_test.rb +5 -11
- data/test/lib/rubycritic/source_locator_test.rb +26 -17
- data/test/lib/rubycritic/turbulence_test.rb +17 -0
- data/test/lib/rubycritic/version_test.rb +1 -0
- data/test/samples/flay/smelly.rb +17 -0
- data/test/samples/flog/complex.rb +11 -0
- data/test/samples/flog/smelly.rb +7 -2
- data/test/samples/reek/not_smelly.rb +31 -3
- data/test/test_helper.rb +1 -0
- metadata +97 -32
- data/SPEC.md +0 -58
- data/lib/rubycritic/report_generators/base_generator.rb +0 -42
- data/lib/rubycritic/report_generators/file_generator.rb +0 -42
- data/lib/rubycritic/report_generators/index_generator.rb +0 -28
- data/lib/rubycritic/report_generators/line_generator.rb +0 -27
- data/lib/rubycritic/report_generators/reporter.rb +0 -45
- data/lib/rubycritic/report_generators/templates/file.html.erb +0 -3
- data/lib/rubycritic/report_generators/templates/index.html.erb +0 -14
- data/lib/rubycritic/smell_adapters/flog.rb +0 -41
- data/lib/rubycritic/smell_adapters/reek.rb +0 -35
- data/lib/rubycritic/smells_aggregator.rb +0 -29
- data/lib/rubycritic/smelly_pathnames_serializer.rb +0 -34
- data/lib/rubycritic/source_control_systems/source_control_system.rb +0 -42
- data/test/lib/rubycritic/metric_adapters/flog_adapter_test.rb +0 -25
- data/test/lib/rubycritic/metric_adapters/reek_adapter_test.rb +0 -34
- data/test/lib/rubycritic/smell_test.rb +0 -71
- data/test/lib/rubycritic/smells_aggregator_test.rb +0 -47
- /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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a43f9d486a902fa7eda73e1df3e492cdb6ab085
|
|
4
|
+
data.tar.gz: a29c16803a4e83f4b559065630b024b76b45a69c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0720b07a319110c0cb31dc6c5ca1ec69fef4eb60d7759069ba1e1e869a16f537d1e7d49553aa5ca544ccc91993c481f16f6fe7da2abe8917358499f952e2436
|
|
7
|
+
data.tar.gz: 05e48772690e0230b7e8d5788751204e754ccaaf0c4e40d72a4e5520fc0bad81ddc3f87a09f141d9a494372de94ae9c1e6df5adf808d2b765de22a3ae23294d1
|
data/.travis.yml
ADDED
data/README.md
CHANGED
|
@@ -1,33 +1,73 @@
|
|
|
1
|
-
|
|
1
|
+
RubyCritic
|
|
2
2
|
===========
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
[](http://badge.fury.io/rb/rubycritic)
|
|
5
|
+
[](https://travis-ci.org/whitesmith/rubycritic)
|
|
6
|
+
[](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
|
+

|
|
16
|
+
|
|
17
|
+
2. An index of the project files with their respective number of smells:
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
3. An index of the smells detected:
|
|
22
|
+
|
|
23
|
+

|
|
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
|
+

|
|
40
|
+
|
|
41
|
+
Into something like this:
|
|
42
|
+
|
|
43
|
+

|
|
5
44
|
|
|
6
45
|
Installation
|
|
7
46
|
------------
|
|
8
47
|
|
|
9
|
-
|
|
48
|
+
RubyCritic can be installed with the following command:
|
|
10
49
|
|
|
11
|
-
```
|
|
12
|
-
gem
|
|
50
|
+
```bash
|
|
51
|
+
$ gem install rubycritic
|
|
13
52
|
```
|
|
14
53
|
|
|
15
|
-
|
|
54
|
+
If you'd rather install RubyCritic using Bundler, add this line to your
|
|
55
|
+
application's Gemfile:
|
|
16
56
|
|
|
17
|
-
```
|
|
18
|
-
|
|
57
|
+
```ruby
|
|
58
|
+
gem "rubycritic", :require => false
|
|
19
59
|
```
|
|
20
60
|
|
|
21
|
-
|
|
61
|
+
And then execute:
|
|
22
62
|
|
|
23
63
|
```bash
|
|
24
|
-
$
|
|
64
|
+
$ bundle
|
|
25
65
|
```
|
|
26
66
|
|
|
27
67
|
Usage
|
|
28
68
|
-----
|
|
29
69
|
|
|
30
|
-
Running `rubycritic` with no arguments will
|
|
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
|
@@ -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,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/
|
|
2
|
-
require "rubycritic/
|
|
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
|
-
|
|
11
|
+
SMELL_ANALYSERS = [SmellAdapter::Flay, SmellAdapter::Flog, SmellAdapter::Reek]
|
|
8
12
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@
|
|
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
|
-
|
|
15
|
-
|
|
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
|
|
data/lib/rubycritic/cli.rb
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
7
|
+
class Cli
|
|
8
|
+
STATUS_SUCCESS = 0
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
def initialize(argv)
|
|
11
|
+
@argv = argv
|
|
12
|
+
@argv << "." if @argv.empty?
|
|
13
|
+
@main_command = true
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|