bootsnap 1.7.6 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57f9ed21a419f1e3b29ab1b950b5f221739d448f991a8a4ce2d89f364d076ce9
4
- data.tar.gz: 5a3626fcc6cd579fad8e3c018af381739cd62d04d73350dc094346e3e297b3a8
3
+ metadata.gz: 40e9beab427c546339a38041cdc59b826389b30d1976fb9586b6782daf728ed3
4
+ data.tar.gz: 1cfa891e84615fe3ed6d76a8c222420568c9ddb09077378199f6ba8862789d9f
5
5
  SHA512:
6
- metadata.gz: ce27150d86e61ebf5cb85a213461b89ecefa3cfab393ecb507714a3635286349b75d8e9ca9576683482136a6edf5cc06a1f1fb6723a22cd3872ac6f6abb03ebe
7
- data.tar.gz: 42aac8fd70e6c6f5d13531f5c8b17d364b83eeaed17cbbb41ae2c744b607264b08867fbbeab232ede72d24d3ada1cfd411e774766c55d1b99546a457be48d14d
6
+ metadata.gz: 5d74ccbd9cadf8fa00e6cef7c4159a34448a2f5e8fb0b94b6e63ee3b0032f1555ca613706821236965ad6f005f00eb305727e6eb62a6125c449a419bbb1db34a
7
+ data.tar.gz: 5a2e9d7e599797747370bbf3733ed442a795f69f040f2fa5db0736132de815a4c6a82254cf988602aec47c10fbfb73e696bf752581723a83b241a22f5d08b2c2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.9.0
4
+
5
+ * Added a compilation cache for `JSON.load_file`. (#370)
6
+
7
+ # 1.8.1
8
+
9
+ * Fixed support for older Psych. (#369)
10
+
11
+ # 1.8.0
12
+
13
+ * Improve support for Pysch 4. (#368)
14
+
15
+ # 1.7.7
16
+
17
+ * Fix `require_relative` in evaled code on latest ruby 3.1.0-dev. (#366)
18
+
19
+ # 1.7.6
20
+
3
21
  * Fix reliance on `set` to be required.
4
22
  * Fix `Encoding::UndefinedConversionError` error for Rails applications when precompiling cache. (#364)
5
23
 
data/lib/bootsnap/cli.rb CHANGED
@@ -21,7 +21,7 @@ module Bootsnap
21
21
 
22
22
  attr_reader :cache_dir, :argv
23
23
 
24
- attr_accessor :compile_gemfile, :exclude, :verbose, :iseq, :yaml, :jobs
24
+ attr_accessor :compile_gemfile, :exclude, :verbose, :iseq, :yaml, :json, :jobs
25
25
 
26
26
  def initialize(argv)
27
27
  @argv = argv
@@ -32,37 +32,44 @@ module Bootsnap
32
32
  self.jobs = Etc.nprocessors
33
33
  self.iseq = true
34
34
  self.yaml = true
35
+ self.json = true
35
36
  end
36
37
 
37
38
  def precompile_command(*sources)
38
39
  require 'bootsnap/compile_cache/iseq'
39
40
  require 'bootsnap/compile_cache/yaml'
41
+ require 'bootsnap/compile_cache/json'
40
42
 
41
43
  fix_default_encoding do
42
44
  Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir
43
45
  Bootsnap::CompileCache::YAML.init!
44
46
  Bootsnap::CompileCache::YAML.cache_dir = self.cache_dir
47
+ Bootsnap::CompileCache::JSON.init!
48
+ Bootsnap::CompileCache::JSON.cache_dir = self.cache_dir
45
49
 
46
50
  @work_pool = WorkerPool.create(size: jobs, jobs: {
47
51
  ruby: method(:precompile_ruby),
48
52
  yaml: method(:precompile_yaml),
53
+ json: method(:precompile_json),
49
54
  })
50
55
  @work_pool.spawn
51
56
 
52
57
  main_sources = sources.map { |d| File.expand_path(d) }
53
58
  precompile_ruby_files(main_sources)
54
59
  precompile_yaml_files(main_sources)
60
+ precompile_json_files(main_sources)
55
61
 
56
62
  if compile_gemfile
57
63
  # Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
58
64
  gem_exclude = Regexp.union([exclude, '/spec/', '/test/'].compact)
59
65
  precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
60
66
 
61
- # Gems that include YAML files usually don't put them in `lib/`.
67
+ # Gems that include JSON or YAML files usually don't put them in `lib/`.
62
68
  # So we look at the gem root.
63
69
  gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems\/[^/]+}
64
70
  gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.compact.uniq
65
71
  precompile_yaml_files(gem_paths, exclude: gem_exclude)
72
+ precompile_json_files(gem_paths, exclude: gem_exclude)
66
73
  end
67
74
 
68
75
  if exitstatus = @work_pool.shutdown
@@ -137,6 +144,29 @@ module Bootsnap
137
144
  end
138
145
  end
139
146
 
147
+ def precompile_json_files(load_paths, exclude: self.exclude)
148
+ return unless json
149
+
150
+ load_paths.each do |path|
151
+ if !exclude || !exclude.match?(path)
152
+ list_files(path, '**/*.json').each do |json_file|
153
+ # We ignore hidden files to not match the various .config.json files
154
+ if !File.basename(json_file).start_with?('.') && (!exclude || !exclude.match?(json_file))
155
+ @work_pool.push(:json, json_file)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def precompile_json(*json_files)
163
+ Array(json_files).each do |json_file|
164
+ if p(CompileCache::JSON.precompile(json_file, cache_dir: cache_dir))
165
+ STDERR.puts(json_file) if verbose
166
+ end
167
+ end
168
+ end
169
+
140
170
  def precompile_ruby_files(load_paths, exclude: self.exclude)
141
171
  return unless iseq
142
172
 
@@ -240,6 +270,11 @@ module Bootsnap
240
270
  Disable YAML precompilation.
241
271
  EOS
242
272
  opts.on('--no-yaml', help) { self.yaml = false }
273
+
274
+ help = <<~EOS
275
+ Disable JSON precompilation.
276
+ EOS
277
+ opts.on('--no-json', help) { self.json = false }
243
278
  end
244
279
  end
245
280
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ require('bootsnap/bootsnap')
3
+
4
+ module Bootsnap
5
+ module CompileCache
6
+ module JSON
7
+ class << self
8
+ attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
9
+
10
+ def input_to_storage(payload, _)
11
+ obj = ::JSON.parse(payload)
12
+ msgpack_factory.dump(obj)
13
+ end
14
+
15
+ def storage_to_output(data, kwargs)
16
+ if kwargs && kwargs.key?(:symbolize_names)
17
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
18
+ end
19
+ msgpack_factory.load(data, kwargs)
20
+ end
21
+
22
+ def input_to_output(data, kwargs)
23
+ ::JSON.parse(data, **(kwargs || {}))
24
+ end
25
+
26
+ def precompile(path, cache_dir: self.cache_dir)
27
+ Bootsnap::CompileCache::Native.precompile(
28
+ cache_dir,
29
+ path.to_s,
30
+ self,
31
+ )
32
+ end
33
+
34
+ def install!(cache_dir)
35
+ self.cache_dir = cache_dir
36
+ init!
37
+ if ::JSON.respond_to?(:load_file)
38
+ ::JSON.singleton_class.prepend(Patch)
39
+ end
40
+ end
41
+
42
+ def init!
43
+ require('json')
44
+ require('msgpack')
45
+
46
+ self.msgpack_factory = MessagePack::Factory.new
47
+ self.supported_options = [:symbolize_names]
48
+ if ::JSON.parse('["foo"]', freeze: true).first.frozen?
49
+ self.supported_options = [:freeze]
50
+ end
51
+ self.supported_options.freeze
52
+ end
53
+ end
54
+
55
+ module Patch
56
+ def load_file(path, *args)
57
+ return super if args.size > 1
58
+ if kwargs = args.first
59
+ return super unless kwargs.is_a?(Hash)
60
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
61
+ end
62
+
63
+ begin
64
+ ::Bootsnap::CompileCache::Native.fetch(
65
+ Bootsnap::CompileCache::JSON.cache_dir,
66
+ File.realpath(path),
67
+ ::Bootsnap::CompileCache::JSON,
68
+ kwargs,
69
+ )
70
+ rescue Errno::EACCES
71
+ ::Bootsnap::CompileCache.permission_error(path)
72
+ end
73
+ end
74
+
75
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -23,7 +23,11 @@ module Bootsnap
23
23
  end
24
24
 
25
25
  def input_to_output(data, kwargs)
26
- ::YAML.load(data, **(kwargs || {}))
26
+ if ::YAML.respond_to?(:unsafe_load)
27
+ ::YAML.unsafe_load(data, **(kwargs || {}))
28
+ else
29
+ ::YAML.load(data, **(kwargs || {}))
30
+ end
27
31
  end
28
32
 
29
33
  def strict_load(payload, *args)
@@ -52,6 +56,13 @@ module Bootsnap
52
56
  require('msgpack')
53
57
  require('date')
54
58
 
59
+ if Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
60
+ Patch.send(:remove_method, :unsafe_load_file)
61
+ end
62
+ if Patch.method_defined?(:load_file) && ::YAML::VERSION >= '4'
63
+ Patch.send(:remove_method, :load_file)
64
+ end
65
+
55
66
  # MessagePack serializes symbols as strings by default.
56
67
  # We want them to roundtrip cleanly, so we use a custom factory.
57
68
  # see: https://github.com/msgpack/msgpack-ruby/pull/122
@@ -126,6 +137,27 @@ module Bootsnap
126
137
  end
127
138
 
128
139
  ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
140
+
141
+ def unsafe_load_file(path, *args)
142
+ return super if args.size > 1
143
+ if kwargs = args.first
144
+ return super unless kwargs.is_a?(Hash)
145
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
146
+ end
147
+
148
+ begin
149
+ ::Bootsnap::CompileCache::Native.fetch(
150
+ Bootsnap::CompileCache::YAML.cache_dir,
151
+ File.realpath(path),
152
+ ::Bootsnap::CompileCache::YAML,
153
+ kwargs,
154
+ )
155
+ rescue Errno::EACCES
156
+ ::Bootsnap::CompileCache.permission_error(path)
157
+ end
158
+ end
159
+
160
+ ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
129
161
  end
130
162
  end
131
163
  end
@@ -4,7 +4,7 @@ module Bootsnap
4
4
  Error = Class.new(StandardError)
5
5
  PermissionError = Class.new(Error)
6
6
 
7
- def self.setup(cache_dir:, iseq:, yaml:)
7
+ def self.setup(cache_dir:, iseq:, yaml:, json:)
8
8
  if iseq
9
9
  if supported?
10
10
  require_relative('compile_cache/iseq')
@@ -22,6 +22,15 @@ module Bootsnap
22
22
  warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
23
23
  end
24
24
  end
25
+
26
+ if json
27
+ if supported?
28
+ require_relative('compile_cache/json')
29
+ Bootsnap::CompileCache::JSON.install!(cache_dir)
30
+ elsif $VERBOSE
31
+ warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
32
+ end
33
+ end
25
34
  end
26
35
 
27
36
  def self.permission_error(path)
@@ -47,8 +47,9 @@ module Kernel
47
47
 
48
48
  alias_method(:require_relative_without_bootsnap, :require_relative)
49
49
  def require_relative(path)
50
+ location = caller_locations(1..1).first
50
51
  realpath = Bootsnap::LoadPathCache.realpath_cache.call(
51
- caller_locations(1..1).first.absolute_path, path
52
+ location.absolute_path || location.path, path
52
53
  )
53
54
  require(realpath)
54
55
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Bootsnap
3
- VERSION = "1.7.6"
3
+ VERSION = "1.9.0"
4
4
  end
data/lib/bootsnap.rb CHANGED
@@ -43,7 +43,8 @@ module Bootsnap
43
43
  autoload_paths_cache: nil,
44
44
  disable_trace: nil,
45
45
  compile_cache_iseq: true,
46
- compile_cache_yaml: true
46
+ compile_cache_yaml: true,
47
+ compile_cache_json: true
47
48
  )
48
49
  unless autoload_paths_cache.nil?
49
50
  warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
@@ -69,7 +70,8 @@ module Bootsnap
69
70
  Bootsnap::CompileCache.setup(
70
71
  cache_dir: cache_dir + '/bootsnap/compile-cache',
71
72
  iseq: compile_cache_iseq,
72
- yaml: compile_cache_yaml
73
+ yaml: compile_cache_yaml,
74
+ json: compile_cache_json,
73
75
  )
74
76
  end
75
77
 
@@ -113,6 +115,7 @@ module Bootsnap
113
115
  load_path_cache: !ENV['DISABLE_BOOTSNAP_LOAD_PATH_CACHE'],
114
116
  compile_cache_iseq: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'] && iseq_cache_supported?,
115
117
  compile_cache_yaml: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'],
118
+ compile_cache_json: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'],
116
119
  )
117
120
 
118
121
  if ENV['BOOTSNAP_LOG']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.6
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-26 00:00:00.000000000 Z
11
+ date: 2021-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -116,6 +116,7 @@ files:
116
116
  - lib/bootsnap/cli/worker_pool.rb
117
117
  - lib/bootsnap/compile_cache.rb
118
118
  - lib/bootsnap/compile_cache/iseq.rb
119
+ - lib/bootsnap/compile_cache/json.rb
119
120
  - lib/bootsnap/compile_cache/yaml.rb
120
121
  - lib/bootsnap/explicit_require.rb
121
122
  - lib/bootsnap/load_path_cache.rb