koality 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ module Koality
2
+ module Reporter
3
+ class Cane < Base
4
+
5
+ def report(type, violations)
6
+ unless violations.count > 0
7
+ report_success(type)
8
+ return
9
+ end
10
+
11
+ show_table(type, violations)
12
+ end
13
+
14
+ def show_table(type, errors)
15
+ return if errors.empty?
16
+
17
+ table = build_table
18
+ table.title = color("Cane - #{type} - #{errors.count} Errors", :bold)
19
+
20
+ if type == :style
21
+ by_message = errors.group_by { |e| e.message.split(/\(\d+\)/).first }
22
+ by_message.each do |message, errors|
23
+ msg = color(message, :red)
24
+ locations = errors.map { |e| " #{e.file_name}:#{e.line}" }
25
+
26
+ table.add_row ["#{msg}\n#{locations.join("\n")}", errors.count]
27
+ table.add_row :separator unless message == by_message.keys.last
28
+ end
29
+ else
30
+ errors.each do |error|
31
+ table.add_row columns_for_type(type, error)
32
+ table.add_row :separator unless error == errors.last
33
+ end
34
+ end
35
+
36
+ puts table
37
+ end
38
+
39
+ def columns_for_type(type, error)
40
+ case type
41
+ when :abc
42
+ ["#{color(error.detail, :red)}\n #{error.file_name}", error.complexity]
43
+ when :style
44
+ ["#{color(error.message, :red)}\n #{error.file_name}:#{error.line}"]
45
+ when :threshold
46
+ [color(error.name, :red), "expected: #{error.operator} #{color(error.limit, :green)}, actual: #{color(error.value, :red)}"]
47
+ else
48
+ error.columns
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def report_success(type)
55
+ puts color("Cane - #{type} - 0 Errors", :green)
56
+ end
57
+
58
+ def grouped_errors(errors)
59
+ errors.group_by(&:url)
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ module Koality
2
+ module Reporter
3
+ class RailsBestPractices < Base
4
+
5
+ attr_reader :table
6
+
7
+ def initialize
8
+ @table = build_table
9
+ end
10
+
11
+ def report(errors)
12
+ unless errors.count > 0
13
+ report_success
14
+ return
15
+ end
16
+
17
+ table.title = color("Rails Best Practices - #{errors.count} Errors", :bold)
18
+ rows = grouped_errors(errors).map do |message, errors|
19
+ info = "#{color(message, :red)}\n#{color(errors.first.url, :cyan)}\n"
20
+ info << errors.map { |e| " #{e.short_filename}:#{e.line_number}" }.join("\n")
21
+ [info, errors.count]
22
+ end
23
+
24
+ rows.each do |row|
25
+ table.add_row row
26
+ table.add_row :separator unless row == rows.last
27
+ end
28
+
29
+ puts table
30
+ end
31
+
32
+ private
33
+
34
+ def report_success
35
+ puts color("Rails Best Practices - 0 Errors", :green)
36
+ end
37
+
38
+ def grouped_errors(errors)
39
+ errors.group_by(&:message)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,78 @@
1
+ require 'cane'
2
+
3
+ module Koality
4
+ module Runner
5
+ class Cane
6
+
7
+ attr_reader :cane_options, :violations
8
+
9
+ def initialize(options)
10
+ @cane_options = translate_options(options)
11
+ @violations = {}
12
+ end
13
+
14
+ def run
15
+ violations.clear
16
+ checkers.each { |type, _| run_checker(type) }
17
+ success?
18
+ end
19
+
20
+ def checkers
21
+ ::Cane::Runner::CHECKERS.select { |type, _| cane_options.key?(type) }
22
+ end
23
+
24
+ def run_checker(type)
25
+ Koality::Reporter::Cane.start do |reporter|
26
+ checker = checkers[type].new(cane_options[type])
27
+ self.violations[type] = checker.violations
28
+
29
+ reporter.report(type, violations[type])
30
+ end
31
+ end
32
+
33
+ def success?
34
+ return false if exceeds_total_violations_threshold?
35
+ violations.none? { |type, _| exceeds_violations_threshold?(type) }
36
+ end
37
+
38
+ def exceeds_total_violations_threshold?
39
+ max_violations = Koality.options.total_violations_threshold
40
+ total_violations = violations.values.flatten.count
41
+
42
+ max_violations >= 0 && total_violations > max_violations
43
+ end
44
+
45
+ def exceeds_violations_threshold?(type)
46
+ max_violations = Koality.options[:"#{type}_violations_threshold"].to_i
47
+ total_violations = violations[type].count
48
+
49
+ max_violations >= 0 && total_violations > max_violations
50
+ end
51
+
52
+ private
53
+
54
+ def translate_options(options)
55
+ Hash.new.tap do |cane_opts|
56
+ cane_opts[:abc] = {
57
+ :files => options[:abc_file_pattern],
58
+ :max => options[:abc_threshold]
59
+ } if options[:abc_enabled]
60
+
61
+ cane_opts[:style] = {
62
+ :files => options[:style_file_pattern],
63
+ :measure => options[:style_line_length_threshold]
64
+ } if options[:style_enabled]
65
+
66
+ cane_opts[:doc] = {
67
+ :files => options[:doc_file_pattern]
68
+ } if options[:doc_enabled]
69
+
70
+ cane_opts[:threshold] = options.thresholds if options.thresholds.any?
71
+ end
72
+ end
73
+
74
+
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,44 @@
1
+ module Koality
2
+ module Runner
3
+ class RailsBestPractices
4
+
5
+ attr_reader :output_file, :rbp_options
6
+
7
+ def initialize(options)
8
+ require 'rails_best_practices'
9
+
10
+ @output_file = options.output_file(:rails_bp_error_file)
11
+ @rbp_options = translate_options(options)
12
+ end
13
+
14
+ def run
15
+ analyzer = ::RailsBestPractices::Analyzer.new('.', rbp_options)
16
+
17
+ Koality::Reporter::RailsBestPractices.start do |reporter|
18
+ analyzer.analyze
19
+ reporter.report(analyzer.errors)
20
+ end
21
+
22
+ File.open(output_file, 'w') do |f|
23
+ f << analyzer.errors.count
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def translate_options(options)
30
+ {
31
+ 'silent' => true,
32
+ 'only' => regexp_list(options[:rails_bp_accept_patterns]),
33
+ 'except' => regexp_list(options[:rails_bp_ignore_patterns])
34
+ }
35
+ end
36
+
37
+ def regexp_list(list)
38
+ list.map { |item| Regexp.new item }
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,2 @@
1
+ require 'koality/simplecov/formatter'
2
+ SimpleCov.formatter = Koality::SimpleCov::Formatter
@@ -0,0 +1,21 @@
1
+ require 'koality'
2
+ require 'simplecov'
3
+
4
+ module Koality
5
+ module SimpleCov
6
+ class Formatter
7
+
8
+ def format(result)
9
+ ::SimpleCov::Formatter::HTMLFormatter.new.format(result)
10
+
11
+ Koality.options.ensure_output_directory_exists
12
+ threshold_file = Koality.options.output_file(:code_coverage_file)
13
+
14
+ File.open(threshold_file, "w") do |f|
15
+ f << result.source_files.covered_percent.to_f
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Koality
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe Koality::Options do
4
+
5
+ let(:default_options) do
6
+ {
7
+ :abc_enabled => true,
8
+ :style_enabled => true,
9
+
10
+ :code_coverage_threshold => 90,
11
+ :code_coverage_file => 'code_coverage',
12
+ :code_coverage_enabled => true,
13
+
14
+ :rails_bp_error_file => 'rails_bp_errors',
15
+ :rails_bp_errors_threshold => 5,
16
+ :rails_bp_enabled => true,
17
+
18
+ :custom_thresholds => [],
19
+ :total_violations_threshold => 0,
20
+ :abort_on_failure => true,
21
+ :output_directory => 'quality'
22
+ }
23
+ end
24
+
25
+ describe '.new' do
26
+ it 'allows you to override default options' do
27
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
28
+ opts = Koality::Options.new(:abc_enabled => false, :style_enabled => false)
29
+
30
+ opts[:abc_enabled].should be_false
31
+ opts[:style_enabled].should be_false
32
+ opts[:code_coverage_enabled].should be_true
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#[]' do
38
+ it 'delegates to the underlying options' do
39
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
40
+ opts = Koality::Options.new
41
+ opts[:abc_enabled].should be_true
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#[]=' do
47
+ it 'delegates to the underlying options' do
48
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
49
+ opts = Koality::Options.new
50
+ opts[:abc_enabled] = false
51
+ opts[:abc_enabled].should be_false
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'dynamic reader methods' do
57
+ it 'delegates to the underlying options' do
58
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
59
+ opts = Koality::Options.new
60
+ opts.abc_enabled.should be_true
61
+ end
62
+ end
63
+ end
64
+
65
+ describe 'dynamic reader methods' do
66
+ it 'delegates to the underlying options' do
67
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
68
+ opts = Koality::Options.new
69
+ opts.abc_enabled = false
70
+ opts.abc_enabled.should be_false
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#add_threshold' do
76
+ it 'adds a new threshold in the format Cane expects' do
77
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
78
+ opts = Koality::Options.new
79
+ opts.add_threshold('quality/foo', :>=, 99)
80
+ opts.custom_thresholds.should == [[:>=, 'quality/foo', 99]]
81
+
82
+ opts.add_threshold('quality/bar', :==, 75)
83
+ opts.custom_thresholds.should == [[:>=, 'quality/foo', 99], [:==, 'quality/bar', 75]]
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '#runner_thresholds' do
89
+ it 'is empty if no extra runners are enabled' do
90
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
91
+ opts = Koality::Options.new(:rails_bp_enabled => false, :code_coverage_enabled => false)
92
+ opts.runner_thresholds.should be_empty
93
+ end
94
+ end
95
+
96
+ it 'includes a threshold check for Rails BP if enabled' do
97
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
98
+ opts = Koality::Options.new
99
+ opts.stubs(:rails_bp_enabled? => true)
100
+ opts.runner_thresholds.should include([:<=, 'quality/rails_bp_errors', 5])
101
+ end
102
+ end
103
+
104
+ it 'includes a threshold check for code coverage if enabled' do
105
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
106
+ opts = Koality::Options.new
107
+ opts.stubs(:code_coverage_enabled? => true)
108
+ opts.runner_thresholds.should include([:>=, 'quality/code_coverage', 90])
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#thresholds' do
114
+ it 'returns the union of custom_thresholds and runner_thresholds' do
115
+ opts = Koality::Options.new
116
+ opts.stubs(:custom_thresholds => [:c1, :c2], :runner_thresholds => [:r1])
117
+ opts.thresholds.should == [:c1, :c2, :r1]
118
+ end
119
+ end
120
+
121
+ describe '#output_file' do
122
+ it 'returns the path for a file relative to the output directory' do
123
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
124
+ opts = Koality::Options.new
125
+ opts.output_file(:rails_bp_error_file).should == 'quality/rails_bp_errors'
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#rails_bp_enabled?' do
131
+ context 'when inside a rails project' do
132
+ it 'returns true if the :rails_bp_enabled flag is set to true' do
133
+ Koality::Options.with_constants(:Rails => true, :DEFAULTS => default_options) do
134
+ opts = Koality::Options.new(:rails_bp_enabled => true)
135
+ opts.rails_bp_enabled?.should be_true
136
+ end
137
+ end
138
+
139
+ it 'returns false if the :rails_bp_enabled flag is set to false' do
140
+ Koality::Options.with_constants(:Rails => true, :DEFAULTS => default_options) do
141
+ opts = Koality::Options.new(:rails_bp_enabled => false)
142
+ opts.rails_bp_enabled?.should be_false
143
+ end
144
+ end
145
+ end
146
+
147
+ context 'when not inside a rails project' do
148
+ it 'returns false' do
149
+ Koality::Options.with_constants(:DEFAULTS => default_options) do
150
+ opts = Koality::Options.new(:rails_bp_enabled => true)
151
+ opts.rails_bp_enabled?.should be_false
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'koality/rake_task'
3
+
4
+ describe Koality::RakeTask do
5
+
6
+ before do
7
+ Rake::Task.clear
8
+ end
9
+
10
+ describe '.new' do
11
+ it 'allows you to configure options' do
12
+ task = Koality::RakeTask.new do |options|
13
+ options.should == Koality.options
14
+ end
15
+ end
16
+
17
+ it 'defines a task that runs Koality with the options' do
18
+ Koality::RakeTask.new
19
+ task = Rake::Task[:koality]
20
+
21
+ Koality.expects(:run)
22
+ task.invoke
23
+ end
24
+ end
25
+ end