spring 4.1.0 → 4.3.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: 25f487be691e4b74fec8082de6dae9c31b2b5452adea9546e615fe972bb92302
4
- data.tar.gz: 7b7997de4635a441fdba39157260f18f6be1f7c4c41b3fe1b43b21a0fada61d6
3
+ metadata.gz: 3e92617adaa3e6c31e4d466d76894c94ec17a46fd7206864501218795ac8998d
4
+ data.tar.gz: aee2d23a222df8747942652dda1d6a725f59bb68b395fbd37a9084c5ed75fe7b
5
5
  SHA512:
6
- metadata.gz: a5a1a3b647476c8582da82cfbaa35524e427303149a1bc2e375f115ba25ef32166fd5800e968f3873afef84e6a38b686447b68c1145e9bfffe6aecff03379cb8
7
- data.tar.gz: 985b243181c0a7c6a4c37052fe504f1f3e51303f7c014924ff2dc8c10693e17b45bba8cf0b39f49a2c378da761b715b501d4f535102f3b82bf5006618461235f
6
+ metadata.gz: 16e03232e4453b528812c925c4186360e1157b10aab0d663d1dc3d4a80a7d4bdf1da8f06d31c6ace1456fa4f6f5e20b2c62637ce0a23da6cae705b56baa6882b
7
+ data.tar.gz: 1a4c70a381783cc3f96372e28efc6b702b3b75a28c31a1a88eacace55c3a3cbd2aee3d3ff81236526366149a612754c22d9ec0861bc0a3dfb8669108f9bb0aba
data/README.md CHANGED
@@ -16,7 +16,7 @@ boot it every time you run a test, rake task or migration.
16
16
 
17
17
  ## Compatibility
18
18
 
19
- * Ruby versions: MRI 2.7, MRI 3.0, MRI 3.1
19
+ * Ruby versions: MRI 2.7, MRI 3.0, MRI 3.1, MRI 3.2
20
20
  * Rails versions: 6.0, 6.1, 7.0
21
21
  * Bundler v2.1+
22
22
 
@@ -71,11 +71,14 @@ Rails directory.
71
71
  Spring reloads application code, and therefore needs the application to have
72
72
  reloading enabled.
73
73
 
74
- Please, make sure `config.cache_classes` is `false` in the environments that
74
+ Ensure that `config.enable_reloading` is `true` in the environments that
75
75
  Spring manages. That setting is typically configured in
76
- `config/environments/*.rb`. In particular, make sure it is `false` for the
76
+ `config/environments/*.rb`. In particular, make sure it is `true` for the
77
77
  `test` environment.
78
78
 
79
+ Note: in versions of Rails before 7.1, the setting is called `cache_classes`,
80
+ and it needs to be `false` for Spring to work.
81
+
79
82
  ### Usage
80
83
 
81
84
  For this walkthrough I've generated a new Rails application, and run
