faster_path 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fb5780bd8ce8ea17d538b540ac69b1c023118d1351e167cde418ee48a4f8085
4
- data.tar.gz: b12bc7a5ab8e4e26cfd46328bdb9e4b57eea367a9c40e76aeba74e12f06eb0e7
3
+ metadata.gz: fc913650b2f0738a6fd67abfadedea3bd40b91942a0743ef30e57ec8c976724d
4
+ data.tar.gz: 73316052dfde708cedc6ee10bfc41a63554411a02a8ad10e39bfd94d2280445e
5
5
  SHA512:
6
- metadata.gz: fa615d0278378e74432832369cf56b16147ecc0e5e27bd12b1fd90c2690fab41dc68fd3042e8dd7c92b8ebb7741f7d08e9db25bd0cec61b384b520f9bd8b400a
7
- data.tar.gz: 905389671e4062ef0824e7fb7af8d05fefb1df0ad2a5919bc8a54a8506309ba77cea577859ea49da4e15616fd2fb4bf1d025e5082baef6e61bf18c0c9799d000
6
+ metadata.gz: 87fc380b17b8c7ce94a3d5cc1026fc41902257624c058eb4806eab4e6fa2a4b102f6dcc7819304888f8abdbf39f9b45b59ad4aa00ece6fc5622aa25d8404e5b6
7
+ data.tar.gz: '083f2de1d9e2852a56ecd1d44d3232da1b52a5975d9eb6e7de36ece4dcb91e69280daff0091d337d8b03bda33ed3a2a3b48c5334b3049b727c995481602875ac'
data/Cargo.lock CHANGED
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "faster_path"
7
- version = "0.5.0"
7
+ version = "0.5.1"
8
8
  dependencies = [
9
9
  "memchr",
10
10
  "rutie",
data/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "faster_path"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  authors = ["Daniel P. Clark <6ftdan@gmail.com>"]
5
5
  description = "Alternative to Pathname"
6
6
  homepage = "https://github.com/danielpclark/faster_path"
data/README.md CHANGED
@@ -114,12 +114,16 @@ As mentioned earlier Sprockets, which handles assets, changed away from using `P
114
114
  ## Requirements
115
115
 
116
116
  * Ruby 3.2, 3.3 or 3.4 (the Rust extension is built with [Rutie](https://github.com/danielpclark/rutie) 0.13, which supports these)
117
- * Rust 1.71 or later: `gem install` builds the extension with Cargo (through the
117
+ * Rust 1.71 or later, unless a prebuilt extension exists for your platform (see
118
+ [Installation](#installation)): `gem install` builds the extension with Cargo (through the
118
119
  [rutie](https://rubygems.org/gems/rutie) gem's Rake task) and keeps only the built library
119
120
 
120
121
  ## Installation
121
122
 
122
- Ensure Rust is installed, with `cargo` on your `PATH`:
123
+ Each [release](https://github.com/danielpclark/faster_path/releases) carries a prebuilt
124
+ extension for Ruby 3.4 on Linux x86_64, macOS (arm64 and x86_64) and Windows x64. On those,
125
+ `gem install` needs no Rust. Everywhere else (another Ruby version, OS or CPU), ensure Rust is
126
+ installed, with `cargo` on your `PATH`:
123
127
 
124
128
  [Install Rust](https://www.rust-lang.org/tools/install)
125
129
 
@@ -130,7 +134,7 @@ curl -sSf https://sh.rustup.rs | sh
130
134
  Add this line to your application's Gemfile:
131
135
 
132
136
  ```ruby
133
- gem 'faster_path', '~> 0.5.0'
137
+ gem 'faster_path', '~> 0.5.1'
134
138
  ```
135
139
 
136
140
  And then execute:
@@ -143,9 +147,11 @@ Or install it yourself as:
143
147
 
144
148
  Installing compiles the Rust extension for the Ruby doing the installing ("Building native
145
149
  extensions. This could take a while..."), which takes about a minute the first time while Cargo
146
- fetches and builds the dependencies. If Rust is missing, the install fails at that step with
147
- Cargo not being found; install Rust and run it again. Nothing is downloaded from GitHub: there are
148
- no prebuilt binaries.
150
+ fetches and builds the dependencies. When Cargo is not found, the install downloads the release's
151
+ prebuilt extension for your platform instead (`faster_path-<version>-ruby<XY>-<os>-<cpu>.tar.gz`,
152
+ checked against its SHA-256 file) and fails with a message naming that file if there is none.
153
+ `FASTER_PATH_PREBUILT_URL` points the download at another directory holding the same files, for
154
+ mirrors or machines without access to GitHub.
149
155
 
150
156
  ## Visual Benchmarks
151
157
 
data/Rakefile CHANGED
@@ -34,6 +34,13 @@ end
34
34
  desc 'Build the Rust extension'
35
35
  task build_lib: 'rutie:build'
36
36
 
37
+ desc 'Package the built extension as the prebuilt release asset for this platform, in pkg/'
38
+ task prebuilt: 'rutie:build' do
39
+ require 'faster_path/prebuilt'
40
+ FileUtils.mkdir_p('pkg')
41
+ puts FasterPath::Prebuilt.package(FasterPath::BuildTask.built_library, 'pkg')
42
+ end
43
+
37
44
  desc 'Code Quality Check'
38
45
  task :lint do
39
46
  puts
data/ext/Rakefile CHANGED
@@ -1,14 +1,40 @@
1
1
  require 'fileutils'
2
2
  require_relative '../lib/faster_path/build_task'
3
+ require_relative '../lib/faster_path/prebuilt'
3
4
 
4
5
  FasterPath::BuildTask.define
5
6
 
6
- # Keeps the built library (in lib/faster_path, where FasterPath loads it from)
7
- # and removes the rest of Cargo's output from the installed gem.
7
+ # Builds the extension, keeps the built library (in lib/faster_path, where
8
+ # FasterPath loads it from) and removes the rest of Cargo's output from the
9
+ # installed gem.
8
10
  task install_library: 'rutie:build' do
9
11
  FileUtils.mkdir_p(FasterPath::BuildTask::INSTALLED_LIBRARY_DIR)
10
12
  FileUtils.cp(FasterPath::BuildTask.built_library, FasterPath::BuildTask::INSTALLED_LIBRARY_DIR)
11
13
  Rake::Task['rutie:clean'].invoke
12
14
  end
13
15
 
14
- task default: :install_library
16
+ NO_PREBUILT = <<~MESSAGE
17
+ ****
18
+ Rust's Cargo was not found, and this release of faster_path has no prebuilt
19
+ extension for this platform (%s).
20
+ Install Rust (https://www.rust-lang.org/tools/install) and run the install
21
+ again, or set FASTER_PATH_PREBUILT_URL to a directory holding the asset.
22
+ ****
23
+ MESSAGE
24
+
25
+ # Installs the prebuilt library from this version's GitHub release instead.
26
+ task :install_prebuilt do
27
+ library = FasterPath::Prebuilt.install(FasterPath::BuildTask::PROJECT_DIR)
28
+ raise format(NO_PREBUILT, FasterPath::Prebuilt.asset_name) unless library
29
+
30
+ puts "Installed the prebuilt extension #{File.basename(library)}"
31
+ end
32
+
33
+ task :default do
34
+ if FasterPath::BuildTask.cargo?
35
+ Rake::Task[:install_library].invoke
36
+ else
37
+ puts 'Cargo was not found; downloading the prebuilt extension for this platform instead'
38
+ Rake::Task[:install_prebuilt].invoke
39
+ end
40
+ end
data/faster_path.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.files = [
17
17
  'Cargo.lock', 'Cargo.toml', 'Gemfile', 'MIT-LICENSE.txt', 'README.md', 'Rakefile',
18
18
  'bin/console', 'bin/setup', 'ext/Rakefile', 'faster_path.gemspec', 'lib/faster_path.rb',
19
- 'lib/faster_path/version.rb', 'lib/faster_path/build_task.rb',
19
+ 'lib/faster_path/version.rb', 'lib/faster_path/build_task.rb', 'lib/faster_path/prebuilt.rb',
20
20
  'lib/faster_path/optional/monkeypatches.rb', 'lib/faster_path/optional/refinements.rb'
21
21
  ]
22
22
  spec.files += Dir['src/**/*']
@@ -28,5 +28,15 @@ module FasterPath
28
28
  def self.built_library
29
29
  Rutie.new(:faster_path).ffi_library(File.join(PROJECT_DIR, 'lib'))
30
30
  end
31
+
32
+ # Whether Cargo can be run: named by CARGO, or on the PATH
33
+ def self.cargo?
34
+ return true if ENV['CARGO']
35
+
36
+ exts = RbConfig::CONFIG['EXEEXT'].empty? ? [''] : ['', RbConfig::CONFIG['EXEEXT']]
37
+ ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).any? do |dir|
38
+ exts.any? { |ext| File.executable?(File.join(dir, "cargo#{ext}")) }
39
+ end
40
+ end
31
41
  end
32
42
  end
@@ -0,0 +1,118 @@
1
+ require 'digest/sha2'
2
+ require 'fileutils'
3
+ require 'net/http'
4
+ require 'rbconfig'
5
+ require 'rubygems/package'
6
+ require 'stringio'
7
+ require 'uri'
8
+ require 'zlib'
9
+ require_relative 'version'
10
+
11
+ module FasterPath
12
+ # The prebuilt extension that the GitHub release of each version carries
13
+ # for a few platforms, so that the gem installs without Rust there:
14
+ # `gem install` falls back to it when Cargo is not found (see ext/Rakefile),
15
+ # and the release workflow builds it with `rake prebuilt`.
16
+ module Prebuilt
17
+ RELEASES = 'https://github.com/danielpclark/faster_path/releases/download'.freeze
18
+ # Where the library sits in the tarball and, unpacked, in the gem
19
+ LIBRARY_DIR = File.join('lib', 'faster_path').freeze
20
+ MAX_REDIRECTS = 5
21
+
22
+ # The asset for the running Ruby, e.g. "faster_path-0.5.0-ruby34-linux-x86_64.tar.gz":
23
+ # the gem version, the Ruby minor version the library was built for (an
24
+ # extension works with every patch release of it), the OS family and the CPU.
25
+ def self.asset_name(rbconfig = RbConfig::CONFIG, version = FasterPath::VERSION)
26
+ major, minor = rbconfig['ruby_version'].split('.')
27
+ "faster_path-#{version}-ruby#{major}#{minor}-#{os(rbconfig)}-#{cpu(rbconfig)}.tar.gz"
28
+ end
29
+
30
+ def self.os(rbconfig)
31
+ case rbconfig['host_os']
32
+ when /darwin/ then 'darwin'
33
+ when /mingw|mswin|cygwin/ then 'windows'
34
+ else rbconfig['host_os'][/\A[a-z]+/]
35
+ end
36
+ end
37
+
38
+ def self.cpu(rbconfig)
39
+ { 'x64' => 'x86_64', 'amd64' => 'x86_64', 'aarch64' => 'arm64' }.fetch(rbconfig['target_cpu'], rbconfig['target_cpu'])
40
+ end
41
+
42
+ # Packs the built `library` as lib/faster_path/<file> into `dir`, with a
43
+ # SHA-256 checksum file next to it, and returns the tarball's path.
44
+ def self.package(library, dir, asset = asset_name)
45
+ path = File.join(dir, asset)
46
+ data = File.binread(library)
47
+ File.open(path, 'wb') do |file|
48
+ Zlib::GzipWriter.wrap(file) do |gz|
49
+ Gem::Package::TarWriter.new(gz) do |tar|
50
+ tar.add_file_simple(File.join(LIBRARY_DIR, File.basename(library)), 0o644, data.bytesize) { |io| io.write(data) }
51
+ end
52
+ end
53
+ end
54
+ File.write("#{path}.sha256", "#{Digest::SHA256.file(path).hexdigest} #{asset}\n")
55
+ path
56
+ end
57
+
58
+ # Downloads this platform's asset of this version's release, checks it
59
+ # against its checksum file and unpacks the library into `project_dir`.
60
+ # Returns the library's path, or nil when the release has no asset for
61
+ # this platform. FASTER_PATH_PREBUILT_URL replaces the release's URL.
62
+ def self.install(project_dir, base_url = ENV.fetch('FASTER_PATH_PREBUILT_URL', "#{RELEASES}/v#{FasterPath::VERSION}"))
63
+ asset = asset_name
64
+ tarball = fetch("#{base_url}/#{asset}")
65
+ return nil unless tarball
66
+
67
+ checksum = fetch("#{base_url}/#{asset}.sha256")
68
+ raise "#{asset} has no checksum file at #{base_url}" unless checksum
69
+
70
+ verify(tarball, checksum, asset)
71
+ unpack(tarball, project_dir)
72
+ end
73
+
74
+ # The body at `url`, following redirects, or nil if it does not exist.
75
+ def self.fetch(url, redirects = MAX_REDIRECTS)
76
+ uri = URI(url)
77
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.get(uri.request_uri) }
78
+ case response
79
+ when Net::HTTPSuccess then response.body
80
+ when Net::HTTPNotFound then nil
81
+ when Net::HTTPRedirection
82
+ raise "Too many redirects fetching #{url}" if redirects.zero?
83
+
84
+ fetch(URI.join(url, response['Location']).to_s, redirects - 1)
85
+ else raise "#{url}: #{response.code} #{response.message}"
86
+ end
87
+ end
88
+
89
+ def self.verify(tarball, checksum, asset)
90
+ expected = checksum.split.first.to_s.downcase
91
+ actual = Digest::SHA256.hexdigest(tarball)
92
+ raise "#{asset} does not match its checksum (expected #{expected}, got #{actual})" unless expected == actual
93
+ end
94
+
95
+ # Unpacks the library of the tarball `data` into `project_dir` and returns its path.
96
+ def self.unpack(data, project_dir)
97
+ installed = nil
98
+ Zlib::GzipReader.wrap(StringIO.new(data)) do |gz|
99
+ Gem::Package::TarReader.new(gz) do |tar|
100
+ tar.each do |entry|
101
+ next unless entry.file? && File.dirname(entry.full_name) == LIBRARY_DIR
102
+
103
+ installed = write_library(project_dir, File.basename(entry.full_name), entry.read)
104
+ end
105
+ end
106
+ end
107
+ installed || raise("No #{LIBRARY_DIR} library in the tarball")
108
+ end
109
+
110
+ def self.write_library(project_dir, name, data)
111
+ dir = File.join(project_dir, LIBRARY_DIR)
112
+ FileUtils.mkdir_p(dir)
113
+ path = File.join(dir, name)
114
+ File.binwrite(path, data)
115
+ path
116
+ end
117
+ end
118
+ end
@@ -1,3 +1,3 @@
1
1
  module FasterPath
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faster_path
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
@@ -157,6 +157,7 @@ files:
157
157
  - lib/faster_path/build_task.rb
158
158
  - lib/faster_path/optional/monkeypatches.rb
159
159
  - lib/faster_path/optional/refinements.rb
160
+ - lib/faster_path/prebuilt.rb
160
161
  - lib/faster_path/version.rb
161
162
  - src/basename.rs
162
163
  - src/chop_basename.rs