Skip to content

Commit

Permalink
feat: make gem work again
Browse files Browse the repository at this point in the history
  • Loading branch information
ronaldtse committed Jul 24, 2024
1 parent 8ba789e commit 2ef1fae
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 82 deletions.
16 changes: 8 additions & 8 deletions lib/poepod/cli.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# lib/poepod/cli.rb
# frozen_string_literal: true

# lib/poepod/cli.rb
require "thor"
require_relative "file_processor"
require_relative "gem_processor"
Expand All @@ -10,7 +10,7 @@ module Poepod
class Cli < Thor
# Define shared options
def self.shared_options
option :exclude, type: :array, default: Poepod::FileProcessor::EXCLUDE_DEFAULT,
option :exclude, type: :array, default: nil,
desc: "List of patterns to exclude"
option :config, type: :string, desc: "Path to configuration file"
option :include_binary, type: :boolean, default: false, desc: "Include binary files (encoded in MIME format)"
Expand All @@ -36,16 +36,17 @@ def concat(*files)

def wrap(gemspec_path)
base_dir = options[:base_dir] || File.dirname(gemspec_path)
output_file = options[:output_file] || File.join(base_dir, "#{File.basename(gemspec_path, ".*")}_wrapped.txt")
processor = Poepod::GemProcessor.new(
gemspec_path,
include_unstaged: options[:include_unstaged],
exclude: options[:exclude],
include_binary: options[:include_binary],
include_dot_files: options[:include_dot_files],
base_dir: base_dir,
config_file: options[:config]
config_file: options[:config],
)
success, result, unstaged_files = processor.process
success, result, unstaged_files = processor.process(output_file)
if success
handle_wrap_result(success, result, unstaged_files)
else
Expand Down Expand Up @@ -80,9 +81,9 @@ def process_files(files, output_file, base_dir)
include_binary: options[:include_binary],
include_dot_files: options[:include_dot_files],
exclude: options[:exclude],
base_dir: base_dir
base_dir: base_dir,
)
total_files, copied_files = processor.process
total_files, copied_files = processor.process(output_path.to_s)
print_result(total_files, copied_files, output_path)
end

Expand Down Expand Up @@ -114,8 +115,7 @@ def default_output_file(first_pattern)
if File.directory?(first_item)
"#{File.basename(first_item)}.txt"
else
"#{File.basename(first_item,
".*")}_concat.txt"
"#{File.basename(first_item, ".*")}_concat.txt"
end
else
"concatenated_output.txt"
Expand Down
20 changes: 6 additions & 14 deletions lib/poepod/file_processor.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
# lib/poepod/file_processor.rb
# frozen_string_literal: true

require_relative "processor"

module Poepod
# Processes files for concatenation, handling binary and dot files
class FileProcessor < Processor
EXCLUDE_DEFAULT = [
%r{node_modules/}, %r{.git/}, /.gitignore$/, /.DS_Store$/, /^\..+/
].freeze

