spring 2.0.2 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,56 +1,48 @@
1
- require 'set'
2
-
3
1
  module Spring
4
2
  module Client
5
3
  class Binstub < Command
6
- SHEBANG = /\#\!.*\n/
4
+ SHEBANG = /\#\!.*\n(\#.*\n)*/
7
5
 
8
- # If loading the bin/spring file works, it'll run spring which will
6
+ # If loading the bin/spring file works, it'll run Spring which will
9
7
  # eventually call Kernel.exit. This means that in the client process
10
- # we will never execute the lines after this block. But if the spring
8
+ # we will never execute the lines after this block. But if the Spring
11
9
  # client is not invoked for whatever reason, then the Kernel.exit won't
12
10
  # happen, and so we'll fall back to the lines after this block, which
13
11
  # should cause the "unsprung" version of the command to run.
14
- LOADER = <<CODE
15
- begin
16
- load File.expand_path('../spring', __FILE__)
17
- rescue LoadError => e
18
- raise unless e.message.include?('spring')
19
- end
20
- CODE
12
+ LOADER = <<~CODE
13
+ load File.expand_path("spring", __dir__)
14
+ CODE
21
15
 
22
16
  # The defined? check ensures these lines don't execute when we load the
23
17
  # binstub from the application process. Which means that in the application
24
18
  # process we'll execute the lines which come after the LOADER block, which
25
19
  # is what we want.
26
- SPRING = <<'CODE'
27
- #!/usr/bin/env ruby
28
-
29
- # This file loads spring without using Bundler, in order to be fast.
30
- # It gets overwritten when you run the `spring binstub` command.
31
-
32
- unless defined?(Spring)
33
- require 'rubygems'
34
- require 'bundler'
35
-
36
- lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
37
- spring = lockfile.specs.detect { |spec| spec.name == "spring" }
38
- if spring
39
- Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
40
- gem 'spring', spring.version
41
- require 'spring/binstub'
42
- end
43
- end
44
- CODE
20
+ SPRING = <<~CODE
21
+ #!/usr/bin/env ruby
22
+
23
+ # This file loads Spring without loading other gems in the Gemfile in order to be fast.
24
+ # It gets overwritten when you run the `spring binstub` command.
25
+
26
+ if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
27
+ require "bundler"
28
+
29
+ Bundler.locked_gems.specs.find { |spec| spec.name == "spring" }&.tap do |spring|
30
+ Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
31
+ gem "spring", spring.version
32
+ require "spring/binstub"
33
+ end
34
+ end
35
+ CODE
45
36
 
46
37
  OLD_BINSTUB = %{if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?}
47
38
 