@@ -11,9 +11,15 @@ app = Spring::Application.new(
11
11
 
12
12
  Signal.trap("TERM") { app.terminate }
13
13
 
14
- Spring::ProcessTitleUpdater.run { |distance|
15
- "spring app | #{app.app_name} | started #{distance} ago | #{app.app_env} mode"
16
- }
14
+ Spring::ProcessTitleUpdater.run do |distance|
15
+ attributes = [
16
+ app.app_name,
17
+ "started #{distance} ago",
18
+ "#{app.app_env} mode",
19
+ app.spawn_env,
20
+ ].compact
21
+ "spring app | #{attributes.join(" | ")}"
22
+ end
17
23
 
18
24
  app.eager_preload if ENV.delete("SPRING_PRELOAD") == "1"
19
25
  app.run
@@ -28,6 +28,11 @@ module Spring
28
28
  @interrupt.last.write "."
29
29
  end
30
30
 
31
+ def spawn_env
32
+ env = JSON.load(ENV["SPRING_SPAWN_ENV"].dup).map { |key, value| "#{key}=#{value}" }
33
+ env.join(", ") if env.any?
34
+ end
35
+
31
36
  def app_env
32
37
  ENV['RAILS_ENV']
33
38
  end
@@ -119,6 +124,11 @@ module Spring
119
124
 
120
125
  if defined?(Rails) && Rails.application
121
126
  watcher.add Rails.application.paths["config/initializers"]
127
+ Rails::Engine.descendants.each do |engine|
128
+ if engine.root.to_s.start_with?(Rails.root.to_s)
129
+ watcher.add engine.paths["config/initializers"].expanded
130
+ end
131
+ end
122
132
  watcher.add Rails.application.paths["config/database"]
123
133
  if secrets_path = Rails.application.paths["config/secrets"]
124
134
  watcher.add secrets_path
@@ -302,13 +312,26 @@ module Spring
302
312
  Kernel.module_eval do
303
313
  old_raise = Kernel.method(:raise)
304
314
  remove_method :raise
305
- define_method :raise do |*args|
306
- begin
307
- old_raise.call(*args)
308
- ensure
309
- if $!
310
- lib = File.expand_path("..", __FILE__)
311
- $!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
315
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2.0')
316
+ define_method :raise do |*args, **kwargs|
317
+ begin
318
+ old_raise.call(*args, **kwargs)
319
+ ensure
320
+ if $!
321
+ lib = File.expand_path("..", __FILE__)
322
+ $!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
323
+ end
324
+ end
325
+ end
326
+ else
327
+ define_method :raise do |*args|
328
+ begin
329
+ old_raise.call(*args)
330
+ ensure
331
+ if $!
332
+ lib = File.expand_path("..", __FILE__)
333
+ $!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
334
+ end
312
335
  end
313
336
  end
314
337
  end
@@ -1,9 +1,10 @@
1
1
  module Spring
2
2
  class ApplicationManager
3
- attr_reader :pid, :child, :app_env, :spring_env, :status
3
+ attr_reader :pid, :child, :app_env, :spawn_env, :spring_env, :status
4
4
 
5
- def initialize(app_env, spring_env)
5
+ def initialize(app_env, spawn_env, spring_env)
6
6
  @app_env = app_env
7
+ @spawn_env = spawn_env
7
8
  @spring_env = spring_env
8
9
  @mutex = Mutex.new
9
10
  @state = :running
@@ -100,7 +101,9 @@ module Spring
100
101
  "RAILS_ENV" => app_env,
101
102
  "RACK_ENV" => app_env,
102
103
  "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
103
- "SPRING_PRELOAD" => preload ? "1" : "0"
104
+ "SPRING_PRELOAD" => preload ? "1" : "0",
105
+ "SPRING_SPAWN_ENV" => JSON.dump(spawn_env),
106
+ **spawn_env,
104
107
  },
105
108
  "ruby",
106
109
  *(bundler_dir != RbConfig::CONFIG["rubylibdir"] ? ["-I", bundler_dir] : []),
@@ -123,7 +126,8 @@ module Spring
123
126
  # as if it does we're no longer interested in the child
124
127
  loop do
125
128
  IO.select([child])
126
- break if child.recv(1, Socket::MSG_PEEK).empty?
129
+ peek = child.recv(1, Socket::MSG_PEEK)
130
+ break if peek.nil? || peek.empty?
127
131
  sleep 0.01
128
132
  end
129
133
 
@@ -20,7 +20,7 @@ module Spring
20
20
  SPRING = <<~CODE
21
21
  #!/usr/bin/env ruby
22
22
 
23
- # This file loads Spring without using loading other gems in the Gemfile, in order to be fast.
23
+ # This file loads Spring without loading other gems in the Gemfile in order to be fast.
24
24
  # It gets overwritten when you run the `spring binstub` command.
25
25
 
26
26
  if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
@@ -6,8 +6,6 @@ module Spring
6
6
  module Client
7
7
  class Run < Command
8
8
  FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
9
- CONNECT_TIMEOUT = 1
10
- BOOT_TIMEOUT = 20
11
9
 
12
10
  attr_reader :server
13
11
 
@@ -73,8 +71,8 @@ module Spring
73
71
  def boot_server
74
72
  env.socket_path.unlink if env.socket_path.exist?
75
73
 
76
- pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
77
- timeout = Time.now + BOOT_TIMEOUT
74
+ pid = Process.spawn(server_process_env, env.server_command, out: File::NULL)
75
+ timeout = Time.now + Spring.boot_timeout
78
76
 
79
77
  @server_booted = true
80
78
 
@@ -85,7 +83,7 @@ module Spring
85
83
  exit status.exitstatus
86
84
  elsif Time.now > timeout
87
85
  $stderr.puts "Starting Spring server with `#{env.server_command}` " \
