rails_product 0.5
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.
- data/CHANGELOG +619 -0
- data/MIT-LICENSE +21 -0
- data/README +195 -0
- data/Rakefile +413 -0
- data/bin/benchmarker +19 -0
- data/bin/breakpointer +3 -0
- data/bin/breakpointer_for_gem +4 -0
- data/bin/console +23 -0
- data/bin/console_sandbox +0 -0
- data/bin/destroy +7 -0
- data/bin/generate +7 -0
- data/bin/listener +86 -0
- data/bin/process/reaper +123 -0
- data/bin/process/spawner +54 -0
- data/bin/process/spinner +60 -0
- data/bin/profiler +34 -0
- data/bin/rails +17 -0
- data/bin/rails_product +17 -0
- data/bin/runner +28 -0
- data/bin/server +125 -0
- data/bin/tracker +69 -0
- data/bin/update +5 -0
- data/configs/apache/vhost.example.conf +42 -0
- data/configs/apache.conf +40 -0
- data/configs/database.yml +23 -0
- data/configs/empty.log +0 -0
- data/configs/routes.rb +19 -0
- data/dispatches/dispatch.fcgi +24 -0
- data/dispatches/dispatch.rb +10 -0
- data/dispatches/gateway.cgi +97 -0
- data/doc/README_FOR_APP +2 -0
- data/environments/development.rb +14 -0
- data/environments/environment.rb +101 -0
- data/environments/production.rb +8 -0
- data/environments/test.rb +17 -0
- data/fresh_rakefile +223 -0
- data/helpers/application.rb +4 -0
- data/helpers/application_helper.rb +3 -0
- data/helpers/test_helper.rb +26 -0
- data/html/404.html +8 -0
- data/html/500.html +8 -0
- data/html/favicon.ico +0 -0
- data/html/index.html +78 -0
- data/html/javascripts/controls.js +446 -0
- data/html/javascripts/dragdrop.js +537 -0
- data/html/javascripts/effects.js +612 -0
- data/html/javascripts/prototype.js +1038 -0
- data/html/robots.txt +1 -0
- data/lib/binding_of_caller.rb +83 -0
- data/lib/breakpoint.rb +523 -0
- data/lib/breakpoint_client.rb +196 -0
- data/lib/code_statistics.rb +104 -0
- data/lib/console_sandbox.rb +6 -0
- data/lib/dispatcher.rb +59 -0
- data/lib/fcgi_handler.rb +156 -0
- data/lib/productize.rb +116 -0
- data/lib/rails_generator/base.rb +203 -0
- data/lib/rails_generator/commands.rb +409 -0
- data/lib/rails_generator/generators/applications/app/USAGE +16 -0
- data/lib/rails_generator/generators/applications/app/app_generator.rb +126 -0
- data/lib/rails_generator/generators/applications/productized_app/USAGE +16 -0
- data/lib/rails_generator/generators/applications/productized_app/productized_app_generator.rb +133 -0
- data/lib/rails_generator/generators/components/controller/USAGE +30 -0
- data/lib/rails_generator/generators/components/controller/controller_generator.rb +38 -0
- data/lib/rails_generator/generators/components/controller/templates/controller.rb +10 -0
- data/lib/rails_generator/generators/components/controller/templates/functional_test.rb +18 -0
- data/lib/rails_generator/generators/components/controller/templates/helper.rb +2 -0
- data/lib/rails_generator/generators/components/controller/templates/view.rhtml +2 -0
- data/lib/rails_generator/generators/components/mailer/USAGE +19 -0
- data/lib/rails_generator/generators/components/mailer/mailer_generator.rb +32 -0
- data/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml +3 -0
- data/lib/rails_generator/generators/components/mailer/templates/mailer.rb +13 -0
- data/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +37 -0
- data/lib/rails_generator/generators/components/mailer/templates/view.rhtml +3 -0
- data/lib/rails_generator/generators/components/migration/USAGE +14 -0
- data/lib/rails_generator/generators/components/migration/migration_generator.rb +9 -0
- data/lib/rails_generator/generators/components/migration/templates/migration.rb +7 -0
- data/lib/rails_generator/generators/components/model/USAGE +17 -0
- data/lib/rails_generator/generators/components/model/model_generator.rb +18 -0
- data/lib/rails_generator/generators/components/model/templates/fixtures.yml +5 -0
- data/lib/rails_generator/generators/components/model/templates/model.rb +2 -0
- data/lib/rails_generator/generators/components/model/templates/unit_test.rb +14 -0
- data/lib/rails_generator/generators/components/scaffold/USAGE +32 -0
- data/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +178 -0
- data/lib/rails_generator/generators/components/scaffold/templates/controller.rb +54 -0
- data/lib/rails_generator/generators/components/scaffold/templates/form.rhtml +3 -0
- data/lib/rails_generator/generators/components/scaffold/templates/form_scaffolding.rhtml +1 -0
- data/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb +98 -0
- data/lib/rails_generator/generators/components/scaffold/templates/helper.rb +2 -0
- data/lib/rails_generator/generators/components/scaffold/templates/layout.rhtml +13 -0
- data/lib/rails_generator/generators/components/scaffold/templates/style.css +74 -0
- data/lib/rails_generator/generators/components/scaffold/templates/view_edit.rhtml +9 -0
- data/lib/rails_generator/generators/components/scaffold/templates/view_list.rhtml +27 -0
- data/lib/rails_generator/generators/components/scaffold/templates/view_new.rhtml +8 -0
- data/lib/rails_generator/generators/components/scaffold/templates/view_show.rhtml +8 -0
- data/lib/rails_generator/generators/components/web_service/USAGE +28 -0
- data/lib/rails_generator/generators/components/web_service/templates/api_definition.rb +5 -0
- data/lib/rails_generator/generators/components/web_service/templates/controller.rb +8 -0
- data/lib/rails_generator/generators/components/web_service/templates/functional_test.rb +19 -0
- data/lib/rails_generator/generators/components/web_service/web_service_generator.rb +29 -0
- data/lib/rails_generator/lookup.rb +206 -0
- data/lib/rails_generator/manifest.rb +53 -0
- data/lib/rails_generator/options.rb +134 -0
- data/lib/rails_generator/scripts/destroy.rb +7 -0
- data/lib/rails_generator/scripts/generate.rb +7 -0
- data/lib/rails_generator/scripts/update.rb +12 -0
- data/lib/rails_generator/scripts.rb +83 -0
- data/lib/rails_generator/simple_logger.rb +46 -0
- data/lib/rails_generator/spec.rb +44 -0
- data/lib/rails_generator.rb +43 -0
- data/lib/rubyprof_ext.rb +35 -0
- data/lib/webrick_server.rb +148 -0
- data/sites/fresh_rakefile +176 -0
- metadata +250 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require 'breakpoint'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
require 'timeout'
|
|
4
|
+
|
|
5
|
+
Options = {
|
|
6
|
+
:ClientURI => nil,
|
|
7
|
+
:ServerURI => "druby://localhost:42531",
|
|
8
|
+
:RetryDelay => 2,
|
|
9
|
+
:Permanent => true,
|
|
10
|
+
:Verbose => false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ARGV.options do |opts|
|
|
14
|
+
script_name = File.basename($0)
|
|
15
|
+
opts.banner = [
|
|
16
|
+
"Usage: ruby #{script_name} [Options] [server uri]",
|
|
17
|
+
"",
|
|
18
|
+
"This tool lets you connect to a breakpoint service ",
|
|
19
|
+
"which was started via Breakpoint.activate_drb.",
|
|
20
|
+
"",
|
|
21
|
+
"The server uri defaults to druby://localhost:42531"
|
|
22
|
+
].join("\n")
|
|
23
|
+
|
|
24
|
+
opts.separator ""
|
|
25
|
+
|
|
26
|
+
opts.on("-c", "--client-uri=uri",
|
|
27
|
+
"Run the client on the specified uri.",
|
|
28
|
+
"This can be used to specify the port",
|
|
29
|
+
"that the client uses to allow for back",
|
|
30
|
+
"connections from the server.",
|
|
31
|
+
"Default: Find a good URI automatically.",
|
|
32
|
+
"Example: -c druby://localhost:12345"
|
|
33
|
+
) { |Options[:ClientURI]| }
|
|
34
|
+
|
|
35
|
+
opts.on("-s", "--server-uri=uri",
|
|
36
|
+
"Connect to the server specified at the",
|
|
37
|
+
"specified uri.",
|
|
38
|
+
"Default: druby://localhost:42531"
|
|
39
|
+
) { |Options[:ServerURI]| }
|
|
40
|
+
|
|
41
|
+
opts.on("-R", "--retry-delay=delay", Integer,
|
|
42
|
+
"Automatically try to reconnect to the",
|
|
43
|
+
"server after delay seconds when the",
|
|
44
|
+
"connection failed or timed out.",
|
|
45
|
+
"A value of 0 disables automatical",
|
|
46
|
+
"reconnecting completely.",
|
|
47
|
+
"Default: 10"
|
|
48
|
+
) { |Options[:RetryDelay]| }
|
|
49
|
+
|
|
50
|
+
opts.on("-P", "--[no-]permanent",
|
|
51
|
+
"Run the breakpoint client in permanent mode.",
|
|
52
|
+
"This means that the client will keep continue",
|
|
53
|
+
"running even after the server has closed the",
|
|
54
|
+
"connection. Useful for example in Rails."
|
|
55
|
+
) { |Options[:Permanent]| }
|
|
56
|
+
|
|
57
|
+
opts.on("-V", "--[no-]verbose",
|
|
58
|
+
"Run the breakpoint client in verbose mode.",
|
|
59
|
+
"Will produce more messages, for example between",
|
|
60
|
+
"individual breakpoints. This might help in seeing",
|
|
61
|
+
"that the breakpoint client is still alive, but adds",
|
|
62
|
+
"quite a bit of clutter."
|
|
63
|
+
) { |Options[:Verbose]| }
|
|
64
|
+
|
|
65
|
+
opts.separator ""
|
|
66
|
+
|
|
67
|
+
opts.on("-h", "--help",
|
|
68
|
+
"Show this help message."
|
|
69
|
+
) { puts opts; exit }
|
|
70
|
+
opts.on("-v", "--version",
|
|
71
|
+
"Display the version information."
|
|
72
|
+
) do
|
|
73
|
+
id = %q$Id: breakpoint_client.rb 91 2005-02-04 22:34:08Z flgr $
|
|
74
|
+
puts id.sub("Id: ", "")
|
|
75
|
+
puts "(Breakpoint::Version = #{Breakpoint::Version})"
|
|
76
|
+
exit
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
opts.parse!
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Options[:ServerURI] = ARGV[0] if ARGV[0]
|
|
83
|
+
|
|
84
|
+
module Handlers
|
|
85
|
+
extend self
|
|
86
|
+
|
|
87
|
+
def breakpoint_handler(workspace, message)
|
|
88
|
+
puts message
|
|
89
|
+
IRB.start(nil, nil, workspace)
|
|
90
|
+
|
|
91
|
+
puts ""
|
|
92
|
+
if Options[:Verbose] then
|
|
93
|
+
puts "Resumed execution. Waiting for next breakpoint...", ""
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def eval_handler(code)
|
|
98
|
+
result = eval(code, TOPLEVEL_BINDING)
|
|
99
|
+
if result then
|
|
100
|
+
DRbObject.new(result)
|
|
101
|
+
else
|
|
102
|
+
result
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def collision_handler()
|
|
107
|
+
msg = [
|
|
108
|
+
" *** Breakpoint service collision ***",
|
|
109
|
+
" Another Breakpoint service tried to use the",
|
|
110
|
+
" port already occupied by this one. It will",
|
|
111
|
+
" keep waiting until this Breakpoint service",
|
|
112
|
+
" is shut down.",
|
|
113
|
+
" ",
|
|
114
|
+
" If you are using the Breakpoint library for",
|
|
115
|
+
" debugging a Rails or other CGI application",
|
|
116
|
+
" this likely means that this Breakpoint",
|
|
117
|
+
" session belongs to an earlier, outdated",
|
|
118
|
+
" request and should be shut down via 'exit'."
|
|
119
|
+
].join("\n")
|
|
120
|
+
|
|
121
|
+
if RUBY_PLATFORM["win"] then
|
|
122
|
+
# This sucks. Sorry, I'm not doing this because
|
|
123
|
+
# I like funky message boxes -- I need to do this
|
|
124
|
+
# because on Windows I have no way of displaying
|
|
125
|
+
# my notification via puts() when gets() is still
|
|
126
|
+
# being performed on STDIN. I have not found a
|
|
127
|
+
# better solution.
|
|
128
|
+
begin
|
|
129
|
+
require 'tk'
|
|
130
|
+
root = TkRoot.new { withdraw }
|
|
131
|
+
Tk.messageBox('message' => msg, 'type' => 'ok')
|
|
132
|
+
root.destroy
|
|
133
|
+
rescue Exception
|
|
134
|
+
puts "", msg, ""
|
|
135
|
+
end
|
|
136
|
+
else
|
|
137
|
+
puts "", msg, ""
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Used for checking whether we are currently in the reconnecting loop.
|
|
143
|
+
reconnecting = false
|
|
144
|
+
|
|
145
|
+
loop do
|
|
146
|
+
DRb.start_service(Options[:ClientURI])
|
|
147
|
+
|
|
148
|
+
begin
|
|
149
|
+
service = DRbObject.new(nil, Options[:ServerURI])
|
|
150
|
+
|
|
151
|
+
begin
|
|
152
|
+
ehandler = Handlers.method(:eval_handler)
|
|
153
|
+
chandler = Handlers.method(:collision_handler)
|
|
154
|
+
handler = Handlers.method(:breakpoint_handler)
|
|
155
|
+
service.eval_handler = ehandler
|
|
156
|
+
service.collision_handler = chandler
|
|
157
|
+
service.handler = handler
|
|
158
|
+
|
|
159
|
+
reconnecting = false
|
|
160
|
+
if Options[:Verbose] then
|
|
161
|
+
puts "Connection established. Waiting for breakpoint...", ""
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
loop do
|
|
165
|
+
begin
|
|
166
|
+
service.ping
|
|
167
|
+
rescue DRb::DRbConnError => error
|
|
168
|
+
puts "Server exited. Closing connection...", ""
|
|
169
|
+
exit! unless Options[:Permanent]
|
|
170
|
+
break
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
sleep(0.5)
|
|
174
|
+
end
|
|
175
|
+
ensure
|
|
176
|
+
service.eval_handler = nil
|
|
177
|
+
service.collision_handler = nil
|
|
178
|
+
service.handler = nil
|
|
179
|
+
end
|
|
180
|
+
rescue Exception => error
|
|
181
|
+
if Options[:RetryDelay] > 0 then
|
|
182
|
+
if not reconnecting then
|
|
183
|
+
reconnecting = true
|
|
184
|
+
puts "No connection to breakpoint service at #{Options[:ServerURI]} " +
|
|
185
|
+
"(#{error.class})"
|
|
186
|
+
puts error.backtrace if $DEBUG
|
|
187
|
+
puts "Tries to connect will be made every #{Options[:RetryDelay]} seconds..."
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
sleep Options[:RetryDelay]
|
|
191
|
+
retry
|
|
192
|
+
else
|
|
193
|
+
raise
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
class CodeStatistics
|
|
2
|
+
def initialize(*pairs)
|
|
3
|
+
@pairs = pairs
|
|
4
|
+
@statistics = calculate_statistics
|
|
5
|
+
@total = calculate_total if pairs.length > 1
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_s
|
|
9
|
+
print_header
|
|
10
|
+
@pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
|
|
11
|
+
print_splitter
|
|
12
|
+
|
|
13
|
+
if @total
|
|
14
|
+
print_line("Total", @total)
|
|
15
|
+
print_splitter
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
print_code_test_stats
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def calculate_statistics
|
|
23
|
+
@pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def calculate_directory_statistics(directory, pattern = /.*\.rb$/)
|
|
27
|
+
stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
|
28
|
+
|
|
29
|
+
Dir.foreach(directory) do |file_name|
|
|
30
|
+
if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name)
|
|
31
|
+
newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
|
|
32
|
+
stats.each { |k, v| stats[k] += newstats[k] }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
next unless file_name =~ pattern
|
|
36
|
+
|
|
37
|
+
f = File.open(directory + "/" + file_name)
|
|
38
|
+
|
|
39
|
+
while line = f.gets
|
|
40
|
+
stats["lines"] += 1
|
|
41
|
+
stats["classes"] += 1 if line =~ /class [A-Z]/
|
|
42
|
+
stats["methods"] += 1 if line =~ /def [a-z]/
|
|
43
|
+
stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
stats
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def calculate_total
|
|
51
|
+
total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
|
52
|
+
@statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
|
|
53
|
+
total
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def calculate_code
|
|
57
|
+
code_loc = 0
|
|
58
|
+
@statistics.each { |k, v| code_loc += v['codelines'] unless ['Units', 'Functionals'].include? k }
|
|
59
|
+
code_loc
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def calculate_tests
|
|
63
|
+
test_loc = 0
|
|
64
|
+
@statistics.each { |k, v| test_loc += v['codelines'] if ['Units', 'Functionals'].include? k }
|
|
65
|
+
test_loc
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def print_header
|
|
69
|
+
print_splitter
|
|
70
|
+
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
|
|
71
|
+
print_splitter
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def print_splitter
|
|
75
|
+
puts "+----------------------+-------+-------+---------+---------+-----+-------+"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def print_line(name, statistics)
|
|
79
|
+
m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0
|
|
80
|
+
loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
|
|
81
|
+
|
|
82
|
+
start = if ['Units', 'Functionals'].include? name
|
|
83
|
+
"| #{name.ljust(18)} "
|
|
84
|
+
else
|
|
85
|
+
"| #{name.ljust(20)} "
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
puts start +
|
|
89
|
+
"| #{statistics["lines"].to_s.rjust(5)} " +
|
|
90
|
+
"| #{statistics["codelines"].to_s.rjust(5)} " +
|
|
91
|
+
"| #{statistics["classes"].to_s.rjust(7)} " +
|
|
92
|
+
"| #{statistics["methods"].to_s.rjust(7)} " +
|
|
93
|
+
"| #{m_over_c.to_s.rjust(3)} " +
|
|
94
|
+
"| #{loc_over_m.to_s.rjust(5)} |"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def print_code_test_stats
|
|
98
|
+
code = calculate_code
|
|
99
|
+
tests = calculate_tests
|
|
100
|
+
|
|
101
|
+
puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
|
|
102
|
+
puts ""
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/dispatcher.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2004 David Heinemeier Hansson
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#++
|
|
23
|
+
|
|
24
|
+
require 'breakpoint'
|
|
25
|
+
|
|
26
|
+
class Dispatcher
|
|
27
|
+
class << self
|
|
28
|
+
def dispatch(cgi = CGI.new, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
|
|
29
|
+
begin
|
|
30
|
+
request, response = ActionController::CgiRequest.new(cgi, session_options), ActionController::CgiResponse.new(cgi)
|
|
31
|
+
prepare_application
|
|
32
|
+
ActionController::Routing::Routes.recognize!(request).process(request, response).out(output)
|
|
33
|
+
rescue Object => exception
|
|
34
|
+
ActionController::Base.process_with_exception(request, response, exception).out(output)
|
|
35
|
+
ensure
|
|
36
|
+
reset_after_dispatch
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def reset_application!
|
|
41
|
+
Controllers.clear!
|
|
42
|
+
Dependencies.clear
|
|
43
|
+
Dependencies.remove_subclasses_for(ActiveRecord::Base, ActiveRecord::Observer, ActionController::Base)
|
|
44
|
+
Dependencies.remove_subclasses_for(ActionMailer::Base) if defined?(ActionMailer::Base)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
def prepare_application
|
|
49
|
+
ActionController::Routing::Routes.reload if Dependencies.load?
|
|
50
|
+
Breakpoint.activate_drb("druby://localhost:#{BREAKPOINT_SERVER_PORT}", nil, !defined?(FastCGI)) if defined?(BREAKPOINT_SERVER_PORT) rescue nil
|
|
51
|
+
Controllers.const_load!(:ApplicationController, "application") unless Controllers.const_defined?(:ApplicationController)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def reset_after_dispatch
|
|
55
|
+
reset_application! if Dependencies.load?
|
|
56
|
+
Breakpoint.deactivate_drb if defined?(BREAKPOINT_SERVER_PORT)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/fcgi_handler.rb
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require 'fcgi'
|
|
2
|
+
require 'logger'
|
|
3
|
+
require 'dispatcher'
|
|
4
|
+
|
|
5
|
+
class RailsFCGIHandler
|
|
6
|
+
SIGNALS = {
|
|
7
|
+
'HUP' => :reload,
|
|
8
|
+
'TERM' => :graceful_exit,
|
|
9
|
+
'USR1' => :graceful_exit
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
attr_reader :when_ready
|
|
13
|
+
|
|
14
|
+
attr_accessor :log_file_path
|
|
15
|
+
attr_accessor :gc_request_period
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Initialize and run the FastCGI instance, passing arguments through to new.
|
|
19
|
+
def self.process!(*args, &block)
|
|
20
|
+
new(*args, &block).process!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Initialize the FastCGI instance with the path to a crash log
|
|
24
|
+
# detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log)
|
|
25
|
+
# and the number of requests to process between garbage collection runs
|
|
26
|
+
# (default nil for normal GC behavior.) Optionally, pass a block which
|
|
27
|
+
# takes this instance as an argument for further configuration.
|
|
28
|
+
def initialize(log_file_path = nil, gc_request_period = nil)
|
|
29
|
+
@when_ready = nil
|
|
30
|
+
|
|
31
|
+
self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log"
|
|
32
|
+
self.gc_request_period = gc_request_period
|
|
33
|
+
|
|
34
|
+
# Yield for additional configuration.
|
|
35
|
+
yield self if block_given?
|
|
36
|
+
|
|
37
|
+
# Safely install signal handlers.
|
|
38
|
+
install_signal_handlers
|
|
39
|
+
|
|
40
|
+
# Start error timestamp at 11 seconds ago.
|
|
41
|
+
@last_error_on = Time.now - 11
|
|
42
|
+
|
|
43
|
+
dispatcher_log(:info, "starting")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def process!(provider = FCGI)
|
|
47
|
+
# Make a note of $" so we can safely reload this instance.
|
|
48
|
+
mark!
|
|
49
|
+
|
|
50
|
+
# Begin countdown to garbage collection.
|
|
51
|
+
run_gc! if gc_request_period
|
|
52
|
+
|
|
53
|
+
provider.each_cgi do |cgi|
|
|
54
|
+
# Safely reload this instance if requested.
|
|
55
|
+
if when_ready == :reload
|
|
56
|
+
run_gc! if gc_request_period
|
|
57
|
+
restore!
|
|
58
|
+
@when_ready = nil
|
|
59
|
+
dispatcher_log(:info, "reloaded")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
process_request(cgi)
|
|
63
|
+
|
|
64
|
+
# Break if graceful exit requested.
|
|
65
|
+
break if when_ready == :exit
|
|
66
|
+
|
|
67
|
+
# Garbage collection countdown.
|
|
68
|
+
if gc_request_period
|
|
69
|
+
@gc_request_countdown -= 1
|
|
70
|
+
run_gc! if @gc_request_countdown <= 0
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
GC.enable
|
|
75
|
+
dispatcher_log(:info, "terminated gracefully")
|
|
76
|
+
|
|
77
|
+
rescue SystemExit => exit_error
|
|
78
|
+
dispatcher_log(:info, "terminated by explicit exit")
|
|
79
|
+
|
|
80
|
+
rescue Object => fcgi_error
|
|
81
|
+
# retry on errors that would otherwise have terminated the FCGI process,
|
|
82
|
+
# but only if they occur more than 10 seconds apart.
|
|
83
|
+
if !(SignalException === fcgi_error) && Time.now - @last_error_on > 10
|
|
84
|
+
@last_error_on = Time.now
|
|
85
|
+
dispatcher_error(fcgi_error, "almost killed by this error")
|
|
86
|
+
retry
|
|
87
|
+
else
|
|
88
|
+
dispatcher_error(fcgi_error, "killed by this error")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
def logger
|
|
95
|
+
@logger ||= Logger.new(@log_file_path)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def dispatcher_log(level, msg)
|
|
99
|
+
time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S")
|
|
100
|
+
logger.send(level, "[#{time_str} :: #{$$}] #{msg}")
|
|
101
|
+
rescue Object => log_error
|
|
102
|
+
STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
|
|
103
|
+
STDERR << " #{log_error.class}: #{log_error.message}\n"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def dispatcher_error(e,msg="")
|
|
107
|
+
error_message =
|
|
108
|
+
"Dispatcher failed to catch: #{e} (#{e.class})\n" +
|
|
109
|
+
" #{e.backtrace.join("\n ")}\n#{msg}"
|
|
110
|
+
dispatcher_log(:error, error_message)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def install_signal_handlers
|
|
114
|
+
SIGNALS.each do |signal, handler_name|
|
|
115
|
+
install_signal_handler signal, method("#{handler_name}_handler").to_proc
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def install_signal_handler(signal, handler)
|
|
120
|
+
trap signal, handler
|
|
121
|
+
rescue ArgumentError
|
|
122
|
+
dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def graceful_exit_handler(signal)
|
|
126
|
+
dispatcher_log :info, "asked to terminate ASAP"
|
|
127
|
+
@when_ready = :exit
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def reload_handler(signal)
|
|
131
|
+
@when_ready = :reload
|
|
132
|
+
dispatcher_log :info, "asked to reload ASAP"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def process_request(cgi)
|
|
136
|
+
Dispatcher.dispatch(cgi)
|
|
137
|
+
rescue Object => e
|
|
138
|
+
raise if SignalException === e
|
|
139
|
+
dispatcher_error(e)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def mark!
|
|
143
|
+
@features = $".clone
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def restore!
|
|
147
|
+
$".replace @features
|
|
148
|
+
Dispatcher.reset_application!
|
|
149
|
+
ActionController::Routing::Routes.reload
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def run_gc!
|
|
153
|
+
@gc_request_countdown = gc_request_period
|
|
154
|
+
GC.enable; GC.start; GC.disable
|
|
155
|
+
end
|
|
156
|
+
end
|
data/lib/productize.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
SITE_ROOT = File.join(RAILS_ROOT, 'sites', SITE)
|
|
2
|
+
|
|
3
|
+
# Load site-specific controllers and let them re-open their corresponding class
|
|
4
|
+
module Dependencies
|
|
5
|
+
def require_or_load(file_name)
|
|
6
|
+
file_name = "#{file_name}.rb" unless ! load? || file_name[-3..-1] == '.rb'
|
|
7
|
+
load? ? load(file_name) : require(file_name)
|
|
8
|
+
if file_name.include? 'controller'
|
|
9
|
+
file_name = File.join(SITE_ROOT, 'app', 'controllers', File.basename(file_name))
|
|
10
|
+
if File.exist? file_name
|
|
11
|
+
load? ? load(file_name) : require(file_name)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Prefer site-specific templates, partials etc. if they exist. Otherwise, use the base
|
|
18
|
+
# application's generic files.
|
|
19
|
+
module ActionView
|
|
20
|
+
class Base
|
|
21
|
+
private
|
|
22
|
+
def full_template_path(template_path, extension)
|
|
23
|
+
# Check to see if the partial exists in our 'sites' folder first
|
|
24
|
+
site_specific_path = File.join(SITE_ROOT, 'app', 'views', template_path + '.' + extension)
|
|
25
|
+
|
|
26
|
+
if File.exist?(site_specific_path)
|
|
27
|
+
site_specific_path
|
|
28
|
+
else
|
|
29
|
+
"#{@base_path}/#{template_path}.#{extension}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Scoop up both the base's migration files (those that should apply to ALL sites), plus
|
|
36
|
+
# the site-specific migration files. Sort them. Apply them in turn.
|
|
37
|
+
module ActiveRecord
|
|
38
|
+
class Migrator
|
|
39
|
+
private
|
|
40
|
+
def migration_files
|
|
41
|
+
generic_files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort
|
|
42
|
+
puts generic_files.inspect
|
|
43
|
+
# Include the site-specific files in our complete list of migration files
|
|
44
|
+
if defined? SITE_ROOT and File.exist?("#{SITE_ROOT}/db/migrate")
|
|
45
|
+
# Note that a tilde (~) is used intentionally because its ascii value
|
|
46
|
+
# is greater than both the lower-case and upper-case alphabets (thereby
|
|
47
|
+
# causing the sort! below to behave as expected).
|
|
48
|
+
site_specific_files = Dir["#{SITE_ROOT}/db/migrate/[0-9]*\.[0-9]*_*.rb"]
|
|
49
|
+
files = generic_files + site_specific_files
|
|
50
|
+
# Sort by filename, ignoring the path to get there. Also convert '.'
|
|
51
|
+
# to '~' so that sorting occurs in the correct order.
|
|
52
|
+
files.sort! { |a,b| a.gsub(".", "~").match(/.+[\/\\](.+)/)[1] <=> b.gsub(".", "~").match(/.+[\/\\](.+)/)[1] }
|
|
53
|
+
else
|
|
54
|
+
files = generic_files
|
|
55
|
+
end
|
|
56
|
+
down? ? files.reverse : files
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Make routes site-specific for sites whose config/routes.rb exists
|
|
63
|
+
module ActionController
|
|
64
|
+
module Routing #:nodoc:
|
|
65
|
+
class RouteSet
|
|
66
|
+
def replace(*args)
|
|
67
|
+
new_route = Route.new(*args)
|
|
68
|
+
# Remove the old route that we're replacing
|
|
69
|
+
@routes.delete_if { |r| r.path == args[0] }
|
|
70
|
+
# Add the new one back
|
|
71
|
+
@routes << new_route
|
|
72
|
+
return new_route
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def draw_append
|
|
76
|
+
yield self
|
|
77
|
+
write_generation
|
|
78
|
+
write_recognition
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get both the base routes.rb file AND the site-specific routes.rb file
|
|
82
|
+
def reload
|
|
83
|
+
NamedRoutes.clear
|
|
84
|
+
|
|
85
|
+
loaded_routes = false
|
|
86
|
+
|
|
87
|
+
# Load the initial set of routes for the base application
|
|
88
|
+
if defined?(RAILS_ROOT) and File.exist?(File.join(RAILS_ROOT, 'config', 'routes.rb'))
|
|
89
|
+
load(File.join(RAILS_ROOT, 'config', 'routes.rb'))
|
|
90
|
+
loaded_routes = true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Add on site-specific routes
|
|
94
|
+
if defined?(SITE_ROOT) and File.exist?(File.join(SITE_ROOT, 'config', 'routes.rb'))
|
|
95
|
+
load(File.join(SITE_ROOT, 'config', 'routes.rb'))
|
|
96
|
+
loaded_routes = true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Use a sensible default if nothing could be loaded
|
|
100
|
+
unless loaded_routes
|
|
101
|
+
connect(':controller/:action/:id', :action => 'index', :id => nil)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
NamedRoutes.install
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Put cached pages in the site-specific public folder
|
|
111
|
+
ActionController::Base.page_cache_directory = "#{SITE_ROOT}/public"
|
|
112
|
+
|
|
113
|
+
# Load any site-specific models
|
|
114
|
+
# TODO: Make site-specific models re-open classes rather than
|
|
115
|
+
# this temporary either/or hack
|
|
116
|
+
ADDITIONAL_LOAD_PATHS.concat(Dir["#{SITE_ROOT}/app/models/[_a-z]*"])
|