48
39
  BINSTUB_VARIATIONS = Regexp.union [
49
- %{begin\n load File.expand_path("../spring", __FILE__)\nrescue LoadError\nend\n},
40
+ %{load File.expand_path("spring", __dir__)\n},
41
+ %{begin\n load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n raise unless e.message.include?('spring')\nend\n},
50
42
  %{begin\n load File.expand_path('../spring', __FILE__)\nrescue LoadError\nend\n},
51
43
  %{begin\n spring_bin_path = File.expand_path('../spring', __FILE__)\n load spring_bin_path\nrescue LoadError => e\n raise unless e.message.end_with? spring_bin_path, 'spring/binstub'\nend\n},
52
44
  LOADER
53
- ]
45
+ ].map { |binstub| /#{Regexp.escape(binstub).gsub("'", "['\"]")}/ }
54
46
 
55
47
  class Item
56
48
  attr_reader :command, :existing
@@ -79,7 +71,7 @@ CODE
79
71
  generate(fallback)
80
72
  status "upgraded"
81
73
  elsif existing.include?(LOADER)
82
- status "spring already present"
74
+ status "Spring already present"
83
75
  elsif existing =~ BINSTUB_VARIATIONS
84
76
  upgraded = existing.sub(BINSTUB_VARIATIONS, LOADER)
85
77
  File.write(command.binstub, upgraded)
@@ -94,15 +86,15 @@ CODE
94
86
  end
95
87
 
96
88
  File.write(command.binstub, "#{head}#{shebang}#{LOADER}#{tail}")
97
- status "spring inserted"
89
+ status "Spring inserted"
98
90
  else
99
- status "doesn't appear to be ruby, so cannot use spring", $stderr
91
+ status "doesn't appear to be ruby, so cannot use Spring", $stderr
100
92
  exit 1
101
93
  end
102
94
  end
103
95
  else
104
96
  generate
105
- status "generated with spring"
97
+ status "generated with Spring"
106
98
  end
107
99
  end
108
100
 
@@ -119,7 +111,7 @@ CODE
119
111
  def remove
120
112
  if existing
121
113
  File.write(command.binstub, existing.sub(BINSTUB_VARIATIONS, ""))
122
- status "spring removed"
114
+ status "Spring removed"
123
115
  end
124
116
  end
125
117
  end
@@ -127,7 +119,7 @@ CODE
127
119
  attr_reader :bindir, :items
128
120
 
129
121
  def self.description
130
- "Generate spring based binstubs. Use --all to generate a binstub for all known commands. Use --remove to revert."
122
+ "Generate Spring based binstubs. Use --all to generate a binstub for all known commands. Use --remove to revert."
131
123
  end
132
124
 
133
125
  def self.rails_command
@@ -147,7 +139,7 @@ CODE
147
139
  @mode = :add
148
140
  @items = args.drop(1)
149
141
  .map { |name| find_commands name }
150
- .inject(Set.new, :|)
142
+ .flatten.uniq
151
143
  .map { |command| Item.new(command) }
152
144
  end
153
145
 
@@ -32,7 +32,7 @@ module Spring
32
32
  def formatted_help
33
33
  ["Version: #{env.version}\n",
34
34
  "Usage: spring COMMAND [ARGS]\n",
35
- *command_help("spring itself", spring_commands),
35
+ *command_help("Spring itself", spring_commands),
36
36
  '',
37
37
  *command_help("your application", application_commands)].join("\n")
38
38
  end
@@ -1,9 +1,7 @@
1
- require "set"
2
-
3
1
  module Spring
4
2
  module Client
5
3
  class Rails < Command
6
- COMMANDS = Set.new %w(console runner generate destroy test)
4
+ COMMANDS = %w(console runner generate destroy test)
7
5
 
8
6
  ALIASES = {
9
7
  "c" => "console",
@@ -14,7 +12,7 @@ module Spring
14
12
  }
15
13
 
16
14
  def self.description
17
- "Run a rails command. The following sub commands will use spring: #{COMMANDS.to_a.join ', '}."
15
+ "Run a rails command. The following sub commands will use Spring: #{COMMANDS.to_a.join ', '}."
18
16
  end
19
17
 
20
18
  def call
@@ -22,6 +20,8 @@ module Spring
22
20
 
23
21
  if COMMANDS.include?(command_name)
24
22
  Run.call(["rails_#{command_name}", *args.drop(2)])
23
+ elsif command_name&.start_with?("db:") && !command_name.start_with?("db:system")
24
+ Run.call(["rake", *args.drop(1)])
25
25
  else
26
26
  require "spring/configuration"
27
27
  ARGV.shift
@@ -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
 
@@ -44,7 +42,7 @@ module Spring
44
42
  require "spring/commands"
45
43
 
46
44
  if Spring.command?(args.first)
47
- # Command installed since spring started
45
+ # Command installed since Spring started
48
46
  stop_server
49
47
  cold_run
50
48
  else
@@ -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,9 +120,18 @@ 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
- $stderr.puts "There is a version mismatch between the spring client " \
134
+ $stderr.puts "There is a version mismatch between the Spring client " \
120
135
  "(#{env.version}) and the server (#{server_version})."
121
136
 
122
137
  if server_booted?
@@ -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"
@@ -142,12 +157,17 @@ module Spring
142
157
  end
143
158
 
144
159
  def run_command(client, application)
145
- log "sending command"
146
-
147
160
  application.send_io STDOUT
148
161
  application.send_io STDERR
149
162
  application.send_io STDIN
150
163
 
164
+ log "waiting for the application to be preloaded"
165
+ preload_status = application.gets
166
+ preload_status = preload_status.chomp if preload_status
167
+ log "app preload status: #{preload_status}"
168
+ exit 1 if preload_status == "1"
169
+
170
+ log "sending command"
151
171
  send_json application, "args" => args, "env" => ENV.to_hash
152
172
 
153
173
  pid = server.gets
@@ -161,6 +181,8 @@ module Spring
161
181
  if pid && !pid.empty?
162
182
  log "got pid: #{pid}"
163
183
 
184
+ suspend_resume_on_tstp_cont(pid)
185
+
164
186
  forward_signals(application)
165
187
  status = application.read.to_i
166
188
 
@@ -181,6 +203,18 @@ module Spring
181
203
  end
182
204
  end
183
205
 
206
+ def suspend_resume_on_tstp_cont(pid)
207
+ trap("TSTP") {
208
+ log "suspended"
209
+ Process.kill("STOP", pid.to_i)
210
+ Process.kill("STOP", Process.pid)
211
+ }
212
+ trap("CONT") {
213
+ log "resumed"
214
+ Process.kill("CONT", pid.to_i)
215
+ }
216
+ end
217
+
184
218
  def forward_signals(application)
185
219
  @signal_queue.each { |sig| kill sig, application }
186
220
 
@@ -213,6 +247,10 @@ module Spring
213
247
  def default_rails_env
214
248
  ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
215
249
  end
250
+
251
+ def spawn_env
252
+ ENV.slice(*Spring.spawn_on_env)
253
+ end
216
254
  end
217
255
  end
218
256
  end
@@ -4,7 +4,7 @@ module Spring
4
4
  module Client
5
5
  class Stop < Command
6
6
  def self.description
7
- "Stop all spring processes for this project."
7
+ "Stop all Spring processes for this project."
8
8
  end
9
9
 
10
10
  def call
@@ -28,11 +28,18 @@ module Spring
28
28
  config = File.expand_path("~/.spring.rb")
29
29
  require config if File.exist?(config)
30
30
 
31
- # If the config/spring.rb contains requires for commands from other gems,
32
- # then we need to be under bundler.
33
- require "bundler/setup"
31
+ # We force the TTY so bundler doesn't show a backtrace in case gems are missing.
32
+ old_env = ENV["BUNDLER_FORCE_TTY"]
33
+ ENV["BUNDLER_FORCE_TTY"] = "true"
34
+ begin
35
+ # If the config/spring.rb contains requires for commands from other gems,
36
+ # then we need to be under bundler.
37
+ require "bundler/setup"
38
+ ensure
39
+ ENV["BUNDLER_FORCE_TTY"] = old_env
40
+ end
34
41
 
35
- # Auto-require any spring extensions which are in the Gemfile
42
+ # Auto-require any Spring extensions which are in the Gemfile
36
43
  Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
37
44
  begin
38
45
  require command
@@ -1,11 +1,30 @@
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, :quiet
8
+ attr_accessor :application_root, :connect_timeout, :boot_timeout
9
+ attr_writer :quiet
6
10
 
7
11
  def gemfile
8
- ENV['BUNDLE_GEMFILE'] || "Gemfile"
12
+ require "bundler"
13
+
14
+ if /\s1.9.[0-9]/ === Bundler.ruby_scope.gsub(/[\/\s]+/,'')
15
+ Pathname.new(ENV["BUNDLE_GEMFILE"] || "Gemfile").expand_path
16
+ else
17
+ Bundler.default_gemfile
18
+ end
19
+ end
20
+
21
+ def gemfile_lock
22
+ case gemfile.to_s
23
+ when /\bgems\.rb\z/
24
+ gemfile.sub_ext('.locked')
25
+ else
26
+ gemfile.sub_ext('.lock')
27
+ end
9
28
  end
10
29
 
11
30
  def after_fork_callbacks
@@ -16,6 +35,14 @@ module Spring
16
35
  after_fork_callbacks << block
17
36
  end
18
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
+
19
46
  def verify_environment
20
47
  application_root_path
21
48
  end
@@ -37,6 +64,10 @@ module Spring
37
64
  @project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
38
65
  end
39
66
 
67
+ def quiet
68
+ @quiet || ENV.key?('SPRING_QUIET')
69
+ end
70
+
40
71
  private
41
72
 
42
73
  def find_project_root(current_dir)
data/lib/spring/env.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  require "pathname"
2
- require "fileutils"
3
- require "digest/md5"
4
- require "tmpdir"
5
2
 
6
3
  require "spring/version"
7
- require "spring/sid"
8
4
  require "spring/configuration"
9
5
 
10
6
  module Spring
@@ -33,15 +29,18 @@ module Spring
33
29
  end
34
30
 
35
31
  def tmp_path
32
+ require "tmpdir"
36
33
  path = Pathname.new(
37
34
  ENV["SPRING_TMP_PATH"] ||
38
35
  File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
39
36
  )
37
+ require "fileutils"
40
38
  FileUtils.mkdir_p(path) unless path.exist?
41
39
  path
42
40
  end
43
41
 
44
42
  def application_id
43
+ require "digest/md5"
45
44
  ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
46
45
  end
47
46
 
data/lib/spring/errors.rb CHANGED
@@ -24,7 +24,7 @@ module Spring
24
24
 
25
25
  def message
26
26
  "Spring was unable to find your config/application.rb file. " \
27
- "Your project root was detected at #{project_root}, so spring " \
27
+ "Your project root was detected at #{project_root}, so Spring " \
28
28
  "looked for #{project_root}/config/application.rb but it doesn't exist. You can " \
29
29
  "configure the root of your application by setting Spring.application_root in " \
30
30
  "config/spring.rb."
data/lib/spring/json.rb CHANGED
@@ -46,11 +46,9 @@ end
46
46
 
47
47
  # See https://github.com/kr/okjson for updates.
48
48
 
49
- require 'stringio'
50
-
51
49
  # Some parts adapted from
52
- # http://golang.org/src/pkg/json/decode.go and
53
- # http://golang.org/src/pkg/utf8/utf8.go
50
+ # https://golang.org/src/pkg/json/decode.go and
51
+ # https://golang.org/src/pkg/utf8/utf8.go
54
52
  module Spring
55
53
  module OkJson
56
54
  Upstream = '43'
@@ -468,19 +466,18 @@ private
468
466
 
469
467
 
470
468
  def strenc(s)
471
- t = StringIO.new
472
- t.putc(?")
469
+ t = '"'.b
473
470
  r = 0
474
471
 
475
472
  while r < s.length
476
473
  case s[r]
477
- when ?" then t.print('\\"')
478
- when ?\\ then t.print('\\\\')
479
- when ?\b then t.print('\\b')
480
- when ?\f then t.print('\\f')
481
- when ?\n then t.print('\\n')
482
- when ?\r then t.print('\\r')
483
- when ?\t then t.print('\\t')
474
+ when ?" then t << '\\"'
475
+ when ?\\ then t << '\\\\'
476
+ when ?\b then t << '\\b'
477
+ when ?\f then t << '\\f'
478
+ when ?\n then t << '\\n'
479
+ when ?\r then t << '\\r'
480
+ when ?\t then t << '\\t'
484
481
  else
485
482
  c = s[r]
486
483
  # In ruby >= 1.9, s[r] is a codepoint, not a byte.
@@ -490,14 +487,14 @@ private
490
487
  if c.ord < Spc.ord
491
488
  c = "\\u%04x" % [c.ord]
492
489
  end
493
- t.write(c)
490
+ t << c
494
491
  rescue
495
- t.write(Ustrerr)
492
+ t << Ustrerr
496
493
  end
497
494
  elsif c < Spc
498
- t.write("\\u%04x" % c)
495
+ t << "\\u%04x" % c
499
496
  elsif Spc <= c && c <= ?~
500
- t.putc(c)
497
+ t << c
501
498
  else
502
499
  n = ucharcopy(t, s, r) # ensure valid UTF-8 output
503
500
  r += n - 1 # r is incremented below
@@ -505,8 +502,8 @@ private
505
502
  end
506
503
  r += 1
507
504
  end
508
- t.putc(?")
509
- t.string
505
+ t << '"'
506
+ t
510
507
  end
511
508
 
512
509
 
@@ -531,7 +528,7 @@ private
531
528
 
532
529
  # 1-byte, 7-bit sequence?
533
530
  if c0 < Utagx
534
- t.putc(c0)
531
+ t << c0
535
532
  return 1
536
533
  end
537
534
 
@@ -544,8 +541,8 @@ private
544
541
  # 2-byte, 11-bit sequence?
545
542
  if c0 < Utag3
546
543
  raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
547
- t.putc(c0)
548
- t.putc(c1)
544
+ t << c0
545
+ t << c1
549
546
  return 2
550
547
  end
551
548
 
@@ -559,9 +556,9 @@ private
559
556
  if c0 < Utag4
560
557
  u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
561
558
  raise Utf8Error if u <= Uchar2max
562
- t.putc(c0)
563
- t.putc(c1)
564
- t.putc(c2)
559
+ t << c0
560
+ t << c1
561
+ t << c2
565
562
  return 3
566
563
  end
567
564
 
@@ -574,16 +571,16 @@ private
574
571
  if c0 < Utag5
575
572
  u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
576
573
  raise Utf8Error if u <= Uchar3max
577
- t.putc(c0)
578
- t.putc(c1)
579
- t.putc(c2)
580
- t.putc(c3)
574
+ t << c0
575
+ t << c1
576
+ t << c2
577
+ t << c3
581
578
  return 4
582
579
  end
583
580
 
584
581
  raise Utf8Error
585
582
  rescue Utf8Error
586
- t.write(Ustrerr)
583
+ t << Ustrerr
587
584
  return 1
588
585
  end
589
586
 
@@ -1,6 +1,6 @@
1
1
  module Spring
2
2
  # Yes, I know this reimplements a bunch of stuff in Active Support, but
3
- # I don't want the spring client to depend on AS, in order to keep its
3
+ # I don't want the Spring client to depend on AS, in order to keep its
4
4
  # load time down.
5
5
  class ProcessTitleUpdater
6
6
  SECOND = 1
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,15 +78,16 @@ 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.
81
86
  # This will cause it to be automatically killed once the session
82
87
  # ends (i.e. when the user closes their terminal).
83
88
  def set_pgid
84
- Process.setpgid(0, SID.pgid)
89
+ pgid = Process.getpgid(Process.getsid)
90
+ Process.setpgid(0, pgid)
85
91
  end
86
92
 
87
93
  # Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
@@ -135,6 +141,10 @@ module Spring
135
141
 
136
142
  private
137
143
 
144
+ def reset_if_env_changed(application, reset_env)
145
+ application.stop if ENV.slice(*reset_env.keys) != reset_env
146
+ end
147
+
138
148
  def default_env
139
149
  Env.new(log_file: default_log_file)
140
150
  end
@@ -1,3 +1,3 @@
1
1
  module Spring
2
- VERSION = "2.0.2"
2
+ VERSION = "4.2.1"
3
3
  end