88
- "timed out after #{BOOT_TIMEOUT} seconds"
86
+ "timed out after #{Spring.boot_timeout} seconds"
89
87
  exit 1
90
88
  end
91
89
 
@@ -107,6 +105,14 @@ module Spring
107
105
  }
108
106
  end
109
107
 
108
+ def reset_env
109
+ ENV.slice(*Spring.reset_on_env)
110
+ end
111
+
112
+ def server_process_env
113
+ reset_env.merge(gem_env)
114
+ end
115
+
110
116
  def stop_server
111
117
  server.close
112
118
  @server = nil
@@ -114,7 +120,16 @@ module Spring
114
120
  end
115
121
 
116
122
  def verify_server_version
117
- server_version = server.gets.chomp
123
+ unless IO.select([server], [], [], Spring.connect_timeout)
124
+ raise "Error connecting to Spring server"
125
+ end
126
+
127
+ line = server.gets
128
+ unless line
129
+ raise "Error connecting to Spring server"
130
+ end
131
+
132
+ server_version = line.chomp
118
133
  if server_version != env.version
119
134
  $stderr.puts "There is a version mismatch between the Spring client " \
120
135
  "(#{env.version}) and the server (#{server_version})."
@@ -132,9 +147,9 @@ module Spring
132
147
 
133
148
  def connect_to_application(client)
134
149
  server.send_io client
135
- send_json server, "args" => args, "default_rails_env" => default_rails_env
150
+ send_json server, "args" => args, "default_rails_env" => default_rails_env, "spawn_env" => spawn_env, "reset_env" => reset_env
136
151
 
137
- if IO.select([server], [], [], CONNECT_TIMEOUT)
152
+ if IO.select([server], [], [], Spring.connect_timeout)
138
153
  server.gets or raise CommandNotFound
139
154
  else
140
155
  raise "Error connecting to Spring server"
@@ -232,6 +247,10 @@ module Spring
232
247
  def default_rails_env
233
248
  ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
234
249
  end
250
+
251
+ def spawn_env
252
+ ENV.slice(*Spring.spawn_on_env)
253
+ end
235
254
  end
236
255
  end
237
256
  end
@@ -1,8 +1,11 @@
1
1
  require "spring/errors"
2
2
 
3
3
  module Spring
4
+ @connect_timeout = 5
5
+ @boot_timeout = 20
6
+
4
7
  class << self
5
- attr_accessor :application_root
8
+ attr_accessor :application_root, :connect_timeout, :boot_timeout
6
9
  attr_writer :quiet
7
10
 
8
11
  def gemfile
@@ -32,6 +35,14 @@ module Spring
32
35
  after_fork_callbacks << block
33
36
  end
34
37
 
38
+ def spawn_on_env
39
+ @spawn_on_env ||= []
40
+ end
41
+
42
+ def reset_on_env
43
+ @reset_on_env ||= []
44
+ end
45
+
35
46
  def verify_environment
36
47
  application_root_path
37
48
  end
data/lib/spring/json.rb CHANGED
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # ### WHY SPRING VENDORS A JSON LIBRARY ###
4
4
  #
@@ -13,7 +13,6 @@
13
13
  module Spring
14
14
  module JSON
15
15
  def self.load(string)
16
- string.force_encoding("utf-8")
17
16
  OkJson.decode(string)
18
17
  end
19
18
 
@@ -364,7 +363,7 @@ private
364
363
  end
365
364
  end
366
365
  if rubydoesenc?
367
- a[w] = '' << uchar
366
+ a[w] = +'' << uchar
368
367
  w += 1
369
368
  else
370
369
  w += ucharenc(a, w, uchar)
data/lib/spring/server.rb CHANGED
@@ -19,7 +19,9 @@ module Spring
19
19
  def initialize(options = {})
20
20
  @foreground = options.fetch(:foreground, false)
21
21
  @env = options[:env] || default_env
22
- @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k, env) }
22
+ @applications = Hash.new do |hash, key|
23
+ hash[key] = ApplicationManager.new(*key, env)
24
+ end
23
25
  @pidfile = env.pidfile_path.open('a')
24
26
  @mutex = Mutex.new
25
27
  end
@@ -57,12 +59,15 @@ module Spring
57
59
  app_client = client.recv_io
58
60
  command = JSON.load(client.read(client.gets.to_i))
59
61
 
