Skip to content

Commit

Permalink
Convert RefinePathname to refinements and adjust loading strategy
Browse files Browse the repository at this point in the history
- Transformed RefinePathname from a prepended module to a refinement to prevent unintended effects on user scripts.
- Implemented `using` for selective application of RefinePathname in files that specifically require it.
- Ensured that RefinePathname does not globally affect `$LOADED_FEATURES`.
- Utilized `load` to manage the inclusion of RefinePathname in the `Option` and `RuntimeEnvironment` classes at specific times to control its impact on `$LOADED_FEATURES`.
  • Loading branch information
shinokaro committed Jul 27, 2024
1 parent eaef4c7 commit b7e7125
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 85 deletions.
3 changes: 0 additions & 3 deletions bin/ocran
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ module Ocran
def self.run_script? = @option.run_script?

def Ocran.init
load File.expand_path("../lib/ocran/refine_pathname.rb", __dir__)
::Pathname.prepend(RefinePathname)

load File.expand_path("../lib/ocran/runtime_environment.rb", __dir__)
@pre_env = RuntimeEnvironment.save

Expand Down
3 changes: 3 additions & 0 deletions lib/ocran/build_helper.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# frozen_string_literal: true
require "pathname"
require_relative "refine_pathname"

module Ocran
module BuildHelper
using RefinePathname

EMPTY_SOURCE = File.expand_path("empty_source", __dir__).freeze

def copy_to_bin(source, target)
Expand Down
5 changes: 4 additions & 1 deletion lib/ocran/direction.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# frozen_string_literal: true

require "pathname"
require_relative "refine_pathname"
require_relative "host_config_helper"

module Ocran
class Direction
using RefinePathname

# Match the load path against standard library, site_ruby, and vendor_ruby paths
# This regular expression matches:
# - /ruby/3.0.0/
Expand Down
2 changes: 2 additions & 0 deletions lib/ocran/file_path_set.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# frozen_string_literal: true
require "pathname"
require_relative "refine_pathname"

module Ocran
class FilePathSet
using RefinePathname
include Enumerable

def initialize
Expand Down
3 changes: 3 additions & 0 deletions lib/ocran/gem_spec_queryable.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# frozen_string_literal: true
require "rubygems"
require "pathname"
require_relative "refine_pathname"

module Ocran
module GemSpecQueryable
using RefinePathname