def initialize(
files,
patterns,
output_file,
config_file: nil,
include_binary: false,
include_dot_files: false,
exclude: [],
exclude: nil,
base_dir: nil
)
super(
Expand All @@ -25,20 +22,15 @@ def initialize(
exclude: exclude,
base_dir: base_dir,
)
@files = files
@patterns = patterns
@output_file = output_file
end

private

def collect_files_to_process
@files.flatten.each_with_object([]) do |file, files_to_process|
Dir.glob(file, File::FNM_DOTMATCH).each do |matched_file|
next unless File.file?(matched_file)
next if should_exclude?(matched_file)

files_to_process << matched_file
end
@patterns.flatten.each_with_object([]) do |pattern, files_to_process|
files_to_process.concat(collect_files_from_pattern(pattern))
end
end
end
Expand Down
33 changes: 16 additions & 17 deletions lib/poepod/gem_processor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# frozen_string_literal: true

# lib/poepod/gem_processor.rb
require_relative "processor"
require "rubygems/specification"
require "git"
Expand All @@ -11,7 +10,7 @@ class GemProcessor < Processor
def initialize(
gemspec_path,
include_unstaged: false,
exclude: [],
exclude: nil,
include_binary: false,
include_dot_files: false,
base_dir: nil,
Expand All @@ -28,29 +27,23 @@ def initialize(
@include_unstaged = include_unstaged
end

def process
def process(output_file)
return error_no_gemspec unless File.exist?(@gemspec_path)

spec = load_gemspec
return spec unless spec.is_a?(Gem::Specification)

gem_name = spec.name
@output_file = "#{gem_name}_wrapped.txt"
unstaged_files = check_unstaged_files

super()
total_files, copied_files = super(output_file)

[true, @output_file, unstaged_files]
[true, output_file, unstaged_files]
end

private

def collect_files_to_process
spec = load_gemspec
files_to_include = (spec.files +
spec.test_files +
find_readme_files).uniq

files_to_include = find_gemspec_files
files_to_include += check_unstaged_files if @include_unstaged

files_to_include.sort.uniq.reject do |relative_path|
Expand All @@ -60,6 +53,13 @@ def collect_files_to_process
end
end

def find_gemspec_files
spec = load_gemspec
executables = spec.bindir ? collect_files_from_pattern(File.join(@base_dir, spec.bindir, "*")) : []

(spec.files + spec.test_files + find_readme_files + executables).uniq
end

def error_no_gemspec
[false, "Error: The specified gemspec file '#{@gemspec_path}' does not exist."]
end
Expand All @@ -71,10 +71,9 @@ def load_gemspec
end

def find_readme_files
Dir.glob(File.join(File.dirname(@gemspec_path), "README*")).map do |path|
Pathname.new(path).relative_path_from(
Pathname.new(File.dirname(@gemspec_path))
).to_s
gemspec_dir = Pathname.new(File.dirname(@gemspec_path))
Dir.glob(gemspec_dir.join("README*")).map do |path|
Pathname.new(path).relative_path_from(gemspec_dir).to_s
end
end

Expand All @@ -86,7 +85,7 @@ def check_unstaged_files
modified_files = git.status.changed.keys

(untracked_files + modified_files).select do |file|
file.start_with?("lib/", "spec/", "test/")
file.start_with?("bin/", "exe/", "lib/", "spec/", "test/")
end
rescue Git::GitExecuteError => e
warn "Git error: #{e.message}. Assuming no unstaged files."
Expand Down
72 changes: 49 additions & 23 deletions lib/poepod/processor.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# lib/poepod/processor.rb
# frozen_string_literal: true

require "yaml"
Expand All @@ -8,33 +9,65 @@
module Poepod
# Base processor class
class Processor
EXCLUDE_DEFAULT = [
%r{node_modules/}, %r{.git/}, /.gitignore$/, /.DS_Store$/,
].freeze

def initialize(
config_file = nil,
include_binary: false,
include_dot_files: false,
exclude: [],
exclude: nil,
base_dir: nil
)
@config = load_config(config_file)
@include_binary = include_binary
@include_dot_files = include_dot_files
@exclude = exclude || []
@exclude = exclude || EXCLUDE_DEFAULT
@base_dir = base_dir
@failed_files = []
end

def process
def process(output_file)
files_to_process = collect_files_to_process
total_files, copied_files = process_files(files_to_process)
total_files, copied_files = process_files(files_to_process, output_file)
[total_files, copied_files]
end

private

def process_files(files, output_file)
total_files = files.size
copied_files = 0

File.open(output_file, "w", encoding: "utf-8") do |output|
files.sort.each do |file_path|
process_file(output, file_path)
copied_files += 1
end
end

[total_files, copied_files]
end

def collect_files_to_process
raise NotImplementedError, "Subclasses must implement collect_files_to_process"
end

def collect_files_from_pattern(pattern)
expanded_pattern = File.expand_path(pattern)
if File.directory?(expanded_pattern)
expanded_pattern = File.join(expanded_pattern, "**", "*")
end

Dir.glob(expanded_pattern, File::FNM_DOTMATCH).each_with_object([]) do |file_path, acc|
next unless File.file?(file_path)
next if should_exclude?(file_path)

acc << file_path
end
end

def load_config(config_file)
return {} unless config_file && File.exist?(config_file)

Expand All @@ -46,33 +79,26 @@ def binary_file?(file_path)

File.open(file_path, "rb") do |file|
content = file.read(8192) # Read first 8KB for magic byte detection
mime_type = Marcel::MimeType.for(content, name: File.basename(file_path), declared_type: "text/plain")
!mime_type.start_with?("text/") && mime_type != "application/json"
end
end
mime_type = Marcel::MimeType.for(
content,
name: File.basename(file_path),
declared_type: "text/plain",
)

def process_files(files)
total_files = files.size
copied_files = 0

File.open(@output_file, "w", encoding: "utf-8") do |output|
files.sort.each do |file_path|
process_file(output, file_path)
copied_files += 1
end
!mime_type.start_with?("text/") && mime_type != "application/json"
end

[total_files, copied_files]
end

def process_file(output = nil, file_path)
output ||= StringIO.new

relative_path = if @base_dir
Pathname.new(file_path).relative_path_from(Pathname.new(@base_dir)).to_s
else
file_path
end
Pathname.new(file_path).relative_path_from(@base_dir).to_s
else
file_path
end

puts "Adding to bundle: #{relative_path}"

output.puts "--- START FILE: #{relative_path} ---"

Expand Down
2 changes: 0 additions & 2 deletions poepod.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0").reject do |f|
f.match(%r{^(test|spec|features)/})
Expand Down
15 changes: 7 additions & 8 deletions spec/poepod/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
it "concatenates text files and excludes binary and dot files by default" do
output_file = File.join(temp_dir, "output.txt")
expect do
cli.invoke(:concat, [File.join(temp_dir, "*")], { output_file: output_file })
cli.invoke(:concat, [text_file], { output_file: output_file })
end.to output(/1 files detected\.\n.*1 files have been concatenated/).to_stdout
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
Expand All @@ -36,7 +36,7 @@
it "includes binary files when specified" do
output_file = File.join(temp_dir, "output.txt")
expect do
cli.invoke(:concat, [File.join(temp_dir, "*")], { output_file: output_file, include_binary: true })
cli.invoke(:concat, [text_file, binary_file], { output_file: output_file, include_binary: true })
end.to output(/2 files detected\.\n.*2 files have been concatenated/).to_stdout
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
Expand All @@ -47,7 +47,7 @@
it "includes dot files when specified" do
output_file = File.join(temp_dir, "output.txt")
expect do
cli.invoke(:concat, [File.join(temp_dir, "*")], { output_file: output_file, include_dot_files: true })
cli.invoke(:concat, [text_file, dot_file], { output_file: output_file, include_dot_files: true })
end.to output(/2 files detected\.\n.*2 files have been concatenated/).to_stdout
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
Expand All @@ -57,9 +57,8 @@

it "uses the specified base directory for relative paths" do
output_file = File.join(temp_dir, "output.txt")
base_dir = File.dirname(text_file)
expect do
cli.invoke(:concat, [File.join(temp_dir, "*")], { output_file: output_file, base_dir: base_dir })
cli.invoke(:concat, [text_file], { output_file: output_file, base_dir: temp_dir })
end.to output(/1 files detected\.\n.*1 files have been concatenated/).to_stdout
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
Expand Down Expand Up @@ -95,8 +94,8 @@
end

it "wraps a gem" do
output_file = File.join(temp_dir, "test_gem_wrapped.txt")
expect { cli.wrap(gemspec_file) }.to output(/The gem has been wrapped into/).to_stdout
output_file = File.join(Dir.pwd, "test_gem_wrapped.txt")
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
expect(content).to include("--- START FILE: lib/test_gem.rb ---")
Expand All @@ -112,10 +111,10 @@

it "uses the specified base directory for relative paths" do
base_dir = File.dirname(gemspec_file)
output_file = File.join(base_dir, "test_gem_wrapped.txt")
expect do
cli.invoke(:wrap, [gemspec_file], { base_dir: base_dir })
cli.invoke(:wrap, [gemspec_file], { base_dir: base_dir, output_file: output_file })
end.to output(/The gem has been wrapped into/).to_stdout
output_file = File.join(Dir.pwd, "test_gem_wrapped.txt")
expect(File.exist?(output_file)).to be true
content = File.read(output_file)
expect(content).to include("--- START FILE: lib/test_gem.rb ---")
Expand Down
Loading

0 comments on commit 2ef1fae

Please sign in to comment.