60
- args, default_rails_env = command.values_at('args', 'default_rails_env')
62
+ args, default_rails_env, spawn_env, reset_env = command.values_at('args', 'default_rails_env', 'spawn_env', 'reset_env')
61
63
 
62
64
  if Spring.command?(args.first)
65
+ application = @applications[rails_env_for(args, default_rails_env, spawn_env)]
66
+ reset_if_env_changed(application, reset_env)
67
+
63
68
  log "running command #{args.first}"
64
69
  client.puts
65
- client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client)
70
+ client.puts application.run(app_client)
66
71
  else
67
72
  log "command not found #{args.first}"
68
73
  client.close
@@ -73,8 +78,8 @@ module Spring
73
78
  redirect_output
74
79
  end
75
80
 
76
- def rails_env_for(args, default_rails_env)
77
- Spring.command(args.first).env(args.drop(1)) || default_rails_env
81
+ def rails_env_for(args, default_rails_env, spawn_env)
82
+ [Spring.command(args.first).env(args.drop(1)) || default_rails_env, spawn_env]
78
83
  end
79
84
 
80
85
  # Boot the server into the process group of the current session.
@@ -136,6 +141,10 @@ module Spring
136
141
 
137
142
  private
138
143
 
144
+ def reset_if_env_changed(application, reset_env)
145
+ application.stop if ENV.slice(*reset_env.keys) != reset_env
146
+ end
147
+
139
148
  def default_env
140
149
  Env.new(log_file: default_log_file)
141
150
  end
@@ -1,3 +1,3 @@
1
1
  module Spring
2
- VERSION = "4.1.0"
2
+ VERSION = "4.3.0"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require "pathname"
2
- require "mutex_m"
3
2
 
4
3
  module Spring
5
4
  module Watcher
@@ -9,13 +8,10 @@ module Spring
9
8
  # IO.select([watcher]) # watcher is running in background
10
9
  # watcher.stale? # => true
11
10
  class Abstract
12
- include Mutex_m
13
-
14
11
  attr_reader :files, :directories, :root, :latency
15
12
 
16
13
  def initialize(root, latency)
17
- super()
18
-
14
+ @mutex = Mutex.new
19
15
  @root = File.realpath(root)
20
16
  @latency = latency
21
17
  @files = {}
@@ -26,6 +22,11 @@ module Spring
26
22
  @on_debug = nil
27
23
  end
28
24
 
25
+ def synchronize(&block)
26
+ # Used by some gems.
27
+ @mutex.synchronize(&block)
28
+ end
29
+
29
30
  def on_debug(&block)
30
31
  @on_debug = block
31
32
  end
@@ -59,7 +60,7 @@ module Spring
59
60
  end
60
61
  end
61
62
 
62
- synchronize {
63
+ @mutex.synchronize do
63
64
  items.each do |item|
64
65
  if item.directory?
65
66
  directories[item.realpath.to_s] = true
@@ -75,7 +76,7 @@ module Spring
75
76
  end
76
77
 
77
78
  subjects_changed
78
- }
79
+ end
79
80
  end
80
81
 
81
82
  def stale?
@@ -12,7 +12,7 @@ module Spring
12
12
  end
13
13
 
14
14
  def check_stale
15
- synchronize do
15
+ @mutex.synchronize do
16
16
  computed = compute_mtime
17
17
  if mtime < computed
18
18
  debug { "check_stale: mtime=#{mtime.inspect} < computed=#{computed.inspect}" }
metadata CHANGED
@@ -1,57 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spring
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Leighton
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-09-21 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rake
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: bump
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: activesupport
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
10
+ date: 2025-03-12 00:00:00.000000000 Z
11
+ dependencies: []
55
12
  description: Preloads your application so things like console, rake and tests run
56
13
  faster
57
14
  email:
@@ -99,7 +56,6 @@ licenses:
99
56
  - MIT
100
57
  metadata:
101
58
  rubygems_mfa_required: 'true'
102
- post_install_message:
103
59
  rdoc_options: []
104
60
  require_paths:
105
61
  - lib
@@ -114,8 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
70
  - !ruby/object:Gem::Version
115
71
  version: '0'
116
72
  requirements: []
117
- rubygems_version: 3.3.7
118
- signing_key:
73
+ rubygems_version: 3.6.2
119
74
  specification_version: 4
120
75
  summary: Rails application preloader
121
76
  test_files: []