onload 1.0.4 → 1.1.0

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: f9da25e881992956871df8379241638cc78561b87d09b9040390b9e7e1331b47
4
- data.tar.gz: bd7eed8c12d1557af7cfa02c29f3e99e3c60d803980f5f5342ee226f98491fc0
3
+ metadata.gz: f3b85b579c018d6457e7bfe46bf77af3c275edff7393131711c49b4cf16c76e4
4
+ data.tar.gz: 744b42532004241a66de3c6cabf0a7d0013e3924c82c16ae2b7e31b0d8dc3d83
5
5
  SHA512:
6
- metadata.gz: 5cd23e59b466a20c66c7cc2bfcffe0f43363ff677d282adc31b5b897edb41b47af83322cfaa821e965d39a3ed563b0a9923acda00525771a763d725137b39a88
7
- data.tar.gz: 5fa4b2f9e471429675ad855c9e945aeae6fa516e5c1abfd3295e298b3c6929baed89b0e66f8e2b61ad07af05e97ff78ea542195ff7dcdb2adc1e323c3feebe58
6
+ metadata.gz: '09dcca366074cce184242321ead06ab87cf46eac0adfb1af585486d1e162bac051f87d323c26b8501bd57ef211f95e4074f29b5a11986541ff1791cdc4ff05dc'
7
+ data.tar.gz: c61baba2a8de0daf0461643d28f1b3d978898e3104024dc63f6deff37583bc9a02fbb5f1fa4a0b8b92f3f15f61dbd3eed75917d42030300a761d91816f6530be
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.1.0
2
+ * Automatically add generated files to the specified ignore file, eg. `.gitignore`, when they are written.
3
+ - Specify an ignore file by setting `Onload.config.ignore_path` to the path to the ignore file you'd like to use.
4
+ * Use appraisal-run to run tests locally for all Ruby and Rails versions.
5
+
6
+ ## 1.0.5
7
+ * Fix issues with Zeitwerk v2.7.3 and later.
8
+ * Don't attempt to autoload directories.
9
+
1
10
  ## 1.0.4
2
11
  * Fix issues with Zeitwerk v2.6.13 and later.
3
12
  - Zeitwerk introduced the `Cref` class, which encapsulates a `mod` and `cname`. A number of internal methods used to return both of these things individually; now they are wrapped in `Cref`s.
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ end
9
9
 
10
10
  group :development do
11
11
  gem 'appraisal'
12
+ gem 'appraisal-run'
12
13
  gem 'benchmark-ips'
13
14
  end
14
15
 
data/README.md CHANGED
@@ -46,14 +46,25 @@ Onload.install!
46
46
 
47
47
  Now, the contents of any file with a .up file extension will be passed to `UpcasePreprocessor.call`. The return value will be written to a separate Ruby file and loaded instead of the original .up file.
48
48
 
49
+ ## Ignoring Processed Files
50
+
51
+ It can often be desirable to add processed/generated files to your .gitignore file. Onload comes with a mechanism that can do exactly that. Simply set the config option before running onload:
52
+
53
+ ```ruby
54
+ Onload.config.ignore_path = ".gitignore"
55
+ ```
56
+
57
+ Now whenever files are processed, the generated file will be automatically updated.
58
+
49
59
  ## Running Tests
50
60
 
51
- If you're using [asdf](https://asdf-vm.com/), run `./script/run_appraisal.rb` to run Rails and plain Ruby tests for all supported versions.
61
+ Install Docker, then run `./script/run_appraisals` to run Rails and plain Ruby tests for all supported versions.
52
62
 
53
63
  Otherwise, use Appraisal to run tests for Rails or plain ruby:
54
64
 
55
65
  1. Plain ruby: `bundle exec appraisal ruby rake spec:ruby`
56
66
  1. Rails: `bundle exec appraisal <version> rake spec:rails`. Run `bundle exec appraisal list` to see the available versions. To run tests for Rails 7.0, try `bundle exec appraisal rails-7.0 rake spec:rails`
67
+ 1. Note that you must be running the correct version of Ruby specified in each gemfile.
57
68
 
58
69
  ## License
59
70
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Onload
4
+ class Config
5
+ attr_accessor :ignore_path
6
+
7
+ def initialize
8
+ @ignore_path = nil
9
+ end
10
+
11
+ def dup
12
+ self.class.new.tap do |duplicate|
13
+ duplicate.ignore_path = ignore_path.dup
14
+ end
15
+ end
16
+ end
17
+ end
@@ -15,7 +15,12 @@ module Kernel
15
15
  # in order to load the resulting file. Otherwise you get an error about
16
16
  # an uninitialized constant, and it's like... yeah, I _know_ it's
17
17
  # uninitialized, that's why I'm loading this file. Whatevs.
