Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make gem work again #5

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading