From 8c1975813675e730e9447b531ffe60aff0a1fe73 Mon Sep 17 00:00:00 2001 From: Petrik Date: Sat, 24 Aug 2024 09:59:58 +0200 Subject: [PATCH] Namespace CodeStatistics and CodeStatisticsCalculator to Rails CodeStatistics and CodeStatisticsCalculator are currenltly top-level constants, while almost everything in `railties/lib/rails` is namespaced to `Rails`. These have probably never conflicted with other top-level constants, but Rails should not pollute the top-level namespace if it's not necessary. --- railties/CHANGELOG.md | 6 +- railties/lib/rails/code_statistics.rb | 242 +++++++++--------- .../lib/rails/code_statistics_calculator.rb | 154 +++++------ .../lib/rails/commands/stats/stats_command.rb | 4 +- railties/lib/rails/tasks/statistics.rake | 6 +- .../test/code_statistics_calculator_test.rb | 8 +- railties/test/code_statistics_test.rb | 4 +- railties/test/commands/stats_test.rb | 4 +- 8 files changed, 216 insertions(+), 212 deletions(-) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index d56ed00a8bfbf..69bdb62f7b61b 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,13 +1,13 @@ * Deprecate `::STATS_DIRECTORIES`. The global constant `STATS_DIRECTORIES` has been deprecated in favor of - `CodeStatistics.add_directory`. + `Rails::CodeStatistics.add_directory`. - Add extra directories with `CodeStatistics.add_directory(label, path)`: + Add extra directories with `Rails::CodeStatistics.add_directory(label, path)`: ```ruby require "rails/code_statistics" - CodeStatistics.add_directory('My Directory', 'path/to/dir') + Rails::CodeStatistics.add_directory('My Directory', 'path/to/dir') ``` *Petrik de Heus* diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index e89c00ed4825d..c354ff38e2019 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -3,149 +3,151 @@ require "rails/code_statistics_calculator" require "active_support/core_ext/enumerable" -class CodeStatistics - DIRECTORIES = [ - %w(Controllers app/controllers), - %w(Helpers app/helpers), - %w(Jobs app/jobs), - %w(Models app/models), - %w(Mailers app/mailers), - %w(Mailboxes app/mailboxes), - %w(Channels app/channels), - %w(Views app/views), - %w(JavaScripts app/assets/javascripts), - %w(Stylesheets app/assets/stylesheets), - %w(JavaScript app/javascript), - %w(Libraries lib/), - %w(APIs app/apis), - %w(Controller\ tests test/controllers), - %w(Helper\ tests test/helpers), - %w(Job\ tests test/jobs), - %w(Model\ tests test/models), - %w(Mailer\ tests test/mailers), - %w(Mailbox\ tests test/mailboxes), - %w(Channel\ tests test/channels), - %w(Integration\ tests test/integration), - %w(System\ tests test/system), - ] - - TEST_TYPES = ["Controller tests", - "Helper tests", - "Model tests", - "Mailer tests", - "Mailbox tests", - "Channel tests", - "Job tests", - "Integration tests", - "System tests"] - - HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" } - - class_attribute :directories, default: DIRECTORIES - - # Add directories to the output of the `bin/rails stats` command. - # - # CodeStatistics.add_directory("My Directory", "path/to/dir") - def self.add_directory(label, path) - self.directories << [label, path] - end - - def initialize(*pairs) - @pairs = pairs - @statistics = calculate_statistics - @total = calculate_total if pairs.length > 1 - end +module Rails + class CodeStatistics + DIRECTORIES = [ + %w(Controllers app/controllers), + %w(Helpers app/helpers), + %w(Jobs app/jobs), + %w(Models app/models), + %w(Mailers app/mailers), + %w(Mailboxes app/mailboxes), + %w(Channels app/channels), + %w(Views app/views), + %w(JavaScripts app/assets/javascripts), + %w(Stylesheets app/assets/stylesheets), + %w(JavaScript app/javascript), + %w(Libraries lib/), + %w(APIs app/apis), + %w(Controller\ tests test/controllers), + %w(Helper\ tests test/helpers), + %w(Job\ tests test/jobs), + %w(Model\ tests test/models), + %w(Mailer\ tests test/mailers), + %w(Mailbox\ tests test/mailboxes), + %w(Channel\ tests test/channels), + %w(Integration\ tests test/integration), + %w(System\ tests test/system), + ] + + TEST_TYPES = ["Controller tests", + "Helper tests", + "Model tests", + "Mailer tests", + "Mailbox tests", + "Channel tests", + "Job tests", + "Integration tests", + "System tests"] + + HEADERS = { lines: " Lines", code_lines: " LOC", classes: "Classes", methods: "Methods" } + + class_attribute :directories, default: DIRECTORIES + + # Add directories to the output of the `bin/rails stats` command. + # + # Rails::CodeStatistics.add_directory("My Directory", "path/to/dir") + def self.add_directory(label, path) + self.directories << [label, path] + end - def to_s - print_header - @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) } - print_splitter + def initialize(*pairs) + @pairs = pairs + @statistics = calculate_statistics + @total = calculate_total if pairs.length > 1 + end - if @total - print_line("Total", @total) + def to_s + print_header + @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) } print_splitter - end - print_code_test_stats - end + if @total + print_line("Total", @total) + print_splitter + end - private - def calculate_statistics - Hash[@pairs.map { |pair| [pair.first, calculate_directory_statistics(pair.last)] }] + print_code_test_stats end - def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb)$/) - stats = CodeStatisticsCalculator.new + private + def calculate_statistics + Hash[@pairs.map { |pair| [pair.first, calculate_directory_statistics(pair.last)] }] + end + + def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb)$/) + stats = Rails::CodeStatisticsCalculator.new - Dir.foreach(directory) do |file_name| - path = "#{directory}/#{file_name}" + Dir.foreach(directory) do |file_name| + path = "#{directory}/#{file_name}" - if File.directory?(path) && !file_name.start_with?(".") - stats.add(calculate_directory_statistics(path, pattern)) - elsif file_name&.match?(pattern) - stats.add_by_file_path(path) + if File.directory?(path) && !file_name.start_with?(".") + stats.add(calculate_directory_statistics(path, pattern)) + elsif file_name&.match?(pattern) + stats.add_by_file_path(path) + end end - end - stats - end + stats + end - def calculate_total - @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total| - total.add(pair.last) + def calculate_total + @statistics.each_with_object(Rails::CodeStatisticsCalculator.new) do |pair, total| + total.add(pair.last) + end end - end - def calculate_code - code_loc = 0 - @statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k } - code_loc - end + def calculate_code + code_loc = 0 + @statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k } + code_loc + end - def calculate_tests - test_loc = 0 - @statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k } - test_loc - end + def calculate_tests + test_loc = 0 + @statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k } + test_loc + end - def width_for(label) - [@statistics.values.sum { |s| s.public_send(label) }.to_s.size, HEADERS[label].length].max - end + def width_for(label) + [@statistics.values.sum { |s| s.public_send(label) }.to_s.size, HEADERS[label].length].max + end - def print_header - print_splitter - print "| Name " - HEADERS.each do |k, v| - print " | #{v.rjust(width_for(k))}" + def print_header + print_splitter + print "| Name " + HEADERS.each do |k, v| + print " | #{v.rjust(width_for(k))}" + end + puts " | M/C | LOC/M |" + print_splitter end - puts " | M/C | LOC/M |" - print_splitter - end - def print_splitter - print "+----------------------" - HEADERS.each_key do |k| - print "+#{'-' * (width_for(k) + 2)}" + def print_splitter + print "+----------------------" + HEADERS.each_key do |k| + print "+#{'-' * (width_for(k) + 2)}" + end + puts "+-----+-------+" end - puts "+-----+-------+" - end - def print_line(name, statistics) - m_over_c = (statistics.methods / statistics.classes) rescue 0 - loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue 0 + def print_line(name, statistics) + m_over_c = (statistics.methods / statistics.classes) rescue 0 + loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue 0 - print "| #{name.ljust(20)} " - HEADERS.each_key do |k| - print "| #{statistics.send(k).to_s.rjust(width_for(k))} " + print "| #{name.ljust(20)} " + HEADERS.each_key do |k| + print "| #{statistics.send(k).to_s.rjust(width_for(k))} " + end + puts "| #{m_over_c.to_s.rjust(3)} | #{loc_over_m.to_s.rjust(5)} |" end - puts "| #{m_over_c.to_s.rjust(3)} | #{loc_over_m.to_s.rjust(5)} |" - end - def print_code_test_stats - code = calculate_code - tests = calculate_tests + def print_code_test_stats + code = calculate_code + tests = calculate_tests - puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f / code)}" - puts "" - end + puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f / code)}" + puts "" + end + end end diff --git a/railties/lib/rails/code_statistics_calculator.rb b/railties/lib/rails/code_statistics_calculator.rb index 1503eb7eefc20..265116a7e947a 100644 --- a/railties/lib/rails/code_statistics_calculator.rb +++ b/railties/lib/rails/code_statistics_calculator.rb @@ -1,97 +1,99 @@ # frozen_string_literal: true -class CodeStatisticsCalculator # :nodoc: - attr_reader :lines, :code_lines, :classes, :methods +module Rails + class CodeStatisticsCalculator # :nodoc: + attr_reader :lines, :code_lines, :classes, :methods - PATTERNS = { - rb: { - line_comment: /^\s*#/, - begin_block_comment: /^=begin/, - end_block_comment: /^=end/, - class: /^\s*class\s+[_A-Z]/, - method: /^\s*def\s+[_a-z]/, - }, - erb: { - line_comment: %r{((^\s*<%#.*%>)|())}, - }, - css: { - line_comment: %r{^\s*/\*.*\*/}, - }, - scss: { - line_comment: %r{((^\s*/\*.*\*/)|(^\s*//))}, - }, - js: { - line_comment: %r{^\s*//}, - begin_block_comment: %r{^\s*/\*}, - end_block_comment: %r{\*/}, - method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/, - }, - coffee: { - line_comment: /^\s*#/, - begin_block_comment: /^\s*###/, - end_block_comment: /^\s*###/, - class: /^\s*class\s+[_A-Z]/, - method: /[-=]>/, + PATTERNS = { + rb: { + line_comment: /^\s*#/, + begin_block_comment: /^=begin/, + end_block_comment: /^=end/, + class: /^\s*class\s+[_A-Z]/, + method: /^\s*def\s+[_a-z]/, + }, + erb: { + line_comment: %r{((^\s*<%#.*%>)|())}, + }, + css: { + line_comment: %r{^\s*/\*.*\*/}, + }, + scss: { + line_comment: %r{((^\s*/\*.*\*/)|(^\s*//))}, + }, + js: { + line_comment: %r{^\s*//}, + begin_block_comment: %r{^\s*/\*}, + end_block_comment: %r{\*/}, + method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/, + }, + coffee: { + line_comment: /^\s*#/, + begin_block_comment: /^\s*###/, + end_block_comment: /^\s*###/, + class: /^\s*class\s+[_A-Z]/, + method: /[-=]>/, + } } - } - PATTERNS[:minitest] = PATTERNS[:rb].merge method: /^\s*(def|test)\s+['"_a-z]/ - PATTERNS[:rake] = PATTERNS[:rb] + PATTERNS[:minitest] = PATTERNS[:rb].merge method: /^\s*(def|test)\s+['"_a-z]/ + PATTERNS[:rake] = PATTERNS[:rb] - def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0) - @lines = lines - @code_lines = code_lines - @classes = classes - @methods = methods - end + def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0) + @lines = lines + @code_lines = code_lines + @classes = classes + @methods = methods + end - def add(code_statistics_calculator) - @lines += code_statistics_calculator.lines - @code_lines += code_statistics_calculator.code_lines - @classes += code_statistics_calculator.classes - @methods += code_statistics_calculator.methods - end + def add(code_statistics_calculator) + @lines += code_statistics_calculator.lines + @code_lines += code_statistics_calculator.code_lines + @classes += code_statistics_calculator.classes + @methods += code_statistics_calculator.methods + end - def add_by_file_path(file_path) - File.open(file_path) do |f| - add_by_io(f, file_type(file_path)) + def add_by_file_path(file_path) + File.open(file_path) do |f| + add_by_io(f, file_type(file_path)) + end end - end - def add_by_io(io, file_type) - patterns = PATTERNS[file_type] || {} + def add_by_io(io, file_type) + patterns = PATTERNS[file_type] || {} - comment_started = false + comment_started = false - while line = io.gets - @lines += 1 + while line = io.gets + @lines += 1 - if comment_started - if patterns[:end_block_comment] && patterns[:end_block_comment].match?(line) - comment_started = false - end - next - else - if patterns[:begin_block_comment] && patterns[:begin_block_comment].match?(line) - comment_started = true + if comment_started + if patterns[:end_block_comment] && patterns[:end_block_comment].match?(line) + comment_started = false + end next + else + if patterns[:begin_block_comment] && patterns[:begin_block_comment].match?(line) + comment_started = true + next + end end - end - @classes += 1 if patterns[:class] && patterns[:class].match?(line) - @methods += 1 if patterns[:method] && patterns[:method].match?(line) - if !line.match?(/^\s*$/) && (patterns[:line_comment].nil? || !line.match?(patterns[:line_comment])) - @code_lines += 1 + @classes += 1 if patterns[:class] && patterns[:class].match?(line) + @methods += 1 if patterns[:method] && patterns[:method].match?(line) + if !line.match?(/^\s*$/) && (patterns[:line_comment].nil? || !line.match?(patterns[:line_comment])) + @code_lines += 1 + end end end - end - private - def file_type(file_path) - if file_path.end_with? "_test.rb" - :minitest - else - File.extname(file_path).delete_prefix(".").downcase.to_sym + private + def file_type(file_path) + if file_path.end_with? "_test.rb" + :minitest + else + File.extname(file_path).delete_prefix(".").downcase.to_sym + end end - end + end end diff --git a/railties/lib/rails/commands/stats/stats_command.rb b/railties/lib/rails/commands/stats/stats_command.rb index ecc09686184fd..0a7af8da1cd91 100644 --- a/railties/lib/rails/commands/stats/stats_command.rb +++ b/railties/lib/rails/commands/stats/stats_command.rb @@ -8,11 +8,11 @@ def perform require "rails/code_statistics" boot_application! - stat_directories = CodeStatistics.directories.map do |name, dir| + stat_directories = Rails::CodeStatistics.directories.map do |name, dir| [name, Rails::Command.application_root.join(dir)] end.select { |name, dir| File.directory?(dir) } - CodeStatistics.new(*stat_directories).to_s + Rails::CodeStatistics.new(*stat_directories).to_s end end end diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index d35ad1c475c40..f12d38e0fe6e2 100644 --- a/railties/lib/rails/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -2,8 +2,8 @@ require "rails/code_statistics" STATS_DIRECTORIES = ActiveSupport::Deprecation::DeprecatedObjectProxy.new( - CodeStatistics::DIRECTORIES, - "`STATS_DIRECTORIES` is deprecated and will be removed in Rails 8.1! Use `CodeStatistics.add_directory('My Directory', 'path/to/dir)` instead.", + Rails::CodeStatistics::DIRECTORIES, + "`STATS_DIRECTORIES` is deprecated and will be removed in Rails 8.1! Use `Rails::CodeStatistics.add_directory('My Directory', 'path/to/dir)` instead.", Rails.deprecator ) @@ -19,5 +19,5 @@ task :stats do Please use `bin/rails stats` as Rails command instead.\n MSG - CodeStatistics.new(*stat_directories).to_s + Rails::CodeStatistics.new(*stat_directories).to_s end diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb index 49c123fa998e2..7e5b23376f18b 100644 --- a/railties/test/code_statistics_calculator_test.rb +++ b/railties/test/code_statistics_calculator_test.rb @@ -5,7 +5,7 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase def setup - @code_statistics_calculator = CodeStatisticsCalculator.new + @code_statistics_calculator = Rails::CodeStatisticsCalculator.new end test "calculate statistics using #add_by_file_path" do @@ -46,7 +46,7 @@ def test_expectation end test "add statistics to another using #add" do - code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4) + code_statistics_calculator_1 = Rails::CodeStatisticsCalculator.new(1, 2, 3, 4) @code_statistics_calculator.add(code_statistics_calculator_1) assert_equal 1, @code_statistics_calculator.lines @@ -54,7 +54,7 @@ def test_expectation assert_equal 3, @code_statistics_calculator.classes assert_equal 4, @code_statistics_calculator.methods - code_statistics_calculator_2 = CodeStatisticsCalculator.new(2, 3, 4, 5) + code_statistics_calculator_2 = Rails::CodeStatisticsCalculator.new(2, 3, 4, 5) @code_statistics_calculator.add(code_statistics_calculator_2) assert_equal 3, @code_statistics_calculator.lines @@ -64,7 +64,7 @@ def test_expectation end test "accumulate statistics using #add_by_io" do - code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4) + code_statistics_calculator_1 = Rails::CodeStatisticsCalculator.new(1, 2, 3, 4) @code_statistics_calculator.add(code_statistics_calculator_1) code = <<-'CODE' diff --git a/railties/test/code_statistics_test.rb b/railties/test/code_statistics_test.rb index 7ad1ac309486a..d393a3143d5ad 100644 --- a/railties/test/code_statistics_test.rb +++ b/railties/test/code_statistics_test.rb @@ -16,7 +16,7 @@ def teardown test "ignores directories that happen to have source files extensions" do assert_nothing_raised do - @code_statistics = CodeStatistics.new(["tmp dir", @tmp_path]) + @code_statistics = Rails::CodeStatistics.new(["tmp dir", @tmp_path]) end end @@ -28,7 +28,7 @@ def foo CODE assert_nothing_raised do - CodeStatistics.new(["hidden file", @tmp_path]) + Rails::CodeStatistics.new(["hidden file", @tmp_path]) end end end diff --git a/railties/test/commands/stats_test.rb b/railties/test/commands/stats_test.rb index ce1d741514382..603a97bb7c181 100644 --- a/railties/test/commands/stats_test.rb +++ b/railties/test/commands/stats_test.rb @@ -13,7 +13,7 @@ class Rails::Command::StatsTest < ActiveSupport::TestCase app_file "config/initializers/custom.rb", <<~CODE require "rails/code_statistics" - CodeStatistics.add_directory("Custom dir", "custom/dir") + Rails::CodeStatistics.add_directory("Custom dir", "custom/dir") CODE output = rails "stats" @@ -23,7 +23,7 @@ class Rails::Command::StatsTest < ActiveSupport::TestCase test "`bin/rails stats` handles non-existing directories added by third parties" do app_file "config/initializers/custom.rb", <<~CODE require "rails/code_statistics" - CodeStatistics.add_directory("Non Existing", "app/non_existing") + Rails::CodeStatistics.add_directory("Non Existing", "app/non_existing") CODE output = rails "stats"