Module: Ocran::GemSpecQueryable

Defined in:
lib/ocran/gem_spec_queryable.rb

Constant Summary collapse

GEM_SCRIPT_RE =
/\.rbw?$/
GEM_EXTRA_RE =
%r{(
  # Auxiliary files in the root of the gem
  ^(\.\/)?(History|Install|Manifest|README|CHANGES|Licen[sc]e|Contributors|ChangeLog|BSD|GPL).*$ |
  # Installation files in the root of the gem
  ^(\.\/)?(Rakefile|setup.rb|extconf.rb)$ |
  # Documentation/test directories in the root of the gem
  ^(\.\/)?(doc|ext|examples|test|tests|benchmarks|spec)\/ |
  # Directories anywhere
  (^|\/)(\.autotest|\.svn|\.cvs|\.git)(\/|$) |
  # Unlikely extensions
  \.(rdoc|c|cpp|c\+\+|cxx|h|hxx|hpp|obj|o|a)$
)}xi
GEM_NON_FILE_RE =
/(#{GEM_EXTRA_RE}|#{GEM_SCRIPT_RE})/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.detect_gems_from(features, verbose: false) ⇒ Object

Fall back to gem detection



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ocran/gem_spec_queryable.rb', line 87

def detect_gems_from(features, verbose: false)
  features.inject([]) do |gems, feature|
    if gems.any? { |spec| feature.subpath?(spec.gem_dir) }
      # Skip if found in known Gem dir
    elsif (spec = GemSpecQueryable.find_spec(feature))
      gems << spec
    else
      puts "Failed to load gemspec for #{feature}" if verbose
    end

    gems
  end
end

.find_gem_path(path) ⇒ Object

find_gem_path method searches for the path of the gem containing the specified path. The 'path' argument is a file or directory path. It checks each gem's installation path in Gem.path to see if the specified path is a subpath. Returns the gem's path if found, or nil if not found.



31
32
33
34
35
# File 'lib/ocran/gem_spec_queryable.rb', line 31

def find_gem_path(path)
  return unless defined?(Gem)

  Gem.path.find { |gem_path| Pathname(path).subpath?(gem_path) }
end

.find_spec(path) ⇒ Object

find_spec method searches and returns a Gem::Specification object based on the specified path. Internally, it uses find_spec_file to obtain the path to the gemspec file, and if that file exists, it calls Gem::Specification.load to load the gem's specifications. Returns the loaded Gem::Specification object, or nil if the gemspec file does not exist.



64
65
66
67
68
69
# File 'lib/ocran/gem_spec_queryable.rb', line 64

def find_spec(path)
  return unless defined?(Gem)

  spec_file = find_spec_file(path)
  spec_file && Gem::Specification.load(spec_file)
end

.find_spec_file(path) ⇒ Object

find_spec_file method searches for the path of the gemspec file of the gem containing the specified path. The 'path' argument is a file or directory path. It searches within the "gems" directory in each directory listed in Gem.path to check if the specified path is a subpath. If the gemspec file exists, it returns its path; otherwise, it returns nil.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ocran/gem_spec_queryable.rb', line 43

def find_spec_file(path)
  return unless defined?(Gem)

  feature = Pathname(path)
  Gem.path.each do |gem_path|
    gems_dir = File.join(gem_path, "gems")
    next unless feature.subpath?(gems_dir)

    full_name = feature.relative_path_from(gems_dir).each_filename.first
    spec_path = File.join(gem_path, "specifications", "#{full_name}.gemspec")
    return spec_path if File.exist?(spec_path)
  end
  nil
end

.gem_inclusion_set(spec_name, gem_options) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/ocran/gem_spec_queryable.rb', line 101

def gem_inclusion_set(spec_name, gem_options)
  include = [:loaded, :files]
  gem_options.each do |negate, option, list|
    next unless list.nil? || list.include?(spec_name)

    case option
    when :minimal
      include = [:loaded]
    when :guess
      include = [:loaded, :files]
    when :all
      include = [:scripts, :files]
    when :full
      include = [:scripts, :files, :extras]
    when :spec
      include = [:spec]
    when :scripts, :files, :extras
      if negate
        include.delete(option)
      else
        include.push(option)
      end
    else
      raise "Invalid Gem content detection option: #{option}"
    end
  end
  include.uniq!
  include
end

.scanning_gemfile(gemfile_path) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ocran/gem_spec_queryable.rb', line 71

def scanning_gemfile(gemfile_path)
  # Ensure the necessary libraries are loaded to scan the Gemfile.
  # This is particularly useful in custom-built Ruby environments or
  # where certain libraries might be excluded.
  %w[rubygems bundler].each do |lib|
    require lib
  rescue LoadError
    raise "Couldn't scan Gemfile, unable to load #{lib}"
  end

  ENV["BUNDLE_GEMFILE"] = gemfile_path.to_s
  # Bundler.load.specs includes the spec for Bundler itself
  Bundler.load.specs.to_a
end

Instance Method Details

#extra_filesObject



144
145
146
# File 'lib/ocran/gem_spec_queryable.rb', line 144

def extra_files
  gem_root_files.select { |path| path.relative_path_from(gem_root).to_posix =~ GEM_EXTRA_RE }
end

#find_gem_files(file_sets, features_from_gems) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/ocran/gem_spec_queryable.rb', line 154

def find_gem_files(file_sets, features_from_gems)
  actual_files = file_sets.flat_map do |set|
    case set
    when :spec
      files.map { |file| Pathname(file) }
    when :loaded
      # Some distros expose gem files under additional paths via symlinks
      # (e.g. Fedora symlinks /usr/share/ruby/psych.rb into the psych gem
      # directory) and $LOADED_FEATURES records the symlink path. Resolve
      # each feature to its realpath so such files count as gem files and
      # are packed inside the gem directory. Packing them at the symlink
      # location instead would break require_relative, which resolves
      # against the realpath on the build host but not in the packed app.
      features_from_gems.filter_map do |feature|
        if feature.subpath?(gem_dir)
          feature
        else
          real = begin
            feature.realpath
          rescue SystemCallError
            next
          end
          real if real.subpath?(gem_dir)
        end
      end
    when :files
      resource_files
    when :extras
      extra_files
    when :scripts
      script_files
    else
      raise "Invalid file set: #{set}. Please specify a valid file set (:spec, :loaded, :files, :extras, :scripts)."
    end
  end
  actual_files.uniq
  actual_files
end

#gem_rootObject



132
# File 'lib/ocran/gem_spec_queryable.rb', line 132

def gem_root = Pathname(gem_dir)

#gem_root_filesObject

Find the selected files. Default gems (e.g. fiddle, singleton on Homebrew or distro-packaged Ruby) may have a gemspec without a materialized gem directory - their files live in Ruby's stdlib and are packed via the load path instead.



138
# File 'lib/ocran/gem_spec_queryable.rb', line 138

def gem_root_files = gem_root.directory? ? gem_root.find.select(&:file?) : []

#resource_filesObject



148
149
150
151
152
# File 'lib/ocran/gem_spec_queryable.rb', line 148

def resource_files
  files = gem_root_files.select { |path| path.relative_path_from(gem_root).to_posix !~ GEM_NON_FILE_RE }
  files << Pathname(gem_build_complete_path) if File.exist?(gem_build_complete_path)
  files
end

#script_filesObject



140
141
142
# File 'lib/ocran/gem_spec_queryable.rb', line 140

def script_files
  gem_root_files.select { |path| path.extname =~ GEM_SCRIPT_RE }
end