rubycritic-simplecov 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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +290 -0
  3. data/CONTRIBUTING.md +93 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +259 -0
  7. data/ROADMAP.md +56 -0
  8. data/Rakefile +28 -0
  9. data/bin/rubycritic +10 -0
  10. data/lib/rubycritic/analysers/attributes.rb +29 -0
  11. data/lib/rubycritic/analysers/churn.rb +30 -0
  12. data/lib/rubycritic/analysers/complexity.rb +30 -0
  13. data/lib/rubycritic/analysers/coverage.rb +118 -0
  14. data/lib/rubycritic/analysers/helpers/ast_node.rb +76 -0
  15. data/lib/rubycritic/analysers/helpers/flay.rb +13 -0
  16. data/lib/rubycritic/analysers/helpers/flog.rb +17 -0
  17. data/lib/rubycritic/analysers/helpers/methods_counter.rb +25 -0
  18. data/lib/rubycritic/analysers/helpers/modules_locator.rb +42 -0
  19. data/lib/rubycritic/analysers/helpers/parser.rb +14 -0
  20. data/lib/rubycritic/analysers/helpers/reek.rb +11 -0
  21. data/lib/rubycritic/analysers/smells/flay.rb +79 -0
  22. data/lib/rubycritic/analysers/smells/flog.rb +69 -0
  23. data/lib/rubycritic/analysers/smells/reek.rb +53 -0
  24. data/lib/rubycritic/analysers_runner.rb +41 -0
  25. data/lib/rubycritic/analysis_summary.rb +40 -0
  26. data/lib/rubycritic/browser.rb +19 -0
  27. data/lib/rubycritic/cli/application.rb +34 -0
  28. data/lib/rubycritic/cli/options/argv.rb +137 -0
  29. data/lib/rubycritic/cli/options/file.rb +100 -0
  30. data/lib/rubycritic/cli/options.rb +33 -0
  31. data/lib/rubycritic/colorize.rb +17 -0
  32. data/lib/rubycritic/command_factory.rb +25 -0
  33. data/lib/rubycritic/commands/base.rb +18 -0
  34. data/lib/rubycritic/commands/ci.rb +14 -0
  35. data/lib/rubycritic/commands/compare.rb +106 -0
  36. data/lib/rubycritic/commands/default.rb +38 -0
  37. data/lib/rubycritic/commands/help.rb +18 -0
  38. data/lib/rubycritic/commands/status_reporter.rb +45 -0
  39. data/lib/rubycritic/commands/utils/build_number_file.rb +37 -0
  40. data/lib/rubycritic/commands/version.rb +16 -0
  41. data/lib/rubycritic/configuration.rb +69 -0
  42. data/lib/rubycritic/core/analysed_module.rb +93 -0
  43. data/lib/rubycritic/core/analysed_modules_collection.rb +95 -0
  44. data/lib/rubycritic/core/location.rb +47 -0
  45. data/lib/rubycritic/core/rating.rb +30 -0
  46. data/lib/rubycritic/core/smell.rb +84 -0
  47. data/lib/rubycritic/generators/console_report.rb +20 -0
  48. data/lib/rubycritic/generators/html/assets/fonts/FontAwesome.otf +0 -0
  49. data/lib/rubycritic/generators/html/assets/fonts/Roboto-Medium.ttf +0 -0
  50. data/lib/rubycritic/generators/html/assets/fonts/Roboto-Regular.ttf +0 -0
  51. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.eot +0 -0
  52. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.svg +2671 -0
  53. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.ttf +0 -0
  54. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff +0 -0
  55. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff2 +0 -0
  56. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.svg +288 -0
  58. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
  61. data/lib/rubycritic/generators/html/assets/images/logo.png +0 -0
  62. data/lib/rubycritic/generators/html/assets/javascripts/application.js +281 -0
  63. data/lib/rubycritic/generators/html/assets/javascripts/bootstrap.min.js +7 -0
  64. data/lib/rubycritic/generators/html/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
  65. data/lib/rubycritic/generators/html/assets/javascripts/jquery.filtertable.min.js +13 -0
  66. data/lib/rubycritic/generators/html/assets/javascripts/jquery.min.js +4 -0
  67. data/lib/rubycritic/generators/html/assets/javascripts/jquery.scrollTo.min.js +7 -0
  68. data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.js +1031 -0
  69. data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.min.js +4 -0
  70. data/lib/rubycritic/generators/html/assets/javascripts/jquery.timeago.js +231 -0
  71. data/lib/rubycritic/generators/html/assets/javascripts/prettify.js +46 -0
  72. data/lib/rubycritic/generators/html/assets/stylesheets/application.css +570 -0
  73. data/lib/rubycritic/generators/html/assets/stylesheets/bootstrap.min.css +6 -0
  74. data/lib/rubycritic/generators/html/assets/stylesheets/font-awesome.min.css +4 -0
  75. data/lib/rubycritic/generators/html/assets/stylesheets/prettify.css +1 -0
  76. data/lib/rubycritic/generators/html/assets/stylesheets/prettify.custom_theme.css +69 -0
  77. data/lib/rubycritic/generators/html/base.rb +54 -0
  78. data/lib/rubycritic/generators/html/code_file.rb +51 -0
  79. data/lib/rubycritic/generators/html/code_index.rb +33 -0
  80. data/lib/rubycritic/generators/html/line.rb +37 -0
  81. data/lib/rubycritic/generators/html/overview.rb +38 -0
  82. data/lib/rubycritic/generators/html/simple_cov_index.rb +44 -0
  83. data/lib/rubycritic/generators/html/smells_index.rb +45 -0
  84. data/lib/rubycritic/generators/html/templates/code_file.html.erb +61 -0
  85. data/lib/rubycritic/generators/html/templates/code_index.html.erb +55 -0
  86. data/lib/rubycritic/generators/html/templates/layouts/application.html.erb +66 -0
  87. data/lib/rubycritic/generators/html/templates/line.html.erb +1 -0
  88. data/lib/rubycritic/generators/html/templates/overview.html.erb +68 -0
  89. data/lib/rubycritic/generators/html/templates/simple_cov_index.html.erb +44 -0
  90. data/lib/rubycritic/generators/html/templates/smells_index.html.erb +47 -0
  91. data/lib/rubycritic/generators/html/templates/smelly_line.html.erb +23 -0
  92. data/lib/rubycritic/generators/html/turbulence.rb +17 -0
  93. data/lib/rubycritic/generators/html/view_helpers.rb +58 -0
  94. data/lib/rubycritic/generators/html_report.rb +77 -0
  95. data/lib/rubycritic/generators/json/simple.rb +43 -0
  96. data/lib/rubycritic/generators/json_report.rb +26 -0
  97. data/lib/rubycritic/generators/lint_report.rb +30 -0
  98. data/lib/rubycritic/generators/text/lint.rb +41 -0
  99. data/lib/rubycritic/generators/text/list.rb +45 -0
  100. data/lib/rubycritic/generators/text/templates/lint.erb +3 -0
  101. data/lib/rubycritic/generators/text/templates/list.erb +12 -0
  102. data/lib/rubycritic/rake_task.rb +71 -0
  103. data/lib/rubycritic/reporter.rb +39 -0
  104. data/lib/rubycritic/revision_comparator.rb +45 -0
  105. data/lib/rubycritic/serializer.rb +32 -0
  106. data/lib/rubycritic/smells_status_setter.rb +18 -0
  107. data/lib/rubycritic/source_control_systems/base.rb +41 -0
  108. data/lib/rubycritic/source_control_systems/double.rb +19 -0
  109. data/lib/rubycritic/source_control_systems/git.rb +95 -0
  110. data/lib/rubycritic/source_control_systems/mercurial.rb +29 -0
  111. data/lib/rubycritic/source_control_systems/perforce.rb +116 -0
  112. data/lib/rubycritic/source_locator.rb +52 -0
  113. data/lib/rubycritic/version.rb +5 -0
  114. data/lib/rubycritic.rb +6 -0
  115. metadata +528 -0
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubycritic/commands/base'
4
+
5
+ module RubyCritic
6
+ module Command
7
+ class Help < Base
8
+ def execute
9
+ puts options[:help_text]
10
+ status_reporter
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :options, :status_reporter
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyCritic
4
+ module Command
5
+ class StatusReporter
6
+ attr_reader :status, :status_message, :score
7
+ SUCCESS = 0
8
+ SCORE_BELOW_MINIMUM = 1
9
+
10
+ def initialize(options)
11
+ @options = options
12
+ @status = SUCCESS
13
+ end
14
+
15
+ def score=(input_score)
16
+ @score = input_score.round(2)
17
+ update_status
18
+ end
19
+
20
+ private
21
+
22
+ def update_status
23
+ @status = current_status
24
+ update_status_message
25
+ end
26
+
27
+ def current_status
28
+ satisfy_minimum_score_rule ? SUCCESS : SCORE_BELOW_MINIMUM
29
+ end
30
+
31
+ def satisfy_minimum_score_rule
32
+ score >= @options[:minimum_score].to_f
33
+ end
34
+
35
+ def update_status_message
36
+ case @status
37
+ when SUCCESS
38
+ @status_message = "Score: #{score}"
39
+ when SCORE_BELOW_MINIMUM
40
+ @status_message = "Score (#{score}) is below the minimum #{@options[:minimum_score]}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyCritic
4
+ module Command
5
+ module Utils
6
+ class BuildNumberFile
7
+ attr_reader :file, :build_number
8
+
9
+ def initialize
10
+ open_build_number_file
11
+ end
12
+
13
+ # keep track of the number of builds and
14
+ # use this build number to create separate directory for each build
15
+ def update_build_number
16
+ @build_number = file.read.to_i + 1
17
+ write_build_number
18
+ build_number
19
+ end
20
+
21
+ def write_build_number
22
+ file.rewind
23
+ file.write(build_number)
24
+ file.close
25
+ end
26
+
27
+ def open_build_number_file
28
+ root = Config.root
29
+ FileUtils.mkdir_p(root) unless File.directory?(root)
30
+ location = "#{root}/build_number.txt"
31
+ File.new(location, 'a') unless File.exist?(location)
32
+ @file = File.open(location, 'r+')
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubycritic/version'
4
+ require 'rubycritic/commands/base'
5
+
6
+ module RubyCritic
7
+ module Command
8
+ class Version < Base
9
+ attr_reader :status_reporter
10
+ def execute
11
+ puts "RubyCritic #{VERSION}"
12
+ status_reporter
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubycritic/source_control_systems/base'
4
+
5
+ module RubyCritic
6
+ class Configuration
7
+ attr_reader :root
8
+ attr_accessor :source_control_system, :mode, :formats, :formatters, :deduplicate_symlinks,
9
+ :suppress_ratings, :open_with, :no_browser, :base_branch,
10
+ :feature_branch, :base_branch_score, :feature_branch_score,
11
+ :base_root_directory, :feature_root_directory,
12
+ :compare_root_directory, :threshold_score, :base_branch_collection,
13
+ :feature_branch_collection
14
+
15
+ def set(options)
16
+ self.mode = options[:mode] || :default
17
+ self.root = options[:root] || 'tmp/rubycritic'
18
+ self.deduplicate_symlinks = options[:deduplicate_symlinks]
19
+ self.suppress_ratings = options[:suppress_ratings]
20
+ self.open_with = options[:open_with]
21
+ self.no_browser = options[:no_browser]
22
+ self.base_branch = options[:base_branch]
23
+ self.feature_branch = options[:feature_branch]
24
+ self.threshold_score = options[:threshold_score].to_i
25
+ setup_formats(options)
26
+ end
27
+
28
+ def setup_formats(options)
29
+ formats = options[:formats].to_a
30
+ self.formats = formats.empty? ? [:html] : formats
31
+ self.formatters = options[:formatters] || []
32
+ end
33
+
34
+ def root=(path)
35
+ @root = File.expand_path(path)
36
+ end
37
+
38
+ def source_control_present?
39
+ source_control_system &&
40
+ !source_control_system.is_a?(SourceControlSystem::Double)
41
+ end
42
+ end
43
+
44
+ module Config
45
+ def self.configuration
46
+ @configuration ||= Configuration.new
47
+ end
48
+
49
+ def self.set(options = {})
50
+ configuration.set(options)
51
+ end
52
+
53
+ def self.compare_branches_mode?
54
+ %i[compare_branches ci].include?(Config.mode)
55
+ end
56
+
57
+ def self.build_mode?
58
+ !Config.no_browser && %i[compare_branches ci].include?(Config.mode)
59
+ end
60
+
61
+ def self.method_missing(method, *args, &block)
62
+ configuration.public_send(method, *args, &block)
63
+ end
64
+
65
+ def self.respond_to_missing?(symbol, include_all = false)
66
+ configuration.respond_to_missing?(symbol) || super
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'virtus'
4
+ require 'rubycritic/core/rating'
5
+
6
+ module RubyCritic
7
+ class AnalysedModule
8
+ include Virtus.model
9
+
10
+ # Complexity is reduced by a factor of 25 when calculating cost
11
+ COMPLEXITY_FACTOR = 25.0
12
+
13
+ attribute :coverage
14
+ attribute :name
15
+ attribute :smells_count
16
+ attribute :file_location
17
+ attribute :file_name
18
+ attribute :line_count
19
+ attribute :pathname
20
+ attribute :smells, Array, default: []
21
+ attribute :churn
22
+ attribute :committed_at
23
+ attribute :complexity, Float, default: Float::INFINITY
24
+ attribute :duplication, Integer, default: 0
25
+ attribute :methods_count
26
+
27
+ def path
28
+ @path ||= pathname.to_s
29
+ end
30
+
31
+ def file_location
32
+ pathname.dirname
33
+ end
34
+
35
+ def file_name
36
+ pathname.basename
37
+ end
38
+
39
+ def line_count
40
+ File.read(path).each_line.count
41
+ end
42
+
43
+ def cost
44
+ @cost ||= smells.map(&:cost).inject(0.0, :+) +
45
+ (complexity / COMPLEXITY_FACTOR)
46
+ end
47
+
48
+ def coverage_rating
49
+ @coverage_rating ||= Rating.from_cost(100 - coverage)
50
+ end
51
+
52
+ def rating
53
+ @rating ||= Rating.from_cost(cost)
54
+ end
55
+
56
+ def complexity_per_method
57
+ if methods_count.zero?
58
+ 'N/A'
59
+ else
60
+ complexity.fdiv(methods_count).round(1)
61
+ end
62
+ end
63
+
64
+ def smells_count
65
+ smells.count
66
+ end
67
+
68
+ def smells?
69
+ !smells.empty?
70
+ end
71
+
72
+ def smells_at_location(location)
73
+ smells.select { |smell| smell.at_location?(location) }
74
+ end
75
+
76
+ def <=>(other)
77
+ [rating.to_s, name] <=> [other.rating.to_s, other.name]
78
+ end
79
+
80
+ def to_h
81
+ {
82
+ name: name, path: path, smells: smells,
83
+ churn: churn, committed_at: committed_at, complexity: complexity,
84
+ duplication: duplication, methods_count: methods_count, cost: cost,
85
+ rating: rating
86
+ }
87
+ end
88
+
89
+ def to_json(*options)
90
+ to_h.to_json(*options)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubycritic/source_locator'
4
+ require 'rubycritic/core/analysed_module'
5
+
6
+ module RubyCritic
7
+ class AnalysedModulesCollection
8
+ include Enumerable
9
+
10
+ # Limit used to prevent very bad modules to have excessive impact in the
11
+ # overall result. See #limited_cost_for
12
+ COST_LIMIT = 32.0
13
+ # Score goes from 0 (worst) to 100 (perfect)
14
+ MAX_SCORE = 100.0
15
+ # Projects with an average cost of 16 (or above) will score 0, since 16
16
+ # is where the worst possible rating (F) starts
17
+ ZERO_SCORE_COST = 16.0
18
+ COST_MULTIPLIER = MAX_SCORE / ZERO_SCORE_COST
19
+
20
+ def initialize(paths, modules = nil)
21
+ @modules = SourceLocator.new(paths).pathnames.map do |pathname|
22
+ if modules
23
+ analysed_module = modules.find { |mod| mod.pathname == pathname }
24
+ build_analysed_module(analysed_module)
25
+ else
26
+ AnalysedModule.new(pathname: pathname)
27
+ end
28
+ end
29
+ end
30
+
31
+ def each(&block)
32
+ @modules.each(&block)
33
+ end
34
+
35
+ def where(module_paths)
36
+ @modules.find_all { |mod| module_paths.include? mod.path }
37
+ end
38
+
39
+ def find(module_path)
40
+ @modules.find { |mod| mod.pathname == module_path }
41
+ end
42
+
43
+ def to_json(*options)
44
+ @modules.to_json(*options)
45
+ end
46
+
47
+ def score
48
+ if @modules.any?
49
+ (MAX_SCORE - average_limited_cost * COST_MULTIPLIER).round(2)
50
+ else
51
+ 0.0
52
+ end
53
+ end
54
+
55
+ def summary
56
+ AnalysisSummary.generate(self)
57
+ end
58
+
59
+ def for_rating(rating)
60
+ find_all { |mod| mod.rating.to_s == rating }
61
+ end
62
+
63
+ private
64
+
65
+ def average_limited_cost
66
+ [average_cost, ZERO_SCORE_COST].min
67
+ end
68
+
69
+ def average_cost
70
+ num_modules = @modules.size
71
+ if num_modules.positive?
72
+ map { |mod| limited_cost_for(mod) }.reduce(:+) / num_modules.to_f
73
+ else
74
+ 0.0
75
+ end
76
+ end
77
+
78
+ def limited_cost_for(mod)
79
+ [mod.cost, COST_LIMIT].min
80
+ end
81
+
82
+ def build_analysed_module(analysed_module)
83
+ AnalysedModule.new(
84
+ pathname: analysed_module.pathname,
85
+ name: analysed_module.name,
86
+ smells: analysed_module.smells,
87
+ churn: analysed_module.churn,
88
+ committed_at: analysed_module.committed_at,
89
+ complexity: analysed_module.complexity,
90
+ duplication: analysed_module.duplication,
91
+ methods_count: analysed_module.methods_count
92
+ )
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module RubyCritic
6
+ class Location
7
+ attr_reader :pathname, :line
8
+
9
+ def initialize(path, line)
10
+ @pathname = Pathname.new(path)
11
+ @line = line.to_i
12
+ end
13
+
14
+ def file_name
15
+ @pathname.basename.sub_ext('').to_s
16
+ end
17
+
18
+ def to_s
19
+ "#{pathname}:#{line}"
20
+ end
21
+
22
+ def to_h
23
+ {
24
+ path: pathname.to_s,
25
+ line: line
26
+ }
27
+ end
28
+
29
+ def to_json(*options)
30
+ to_h.to_json(*options)
31
+ end
32
+
33
+ def ==(other)
34
+ state == other.state
35
+ end
36
+
37
+ def <=>(other)
38
+ state <=> other.state
39
+ end
40
+
41
+ protected
42
+
43
+ def state
44
+ [@pathname, @line]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyCritic
4
+ class Rating
5
+ def self.from_cost(cost)
6
+ if cost <= 2 then new('A')
7
+ elsif cost <= 4 then new('B')
8
+ elsif cost <= 8 then new('C')
9
+ elsif cost <= 16 then new('D')
10
+ else new('F')
11
+ end
12
+ end
13
+
14
+ def initialize(letter)
15
+ @letter = letter
16
+ end
17
+
18
+ def to_s
19
+ @letter
20
+ end
21
+
22
+ def to_h
23
+ @letter
24
+ end
25
+
26
+ def to_json(*options)
27
+ to_h.to_json(*options)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'virtus'
4
+ require 'rubycritic/core/location'
5
+
6
+ module RubyCritic
7
+ class Smell
8
+ include Virtus.model
9
+
10
+ attribute :context
11
+ attribute :cost
12
+ attribute :locations, Array, default: []
13
+ attribute :message
14
+ attribute :score
15
+ attribute :status
16
+ attribute :type
17
+ attribute :analyser
18
+
19
+ FLAY_DOCS_URL = 'http://docs.seattlerb.org/flay/'.freeze
20
+ FLOG_DOCS_URL = 'http://docs.seattlerb.org/flog/'.freeze
21
+
22
+ def at_location?(other_location)
23
+ locations.any? { |location| location == other_location }
24
+ end
25
+
26
+ def multiple_locations?
27
+ locations.length > 1
28
+ end
29
+
30
+ def ==(other)
31
+ state == other.state
32
+ end
33
+ alias eql? ==
34
+
35
+ def to_s
36
+ "(#{type}) #{context} #{message}"
37
+ end
38
+
39
+ def to_h
40
+ {
41
+ context: context,
42
+ cost: cost,
43
+ locations: locations,
44
+ message: message,
45
+ score: score,
46
+ status: status,
47
+ type: type
48
+ }
49
+ end
50
+
51
+ def to_json(*options)
52
+ to_h.to_json(*options)
53
+ end
54
+
55
+ def doc_url
56
+ case analyser
57
+ when 'reek'
58
+ "https://github.com/troessner/reek/blob/master/docs/#{dasherized_type}.md"
59
+ when 'flay'
60
+ FLAY_DOCS_URL
61
+ when 'flog'
62
+ FLOG_DOCS_URL
63
+ else
64
+ raise "No documentation URL set for analyser '#{analyser}' smells"
65
+ end
66
+ end
67
+
68
+ def hash
69
+ state.hash
70
+ end
71
+
72
+ protected
73
+
74
+ def state
75
+ [context, message, score, type]
76
+ end
77
+
78
+ private
79
+
80
+ def dasherized_type
81
+ type.gsub(/(?<!^)([A-Z])/, '-\1')
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubycritic/generators/text/list'
4
+
5
+ module RubyCritic
6
+ module Generator
7
+ class ConsoleReport
8
+ def initialize(analysed_modules)
9
+ @analysed_modules = analysed_modules
10
+ end
11
+
12
+ def generate_report
13
+ reports = @analysed_modules.sort.map do |mod|
14
+ Text::List.new(mod).render
15
+ end
16
+ puts reports.join("\n")
17
+ end
18
+ end
19
+ end
20
+ end