zeus-edge 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zeus.rb ADDED
@@ -0,0 +1,161 @@
1
+ # encoding: utf-8
2
+ require 'socket'
3
+ require 'json'
4
+ require 'pty'
5
+
6
+ require 'zeus/load_tracking'
7
+
8
+ module Zeus
9
+ class Plan
10
+ def after_fork ; end
11
+ end
12
+
13
+ class << self
14
+ attr_accessor :plan, :dummy_tty, :master_socket
15
+
16
+ # this is totally asinine, but readline gets super confused when it's
17
+ # required at a time when stdin or stdout is not connected to a TTY,
18
+ # no matter what we do to tell it otherwise later. So we create a dummy
19
+ # TTY in case readline is required.
20
+ #
21
+ # Yup.
22
+ def setup_dummy_tty!
23
+ return if self.dummy_tty
24
+ master, self.dummy_tty = PTY.open
25
+ Thread.new {
26
+ loop { master.read(1024) }
27
+ }
28
+ STDIN.reopen(dummy_tty)
29
+ STDOUT.reopen(dummy_tty)
30
+ end
31
+
32
+ def setup_master_socket!
33
+ return master_socket if master_socket
34
+
35
+ fd = ENV['ZEUS_MASTER_FD'].to_i
36
+ self.master_socket = UNIXSocket.for_fd(fd)
37
+ end
38
+
39
+ def go(identifier=:boot)
40
+ $0 = "zeus slave: #{identifier}"
41
+
42
+ setup_dummy_tty!
43
+ master = setup_master_socket!
44
+
45
+ # I need to give the master a way to talk to me exclusively
46
+ local, remote = UNIXSocket.pair(Socket::SOCK_STREAM)
47
+ master.send_io(remote)
48
+
49
+ # Now I need to tell the master about my PID and ID
50
+ local.write "P:#{Process.pid}:#{identifier}\0"
51
+
52
+ # Now we run the action and report its success/fail status to the master.
53
+ features = Zeus::LoadTracking.features_loaded_by {
54
+ run_action(local, identifier)
55
+ }
56
+
57
+ # the master wants to know about the files that running the action caused us to load.
58
+ Thread.new { notify_features(local, features) }
59
+
60
+ # We are now 'connected'. From this point, we may receive requests to fork.
61
+ loop do
62
+ messages = local.recv(1024)
63
+ messages.split("\0").each do |new_identifier|
64
+ new_identifier =~ /^(.):(.*)/
65
+ code, ident = $1, $2
66
+ if code == "S"
67
+ fork { plan.after_fork ; go(ident.to_sym) }
68
+ else
69
+ fork { plan.after_fork ; command(ident.to_sym, local) }
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def command(identifier, sock)
78
+ $0 = "zeus runner: #{identifier}"
79
+ Process.setsid
80
+
81
+ local, remote = UNIXSocket.pair(:DGRAM)
82
+ sock.send_io(remote)
83
+ remote.close
84
+ sock.close
85
+
86
+ pid_and_arguments = local.recv(1024)
87
+ pid_and_arguments.chomp!("\0")
88
+ # pid_and_arguments.force_encoding("ASCII-8BIT")
89
+ pid_and_arguments =~ /(.*?):(.*)/
90
+ client_pid, arguments = $1.to_i, $2
91
+ arguments.chomp!("\0")
92
+
93
+ pid = fork {
94
+ $0 = "zeus command: #{identifier}"
95
+ plan.after_fork
96
+ client_terminal = local.recv_io
97
+ local.write "P:#{Process.pid}:\0"
98
+ local.close
99
+
100
+ $stdin.reopen(client_terminal)
101
+ $stdout.reopen(client_terminal)
102
+ $stderr.reopen(client_terminal)
103
+ ARGV.replace(JSON.parse(arguments))
104
+
105
+ plan.send(identifier)
106
+ }
107
+
108
+ kill_command_if_client_quits!(pid, client_pid)
109
+
110
+ Process.wait(pid)
111
+ code = $?.exitstatus || 0
112
+
113
+ local.write "#{code}\0"
114
+
115
+ local.close
116
+ end
117
+
118
+ def kill_command_if_client_quits!(command_pid, client_pid)
119
+ Thread.new {
120
+ loop {
121
+ begin
122
+ Process.kill(0, client_pid)
123
+ rescue Errno::ESRCH
124
+ Process.kill(9, command_pid)
125
+ end
126
+ sleep 1
127
+ }
128
+ }
129
+ end
130
+
131
+ def notify_features(sock, features)
132
+ features.each do |t|
133
+ begin
134
+ sock.write "F:#{t}\0"
135
+ sock.flush
136
+ rescue Errno::ENOBUFS
137
+ sleep 0.2
138
+ retry
139
+ end
140
+ end
141
+ end
142
+
143
+ def report_error_to_master(local, error)
144
+ str = "R:"
145
+ str << "#{error.backtrace[0]}: #{error.message} (#{error.class})\n"
146
+ error.backtrace[1..-1].each do |line|
147
+ str << "\tfrom #{line}\n"
148
+ end
149
+ str << "\0"
150
+ local.write str
151
+ end
152
+
153
+ def run_action(socket, identifier)
154
+ plan.send(identifier)
155
+ socket.write "R:OK\0"
156
+ rescue Exception => e
157
+ report_error_to_master(socket, e)
158
+ end
159
+
160
+ end
161
+ end
data/man/build/zeus ADDED
@@ -0,0 +1,61 @@
1
+ .\" generated with Ronn/v0.7.3
2
+ .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
+ .
4
+ .TH "ZEUS" "1" "September 2012" "" ""
5
+ .
6
+ .SH "NAME"
7
+ \fBzeus\fR \- boot rails in under a second
8
+ .
9
+ .SH "SYNOPSIS"
10
+ \fBzeus\fR [\-\-no\-color] COMMAND [ARGS]
11
+ .
12
+ .SH "DESCRIPTION"
13
+ Zeus makes working with large codebases much less painful\.
14
+ .
15
+ .P
16
+ To use Zeus with Ruby on Rails 3\.0+, Just run \fBzeus init\fR in your project directory, then run \fBzeus start\fR, then run \fBzeus commands\fR in a different shell to see a list of available commands\.
17
+ .
18
+ .P
19
+ See \fBhttps://github\.com/burke/zeus/blob/master/docs/ruby/modifying\.md\fR for information on modifying the boot process that Zeus uses by default\.
20
+ .
21
+ .SH "SLIGHTLY MORE TECHNICAL DESCRIPTION"
22
+ Zeus is a language\-agnostic application checkpointer for non\-multithreaded applications\.
23
+ .
24
+ .P
25
+ It is primarily targeted at ruby and other dynamic languages, where application boot time can be tens of seconds, but it can be made to work for nearly any language\. However, zeus does not work well with multithreaded applications\. It relies heavily on \fBfork(2)\fR, which is largely incompatible with multithreaded systems\.
26
+ .
27
+ .P
28
+ Currently only ruby is targeted, but support for other languages is planned\.
29
+ .
30
+ .P
31
+ Zeus lets you define common tasks, and preloads all of them in the background\. When you ask to run them, zeus transparently connects your terminal to the already\-running process in milliseconds\.
32
+ .
33
+ .P
34
+ This lets you, for example, run unit tests in dozens of milliseconds, rather than dozens of seconds\.
35
+ .
36
+ .P
37
+ Zeus also monitors all files loaded by your application, and restarts parts of it when dependencies change, to keep everything up\-to\-date\.
38
+ .
39
+ .SH "WAY MORE TECHNICAL DESCRIPTION"
40
+ See \fBhttps://github\.com/burke/zeus/blob/master/docs/overview\.md\fR
41
+ .
42
+ .SH "OPTIONS"
43
+ .
44
+ .TP
45
+ \fB\-\-no\-color\fR
46
+ Prints all output without color
47
+ .
48
+ .SH "BUILTIN COMMANDS"
49
+ .
50
+ .TP
51
+ zeus start(1) \fIzeus\-start\.1\.html\fR
52
+ Start a zeus server in the current directory using \fBzeus\.json\fR
53
+ .
54
+ .TP
55
+ zeus init(1) \fIzeus\-init\.1\.html\fR
56
+ Generate a template zeus\.json
57
+ .
58
+ .TP
59
+ \fBzeus commands(1)\fR
60
+ List the commands defined by zeus\.json
61
+
@@ -0,0 +1,13 @@
1
+ .\" generated with Ronn/v0.7.3
2
+ .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
+ .
4
+ .TH "ZEUS\-INIT" "1" "September 2012" "" ""
5
+ .
6
+ .SH "NAME"
7
+ \fBzeus\-init\fR \- Generate a template zeus\.json
8
+ .
9
+ .SH "SYNOPSIS"
10
+ \fBzeus init\fR
11
+ .
12
+ .SH "DESCRIPTION"
13
+ Creates a \fBzeus\.json\fR file in the current directory\. This example file is useful for rails applications, but can be modified to suit your needs\.
@@ -0,0 +1,17 @@
1
+ ZEUS-INIT(1) ZEUS-INIT(1)
2
+
3
+
4
+
5
+ NAME
6
+ zeus-init - Generate a template zeus.json
7
+
8
+ SYNOPSIS
9
+ zeus init
10
+
11
+ DESCRIPTION
12
+ Creates a zeus.json file in the current directory. This example file is
13
+ useful for rails applications, but can be modified to suit your needs.
14
+
15
+
16
+
17
+ September 2012 ZEUS-INIT(1)
@@ -0,0 +1,16 @@
1
+ .\" generated with Ronn/v0.7.3
2
+ .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
+ .
4
+ .TH "ZEUS\-START" "1" "September 2012" "" ""
5
+ .
6
+ .SH "NAME"
7
+ \fBzeus\-start\fR \- Start a zeus server
8
+ .
9
+ .SH "SYNOPSIS"
10
+ \fBzeus start\fR
11
+ .
12
+ .SH "DESCRIPTION"
13
+ Start a server\.
14
+ .
15
+ .P
16
+ TODO: Better docs\.
@@ -0,0 +1,18 @@
1
+ ZEUS-START(1) ZEUS-START(1)
2
+
3
+
4
+
5
+ NAME
6
+ zeus-start - Start a zeus server
7
+
8
+ SYNOPSIS
9
+ zeus start
10
+
11
+ DESCRIPTION
12
+ Start a server.
13
+
14
+ TODO: Better docs.
15
+
16
+
17
+
18
+ September 2012 ZEUS-START(1)
@@ -0,0 +1,65 @@
1
+ ZEUS(1) ZEUS(1)
2
+
3
+
4
+
5
+ NAME
6
+ zeus - boot rails in under a second
7
+
8
+ SYNOPSIS
9
+ zeus [--no-color] COMMAND [ARGS]
10
+
11
+ DESCRIPTION
12
+ Zeus makes working with large codebases much less painful.
13
+
14
+ To use Zeus with Ruby on Rails 3.0+, Just run zeus init in your project
15
+ directory, then run zeus start, then run zeus commands in a different
16
+ shell to see a list of available commands.
17
+
18
+ See https://github.com/burke/zeus/blob/master/docs/ruby/modifying.md
19
+ for information on modifying the boot process that Zeus uses by
20
+ default.
21
+
22
+ SLIGHTLY MORE TECHNICAL DESCRIPTION
23
+ Zeus is a language-agnostic application checkpointer for non-multi-
24
+ threaded applications.
25
+
26
+ It is primarily targeted at ruby and other dynamic languages, where
27
+ application boot time can be tens of seconds, but it can be made to
28
+ work for nearly any language. However, zeus does not work well with
29
+ multithreaded applications. It relies heavily on fork(2), which is
30
+ largely incompatible with multithreaded systems.
31
+
32
+ Currently only ruby is targeted, but support for other languages is
33
+ planned.
34
+
35
+ Zeus lets you define common tasks, and preloads all of them in the
36
+ background. When you ask to run them, zeus transparently connects your
37
+ terminal to the already-running process in milliseconds.
38
+
39
+ This lets you, for example, run unit tests in dozens of milliseconds,
40
+ rather than dozens of seconds.
41
+
42
+ Zeus also monitors all files loaded by your application, and restarts
43
+ parts of it when dependencies change, to keep everything up-to-date.
44
+
45
+ WAY MORE TECHNICAL DESCRIPTION
46
+ See https://github.com/burke/zeus/blob/master/docs/overview.md
47
+
48
+ OPTIONS
49
+ --no-color
50
+ Prints all output without color
51
+
52
+ BUILTIN COMMANDS
53
+ zeus start(1) zeus-start.1.html
54
+ Start a zeus server in the current directory using zeus.json
55
+
56
+ zeus init(1) zeus-init.1.html
57
+ Generate a template zeus.json
58
+
59
+ zeus commands(1)
60
+ List the commands defined by zeus.json
61
+
62
+
63
+
64
+
65
+ September 2012 ZEUS(1)
@@ -0,0 +1,31 @@
1
+ module MiniTest
2
+ module Unit
3
+ class TestCase
4
+ end
5
+ end
6
+ end
7
+
8
+ def stub_mini_test_methods
9
+ MiniTest::Unit::TestCase.stub!(:test_suites).and_return [fake_suite]
10
+ MiniTest::Unit.stub!(:runner).and_return fake_runner
11
+ end
12
+
13
+ def fake_runner
14
+ @runner ||= stub("Runner", :run => 0)
15
+ end
16
+
17
+ def fake_suite
18
+ @suite ||= stub("TestSuite",
19
+ :test_methods => [fake_test_method],
20
+ :instance_method => fake_instance_method)
21
+ end
22
+
23
+ def fake_test_method
24
+ "test_method"
25
+ end
26
+
27
+ def fake_instance_method
28
+ @instance_method ||= stub("InstanceMethod",
29
+ :source_location => ["path/to/file.rb", 2],
30
+ :source => "def #{fake_test_method} \n assert true \n end")
31
+ end
data/spec/m_spec.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ require 'fake_mini_test'
3
+
4
+ describe Zeus::M::Runner do
5
+ Runner = Zeus::M::Runner
6
+
7
+ before do
8
+ stub_mini_test_methods
9
+ end
10
+
11
+ context "no option is given" do
12
+ it "runs the test without giving any option" do
13
+ argv = ["path/to/file.rb"]
14
+
15
+ fake_runner.should_receive(:run).with([])
16
+
17
+ lambda { Runner.new(argv).run }.should exit_with_code(0)
18
+ end
19
+ end
20
+
21
+ context "given a line number" do
22
+ it "aborts if no test is found" do
23
+ argv = ["path/to/file.rb:100"]
24
+
25
+ STDERR.should_receive(:write).with(/No tests found on line 100/)
26
+ fake_runner.should_not_receive :run
27
+
28
+ lambda { Runner.new(argv).run }.should_not exit_with_code(0)
29
+ end
30
+
31
+ it "runs the test if the correct line number is given" do
32
+ argv = ["path/to/file.rb:2"]
33
+
34
+ fake_runner.should_receive(:run).with(["-n", "/(#{fake_test_method})/"])
35
+
36
+ lambda { Runner.new(argv).run }.should exit_with_code(0)
37
+ end
38
+ end
39
+
40
+ context "specifying test name" do
41
+ it "runs the specified tests when using a pattern in --name option" do
42
+ argv = ["path/to/file.rb", "--name", "/#{fake_test_method}/"]
43
+
44
+ fake_runner.should_receive(:run).with(["-n", "/#{fake_test_method}/"])
45
+
46
+ lambda { Runner.new(argv).run }.should exit_with_code(0)
47
+ end
48
+
49
+ it "runs the specified tests when using a pattern in -n option" do
50
+ argv = ["path/to/file.rb", "-n", "/method/"]
51
+
52
+ fake_runner.should_receive(:run).with(["-n", "/method/"])
53
+
54
+ lambda { Runner.new(argv).run }.should exit_with_code(0)
55
+ end
56
+
57
+ it "aborts if no test matches the given pattern" do
58
+ argv = ["path/to/file.rb", "-n", "/garbage/"]
59
+
60
+ STDERR.should_receive(:write).with(%r{No test name matches \'/garbage/\'})
61
+ fake_runner.should_not_receive :run
62
+
63
+ lambda { Runner.new(argv).run }.should_not exit_with_code(0)
64
+ end
65
+
66
+ it "runs the specified tests when using a name (no pattern)" do
67
+ argv = ["path/to/file.rb", "-n", "#{fake_test_method}"]
68
+
69
+ fake_runner.should_receive(:run).with(["-n", fake_test_method])
70
+
71
+ lambda { Runner.new(argv).run }.should exit_with_code(0)
72
+ end
73
+
74
+ it "aborts if no test matches the given test name" do
75
+ argv = ["path/to/file.rb", "-n", "method"]
76
+
77
+ STDERR.should_receive(:write).with(%r{No test name matches \'method\'})
78
+ fake_runner.should_not_receive :run
79
+
80
+ lambda { Runner.new(argv).run }.should_not exit_with_code(0)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,38 @@
1
+ require 'zeus/rails'
2
+
3
+ module Zeus::M
4
+ VERSION = "0.0.0.test"
5
+ end
6
+
7
+ RSpec::Matchers.define :exit_with_code do |exp_code|
8
+ actual = nil
9
+ match do |block|
10
+ begin
11
+ block.call
12
+ rescue SystemExit => e
13
+ actual = e.status
14
+ end
15
+ actual and actual == exp_code
16
+ end
17
+ failure_message_for_should do |block|
18
+ "expected block to call exit(#{exp_code}) but exit" +
19
+ (actual.nil? ? " not called" : "(#{actual}) was called")
20
+ end
21
+ failure_message_for_should_not do |block|
22
+ "expected block not to call exit(#{exp_code})"
23
+ end
24
+ description do
25
+ "expect block to call exit(#{exp_code})"
26
+ end
27
+ end
28
+
29
+ def stub_system_methods
30
+ Dir.stub!(:glob).and_return(["path/to/file.rb"])
31
+ Kernel.stub!(:load).and_return
32
+ end
33
+
34
+ RSpec.configure do |config|
35
+ config.before(:each) do
36
+ stub_system_methods
37
+ end
38
+ end
data/zeus.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # This preamble is basically used to deal with bundler/gem_tasks, which loads the gemspec
4
+ # on rake init, even though some prerequisites are not generated until `rake build` is invoked.
5
+ version = begin
6
+ require File.expand_path('../lib/zeus/version', __FILE__)
7
+ Zeus::VERSION
8
+ rescue LoadError
9
+ "0.0.0"
10
+ end
11
+
12
+ files = File.exist?('MANIFEST') ? File.read("MANIFEST").lines.map(&:chomp) : []
13
+
14
+ Gem::Specification.new do |gem|
15
+ gem.authors = ["Burke Libbey", "Arturo Pie"]
16
+ gem.email = ["burke@libbey.me"]
17
+ gem.description = %q{Boot any rails app in under a second}
18
+ gem.summary = %q{Zeus is an intelligent preloader for ruby applications. It allows normal development tasks to be run in a fraction of a second.}
19
+ gem.homepage = "http://zeus.is"
20
+
21
+ gem.files = files
22
+ gem.extensions = ["ext/inotify-wrapper/extconf.rb"]
23
+ gem.executables = ['zeus']
24
+ gem.test_files = []
25
+ gem.name = "zeus-edge"
26
+ gem.require_paths = ["lib"]
27
+ gem.version = version
28
+ gem.license = "MIT"
29
+
30
+ gem.add_development_dependency "rspec", '~>2.12.0'
31
+
32
+ gem.add_runtime_dependency "method_source", ">= 0.6.7"
33
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zeus-edge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.12.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Burke Libbey
9
+ - Arturo Pie
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-11-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 2.12.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: 2.12.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: method_source
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.6.7
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.6.7
47
+ description: Boot any rails app in under a second
48
+ email:
49
+ - burke@libbey.me
50
+ executables:
51
+ - zeus
52
+ extensions:
53
+ - ext/inotify-wrapper/extconf.rb
54
+ extra_rdoc_files: []
55
+ files:
56
+ - MIT-LICENSE
57
+ - lib/zeus.rb
58
+ - lib/zeus/m.rb
59
+ - lib/zeus/rails.rb
60
+ - lib/zeus/load_tracking.rb
61
+ - lib/zeus/m/test_collection.rb
62
+ - lib/zeus/m/test_method.rb
63
+ - lib/zeus/plan.rb
64
+ - build/zeus-linux-amd64
65
+ - build/fsevents-wrapper
66
+ - build/zeus-darwin-amd64
67
+ - build/zeus-linux-386
68
+ - spec/fake_mini_test.rb
69
+ - spec/m_spec.rb
70
+ - spec/spec_helper.rb
71
+ - zeus.gemspec
72
+ - ext/inotify-wrapper/inotify-wrapper.cpp
73
+ - ext/inotify-wrapper/extconf.rb
74
+ - bin/zeus
75
+ - Rakefile
76
+ - Gemfile
77
+ - man/build/zeus-start
78
+ - man/build/zeus-init
79
+ - man/build/zeus
80
+ - man/build/zeus-start.txt
81
+ - man/build/zeus-init.txt
82
+ - man/build/zeus.txt
83
+ homepage: http://zeus.is
84
+ licenses:
85
+ - MIT
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.24
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Zeus is an intelligent preloader for ruby applications. It allows normal
108
+ development tasks to be run in a fraction of a second.
109
+ test_files: []