GEM_SCRIPT_RE = /\.rbw?$/
GEM_EXTRA_RE = %r{(
# Auxiliary files in the root of the gem
Expand Down
3 changes: 3 additions & 0 deletions lib/ocran/option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

module Ocran
class Option
load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
using RefinePathname

def initialize
@options = {
:add_all_core? => false,
Expand Down
164 changes: 83 additions & 81 deletions lib/ocran/refine_pathname.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,98 +5,100 @@ module Ocran
# The Pathname class in Ruby is modified to handle mixed path separators and
# to be case-insensitive.
module RefinePathname
def normalize_file_separator(s)
if File::ALT_SEPARATOR
s.tr(File::ALT_SEPARATOR, File::SEPARATOR)
else
s
refine Pathname do
def normalize_file_separator(s)
if File::ALT_SEPARATOR
s.tr(File::ALT_SEPARATOR, File::SEPARATOR)
else
s
end
end
end
private :normalize_file_separator
private :normalize_file_separator

# Compares two paths for equality based on the case sensitivity of the
# Ruby execution environment's file system.
# If the file system is case-insensitive, it performs a case-insensitive
# comparison. Otherwise, it performs a case-sensitive comparison.
def pathequal(a, b)
if File::FNM_SYSCASE.nonzero?
a.casecmp(b) == 0
else
a == b
# Compares two paths for equality based on the case sensitivity of the
# Ruby execution environment's file system.
# If the file system is case-insensitive, it performs a case-insensitive
# comparison. Otherwise, it performs a case-sensitive comparison.
def pathequal(a, b)
if File::FNM_SYSCASE.nonzero?
a.casecmp(b) == 0
else
a == b
end
end
end
private :pathequal
private :pathequal

def to_posix
normalize_file_separator(to_s)
end
def to_posix
normalize_file_separator(to_s)
end

# Checks if two Pathname objects are equal, considering the file system's
# case sensitivity and path separators. Returns false if the other object is not
# an Pathname.
# This method enables the use of the `uniq` method on arrays of Pathname objects.
def eql?(other)
return false unless other.is_a?(Pathname)
# Checks if two Pathname objects are equal, considering the file system's
# case sensitivity and path separators. Returns false if the other object is not
# an Pathname.
# This method enables the use of the `uniq` method on arrays of Pathname objects.
def eql?(other)
return false unless other.is_a?(Pathname)

a = normalize_file_separator(to_s)
b = normalize_file_separator(other.to_s)
pathequal(a, b)
end
a = normalize_file_separator(to_s)
b = normalize_file_separator(other.to_s)
pathequal(a, b)
end

alias == eql?
alias === eql?
alias == eql?
alias === eql?

# Calculates a normalized hash value for a pathname to ensure consistent
# hashing across different environments, particularly in Windows.
# This method first normalizes the path by:
# 1. Converting the file separator from the platform-specific separator
# to the common POSIX separator ('/') if necessary.
# 2. Converting the path to lowercase if the filesystem is case-insensitive.
# The normalized path string is then hashed, providing a stable hash value
# that is consistent with the behavior of eql? method, thus maintaining
# the integrity of hash-based data structures like Hash or Set.
#
# @return [Integer] A hash integer based on the normalized path.
def hash
path = if File::FNM_SYSCASE.nonzero?
to_s.downcase
else
to_s
end
normalize_file_separator(path).hash
end
# Calculates a normalized hash value for a pathname to ensure consistent
# hashing across different environments, particularly in Windows.
# This method first normalizes the path by:
# 1. Converting the file separator from the platform-specific separator
# to the common POSIX separator ('/') if necessary.
# 2. Converting the path to lowercase if the filesystem is case-insensitive.
# The normalized path string is then hashed, providing a stable hash value
# that is consistent with the behavior of eql? method, thus maintaining
# the integrity of hash-based data structures like Hash or Set.
#
# @return [Integer] A hash integer based on the normalized path.
def hash
path = if File::FNM_SYSCASE.nonzero?
to_s.downcase
else
to_s
end
normalize_file_separator(path).hash
end

# Checks if the current path is a sub path of the specified base_directory.
# Both paths must be either absolute paths or relative paths; otherwise, this
# method returns false.
def subpath?(base_directory)
s = relative_path_from(base_directory).each_filename.first
s != '.' && s != ".."
rescue ArgumentError
false
end
# Checks if the current path is a sub path of the specified base_directory.
# Both paths must be either absolute paths or relative paths; otherwise, this
# method returns false.
def subpath?(base_directory)
s = relative_path_from(base_directory).each_filename.first
s != '.' && s != ".."
rescue ArgumentError
false
end

# Appends the given suffix to the filename, preserving the file extension.
# If the filename has an extension, the suffix is inserted before the extension.
# If the filename does not have an extension, the suffix is appended to the end.
# This method handles both directory and file paths correctly.
#
# Examples:
# pathname = Pathname("path.to/foo.tar.gz")
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar.tar.gz>
#
# pathname = Pathname("path.to/foo")
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar>
#
def append_to_filename(suffix)
dirname + basename.sub(/(\.?[^.]+)?(\..*)?\z/, "\\1#{suffix}\\2")
end
# Appends the given suffix to the filename, preserving the file extension.
# If the filename has an extension, the suffix is inserted before the extension.
# If the filename does not have an extension, the suffix is appended to the end.
# This method handles both directory and file paths correctly.
#
# Examples:
# pathname = Pathname("path.to/foo.tar.gz")
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar.tar.gz>
#
# pathname = Pathname("path.to/foo")
# pathname.append_to_filename("_bar") # => #<Pathname:path.to/foo_bar>
#
def append_to_filename(suffix)
dirname + basename.sub(/(\.?[^.]+)?(\..*)?\z/, "\\1#{suffix}\\2")
end

# Checks if the file's extension matches the expected extension.
# The comparison is case-insensitive.
# Example usage: ocran_pathname.extname?(".exe")
def extname?(expected_ext)
extname.casecmp(expected_ext) == 0
# Checks if the file's extension matches the expected extension.
# The comparison is case-insensitive.
# Example usage: ocran_pathname.extname?(".exe")
def extname?(expected_ext)
extname.casecmp(expected_ext) == 0
end
end
end
end
3 changes: 3 additions & 0 deletions lib/ocran/runtime_environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
require "pathname"

module Ocran
load File.expand_path("refine_pathname.rb", __dir__) unless defined? RefinePathname
using RefinePathname

class RuntimeEnvironment
class << self
alias save new
Expand Down

0 comments on commit b7e7125

Please sign in to comment.