Class: Vegas::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/vegas/runner.rb

Constant Summary collapse

ROOT_DIR =
ENV['HOME'] ? File.expand_path(File.join('~', '.vegas')) : nil
PORT =
5678
HOST =
WINDOWS ? 'localhost' : '0.0.0.0'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, app_name, set_options = {}, runtime_args = ARGV, &block) ⇒ Runner

Returns a new instance of Runner.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/vegas/runner.rb', line 24

def initialize(app, app_name, set_options = {}, runtime_args = ARGV, &block)
  @options = set_options || {}

  self.class.logger.level = options[:debug] ? Logger::DEBUG : Logger::INFO

  @app      = app
  @app_name = app_name

  @filesystem_friendly_app_name = @app_name.gsub(/\W+/, "_")
  @quoted_app_name = "'#{app_name}'"

  @runtime_args = runtime_args
  @rack_handler = setup_rack_handler

  # load options from opt parser
  @args = define_options do |opts|
    if block_given?
      opts.separator ''
      opts.separator "#{quoted_app_name} options:"
      yield(self, opts, app)
    end
  end

  if @should_kill
    kill!
    exit!(0)
  end

  # Handle :before_run hook
  if (before_run = options.delete(:before_run)).respond_to?(:call)
    before_run.call(self)
  end

  # Set app options
  @host = options[:host] || HOST

  if app.respond_to?(:set)
    app.set(options)
    app.set(:vegas, self)
  end

  # Make sure app dir is setup
  FileUtils.mkdir_p(app_dir)

  return if options[:start] == false

  # evaluate the launch_path
  path = if options[:launch_path].respond_to?(:call)
    options[:launch_path].call(self)
  else
    options[:launch_path]
  end

  start(path)
end

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def app
  @app
end

#app_nameObject (readonly)

Returns the value of attribute app_name.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def app_name
  @app_name
end

#argsObject (readonly)

Returns the value of attribute args.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def args
  @args
end

#filesystem_friendly_app_nameObject (readonly)

Returns the value of attribute filesystem_friendly_app_name.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def filesystem_friendly_app_name
  @filesystem_friendly_app_name
end

#hostObject (readonly)

Returns the value of attribute host.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def host
  @host
end

#optionsObject (readonly)

Returns the value of attribute options.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def options
  @options
end

#portObject (readonly)

Returns the value of attribute port.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def port
  @port
end

#quoted_app_nameObject (readonly)

Returns the value of attribute quoted_app_name.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def quoted_app_name
  @quoted_app_name
end

#rack_handlerObject (readonly)

Returns the value of attribute rack_handler.



17
18
19
# File 'lib/vegas/runner.rb', line 17

def rack_handler
  @rack_handler
end

Class Method Details

.loggerObject



255
256
257
258
259
260
261
262
263
# File 'lib/vegas/runner.rb', line 255

def self.logger
  @logger ||= LOGGER if defined?(LOGGER)
  if !@logger
    @logger           = Logger.new(STDOUT)
    @logger.formatter = Proc.new {|s, t, n, msg| "[#{t}] #{msg}\n"}
    @logger
  end
  @logger
end

.logger=(logger) ⇒ Object



251
252
253
# File 'lib/vegas/runner.rb', line 251

def self.logger=(logger)
  @logger = logger
end

Instance Method Details

#announce_port_attemptedObject



139
140
141
# File 'lib/vegas/runner.rb', line 139

def announce_port_attempted
  logger.info "trying port #{port}..."
end

#app_dirObject



80
81
82
83
84
85
# File 'lib/vegas/runner.rb', line 80

def app_dir
  if !options[:app_dir] && !ROOT_DIR
    raise ArgumentError.new("nor --app-dir neither EVN['HOME'] defined")
  end
  options[:app_dir] || File.join(ROOT_DIR, filesystem_friendly_app_name)
end

#check_for_running(path = nil) ⇒ Object



160
161
162
163
164
165
166
167
168
169
# File 'lib/vegas/runner.rb', line 160

def check_for_running(path = nil)
  if File.exists?(pid_file) && File.exists?(url_file)
    running_url = File.read(url_file)
    if !port_open?(running_url)
      logger.warn "#{quoted_app_name} is already running at #{running_url}"
      launch!(running_url, path)
      exit!(1)
    end
  end
end

#daemon_executeObject



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/vegas/runner.rb', line 205

def daemon_execute
  File.umask 0000
  FileUtils.touch log_file
  STDIN.reopen    log_file
  STDOUT.reopen   log_file, "a"
  STDERR.reopen   log_file, "a"

  logger.debug "Child Process: #{Process.pid}"

  File.open(pid_file, 'w') {|f| f.write("#{Process.pid}") }
  at_exit { delete_pid! }
end

