Class: Tommygun

Inherits:
Object
  • Object
show all
Includes:
Rack::Utils
Defined in:
lib/tommygun.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rackup_file, wrapper = nil) ⇒ Tommygun

Returns a new instance of Tommygun.



9
10
11
12
# File 'lib/tommygun.rb', line 9

def initialize(rackup_file, wrapper=nil)
  @rackup_file = rackup_file
  @wrapper = wrapper || lambda { |inner_app| inner_app }
end

Instance Attribute Details

#rackup_fileObject (readonly)

Returns the value of attribute rackup_file.



7
8
9
# File 'lib/tommygun.rb', line 7

def rackup_file
  @rackup_file
end

Instance Method Details

#assemble_appObject



71
72
73
# File 'lib/tommygun.rb', line 71

def assemble_app
  @wrapper.call(inner_app)
end

#call(env) ⇒ Object



14
15
16
# File 'lib/tommygun.rb', line 14

def call(env)
  dup.call!(env)
end

#call!(env) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/tommygun.rb', line 18

def call!(env)
  @env = env
  @reader, @writer = IO.pipe

  # Disable GC before forking in an attempt to get some advantage
  # out of COW.
  GC.disable

  if fork
    proceed_as_parent
  else
    proceed_as_child
  end

ensure
  GC.enable
end

#format_error(result) ⇒ Object



51
52
53
54
55
# File 'lib/tommygun.rb', line 51

def format_error(result)
  message, backtrace = result
  "<h1>FAIL</h1><h3>#{escape_html(message)}</h3>" +
  "<pre>#{escape_html(backtrace.join("\n"))}</pre>"
end

#inner_appObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/tommygun.rb', line 75

def inner_app
  if rackup_file =~ /\.ru$/
    config = File.read(rackup_file)
    eval "Rack::Builder.new {( #{config}\n )}.to_app", nil, rackup_file
  else
    require rackup_file
    if defined? Sinatra::Application
      Sinatra::Application.set :reload, false
      Sinatra::Application.set :logging, false
      Sinatra::Application.set :raise_errors, true
      Sinatra::Application
    else
      Object.const_get(File.basename(rackup_file, '.rb').capitalize)
    end
  end
end

#proceed_as_childObject

Stuff that happens in the forked child process.



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/tommygun.rb', line 59

def proceed_as_child
  @reader.close
  app = assemble_app
  status, headers, body = app.call(@env)
  Marshal.dump([status, headers.to_hash, slurp(body)], @writer)
  @writer.close
rescue Object => boom
  Marshal.dump(["#{boom.class.name}: #{boom.to_s}", boom.backtrace], @writer)
ensure
  exit! 0
end

#proceed_as_parentObject

Stuff that happens in the parent process



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/tommygun.rb', line 38

def proceed_as_parent
  rand # Reseeds 
  @writer.close
  result = Marshal.load(@reader)
  @reader.close
  Process.wait
  if result.length == 3
    result
  else
    [500, {'Content-Type'=>'text/html;charset=utf-8'}, [format_error(result)]]
  end
end

#slurp(body) ⇒ Object



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

def slurp(body)
  return body    if body.respond_to? :to_ary
  return [body]  if body.respond_to? :to_str

  buf = []
  body.each { |part| buf << part }
  buf
end