rails 1.1.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rails might be problematic. Click here for more details.
- data/CHANGELOG +267 -2
- data/MIT-LICENSE +1 -1
- data/README +62 -63
- data/Rakefile +26 -15
- data/bin/process/inspector +3 -0
- data/configs/databases/frontbase.yml +28 -0
- data/configs/databases/mysql.yml +3 -2
- data/configs/databases/oracle.yml +10 -1
- data/configs/databases/sqlite3.yml +3 -0
- data/configs/lighttpd.conf +1 -0
- data/configs/routes.rb +1 -0
- data/environments/boot.rb +4 -3
- data/environments/environment.rb +9 -2
- data/environments/production.rb +1 -1
- data/helpers/application.rb +5 -2
- data/html/404.html +27 -5
- data/html/500.html +27 -5
- data/html/javascripts/controls.js +41 -23
- data/html/javascripts/dragdrop.js +105 -76
- data/html/javascripts/effects.js +293 -163
- data/html/javascripts/prototype.js +897 -389
- data/lib/breakpoint.rb +31 -1
- data/lib/breakpoint_client.rb +5 -5
- data/lib/code_statistics.rb +1 -1
- data/lib/commands/performance/profiler.rb +25 -9
- data/lib/commands/plugin.rb +69 -23
- data/lib/commands/process/inspector.rb +68 -0
- data/lib/commands/process/reaper.rb +88 -69
- data/lib/commands/process/spawner.rb +148 -33
- data/lib/commands/runner.rb +27 -6
- data/lib/commands/server.rb +18 -9
- data/lib/commands/servers/base.rb +19 -0
- data/lib/commands/servers/lighttpd.rb +20 -18
- data/lib/commands/servers/mongrel.rb +65 -0
- data/lib/console_sandbox.rb +2 -2
- data/lib/dispatcher.rb +67 -11
- data/lib/fcgi_handler.rb +52 -34
- data/lib/initializer.rb +190 -111
- data/lib/rails/version.rb +2 -2
- data/lib/rails_generator/base.rb +82 -24
- data/lib/rails_generator/commands.rb +87 -25
- data/lib/rails_generator/generated_attribute.rb +42 -0
- data/lib/rails_generator/generators/applications/app/app_generator.rb +13 -10
- data/lib/rails_generator/generators/components/controller/controller_generator.rb +1 -2
- data/lib/rails_generator/generators/components/mailer/mailer_generator.rb +10 -8
- data/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml +1 -1
- data/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +4 -4
- data/lib/rails_generator/generators/components/mailer/templates/view.rhtml +1 -1
- data/lib/rails_generator/generators/components/migration/templates/migration.rb +1 -1
- data/lib/rails_generator/generators/components/model/USAGE +19 -12
- data/lib/rails_generator/generators/components/model/model_generator.rb +4 -0
- data/lib/rails_generator/generators/components/model/templates/fixtures.yml +8 -2
- data/lib/rails_generator/generators/components/model/templates/migration.rb +3 -1
- data/lib/rails_generator/generators/components/observer/USAGE +15 -0
- data/lib/rails_generator/generators/components/observer/observer_generator.rb +16 -0
- data/lib/rails_generator/generators/components/observer/templates/observer.rb +2 -0
- data/lib/rails_generator/generators/components/observer/templates/unit_test.rb +10 -0
- data/lib/rails_generator/generators/components/plugin/plugin_generator.rb +4 -0
- data/lib/rails_generator/generators/components/plugin/templates/uninstall.rb +1 -0
- data/lib/rails_generator/generators/components/resource/resource_generator.rb +76 -0
- data/lib/rails_generator/generators/components/resource/templates/USAGE +18 -0
- data/lib/rails_generator/generators/components/resource/templates/controller.rb +2 -0
- data/lib/rails_generator/generators/components/resource/templates/fixtures.yml +11 -0
- data/lib/rails_generator/generators/components/resource/templates/functional_test.rb +20 -0
- data/lib/rails_generator/generators/components/resource/templates/helper.rb +2 -0
- data/lib/rails_generator/generators/components/resource/templates/migration.rb +13 -0
- data/lib/rails_generator/generators/components/resource/templates/model.rb +2 -0
- data/lib/rails_generator/generators/components/resource/templates/unit_test.rb +10 -0
- data/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +10 -1
- data/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb +11 -7
- data/lib/rails_generator/generators/components/scaffold/templates/layout.rhtml +5 -1
- data/lib/rails_generator/generators/components/scaffold/templates/view_edit.rhtml +2 -2
- data/lib/rails_generator/generators/components/scaffold/templates/view_list.rhtml +1 -1
- data/lib/rails_generator/generators/components/scaffold/templates/view_new.rhtml +2 -2
- data/lib/rails_generator/generators/components/scaffold_resource/USAGE +29 -0
- data/lib/rails_generator/generators/components/scaffold_resource/scaffold_resource_generator.rb +92 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/controller.rb +79 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/fixtures.yml +11 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/functional_test.rb +57 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/helper.rb +2 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/layout.rhtml +17 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/migration.rb +13 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/model.rb +2 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/style.css +74 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/unit_test.rb +10 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/view_edit.rhtml +19 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/view_index.rhtml +24 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/view_new.rhtml +18 -0
- data/lib/rails_generator/generators/components/scaffold_resource/templates/view_show.rhtml +10 -0
- data/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb +7 -1
- data/lib/rails_generator/generators/components/session_migration/templates/migration.rb +5 -4
- data/lib/rails_generator/lookup.rb +1 -2
- data/lib/rails_generator/options.rb +6 -3
- data/lib/tasks/databases.rake +46 -20
- data/lib/tasks/documentation.rake +1 -0
- data/lib/tasks/framework.rake +1 -3
- data/lib/tasks/pre_namespace_aliases.rake +34 -27
- data/lib/tasks/rails.rb +2 -2
- data/lib/tasks/statistics.rake +6 -5
- data/lib/tasks/testing.rake +28 -13
- data/lib/tasks/tmp.rake +8 -1
- data/lib/test_help.rb +3 -2
- data/lib/webrick_server.rb +6 -8
- metadata +50 -9
data/lib/breakpoint.rb
CHANGED
@@ -16,7 +16,13 @@
|
|
16
16
|
# license please contact me.
|
17
17
|
|
18
18
|
require 'irb'
|
19
|
-
|
19
|
+
if RUBY_VERSION == '1.8.5'
|
20
|
+
def Binding.of_caller(&block)
|
21
|
+
raise "Breakpoints are not currently working with Ruby 1.8.5"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
require 'binding_of_caller'
|
25
|
+
end
|
20
26
|
require 'drb'
|
21
27
|
require 'drb/acl'
|
22
28
|
|
@@ -178,6 +184,30 @@ module Breakpoint
|
|
178
184
|
end
|
179
185
|
end
|
180
186
|
|
187
|
+
# Prints the source code surrounding the location where the
|
188
|
+
# breakpoint was issued.
|
189
|
+
def show_source_list(context = 5)
|
190
|
+
start_line, break_line, result = source_lines(context, true)
|
191
|
+
offset = [(break_line + context).to_s.length, 4].max
|
192
|
+
result.each_with_index do |line, i|
|
193
|
+
mark = (start_line + i == break_line ? '->' : ' ')
|
194
|
+
client.puts("%0#{offset}d%s#{line}" % [start_line + i, mark])
|
195
|
+
end
|
196
|
+
Pathname.new(@__bp_file).cleanpath.to_s
|
197
|
+
end
|
198
|
+
|
199
|
+
# Prints the call stack.
|
200
|
+
def show_call_stack(depth = 10)
|
201
|
+
base = Pathname.new(RAILS_ROOT).cleanpath.to_s
|
202
|
+
caller[1..depth].each do |line|
|
203
|
+
line.sub!(/^[^:]*/) do |path|
|
204
|
+
Pathname.new(path).cleanpath.to_s
|
205
|
+
end
|
206
|
+
client.puts(line.index(base) == 0 ? line[(base.length + 1)..-1] : line)
|
207
|
+
end
|
208
|
+
"#{Pathname.new(@__bp_file).cleanpath.to_s}:#{@__bp_line}"
|
209
|
+
end
|
210
|
+
|
181
211
|
# Lets an object that will forward method calls to the breakpoint
|
182
212
|
# client. This is useful for outputting longer things at the client
|
183
213
|
# and so on. You can for example do these things:
|
data/lib/breakpoint_client.rb
CHANGED
@@ -30,13 +30,13 @@ ARGV.options do |opts|
|
|
30
30
|
"connections from the server.",
|
31
31
|
"Default: Find a good URI automatically.",
|
32
32
|
"Example: -c druby://localhost:12345"
|
33
|
-
) { |Options[:ClientURI]
|
33
|
+
) { |v| Options[:ClientURI] = v }
|
34
34
|
|
35
35
|
opts.on("-s", "--server-uri=uri",
|
36
36
|
"Connect to the server specified at the",
|
37
37
|
"specified uri.",
|
38
38
|
"Default: druby://localhost:42531"
|
39
|
-
) { |Options[:ServerURI]
|
39
|
+
) { |v| Options[:ServerURI] = v }
|
40
40
|
|
41
41
|
opts.on("-R", "--retry-delay=delay", Integer,
|
42
42
|
"Automatically try to reconnect to the",
|
@@ -45,14 +45,14 @@ ARGV.options do |opts|
|
|
45
45
|
"A value of 0 disables automatical",
|
46
46
|
"reconnecting completely.",
|
47
47
|
"Default: 10"
|
48
|
-
) { |Options[:RetryDelay]
|
48
|
+
) { |v| Options[:RetryDelay] = v }
|
49
49
|
|
50
50
|
opts.on("-P", "--[no-]permanent",
|
51
51
|
"Run the breakpoint client in permanent mode.",
|
52
52
|
"This means that the client will keep continue",
|
53
53
|
"running even after the server has closed the",
|
54
54
|
"connection. Useful for example in Rails."
|
55
|
-
) { |Options[:Permanent]
|
55
|
+
) { |v| Options[:Permanent] = v }
|
56
56
|
|
57
57
|
opts.on("-V", "--[no-]verbose",
|
58
58
|
"Run the breakpoint client in verbose mode.",
|
@@ -60,7 +60,7 @@ ARGV.options do |opts|
|
|
60
60
|
"individual breakpoints. This might help in seeing",
|
61
61
|
"that the breakpoint client is still alive, but adds",
|
62
62
|
"quite a bit of clutter."
|
63
|
-
) { |Options[:Verbose]
|
63
|
+
) { |v| Options[:Verbose] = v }
|
64
64
|
|
65
65
|
opts.separator ""
|
66
66
|
|
data/lib/code_statistics.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
if ARGV.empty?
|
2
|
-
$stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times]"
|
2
|
+
$stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]"
|
3
3
|
exit(1)
|
4
4
|
end
|
5
5
|
|
@@ -16,14 +16,30 @@ end
|
|
16
16
|
|
17
17
|
# Use the ruby-prof extension if available. Fall back to stdlib profiler.
|
18
18
|
begin
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
begin
|
20
|
+
require "ruby-prof"
|
21
|
+
$stderr.puts 'Using the ruby-prof extension.'
|
22
|
+
RubyProf.clock_mode = RubyProf::WALL_TIME
|
23
|
+
RubyProf.start
|
24
|
+
profile_me
|
25
|
+
results = RubyProf.stop
|
26
|
+
if ARGV[2]
|
27
|
+
printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify)
|
28
|
+
else
|
29
|
+
printer_class = RubyProf::FlatPrinter
|
30
|
+
end
|
31
|
+
printer = printer_class.new(results)
|
32
|
+
printer.print($stderr, 0)
|
33
|
+
rescue LoadError
|
34
|
+
require "prof"
|
35
|
+
$stderr.puts 'Using the old ruby-prof extension.'
|
36
|
+
Prof.clock_mode = Prof::GETTIMEOFDAY
|
37
|
+
Prof.start
|
38
|
+
profile_me
|
39
|
+
results = Prof.stop
|
40
|
+
require 'rubyprof_ext'
|
41
|
+
Prof.print_profile(results, $stderr)
|
42
|
+
end
|
27
43
|
rescue LoadError
|
28
44
|
require 'profiler'
|
29
45
|
$stderr.puts 'Using the standard Ruby profiler.'
|
data/lib/commands/plugin.rb
CHANGED
@@ -136,7 +136,7 @@ class RailsEnvironment
|
|
136
136
|
Tempfile.open("svn-set-prop") do |file|
|
137
137
|
file.write(items)
|
138
138
|
file.flush
|
139
|
-
system("svn propset -q svn:externals -F #{file.path} \"#{root}/vendor/plugins\"")
|
139
|
+
system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"")
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
@@ -150,10 +150,18 @@ class Plugin
|
|
150
150
|
guess_name(uri)
|
151
151
|
end
|
152
152
|
|
153
|
+
def self.find(name)
|
154
|
+
name =~ /\// ? new(name) : Repositories.instance.find_plugin(name)
|
155
|
+
end
|
156
|
+
|
153
157
|
def to_s
|
154
158
|
"#{@name.ljust(30)}#{@uri}"
|
155
159
|
end
|
156
160
|
|
161
|
+
def svn_url?
|
162
|
+
@uri =~ /svn(?:\+ssh)?:\/\/*/
|
163
|
+
end
|
164
|
+
|
157
165
|
def installed?
|
158
166
|
File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
|
159
167
|
or rails_env.externals.detect{ |name, repo| self.uri == repo }
|
@@ -161,7 +169,7 @@ class Plugin
|
|
161
169
|
|
162
170
|
def install(method=nil, options = {})
|
163
171
|
method ||= rails_env.best_install_method?
|
164
|
-
method
|
172
|
+
method = :export if method == :http and svn_url?
|
165
173
|
|
166
174
|
uninstall if installed? and options[:force]
|
167
175
|
|
@@ -177,6 +185,7 @@ class Plugin
|
|
177
185
|
path = "#{rails_env.root}/vendor/plugins/#{name}"
|
178
186
|
if File.directory?(path)
|
179
187
|
puts "Removing 'vendor/plugins/#{name}'" if $verbose
|
188
|
+
run_uninstall_hook
|
180
189
|
rm_r path
|
181
190
|
else
|
182
191
|
puts "Plugin doesn't exist: #{path}"
|
@@ -187,6 +196,20 @@ class Plugin
|
|
187
196
|
rails_env.externals = externals
|
188
197
|
end
|
189
198
|
|
199
|
+
def info
|
200
|
+
tmp = "#{rails_env.root}/_tmp_about.yml"
|
201
|
+
if svn_url?
|
202
|
+
cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\""
|
203
|
+
puts cmd if $verbose
|
204
|
+
system(cmd)
|
205
|
+
end
|
206
|
+
open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream|
|
207
|
+
stream.read
|
208
|
+
end rescue "No about.yml found in #{uri}"
|
209
|
+
ensure
|
210
|
+
FileUtils.rm_rf tmp if svn_url?
|
211
|
+
end
|
212
|
+
|
190
213
|
private
|
191
214
|
|
192
215
|
def run_install_hook
|
@@ -194,6 +217,11 @@ class Plugin
|
|
194
217
|
load install_hook_file if File.exists? install_hook_file
|
195
218
|
end
|
196
219
|
|
220
|
+
def run_uninstall_hook
|
221
|
+
uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb"
|
222
|
+
load uninstall_hook_file if File.exists? uninstall_hook_file
|
223
|
+
end
|
224
|
+
|
197
225
|
def install_using_export(options = {})
|
198
226
|
svn_command :export, options
|
199
227
|
end
|
@@ -442,7 +470,7 @@ module Commands
|
|
442
470
|
options.parse!(general)
|
443
471
|
|
444
472
|
command = general.shift
|
445
|
-
if command =~ /^(list|discover|install|source|unsource|sources|remove|update)$/
|
473
|
+
if command =~ /^(list|discover|install|source|unsource|sources|remove|update|info)$/
|
446
474
|
command = Commands.const_get(command.capitalize).new(self)
|
447
475
|
command.parse!(sub)
|
448
476
|
else
|
@@ -549,8 +577,8 @@ module Commands
|
|
549
577
|
def options
|
550
578
|
OptionParser.new do |o|
|
551
579
|
o.set_summary_indent(' ')
|
552
|
-
o.banner = "Usage: #{@base_command.script_name} source REPOSITORY"
|
553
|
-
o.define_head "Add
|
580
|
+
o.banner = "Usage: #{@base_command.script_name} source REPOSITORY [REPOSITORY [REPOSITORY]...]"
|
581
|
+
o.define_head "Add new repositories to the default search list."
|
554
582
|
end
|
555
583
|
end
|
556
584
|
|
@@ -656,13 +684,17 @@ module Commands
|
|
656
684
|
puts "Scraping #{uri}" if $verbose
|
657
685
|
dupes = []
|
658
686
|
content = open(uri).each do |line|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
uri
|
663
|
-
|
664
|
-
|
687
|
+
begin
|
688
|
+
if line =~ /<a[^>]*href=['"]([^'"]*)['"]/ || line =~ /(svn:\/\/[^<|\n]*)/
|
689
|
+
uri = $1
|
690
|
+
if uri =~ /^\w+:\/\// && uri =~ /\/plugins\// && uri !~ /\/browser\// && uri !~ /^http:\/\/wiki\.rubyonrails/ && uri !~ /http:\/\/instiki/
|
691
|
+
uri = extract_repository_uri(uri)
|
692
|
+
yield uri unless dupes.include?(uri) || Repositories.instance.exist?(uri)
|
693
|
+
dupes << uri
|
694
|
+
end
|
665
695
|
end
|
696
|
+
rescue
|
697
|
+
puts "Problems scraping '#{uri}': #{$!.to_s}"
|
666
698
|
end
|
667
699
|
end
|
668
700
|
end
|
@@ -729,19 +761,12 @@ module Commands
|
|
729
761
|
environment = @base_command.environment
|
730
762
|
install_method = determine_install_method
|
731
763
|
puts "Plugins will be installed using #{install_method}" if $verbose
|
732
|
-
args.each do |name|
|
733
|
-
|
734
|
-
::Plugin.new(name).install(install_method, @options)
|
735
|
-
else
|
736
|
-
plugin = Repositories.instance.find_plugin(name)
|
737
|
-
unless plugin.nil?
|
738
|
-
plugin.install(install_method, @options)
|
739
|
-
else
|
740
|
-
puts "Plugin not found: #{name}"
|
741
|
-
exit 1
|
742
|
-
end
|
743
|
-
end
|
764
|
+
args.each do |name|
|
765
|
+
::Plugin.find(name).install(install_method, @options)
|
744
766
|
end
|
767
|
+
rescue
|
768
|
+
puts "Plugin not found: #{args.inspect}"
|
769
|
+
exit 1
|
745
770
|
end
|
746
771
|
end
|
747
772
|
|
@@ -802,6 +827,27 @@ module Commands
|
|
802
827
|
end
|
803
828
|
end
|
804
829
|
|
830
|
+
class Info
|
831
|
+
def initialize(base_command)
|
832
|
+
@base_command = base_command
|
833
|
+
end
|
834
|
+
|
835
|
+
def options
|
836
|
+
OptionParser.new do |o|
|
837
|
+
o.set_summary_indent(' ')
|
838
|
+
o.banner = "Usage: #{@base_command.script_name} info name [name]..."
|
839
|
+
o.define_head "Shows plugin info at {url}/about.yml."
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
def parse!(args)
|
844
|
+
options.parse!(args)
|
845
|
+
args.each do |name|
|
846
|
+
puts ::Plugin.find(name).info
|
847
|
+
puts
|
848
|
+
end
|
849
|
+
end
|
850
|
+
end
|
805
851
|
end
|
806
852
|
|
807
853
|
class RecursiveHTTPFetcher
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
if RUBY_PLATFORM =~ /mswin32/ then abort("Inspector is only for Unix") end
|
4
|
+
|
5
|
+
OPTIONS = {
|
6
|
+
:pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
|
7
|
+
:pattern => "dispatch.*.pid",
|
8
|
+
:ps => "ps -o pid,state,user,start,time,pcpu,vsz,majflt,command -p %s"
|
9
|
+
}
|
10
|
+
|
11
|
+
class Inspector
|
12
|
+
def self.inspect(pid_path, pattern)
|
13
|
+
new(pid_path, pattern).inspect
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(pid_path, pattern)
|
17
|
+
@pid_path, @pattern = pid_path, pattern
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
header = `#{OPTIONS[:ps] % 1}`.split("\n")[0] + "\n"
|
22
|
+
lines = pids.collect { |pid| `#{OPTIONS[:ps] % pid}`.split("\n")[1] }
|
23
|
+
|
24
|
+
puts(header + lines.join("\n"))
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def pids
|
29
|
+
pid_files.collect do |pid_file|
|
30
|
+
File.read(pid_file).to_i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def pid_files
|
35
|
+
Dir.glob(@pid_path + "/" + @pattern)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
ARGV.options do |opts|
|
41
|
+
opts.banner = "Usage: inspector [options]"
|
42
|
+
|
43
|
+
opts.separator ""
|
44
|
+
|
45
|
+
opts.on <<-EOF
|
46
|
+
Description:
|
47
|
+
Displays system information about Rails dispatchers (or other processes that use pid files) through
|
48
|
+
the ps command.
|
49
|
+
|
50
|
+
Examples:
|
51
|
+
inspector # default ps on all tmp/pids/dispatch.*.pid files
|
52
|
+
inspector -s 'ps -o user,start,majflt,pcpu,vsz -p %s' # custom ps, %s is where the pid is interleaved
|
53
|
+
EOF
|
54
|
+
|
55
|
+
opts.on(" Options:")
|
56
|
+
|
57
|
+
opts.on("-s", "--ps=command", "default: #{OPTIONS[:ps]}", String) { |v| OPTIONS[:ps] = v }
|
58
|
+
opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v }
|
59
|
+
opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
|
60
|
+
|
61
|
+
opts.separator ""
|
62
|
+
|
63
|
+
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
|
64
|
+
|
65
|
+
opts.parse!
|
66
|
+
end
|
67
|
+
|
68
|
+
Inspector.inspect(OPTIONS[:pid_path], OPTIONS[:pattern])
|
@@ -4,89 +4,102 @@ require 'uri'
|
|
4
4
|
|
5
5
|
if RUBY_PLATFORM =~ /mswin32/ then abort("Reaper is only for Unix") end
|
6
6
|
|
7
|
-
|
8
|
-
# be queried by "keyword" to find those that meet a specific set of criteria.
|
9
|
-
class ProgramProcess
|
7
|
+
class Killer
|
10
8
|
class << self
|
11
|
-
|
12
9
|
# Searches for all processes matching the given keywords, and then invokes
|
13
10
|
# a specific action on each of them. This is useful for (e.g.) reloading a
|
14
11
|
# set of processes:
|
15
12
|
#
|
16
|
-
#
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
if processes.empty?
|
21
|
-
puts "Couldn't find any process matching: #{keywords.join(" or ")}"
|
22
|
-
else
|
23
|
-
processes.each do |process|
|
24
|
-
puts "#{action.capitalize}ing #{process}"
|
25
|
-
process.send(action)
|
26
|
-
end
|
27
|
-
end
|
13
|
+
# Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid")
|
14
|
+
def process(action, pid_path, pattern, keyword)
|
15
|
+
new(pid_path, pattern, keyword).process(action)
|
28
16
|
end
|
29
17
|
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
process_lines_with_keyword(keyword).split("\n").collect { |line|
|
35
|
-
next if line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/
|
36
|
-
pid, *command = line.split
|
37
|
-
new(pid, command.join(" "))
|
38
|
-
}.compact
|
18
|
+
# Forces the (rails) application to reload by sending a +HUP+ signal to the
|
19
|
+
# process.
|
20
|
+
def reload(pid)
|
21
|
+
`kill -s HUP #{pid}`
|
39
22
|
end
|
40
23
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
24
|
+
# Force the (rails) application to restart by sending a +USR2+ signal to the
|
25
|
+
# process.
|
26
|
+
def restart(pid)
|
27
|
+
`kill -s USR2 #{pid}`
|
28
|
+
end
|
46
29
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
30
|
+
# Forces the (rails) application to gracefully terminate by sending a
|
31
|
+
# +TERM+ signal to the process.
|
32
|
+
def graceful(pid)
|
33
|
+
`kill -s TERM #{pid}`
|
34
|
+
end
|
52
35
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
36
|
+
# Forces the (rails) application to terminate immediately by sending a -9
|
37
|
+
# signal to the process.
|
38
|
+
def kill(pid)
|
39
|
+
`kill -9 #{pid}`
|
40
|
+
end
|
58
41
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
42
|
+
# Send a +USR1+ signal to the process.
|
43
|
+
def usr1(pid)
|
44
|
+
`kill -s USR1 #{pid}`
|
45
|
+
end
|
63
46
|
end
|
64
47
|
|
65
|
-
|
66
|
-
|
67
|
-
def kill
|
68
|
-
`kill -9 #{@pid}`
|
48
|
+
def initialize(pid_path, pattern, keyword=nil)
|
49
|
+
@pid_path, @pattern, @keyword = pid_path, pattern, keyword
|
69
50
|
end
|
70
51
|
|
71
|
-
|
72
|
-
|
73
|
-
`kill -s USR1 #{@pid}`
|
74
|
-
end
|
52
|
+
def process(action)
|
53
|
+
pids = find_processes
|
75
54
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
55
|
+
if pids.empty?
|
56
|
+
warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'"
|
57
|
+
warn "(also looked for processes matching #{@keyword.inspect})" if @keyword
|
58
|
+
else
|
59
|
+
pids.each do |pid|
|
60
|
+
puts "#{action.capitalize}ing #{pid}"
|
61
|
+
self.class.send(action, pid)
|
62
|
+
end
|
63
|
+
|
64
|
+
delete_pid_files if terminating?(action)
|
65
|
+
end
|
80
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def terminating?(action)
|
70
|
+
[ "kill", "graceful" ].include?(action)
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_processes
|
74
|
+
files = pid_files
|
75
|
+
if files.empty?
|
76
|
+
find_processes_via_grep
|
77
|
+
else
|
78
|
+
files.collect { |pid_file| File.read(pid_file).to_i }
|
79
|
+
end
|
80
|
+
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
def find_processes_via_grep
|
83
|
+
lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/).
|
84
|
+
reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ }
|
85
|
+
lines.map { |line| line[/^\s*(\d+)/, 1].to_i }
|
86
|
+
end
|
87
|
+
|
88
|
+
def delete_pid_files
|
89
|
+
pid_files.each { |pid_file| File.delete(pid_file) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def pid_files
|
93
|
+
Dir.glob(@pid_path + "/" + @pattern)
|
94
|
+
end
|
85
95
|
end
|
86
96
|
|
97
|
+
|
87
98
|
OPTIONS = {
|
88
|
-
:action
|
89
|
-
:
|
99
|
+
:action => "restart",
|
100
|
+
:pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
|
101
|
+
:pattern => "dispatch.[0-9]*.pid",
|
102
|
+
:dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi")
|
90
103
|
}
|
91
104
|
|
92
105
|
ARGV.options do |opts|
|
@@ -96,9 +109,13 @@ ARGV.options do |opts|
|
|
96
109
|
|
97
110
|
opts.on <<-EOF
|
98
111
|
Description:
|
99
|
-
The reaper is used to restart, reload, gracefully exit, and forcefully exit
|
100
|
-
running a Rails Dispatcher
|
101
|
-
is
|
112
|
+
The reaper is used to restart, reload, gracefully exit, and forcefully exit processes
|
113
|
+
running a Rails Dispatcher (or any other process responding to the same signals). This
|
114
|
+
is commonly done when a new version of the application is available, so the existing
|
115
|
+
processes can be updated to use the latest code.
|
116
|
+
|
117
|
+
It uses pid files to work on the processes and by default assume them to be located
|
118
|
+
in RAILS_ROOT/tmp/pids.
|
102
119
|
|
103
120
|
The reaper actions are:
|
104
121
|
|
@@ -110,15 +127,17 @@ ARGV.options do |opts|
|
|
110
127
|
Restart is the most common and default action.
|
111
128
|
|
112
129
|
Examples:
|
113
|
-
reaper
|
114
|
-
reaper -a reload
|
115
|
-
reaper -a
|
130
|
+
reaper # restarts the default dispatchers
|
131
|
+
reaper -a reload # reload the default dispatchers
|
132
|
+
reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids
|
116
133
|
EOF
|
117
134
|
|
118
135
|
opts.on(" Options:")
|
119
136
|
|
120
137
|
opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v }
|
121
|
-
opts.on("-
|
138
|
+
opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v }
|
139
|
+
opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
|
140
|
+
opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
|
122
141
|
|
123
142
|
opts.separator ""
|
124
143
|
|
@@ -127,4 +146,4 @@ ARGV.options do |opts|
|
|
127
146
|
opts.parse!
|
128
147
|
end
|
129
148
|
|
130
|
-
|
149
|
+
Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher])
|