refactor: fix Style/OneClassPerFile by extracting top-level definitions into separate files (#18)

This commit is contained in:
Copilot
2026-03-09 16:16:19 +02:00
committed by GitHub
parent 8cea32a4f9
commit fe1ff1be70
8 changed files with 290 additions and 269 deletions

View File

@@ -0,0 +1,9 @@
# typed: strict
# frozen_string_literal: true
# Simple polyfill for Homebrew extensions
class Array
def exclude?(item)
!include?(item)
end
end

View File

@@ -0,0 +1,77 @@
# typed: strict
# frozen_string_literal: true
require "fileutils"
require "pathname"
require "terser"
require "cssminify2"
# Module for processing and copying assets
module AssetProcessor
DOCS_DIR = File.expand_path("../docs", __dir__).freeze
OUTPUT_DIR = DOCS_DIR
THEME_SOURCE_DIR = File.expand_path("../theme", __dir__).freeze
def copy_assets
copy_asset_files
end
def generate_css
css_path = File.join(THEME_SOURCE_DIR, "style.css")
output_path = File.join(OUTPUT_DIR, "style.css")
return unless File.exist?(css_path)
css_content = File.read(css_path)
minified_css = CSSminify2.compress(css_content)
File.write(output_path, minified_css)
puts "📄 Generated CSS file: #{output_path}"
end
def minify_js
js_path = File.join(THEME_SOURCE_DIR, "main.js")
output_path = File.join(OUTPUT_DIR, "main.js")
return unless File.exist?(js_path)
js_content = File.read(js_path)
minified_js = Terser.new.compile(js_content)
File.write(output_path, minified_js)
puts "🔧 Generated JS file: #{output_path}"
end
private
def copy_asset_files
assets_source_dir = File.join(THEME_SOURCE_DIR, "assets")
assets_output_dir = File.join(OUTPUT_DIR, "assets")
FileUtils.mkdir_p(assets_output_dir)
return handle_missing_assets(assets_source_dir) unless Dir.exist?(assets_source_dir)
copy_files_recursively(assets_source_dir, assets_output_dir)
end
def handle_missing_assets(assets_source_dir)
puts "⚠️ Assets source directory not found: #{assets_source_dir}"
end
def copy_files_recursively(source_dir, output_dir)
asset_files = Dir.glob(File.join(source_dir, "**", "*")).reject { |f| File.directory?(f) }
asset_files.each do |source_file|
copy_single_asset(source_file, source_dir, output_dir)
end
puts "📁 Copied #{asset_files.count} asset files to #{output_dir}"
end
def copy_single_asset(source_file, source_dir, output_dir)
relative_path = Pathname.new(source_file).relative_path_from(Pathname.new(source_dir))
output_file = File.join(output_dir, relative_path)
FileUtils.mkdir_p(File.dirname(output_file))
FileUtils.cp(source_file, output_file)
end
end

View File

@@ -9,146 +9,9 @@ require "pathname"
require "time"
require "terser"
require "cssminify2"
# Simple polyfill for Homebrew extensions
class Array
def exclude?(item)
!include?(item)
end
end
# Simple static site generator for homebrew tap documentation
# Module for formatting timestamps and dates
module TimeFormatter
SECONDS_PER_MINUTE = 60
SECONDS_PER_HOUR = 3600
SECONDS_PER_DAY = 86_400
SECONDS_PER_WEEK = 604_800
SECONDS_PER_MONTH = 2_419_200
SECONDS_PER_YEAR = 31_536_000
def format_relative_time(timestamp)
return "" unless timestamp
begin
diff = calculate_time_difference(timestamp)
return "just now" if diff < SECONDS_PER_MINUTE
format_time_by_category(diff)
rescue
""
end
end
def format_date(timestamp)
return "" unless timestamp
begin
Time.parse(timestamp).strftime("%b %d, %Y")
rescue
""
end
end
private
def calculate_time_difference(timestamp)
time = Time.parse(timestamp)
Time.now - time
end
def format_time_by_category(diff)
case diff
when SECONDS_PER_MINUTE...SECONDS_PER_HOUR
format_time_unit(diff / SECONDS_PER_MINUTE, "minute")
when SECONDS_PER_HOUR...SECONDS_PER_DAY
format_time_unit(diff / SECONDS_PER_HOUR, "hour")
when SECONDS_PER_DAY...SECONDS_PER_WEEK
format_time_unit(diff / SECONDS_PER_DAY, "day")
when SECONDS_PER_WEEK...SECONDS_PER_MONTH
format_time_unit(diff / SECONDS_PER_WEEK, "week")
when SECONDS_PER_MONTH...SECONDS_PER_YEAR
format_time_unit(diff / SECONDS_PER_MONTH, "month")
else
format_time_unit(diff / SECONDS_PER_YEAR, "year")
end
end
def format_time_unit(value, unit)
count = value.to_i
"#{count} #{unit}#{"s" if count != 1} ago"
end
end
# Module for processing and copying assets
module AssetProcessor
DOCS_DIR = File.expand_path("../docs", __dir__).freeze
OUTPUT_DIR = DOCS_DIR
THEME_SOURCE_DIR = File.expand_path("../theme", __dir__).freeze
def copy_assets
copy_asset_files
end
def generate_css
css_path = File.join(THEME_SOURCE_DIR, "style.css")
output_path = File.join(OUTPUT_DIR, "style.css")
return unless File.exist?(css_path)
css_content = File.read(css_path)
minified_css = CSSminify2.compress(css_content)
File.write(output_path, minified_css)
puts "📄 Generated CSS file: #{output_path}"
end
def minify_js
js_path = File.join(THEME_SOURCE_DIR, "main.js")
output_path = File.join(OUTPUT_DIR, "main.js")
return unless File.exist?(js_path)
js_content = File.read(js_path)
minified_js = Terser.new.compile(js_content)
File.write(output_path, minified_js)
puts "🔧 Generated JS file: #{output_path}"
end
private
def copy_asset_files
assets_source_dir = File.join(THEME_SOURCE_DIR, "assets")
assets_output_dir = File.join(OUTPUT_DIR, "assets")
FileUtils.mkdir_p(assets_output_dir)
return handle_missing_assets(assets_source_dir) unless Dir.exist?(assets_source_dir)
copy_files_recursively(assets_source_dir, assets_output_dir)
end
def handle_missing_assets(assets_source_dir)
puts "⚠️ Assets source directory not found: #{assets_source_dir}"
end
def copy_files_recursively(source_dir, output_dir)
asset_files = Dir.glob(File.join(source_dir, "**", "*")).reject { |f| File.directory?(f) }
asset_files.each do |source_file|
copy_single_asset(source_file, source_dir, output_dir)
end
puts "📁 Copied #{asset_files.count} asset files to #{output_dir}"
end
def copy_single_asset(source_file, source_dir, output_dir)
relative_path = Pathname.new(source_file).relative_path_from(Pathname.new(source_dir))
output_file = File.join(output_dir, relative_path)
FileUtils.mkdir_p(File.dirname(output_file))
FileUtils.cp(source_file, output_file)
end
end
require_relative "array_extensions"
require_relative "time_formatter"
require_relative "asset_processor"
# Static site generator for homebrew tap documentation
class SiteBuilder

123
scripts/file_watcher.rb Normal file
View File

@@ -0,0 +1,123 @@
# typed: strict
# frozen_string_literal: true
# Module for handling file watching and change detection.
# Classes including this module must define a `build_site` method.
module FileWatcher
def start_file_watcher
Thread.new do
last_mtime = max_mtime
rebuild_pending = false
watched_files = watched_files_count
puts "👀 Watching #{watched_files} files for changes..."
loop do
sleep 1
current_mtime = max_mtime
next if should_skip_rebuild?(current_mtime, last_mtime, rebuild_pending)
rebuild_pending = true
handle_file_change(last_mtime)
last_mtime = perform_rebuild_with_debounce
rebuild_pending = false
puts "✅ Rebuild complete"
rescue => e
puts "⚠️ File watcher error: #{e.message}"
puts "📍 Backtrace: #{e.backtrace.first(3).join(", ")}"
rebuild_pending = false
sleep 2
puts "🔄 File watcher continuing..."
end
end
end
def watched_files_count
all_watched_files.count { |file| !File.directory?(file) }
end
def find_changed_file(since_mtime)
all_watched_files.find do |file|
File.exist?(file) && !File.directory?(file) && File.mtime(file) > since_mtime
end
end
def all_watched_files
[
formula_files,
theme_files,
template_files,
style_and_script_files,
asset_files,
build_script_files,
config_files,
].flatten.compact.uniq
end
def max_mtime
all_watched_files
.select { |file| File.exist?(file) && !File.directory?(file) }
.map { |file| File.mtime(file) }
.max || Time.at(0)
end
private
def should_skip_rebuild?(current_mtime, last_mtime, rebuild_pending)
current_mtime <= last_mtime || rebuild_pending
end
def handle_file_change(last_mtime)
changed_file = find_changed_file(last_mtime)
puts "📝 Changed: #{changed_file}" if changed_file
puts "🔄 Rebuilding in 1 second..."
end
def perform_rebuild_with_debounce
sleep 1 # Debounce: wait for additional changes
final_mtime = max_mtime
puts "🔨 Building site..."
build_site
final_mtime
end
def formula_files
Dir.glob(File.expand_path("../Formula/**/*.rb", __dir__))
end
def theme_files
Dir.glob(File.expand_path("../theme/**/*", __dir__))
end
def template_files
[
Dir.glob(File.expand_path("../theme/*.erb", __dir__)),
Dir.glob(File.expand_path("../theme/_*.erb", __dir__)),
Dir.glob(File.expand_path("../theme/*.html.erb", __dir__)),
Dir.glob(File.expand_path("../theme/_*.html.erb", __dir__)),
].flatten
end
def style_and_script_files
[
Dir.glob(File.expand_path("../theme/*.css", __dir__)),
Dir.glob(File.expand_path("../theme/*.js", __dir__)),
].flatten
end
def asset_files
Dir.glob(File.expand_path("../theme/assets/**/*", __dir__))
end
def build_script_files
[
File.expand_path("../scripts/parse_formulas.rb", __dir__),
File.expand_path("../scripts/build_site.rb", __dir__),
]
end
def config_files
[File.expand_path("../Makefile", __dir__)]
end
end

View File

@@ -6,14 +6,7 @@ require "json"
require "fileutils"
require "pathname"
require "date"
# Simple polyfill for Homebrew extensions
class String
def blank?
# Polyfill implementation to avoid external dependencies
nil? || empty? # rubocop:disable Homebrew/Blank, Lint/RedundantCopDisableDirective
end
end
require_relative "string_extensions"
# Parser class for extracting metadata from Homebrew formulae
class FormulaParser

View File

@@ -6,127 +6,7 @@ require "webrick"
require "fileutils"
require_relative "parse_formulas"
require_relative "build_site"
# Simple development server for the homebrew tap documentation
# Module for handling file watching and change detection
module FileWatcher
def start_file_watcher
Thread.new do
last_mtime = max_mtime
rebuild_pending = false
watched_files = watched_files_count
puts "👀 Watching #{watched_files} files for changes..."
loop do
sleep 1
current_mtime = max_mtime
next if should_skip_rebuild?(current_mtime, last_mtime, rebuild_pending)
rebuild_pending = true
handle_file_change(last_mtime)
last_mtime = perform_rebuild_with_debounce
rebuild_pending = false
puts "✅ Rebuild complete"
rescue => e
puts "⚠️ File watcher error: #{e.message}"
puts "📍 Backtrace: #{e.backtrace.first(3).join(", ")}"
rebuild_pending = false
sleep 2
puts "🔄 File watcher continuing..."
end
end
end
def watched_files_count
all_watched_files.count { |file| !File.directory?(file) }
end
def find_changed_file(since_mtime)
all_watched_files.find do |file|
File.exist?(file) && !File.directory?(file) && File.mtime(file) > since_mtime
end
end
def all_watched_files
[
formula_files,
theme_files,
template_files,
style_and_script_files,
asset_files,
build_script_files,
config_files,
].flatten.compact.uniq
end
def max_mtime
all_watched_files
.select { |file| File.exist?(file) && !File.directory?(file) }
.map { |file| File.mtime(file) }
.max || Time.at(0)
end
private
def should_skip_rebuild?(current_mtime, last_mtime, rebuild_pending)
current_mtime <= last_mtime || rebuild_pending
end
def handle_file_change(last_mtime)
changed_file = find_changed_file(last_mtime)
puts "📝 Changed: #{changed_file}" if changed_file
puts "🔄 Rebuilding in 1 second..."
end
def perform_rebuild_with_debounce
sleep 1 # Debounce: wait for additional changes
final_mtime = max_mtime
puts "🔨 Building site..."
build_site
final_mtime
end
def formula_files
Dir.glob(File.expand_path("../Formula/**/*.rb", __dir__))
end
def theme_files
Dir.glob(File.expand_path("../theme/**/*", __dir__))
end
def template_files
[
Dir.glob(File.expand_path("../theme/*.erb", __dir__)),
Dir.glob(File.expand_path("../theme/_*.erb", __dir__)),
Dir.glob(File.expand_path("../theme/*.html.erb", __dir__)),
Dir.glob(File.expand_path("../theme/_*.html.erb", __dir__)),
].flatten
end
def style_and_script_files
[
Dir.glob(File.expand_path("../theme/*.css", __dir__)),
Dir.glob(File.expand_path("../theme/*.js", __dir__)),
].flatten
end
def asset_files
Dir.glob(File.expand_path("../theme/assets/**/*", __dir__))
end
def build_script_files
[
File.expand_path("../scripts/parse_formulas.rb", __dir__),
File.expand_path("../scripts/build_site.rb", __dir__),
]
end
def config_files
[File.expand_path("../Makefile", __dir__)]
end
end
require_relative "file_watcher"
# Development server with file watching for homebrew tap documentation
class DevServer

View File

@@ -0,0 +1,10 @@
# typed: strict
# frozen_string_literal: true
# Simple polyfill for Homebrew extensions
class String
def blank?
# Polyfill implementation to avoid external dependencies
nil? || empty? # rubocop:disable Homebrew/Blank, Lint/RedundantCopDisableDirective
end
end

66
scripts/time_formatter.rb Normal file
View File

@@ -0,0 +1,66 @@
# typed: strict
# frozen_string_literal: true
require "time"
# Module for formatting timestamps and dates
module TimeFormatter
SECONDS_PER_MINUTE = 60
SECONDS_PER_HOUR = 3600
SECONDS_PER_DAY = 86_400
SECONDS_PER_WEEK = 604_800
SECONDS_PER_MONTH = 2_419_200
SECONDS_PER_YEAR = 31_536_000
def format_relative_time(timestamp)
return "" unless timestamp
begin
diff = calculate_time_difference(timestamp)
return "just now" if diff < SECONDS_PER_MINUTE
format_time_by_category(diff)
rescue
""
end
end
def format_date(timestamp)
return "" unless timestamp
begin
Time.parse(timestamp).strftime("%b %d, %Y")
rescue
""
end
end
private
def calculate_time_difference(timestamp)
time = Time.parse(timestamp)
Time.now - time
end
def format_time_by_category(diff)
case diff
when SECONDS_PER_MINUTE...SECONDS_PER_HOUR
format_time_unit(diff / SECONDS_PER_MINUTE, "minute")
when SECONDS_PER_HOUR...SECONDS_PER_DAY
format_time_unit(diff / SECONDS_PER_HOUR, "hour")
when SECONDS_PER_DAY...SECONDS_PER_WEEK
format_time_unit(diff / SECONDS_PER_DAY, "day")
when SECONDS_PER_WEEK...SECONDS_PER_MONTH
format_time_unit(diff / SECONDS_PER_WEEK, "week")
when SECONDS_PER_MONTH...SECONDS_PER_YEAR
format_time_unit(diff / SECONDS_PER_MONTH, "month")
else
format_time_unit(diff / SECONDS_PER_YEAR, "year")
end
end
def format_time_unit(value, unit)
count = value.to_i
"#{count} #{unit}#{"s" if count != 1} ago"
end
end