18
- loader = Zeitwerk::Registry.loader_for(file)
18
+ loader = if Zeitwerk::Registry.respond_to?(:loader_for)
19
+ Zeitwerk::Registry.loader_for(file)
20
+ else
21
+ Zeitwerk::Registry.autoloads.registered?(file)
22
+ end
23
+
19
24
  parent, cname = loader.send(:autoloads)[file]
20
25
 
21
26
  if defined?(Zeitwerk::Cref) && parent.is_a?(Zeitwerk::Cref)
@@ -3,6 +3,9 @@
3
3
  module Onload
4
4
  module BootsnapAutoloadPatch
5
5
  def autoload(const, path)
6
+ # only autoload files, not directories
7
+ return super if ::File.directory?(path)
8
+
6
9
  # Bootsnap monkeypatches Module.autoload in order to leverage its load
7
10
  # path cache, which effectively converts a relative path into an absolute
8
11
  # one without incurring the cost of searching the load path.
data/lib/onload/file.rb CHANGED
@@ -8,14 +8,22 @@ module Onload
8
8
  @path = path
9
9
  end
10
10
 
11
- def write
11
+ def write(ignore_file: nil)
12
12
  source = ::File.read(path)
13
13
 
14
14
  ::File.extname(path).scan(/\.\w+/).each do |ext|
15
15
  source = Onload.processors[ext].call(source)
16
16
  end
17
17
 
18
- ::File.write(outfile, source)
18
+ ::File.write(outfile, source).tap do
19
+ if ignore_file
20
+ ignore_file.add(outfile)
21
+ elsif Onload.config.ignore_path
22
+ ignore_file = IgnoreFile.load(Onload.config.ignore_path)
23
+ ignore_file.add(outfile)
24
+ ignore_file.persist!
25
+ end
26
+ end
19
27
  end
20
28
 
21
29
  def outfile
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Onload
6
+ class MalformedIgnoreFileError < StandardError; end
7
+
8
+ class IgnoreFile
9
+ ONLOAD_SECTION_START = "##### ONLOAD BUILD ARTIFACTS (AUTO-GENERATED) #####"
10
+ ONLOAD_SECTION_STOP = "##### END ONLOAD BUILD ARTIFACTS #####"
11
+
12
+ class << self
13
+ def load(manifest_path)
14
+ manifest_path = ::File.expand_path(manifest_path)
15
+ lines = ::File.read(manifest_path).split(/\r?\n/)
16
+ start_idx = lines.index(ONLOAD_SECTION_START)
17
+ stop_idx = lines.index(ONLOAD_SECTION_STOP)
18
+
19
+ # Start and stop indices should either both be numbers or both be nil.
20
+ # Anything else, and something weird is going on.
21
+ if start_idx.class != stop_idx.class
22
+ raise MalformedIgnoreFileError, "the ignore file at #{manifest_path} appears to be malformed and onload doesn't know how to modify it"
23
+ end
24
+
25
+ ignored_paths = if start_idx && stop_idx
26
+ lines[start_idx..stop_idx].map(&:strip)
27
+ else
28
+ []
29
+ end
30
+
31
+ ignored_paths.reject! do |path|
32
+ path.empty? || path.start_with?("#")
33
+ end
34
+
35
+ new(manifest_path, Set.new(ignored_paths))
36
+ end
37
+ end
38
+
39
+ attr_reader :manifest_path, :dirty
40
+
41
+ alias dirty? dirty
42
+
43
+ def initialize(manifest_path, ignored_paths)
44
+ @manifest_path = manifest_path
45
+ @ignored_paths = ignored_paths
46
+ @dirty = false
47
+ end
48
+
49
+ def includes?(path)
50
+ @ignored_paths.include?(path)
51
+ end
52
+
53
+ alias include? includes?
54
+
55
+ def add(path)
56
+ ignored_path = ::File.expand_path(path)
57
+ ignored_path_segments = ignored_path.split(::File::SEPARATOR)
58
+
59
+ if ignored_path_segments[0...manifest_dirname_segments.size] != manifest_dirname_segments
60
+ raise "file to ignore #{path} is not relative to the specified ignore file at #{manifest_path}"
61
+ end
62
+
63
+ relative_ignored_path = ignored_path_segments[manifest_dirname_segments.size..-1].join(::File::SEPARATOR)
64
+ @ignored_paths << relative_ignored_path
65
+
66
+ @dirty = true
67
+
68
+ nil
69
+ end
70
+
71
+ def persist!
72
+ return unless dirty?
73
+
74
+ lines = ::File.read(manifest_path).split(/\r?\n/)
75
+ start_idx = lines.index(ONLOAD_SECTION_START)
76
+ stop_idx = lines.index(ONLOAD_SECTION_STOP)
77
+
78
+ if start_idx && stop_idx
79
+ lines[start_idx..stop_idx] = [
80
+ ONLOAD_SECTION_START,
81
+ *@ignored_paths,
82
+ ONLOAD_SECTION_STOP
83
+ ]
84
+ else
85
+ lines += [
86
+ "",
87
+ ONLOAD_SECTION_START,
88
+ *@ignored_paths,
89
+ ONLOAD_SECTION_STOP,
90
+ ""
91
+ ]
92
+ end
93
+
94
+ contents = lines.join("\n")
95
+ contents << "\n" unless contents.end_with?("\n")
96
+
97
+ ::File.write(manifest_path, contents)
98
+
99
+ @dirty = false
100
+ end
101
+
102
+ private
103
+
104
+ def manifest_dirname
105
+ @manifest_dirname ||= ::File.dirname(manifest_path)
106
+ end
107
+
108
+ def manifest_dirname_segments
109
+ @manifest_diranme_segments ||= manifest_dirname.split(::File::SEPARATOR)
110
+ end
111
+ end
112
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Onload
4
- VERSION = "1.0.4"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/onload.rb CHANGED
@@ -3,7 +3,9 @@
3
3
  module Onload