#daemonize!Object

Adapted from Rackup



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/vegas/runner.rb', line 187

def daemonize!
  if JRUBY
    # It's not a true daemon but when executed with & works like one
    thread = Thread.new {daemon_execute}
    thread.join
    
  elsif RUBY_VERSION < "1.9"
    logger.debug "Parent Process: #{Process.pid}"
    exit!(0) if fork
    logger.debug "Child Process: #{Process.pid}"
    daemon_execute
    
  else
    Process.daemon(true, true)
    daemon_execute
  end
end

#find_portObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/vegas/runner.rb', line 120

def find_port
  if @port = options[:port]
    announce_port_attempted

    unless port_open?
      logger.warn "Port #{port} is already in use. Please try another. " +
                  "You can also omit the port flag, and we'll find one for you."
    end
  else
    @port = PORT
    announce_port_attempted

    until port_open?
      @port += 1
      announce_port_attempted
    end
  end
end

#kill!Object



224
225
226
227
228
229
230
# File 'lib/vegas/runner.rb', line 224

def kill!
  pid = File.read(pid_file)
  logger.warn "Sending #{kill_command} to #{pid.to_i}"
  Process.kill(kill_command, pid.to_i)
rescue => e
  logger.warn "pid not found at #{pid_file} : #{e}"
end

#launch!(specific_url = nil, path = nil) ⇒ Object



218
219
220
221
222
# File 'lib/vegas/runner.rb', line 218

def launch!(specific_url = nil, path = nil)
  return if options[:skip_launch]
  cmd = WINDOWS ? "start" : "open"
  system "#{cmd} #{specific_url || url}#{path}"
end

#load_config_file(config_path) ⇒ Object

Loads a config file at config_path and evals it in the context of the @app.



243
244
245
246
247
248
249
# File 'lib/vegas/runner.rb', line 243

def load_config_file(config_path)
  abort "Can not find config file at #{config_path}" if !File.readable?(config_path)
  config = File.read(config_path)
  # trim off anything after __END__
  config.sub!(/^__END__\n.*/, '')
  @app.module_eval(config)
end

#log_fileObject



95
96
97
# File 'lib/vegas/runner.rb', line 95

def log_file
  options[:log_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.log")
end

#loggerObject



265
266
267
# File 'lib/vegas/runner.rb', line 265

def logger
  self.class.logger
end

#pid_fileObject



87
88
89
# File 'lib/vegas/runner.rb', line 87

def pid_file
  options[:pid_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.pid")
end

#port_open?(check_url = nil) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/vegas/runner.rb', line 143

def port_open?(check_url = nil)
  begin
    check_url ||= url
    options[:no_proxy] ? open(check_url, :proxy => nil) : open(check_url)
    false
  rescue Errno::ECONNREFUSED => e
    true
  rescue Errno::EPERM => e
    # catches the "Operation not permitted" under Cygwin
    true
  end
end

#run!Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/vegas/runner.rb', line 171

def run!
  logger.info "Running with Rack handler: #{@rack_handler.inspect}"

  rack_handler.run app, :Host => host, :Port => port do |server|
    kill_commands.each do |command|
      trap(command) do
        ## Use thins' hard #stop! if available, otherwise just #stop
        server.respond_to?(:stop!) ? server.stop! : server.stop
        logger.info "#{quoted_app_name} received INT ... stopping"
        delete_pid!
      end
    end
  end
end

#start(path = nil) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/vegas/runner.rb', line 103

def start(path = nil)
  logger.info "Running with Windows Settings" if WINDOWS
  logger.info "Running with JRuby" if JRUBY
  logger.info "Starting #{quoted_app_name}..."
  begin
    check_for_running(path)
    find_port
    write_url
    launch!(url, path)
    daemonize! unless options[:foreground]
    run!
  rescue RuntimeError => e
    logger.warn "There was an error starting #{quoted_app_name}: #{e}"
    exit
  end
end

#statusObject



232
233
234
235
236
237
238
239
240
# File 'lib/vegas/runner.rb', line 232

def status
  if File.exists?(pid_file)
    logger.info "#{quoted_app_name} running"
    logger.info "PID #{File.read(pid_file)}"
    logger.info "URL #{File.read(url_file)}" if File.exists?(url_file)
  else
    logger.info "#{quoted_app_name} not running!"
  end
end

#urlObject



99
100
101
# File 'lib/vegas/runner.rb', line 99

def url
  "http://#{host}:#{port}"
end

#url_fileObject



91
92
93
# File 'lib/vegas/runner.rb', line 91

def url_file
  options[:url_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.url")
end

#write_urlObject



156
157
158
# File 'lib/vegas/runner.rb', line 156

def write_url
  File.open(url_file, 'w') {|f| f << url }
end