Class: Spring::Application

Inherits:
Object
  • Object
show all
Defined in:
lib/spring/application.rb

Constant Summary collapse

WATCH_INTERVAL =
0.2

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(manager, watcher = Spring.application_watcher) ⇒ Application

Returns a new instance of Application.



17
18
19
20
21
22
23
24
25
26
27
# File 'lib/spring/application.rb', line 17

def initialize(manager, watcher = Spring.application_watcher)
  @manager = manager
  @watcher = watcher
  @setup   = Set.new

  @stdout = IO.new(STDOUT.fileno)
  @stderr = IO.new(STDERR.fileno)
  @stdin  = File.open('/dev/null', 'r')

  STDIN.reopen(@stdin)
end

Instance Attribute Details

#managerObject (readonly)

Returns the value of attribute manager.



15
16
17
# File 'lib/spring/application.rb', line 15

def manager
  @manager
end

#watcherObject (readonly)

Returns the value of attribute watcher.



15
16
17
# File 'lib/spring/application.rb', line 15

def watcher
  @watcher
end

Instance Method Details

#redirect_output(socket) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/spring/application.rb', line 101

def redirect_output(socket)
  STDOUT.reopen socket.recv_io
  STDERR.reopen socket.recv_io
  STDIN.reopen  socket.recv_io

  yield
ensure
  STDOUT.reopen @stdout
  STDERR.reopen @stderr
  STDIN.reopen  @stdin
end

#runObject



48
49
50
51
52
53
# File 'lib/spring/application.rb', line 48

def run
  loop do
    watch_application
    serve manager.recv_io(UNIXSocket)
  end
end

#serve(client) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/spring/application.rb', line 61

def serve(client)
  redirect_output(client) do
    args_length = client.gets.to_i
    args        = args_length.times.map { client.read(client.gets.to_i) }
    command     = Spring.command(args.shift)

    setup command

    ActionDispatch::Reloader.cleanup!
    ActionDispatch::Reloader.prepare!

    pid = fork {
      IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
      command.call(args)
    }

    manager.puts pid
    Process.wait pid
  end
ensure
  client.puts
  client.close
end

#setup(command) ⇒ Object

The command might need to require some files in the main process so that they are cached. For example a test command wants to load the helper file once and have it cached.

FIXME: The watcher.add_files will reset the watcher, which may mean that

previous changes to already-loaded files are missed.


91
92
93
94
95
96
97
98
99
# File 'lib/spring/application.rb', line 91

def setup(command)
  return if @setup.include?(command.class)
  @setup << command.class

  if command.respond_to?(:setup)
    command.setup
    watcher.add_files $LOADED_FEATURES # loaded features may have changed
  end
end

#startObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/spring/application.rb', line 29

def start
  require "./config/application"

  # The test environment has config.cache_classes = true set by default.
  # However, we don't want this to prevent us from performing class reloading,
  # so this gets around that.
  Rails::Application.initializer :initialize_dependency_mechanism, group: :all do
    ActiveSupport::Dependencies.mechanism = :load
  end

  require "./config/environment"

  watcher.add_files $LOADED_FEATURES
  watcher.add_files ["Gemfile", "Gemfile.lock"].map { |f| "#{Rails.root}/#{f}" }
  watcher.add_globs Rails.application.paths["config/initializers"].map { |p| "#{Rails.root}/#{p}/*.rb" }

  run
end

#watch_applicationObject



55
56
57
58
59
# File 'lib/spring/application.rb', line 55

def watch_application
  until IO.select([manager], [], [], WATCH_INTERVAL)
    exit if watcher.stale?
  end
end