4
4
  UNLOADABLE_EXTENSIONS = %w(.bundle .so .dll).freeze
5
5
 
6
- autoload :File, "onload/file"
6
+ autoload :Config, "onload/config"
7
+ autoload :File, "onload/file"
8
+ autoload :IgnoreFile, "onload/ignore_file"
7
9
 
8
10
  class << self
9
11
  attr_accessor :enabled
@@ -105,6 +107,27 @@ module Onload
105
107
  @glob ||= "*{#{each_extension.to_a.join(",")}}"
106
108
  end
107
109
 
110
+ def config
111
+ @config ||= Config.new
112
+ end
113
+
114
+ def with_config(override_hash)
115
+ new_config = config.dup.tap do |new_config|
116
+ override_hash.each_pair do |k, v|
117
+ new_config.send("#{k}=", v)
118
+ end
119
+ end
120
+
121
+ old_config = config
122
+ @config = new_config
123
+
124
+ yield
125
+
126
+ nil
127
+ ensure
128
+ @config = old_config
129
+ end
130
+
108
131
  private
109
132
 
110
133
  def path_cache
@@ -0,0 +1,5 @@
1
+ class Hello
2
+ def hello
3
+ "hello".upcase
4
+ end
5
+ end
@@ -13,6 +13,15 @@ describe HomeController, type: :request do
13
13
  )
14
14
  end
15
15
 
16
+ it "adds an entry to the specified ignore file" do
17
+ Onload.with_config(ignore_path: @ignore_path) do
18
+ get "/"
19
+
20
+ ignore_file = Onload::IgnoreFile.load(@ignore_path)
21
+ expect(ignore_file).to include("fixtures/hello.rb")
22
+ end
23
+ end
24
+
16
25
  it "allows hot reloading" do
17
26
  get "/"
18
27
 
@@ -38,5 +47,14 @@ describe HomeController, type: :request do
38
47
  )
39
48
  end
40
49
  end
50
+
51
+ it "supports file shadowing" do
52
+ get "/action_list"
53
+
54
+ expect(response).to have_http_status(:ok)
55
+ expect(response.body).to(
56
+ have_selector("div", text: "LIST ITEM")
57
+ )
58
+ end
41
59
  end
42
60
  end
@@ -0,0 +1 @@
1
+ <div><%= Primer::Alpha::ActionList.new.list %></div>
@@ -11,5 +11,7 @@ module Onload
11
11
  config.autoload_paths << ::File.expand_path(
12
12
  ::File.join(*%w[.. .. .. fixtures]), __dir__
13
13
  )
14
+
15
+ config.autoload_paths << Rails.root.join("lib").to_s
14
16
  end
15
17
  end
@@ -1,3 +1,4 @@
1
1
  Rails.application.routes.draw do
2
- root to: 'home#index'
2
+ root to: "home#index"
3
+ get "/action_list", to: "home#action_list"
3
4
  end
@@ -0,0 +1,11 @@
1
+ module Primer
2
+ module Alpha
3
+ class ActionList
4
+ class Item
5
+ def item
6
+ "item".upcase
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Primer
2
+ module Alpha
3
+ class ActionList
4
+ class Item
5
+ def item
6
+ "item"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Primer
2
+ module Alpha
3
+ class ActionList
4
+ def list
5
+ "list ".upcase + Item.new.item
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Primer
2
+ module Alpha
3
+ class ActionList
4
+ def list
5
+ "list " + Item.new.item
6
+ end
7
+ end
8
+ end
9
+ end