From b7e7125427f3040d25aeb34f1189da3cb0ad7d9a Mon Sep 17 00:00:00 2001 From: shinokaro Date: Sun, 28 Jul 2024 05:10:30 +0900 Subject: [PATCH] Convert RefinePathname to refinements and adjust loading strategy - 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`. --- bin/ocran | 3 - lib/ocran/build_helper.rb | 3 + lib/ocran/direction.rb | 5 +- lib/ocran/file_path_set.rb | 2 + lib/ocran/gem_spec_queryable.rb | 3 + lib/ocran/option.rb | 3 + lib/ocran/refine_pathname.rb | 164 ++++++++++++++++--------------- lib/ocran/runtime_environment.rb | 3 + 8 files changed, 101 insertions(+), 85 deletions(-) diff --git a/bin/ocran b/bin/ocran index 539d806..994b09f 100644 --- a/bin/ocran +++ b/bin/ocran @@ -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 diff --git a/lib/ocran/build_helper.rb b/lib/ocran/build_helper.rb index a902741..b6c10df 100644 --- a/lib/ocran/build_helper.rb +++ b/lib/ocran/build_helper.rb @@ -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) diff --git a/lib/ocran/direction.rb b/lib/ocran/direction.rb index b5b244c..036fee4 100644 --- a/lib/ocran/direction.rb +++ b/lib/ocran/direction.rb @@ -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/ diff --git a/lib/ocran/file_path_set.rb b/lib/ocran/file_path_set.rb index 44e53a0..57b9836 100644 --- a/lib/ocran/file_path_set.rb +++ b/lib/ocran/file_path_set.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true require "pathname" +require_relative "refine_pathname" module Ocran class FilePathSet + using RefinePathname include Enumerable def initialize diff --git a/lib/ocran/gem_spec_queryable.rb b/lib/ocran/gem_spec_queryable.rb index 26a4c7f..46b0fd3 100644 --- a/lib/ocran/gem_spec_queryable.rb +++ b/lib/ocran/gem_spec_queryable.rb @@ -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 diff --git a/lib/ocran/option.rb b/lib/ocran/option.rb index 7f659d3..c35607d 100644 --- a/lib/ocran/option.rb +++ b/lib/ocran/option.rb @@ -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, diff --git a/lib/ocran/refine_pathname.rb b/lib/ocran/refine_pathname.rb index e2b80fe..1fb4c31 100644 --- a/lib/ocran/refine_pathname.rb +++ b/lib/ocran/refine_pathname.rb @@ -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 = Pathname("path.to/foo") - # pathname.append_to_filename("_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 = Pathname("path.to/foo") + # pathname.append_to_filename("_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 diff --git a/lib/ocran/runtime_environment.rb b/lib/ocran/runtime_environment.rb index c6d1914..a530b5f 100644 --- a/lib/ocran/runtime_environment.rb +++ b/lib/ocran/runtime_environment.rb @@ -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