snailgun 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,177 @@
1
+ Snailgun
2
+ ========
3
+
4
+ Snailgun accelerates the startup of Ruby applications which require large
5
+ numbers of libraries. It does this by preparing a Ruby process with your
6
+ chosen libraries preloaded, and then forking that process whenever a new
7
+ command-line Ruby interpreter is required.
8
+
9
+ Installation
10
+ ------------
11
+
12
+ sudo gem sources -a http://gems.github.com/
13
+ sudo gem install candlerb-snailgun
14
+
15
+ Or for the latest code, `git clone git://github.com/candlerb/snailgun.git`
16
+ and put the bin directory into your PATH.
17
+
18
+ Case 1: standalone
19
+ ------------------
20
+
21
+ # WITHOUT SNAILGUN
22
+ $ time ruby -rubygems -e 'require "active_support"' -e 'puts "".blank?'
23
+ true
24
+
25
+ real 0m2.123s
26
+ user 0m1.424s
27
+ sys 0m0.168s
28
+
29
+ # WITH SNAILGUN
30
+ $ snailgun -rubygems -ractive_support
31
+ Snailgun starting on /home/brian/.snailgun/14781 - 'exit' to end
32
+ $ time fruby -e 'puts "".blank?'
33
+ true
34
+
35
+ real 0m0.064s
36
+ user 0m0.020s
37
+ sys 0m0.004s
38
+
39
+ $ exit
40
+ logout
41
+ Snailgun ended
42
+ $
43
+
44
+ Case 2: Rails app
45
+ -----------------
46
+
47
+ When using Rails or Merb, snailgun will start a process preloaded for the
48
+ `test` environment only unless told otherwise.
49
+
50
+ You need to edit `config/environments/test.rb` and set
51
+ `config.cache_classes = false`. This is so that your application classes
52
+ are loaded each time you run a test, rather than being preloaded into
53
+ the test environment.
54
+
55
+ Snailgun will take several seconds to be ready to process requests. Start
56
+ with `snailgun -v` if you wish to be notified when it is ready.
57
+
58
+ $ rails testapp
59
+ $ cd testapp
60
+ $ vi config/environments/test.rb
61
+ ... set config.cache_classes = false
62
+ $ snailgun
63
+ Use 'exit' to terminate snailgun
64
+
65
+ $ time RAILS_ENV=test fruby script/runner 'puts 1+2'
66
+ 3
67
+
68
+ real 0m0.169s
69
+ user 0m0.040s
70
+ sys 0m0.008s
71
+
72
+ # To run your test suite
73
+ $ frake test # or frake spec
74
+
75
+ Your preloaded process will remain around until you type `exit` to terminate
76
+ it.
77
+
78
+ Note that any attempt by `fruby` or `frake` to perform an action in an
79
+ environment other than 'test' will fail. See below for how to run multiple
80
+ snailgun environments.
81
+
82
+ Merb support has been contributed (using MERB_ENV), but it is untested by
83
+ me.
84
+
85
+ Case 3: Rails with multiple environments
86
+ ----------------------------------------
87
+
88
+ After reading the warnings below, you may choose to start multiple snailgun
89
+ processes each configured for a different environment, as follows:
90
+
91
+ $ snailgun --rails test,development
92
+
93
+ This gives the potential for faster startup of rake tasks which involve
94
+ the development environment (such as migrations) and the console. The
95
+ utility `fconsole` is provided for this.
96
+
97
+ However, beware that frake and fruby need to decide which of the preloaded
98
+ environments to dispatch the command to. The safest way is to force the
99
+ correct one explicitly:
100
+
101
+ RAILS_ENV=test frake test:units
102
+ RAILS_ENV=development fruby script/server
103
+ RAILS_ENV=test fruby script/runner 'puts "".blank?'
104
+
105
+ If you do not specify the environment, then a simple heuristic is used:
106
+
107
+ * `fruby` always defaults to the 'development' environment.
108
+
109
+ * `frake` honours any `RAILS_ENV=xxx` setting on the command line. If
110
+ missing, `frake` defaults to the 'test' environment if no args are given or
111
+ if an arg containing the word 'test' or 'spec' is given; otherwise it falls
112
+ back to the 'development' environment.
113
+
114
+ WARNING: The decision as to which of the preloaded environments to use is
115
+ made *before* actually running the command. If the wrong choice is made, it
116
+ can lead to problems.
117
+
118
+ In the worst case, you may have a 'test'-type task, but find that it is
119
+ wrongly dispatched to your 'development' environment - and possibly ends up
120
+ blowing away your development database. This actually happened to me while
121
+ developing snailgun. SO IF YOUR DEVELOPMENT DATABASE CONTAINS USEFUL DATA,
122
+ KEEP IT BACKED UP.
123
+
124
+ If you run test files individually, it is especially critical that you set
125
+ the correct environment. e.g.
126
+
127
+ RAILS_ENV=test fruby -Ilib -Itest test/unit/some_test.rb
128
+
129
+ autotest
130
+ --------
131
+
132
+ There is some simple support for autotest (from the ZenTest package).
133
+ Just type `fautotest` instead of `autotest` after snailgun has been started.
134
+
135
+ Bypassing rubygems
136
+ ------------------
137
+
138
+ You can get noticeably faster startup if you don't use rubygems to invoke
139
+ the programs. To do this, you can add the binary directory directly into
140
+ the front of your PATH, e.g. for Ubuntu
141
+
142
+ PATH=/var/lib/gems/1.8/gems/snailgun-1.0.2/bin:$PATH
143
+
144
+ Alternatively, create a file called `fruby` somewhere early on in your PATH
145
+ (e.g. under `$HOME/bin`), like this:
146
+
147
+ #!/usr/bin/env ruby
148
+ load '/path/to/the/real/fruby'
149
+
150
+ Repeat for `frake` etc.
151
+
152
+ Other bugs and limitations
153
+ --------------------------
154
+ Only works with Linux/BSD systems, due to use of passing open file
155
+ descriptors across a socket.
156
+
157
+ Ctrl-C doesn't terminate frake processes.
158
+
159
+ `fruby script/console` doesn't give any speedup, because script/console uses
160
+ exec to invoke irb. Use the supplied `fconsole` instead.
161
+
162
+ The environment is not currently passed across the socket to the ruby
163
+ process. This means it's not usable as a fast CGI replacement.
164
+
165
+ In Rails, you need to beware that any changes to your `config/environment*`
166
+ will not be reflected until you stop and restart snailgun.
167
+
168
+ Licence
169
+ -------
170
+ This code is released under the same licence as Ruby itself.
171
+
172
+ Author
173
+ ------
174
+ Brian Candler <B.Candler@pobox.com>
175
+
176
+ Credits:
177
+ Jan X <jan.h.xie@gmail.com>
data/bin/fautotest ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
3
+
4
+ # shortcut for: RAILS_ENV=test fruby /path/to/autotest
5
+
6
+ ENV["RAILS_ENV"] = "test"
7
+ Test::Unit.run = true if defined?(Test::Unit) && Test::Unit.respond_to?(:run=)
8
+ ARGV[0,0] = ["-e","gem 'ZenTest'","-e","load 'autotest'","--"]
9
+ load File.join(File.dirname(__FILE__), "fruby")
data/bin/fconsole ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
3
+
4
+ ENV['RAILS_ENV'] = ARGV.shift if ARGV[0] =~ /^\w/
5
+
6
+ ARGV[0,0] = [
7
+ "-rirb",
8
+ "-rirb/completion",
9
+ "-rconsole_app",
10
+ "-rconsole_with_helpers",
11
+ "-eIRB.start", "--", "--simple-prompt"
12
+ ]
13
+ load File.join(File.dirname(__FILE__), "fruby")
data/bin/frake ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
3
+ # Here we run rake within a pre-forked ruby process
4
+
5
+ # Pre-parse environment - see collect_tasks in rake/lib/rake.rb
6
+ ARGV.each do |arg|
7
+ if arg =~ /^(\w+)=(.*)$/
8
+ ENV[$1] = $2
9
+ end
10
+ end
11
+
12
+ # Make a guess at the correct environment
13
+ if File.exist?("config/boot.rb") || File.exist?("config/init.rb")
14
+ # Rails: default task is 'test'
15
+ $DEFAULT_ENV='test' if ARGV.find { |arg| arg =~ /\b(test|spec)\b/ } || !ARGV.find { |arg| arg !~ /^-/ }
16
+ end
17
+
18
+ ARGV[0,0] = ["-rrake", "-eRake.application.run", "--"]
19
+ load File.join(File.dirname(__FILE__), "fruby")
data/bin/fruby ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
3
+ # This could be rewritten in C for even faster startup
4
+
5
+ require 'socket'
6
+ if ENV['SNAILGUN_SOCK']
7
+ sockname = ENV['SNAILGUN_SOCK']
8
+ elsif File.directory?('tmp/sockets/snailgun')
9
+ env = ENV['RAILS_ENV'] || ENV['MERB_ENV']
10
+ unless env
11
+ # Normally default to development: see railties/lib/tasks/misc.rake
12
+ env = $DEFAULT_ENV || 'development'
13
+ STDERR.puts "Snailgun assuming environment '#{env}'"
14
+ end
15
+ sockname = "tmp/sockets/snailgun/#{env}"
16
+ end
17
+
18
+ unless sockname
19
+ STDERR.puts <<EOS
20
+ Unable to find path to snailgun socket.
21
+ - did you run this in a session with a snailgun parent?
22
+ - you can do 'SNAILGUN_SOCK=/path/to/sock #{$0} ...'
23
+ EOS
24
+ exit 1
25
+ end
26
+
27
+ server = UNIXSocket.open(sockname)
28
+ server.send_io(STDIN)
29
+ server.send_io(STDOUT)
30
+ server.send_io(STDERR)
31
+ args = Marshal.dump([ARGV, Dir.pwd, Process.getpgrp])
32
+ server.write [args.size].pack("N")
33
+ server.write args
34
+ begin
35
+ rc = (server.read(1) || "\000").unpack("C").first
36
+ exit rc
37
+ rescue Interrupt
38
+ server.write('X')
39
+ exit 1
40
+ end
data/bin/snailgun ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
3
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ require 'snailgun/server'
5
+ require 'optparse'
6
+
7
+ sockpath = nil
8
+ mode = nil
9
+ envs = "test"
10
+ rake = false
11
+ verbose = false
12
+
13
+ def fix_rake
14
+ require 'rbconfig'
15
+ Config::CONFIG['bindir'] = File.expand_path(File.dirname(__FILE__))
16
+ Config::CONFIG['ruby_install_name'] = 'fruby'
17
+ require 'rubygems'
18
+ require 'rake'
19
+ require 'rake/testtask'
20
+ require 'rake/rdoctask'
21
+ end
22
+
23
+ OptionParser.new do |opts|
24
+ opts.on("-I DIR", "add to load path") { |v| $:.unshift v }
25
+ opts.on("-r LIB", "require library") { |v| require v }
26
+ opts.on("--rails [ENVS]", "rails mode") { |v| mode = :rails; envs = v }
27
+ opts.on("--ruby [SOCKPATH]", "ruby mode") { |v| mode = :ruby; sockpath = v }
28
+ opts.on("--rake", "add rake support") { rake = true }
29
+ opts.on("-v", "--verbose", "show progress") { verbose = true }
30
+ end.parse!
31
+
32
+ mode ||= if File.exist?("config/boot.rb")
33
+ :rails
34
+ elsif File.exist?("config/init.rb")
35
+ :merb
36
+ else
37
+ :ruby
38
+ end
39
+
40
+ STDERR.puts "Starting in #{mode} mode" if verbose
41
+ case mode
42
+ when :ruby
43
+ unless sockpath
44
+ dir = File.join(ENV['HOME'], '.snailgun')
45
+ begin
46
+ Dir.mkdir dir, 0700
47
+ rescue Errno::EEXIST
48
+ File.chmod 0700, dir
49
+ end
50
+ sockpath = File.join dir, $$.to_s
51
+ end
52
+ fix_rake if rake
53
+ server = Snailgun::Server.new(sockpath)
54
+ server.interactive!
55
+
56
+ when :rails
57
+ conf = File.expand_path('config/boot.rb')
58
+ unless File.exist?(conf)
59
+ raise '#{conf} does not exist, cannot continue'
60
+ end
61
+ sockdir = File.expand_path('tmp/sockets/snailgun')
62
+ begin
63
+ Dir.mkdir sockdir, 0700
64
+ rescue Errno::EEXIST
65
+ File.chmod 0700, sockdir
66
+ end
67
+ pids = {}
68
+ fix_rake # TODO: separate process for rake (but then need to choose right RAILS_ENV)
69
+ envs.split(/[\s,]+/).uniq.each do |env|
70
+ pids[env] = fork do
71
+ server = Snailgun::Server.new("#{sockdir}/#{env}")
72
+ ENV['RAILS_ENV'] = env
73
+ load conf
74
+ require File.expand_path(RAILS_ROOT + '/config/environment')
75
+ # We can get some drastic test speedups by preloading test frameworks
76
+ if env != 'test'
77
+ # do nothing
78
+ elsif File.exist?('test/test_helper.rb')
79
+ require 'test_help'
80
+ elsif File.exist?('spec/spec_helper.rb')
81
+ require 'spec'
82
+ require 'spec/rails'
83
+ end
84
+ if Rails.respond_to?(:configuration) && Rails.configuration.cache_classes
85
+ STDERR.puts <<EOS
86
+ WARNING: Snailgun doesn't work well with `cache_classes`. Strongly recommend
87
+ `config.cache_classes = false` in config/environments/#{env}.rb
88
+ EOS
89
+ end
90
+ STDERR.puts "Started server for #{env}" if verbose
91
+ server.run
92
+ end
93
+ end
94
+ STDERR.puts "Use 'exit' to terminate snailgun"
95
+ Snailgun::Server.shell
96
+ pids.each do |env,pid|
97
+ Process.kill('TERM',pid)
98
+ end
99
+ # TODO: wait a few secs for them to die, 'KILL' if required
100
+ STDERR.puts "Snailgun ended"
101
+
102
+ when :merb
103
+ conf = File.expand_path('config/init.rb')
104
+ unless File.exist?(conf)
105
+ raise '#{conf} does not exist, cannot continue'
106
+ end
107
+ sockdir = File.expand_path('tmp/sockets/snailgun')
108
+ begin
109
+ require 'fileutils'
110
+ FileUtils.mkdir_p sockdir
111
+ ensure
112
+ File.chmod 0700, sockdir
113
+ end
114
+ pids = {}
115
+ fix_rake # TODO: separate process for rake (but then need to choose right RAILS_ENV)
116
+ envs.split(/\s*,\s*/).uniq.each do |env|
117
+ pids[env] = fork do
118
+ server = Snailgun::Server.new("#{sockdir}/#{env}")
119
+ ENV['MERB_ENV'] = env
120
+
121
+ require 'rubygems'
122
+ gem 'merb-core'
123
+ require 'merb'
124
+ Merb.start_environment([env])
125
+
126
+ STDERR.puts "Started server for #{env}" if verbose
127
+ server.run
128
+ end
129
+ end
130
+ STDERR.puts "Use 'exit' to terminate snailgun"
131
+ Snailgun::Server.shell
132
+ pids.each do |env,pid|
133
+ Process.kill('TERM',pid)
134
+ end
135
+ # TODO: wait a few secs for them to die, 'KILL' if required
136
+ STDERR.puts "Snailgun ended"
137
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright (C) Brian Candler 2009. Released under the Ruby licence.
2
+
3
+ # Our at_exit handler must be called *last*, so register it first
4
+ at_exit { $SNAILGUN_EXIT.call if $SNAILGUN_EXIT }
5
+
6
+ # Fix truncation of $0. See http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/336743
7
+ $progname = $0
8
+ alias $PROGRAM_NAME $0
9
+ alias $0 $progname
10
+ trace_var(:$0) {|val| $PROGRAM_NAME = val} # update for ps
11
+
12
+ require 'socket'
13
+ require 'optparse'
14
+
15
+ module Snailgun
16
+ class Server
17
+ attr_accessor :sockname
18
+
19
+ def initialize(sockname = nil)
20
+ @sockname = sockname || "/tmp/snailgun#{$$}"
21
+ File.delete(@sockname) rescue nil
22
+ @socket = UNIXServer.open(@sockname)
23
+ yield self if block_given?
24
+ end
25
+
26
+ def run
27
+ while client = @socket.accept
28
+ fork do
29
+ begin
30
+ STDIN.reopen(client.recv_io)
31
+ STDOUT.reopen(client.recv_io)
32
+ STDERR.reopen(client.recv_io)
33
+ nbytes = client.read(4).unpack("N").first
34
+ args, cwd, pgid = Marshal.load(client.read(nbytes))
35
+ Dir.chdir(cwd)
36
+ begin
37
+ Process.setpgid(0, pgid)
38
+ rescue Errno::EPERM
39
+ end
40
+ exit_status = 0
41
+ $SNAILGUN_EXIT = lambda {
42
+ begin
43
+ client.write [exit_status].pack("C")
44
+ rescue Errno::EPIPE
45
+ end
46
+ }
47
+ #This doesn't work in 1.8.6:
48
+ #Thread.new { client.read(1); Thread.main.raise Interrupt }
49
+ Thread.new { client.read(1); exit 1 }
50
+ start_ruby(args)
51
+ rescue SystemExit => e
52
+ exit_status = e.status
53
+ raise # for the benefit of Test::Unit
54
+ rescue Exception => e
55
+ STDERR.puts "#{e}\n\t#{e.backtrace.join("\n\t")}"
56
+ exit 1
57
+ end
58
+ end
59
+ client.close
60
+ end
61
+ ensure
62
+ File.delete(@sockname) rescue nil
63
+ end
64
+
65
+ # Process the received ruby command line. (TODO: implement more options)
66
+ def start_ruby(args)
67
+ e = []
68
+ OptionParser.new do |opts|
69
+ opts.on("-e EXPR") do |v|
70
+ e << v
71
+ end
72
+ opts.on("-I DIR") do |v|
73
+ $:.unshift v
74
+ end
75
+ opts.on("-r LIB") do |v|
76
+ require v
77
+ end
78
+ end.order!(args)
79
+
80
+ ARGV.replace(args)
81
+ if !e.empty?
82
+ $0 = '-e'
83
+ e.each { |expr| eval(expr, TOPLEVEL_BINDING) }
84
+ elsif ARGV.empty?
85
+ $0 = '-'
86
+ eval(STDIN.read, TOPLEVEL_BINDING)
87
+ else
88
+ cmd = ARGV.shift
89
+ $0 = cmd
90
+ load(cmd)
91
+ end
92
+ end
93
+
94
+ def self.shell
95
+ system(ENV['shell'] || 'bash')
96
+ end
97
+
98
+ # Interactive mode (start a subshell with SNAILGUN_SOCK set up,
99
+ # and terminate the snailgun server when the subshell exits)
100
+ def interactive!
101
+ ENV['SNAILGUN_SOCK'] = @sockname
102
+ pid = Process.fork {
103
+ STDERR.puts "Snailgun starting on #{sockname} - 'exit' to end"
104
+ run
105
+ }
106
+ self.class.shell
107
+ Process.kill('TERM',pid)
108
+ # TODO: wait a few secs for it to die, 'KILL' if required
109
+ STDERR.puts "Snailgun ended"
110
+ end
111
+ end
112
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snailgun
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Brian Candler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-19 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Snailgun accelerates the startup of Ruby applications which require large numbers of libraries
17
+ email: b.candler@pobox.com
18
+ executables:
19
+ - fautotest
20
+ - fconsole
21
+ - frake
22
+ - fruby
23
+ - snailgun
24
+ extensions: []
25
+
26
+ extra_rdoc_files:
27
+ - README.markdown
28
+ files:
29
+ - bin/fautotest
30
+ - bin/fconsole
31
+ - bin/frake
32
+ - bin/fruby
33
+ - bin/snailgun
34
+ - lib/snailgun/server.rb
35
+ - README.markdown
36
+ has_rdoc: true
37
+ homepage: http://github.com/candlerb/snailgun
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --inline-source
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: snailgun
59
+ rubygems_version: 1.3.1
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: Command-line startup accelerator
63
+ test_files: []
64
+