zeus 0.4.5 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ module Zeus
2
+ class Server
3
+ class Stage
4
+
5
+ module ErrorState
6
+ def handle_load_error(e)
7
+ errored_file = full_path_of_file_from_error(e)
8
+
9
+ # register all the decendent acceptors as stubs with errors
10
+ register_acceptors_as_errors(e)
11
+
12
+ feature_notifier.notify_feature(errored_file)
13
+ feature_notifier.notify_new_features
14
+
15
+ # we do not need to do anything. We wait, until a dependency changes.
16
+ # At that point, we get killed and restarted.
17
+ sleep
18
+ end
19
+
20
+ private
21
+
22
+ def full_path_of_file_from_error(e)
23
+ errored_file = e.backtrace[0].scan(/(.+?):\d+:in/)[0][0]
24
+
25
+ # handle relative paths
26
+ unless errored_file =~ /^\//
27
+ errored_file = File.expand_path(errored_file, Dir.pwd)
28
+ end
29
+ end
30
+
31
+ def register_acceptors_as_errors(e)
32
+ descendent_acceptors.each do |acc|
33
+ acc = acc.extend(Acceptor::ErrorState)
34
+ acc.error = e
35
+ acc.run
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ module Zeus
2
+ class Server
3
+ class Stage
4
+ class FeatureNotifier
5
+
6
+ def initialize(server, stage_name)
7
+ @server = server
8
+ @stage_name = stage_name
9
+ end
10
+
11
+ def notify_new_features
12
+ new_features = newly_loaded_features()
13
+ $previously_loaded_features ||= []
14
+ $previously_loaded_features |= new_features
15
+ Thread.new {
16
+ new_features.each { |f| notify_feature(f) }
17
+ }
18
+ end
19
+
20
+ def notify_feature(feature)
21
+ @server.__CHILD__stage_has_feature(@stage_name, feature)
22
+ end
23
+
24
+ private
25
+
26
+ def newly_loaded_features
27
+ old_features = defined?($previously_loaded_features) ? $previously_loaded_features : []
28
+ ($LOADED_FEATURES + @server.extra_features) - old_features
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+
38
+
@@ -1,3 +1,3 @@
1
1
  module Zeus
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.6"
3
3
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ require 'stringio'
4
+
5
+ module Zeus
6
+ describe ErrorPrinter do
7
+
8
+ let(:error) {
9
+ begin
10
+ raise
11
+ rescue => e
12
+ e
13
+ end
14
+ }
15
+
16
+ it 'prints an error just like ruby does by default' do
17
+ io = StringIO.new
18
+ ErrorPrinter.new(error).write_to(io)
19
+ io.rewind
20
+ lines = io.readlines
21
+ lines[0].should =~ /^[^\s]*error_printer_spec.rb:\d+:in `.+': \(RuntimeError\)$/
22
+ lines[1].should =~ /^\tfrom .*\.rb:\d+:in `.*'$/
23
+ lines.size.should > 5
24
+ end
25
+
26
+ end
27
+ end
@@ -7,22 +7,29 @@ describe "Integration" do
7
7
 
8
8
  context "in a non-rails project with a .zeus.rb" do
9
9
  it "starts the zeus server and responds to commands" do
10
- write ".zeus.rb", <<-RUBY
11
- Zeus::Server.define! do
12
- stage :foo do
13
- command :bar do
14
- puts "YES"
15
- end
16
- end
17
- end
18
- RUBY
19
-
10
+ bar_setup("puts 'YES'")
20
11
  start, run = start_and_run("bar")
21
12
  start.should include "spawner `foo`"
22
13
  start.should include "acceptor `bar`"
23
14
  run.should == ["YES\r\n"]
24
15
  end
25
16
 
17
+ it "receives ARGV after command" do
18
+ bar_setup("puts ARGV.join(', ')")
19
+ start, run = start_and_run("bar 1 '2 3 4' --123")
20
+ run.should == ["1, 2 3 4, --123\r\n"]
21
+ end
22
+
23
+ it "cam exist with 0" do
24
+ bar_setup("exit 0")
25
+ start_and_run("bar")
26
+ end
27
+
28
+ it "can exist with non-0" do
29
+ bar_setup("exit 1")
30
+ start_and_run("bar", :fail => true)
31
+ end
32
+
26
33
  it "can run via command alias" do
27
34
  write ".zeus.rb", <<-RUBY
28
35
  Zeus::Server.define! do
@@ -43,6 +50,18 @@ describe "Integration" do
43
50
 
44
51
  private
45
52
 
53
+ def bar_setup(inner)
54
+ write ".zeus.rb", <<-RUBY
55
+ Zeus::Server.define! do
56
+ stage :foo do
57
+ command :bar do
58
+ #{inner}
59
+ end
60
+ end
61
+ end
62
+ RUBY
63
+ end
64
+
46
65
  def zeus(command, options={})
47
66
  command = zeus_command(command)
48
67
  result = `#{command}`
@@ -62,11 +81,11 @@ describe "Integration" do
62
81
  end
63
82
  end
64
83
 
65
- def start_and_run(commands)
84
+ def start_and_run(commands, options={})
66
85
  start_output = ""
67
86
  t1 = Thread.new { record_start(start_output) }
68
87
  sleep 0.1
69
- run_output = [*commands].map{ |cmd| zeus(cmd) }
88
+ run_output = [*commands].map{ |cmd| zeus(cmd, options) }
70
89
  sleep 0.2
71
90
  t1.kill
72
91
  [start_output, run_output]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zeus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-16 00:00:00.000000000 Z
12
+ date: 2012-08-20 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Boot any rails app in under a second
15
15
  email:
@@ -27,13 +27,20 @@ files:
27
27
  - README.md
28
28
  - Rakefile
29
29
  - bin/zeus
30
+ - docs/acceptor_registration.md
31
+ - docs/client_server_handshake.md
30
32
  - ext/fsevents-wrapper/fsevents-wrapper
31
33
  - ext/fsevents-wrapper/main.m
32
34
  - lib/thrud.rb
33
35
  - lib/zeus.rb
34
36
  - lib/zeus/cli.rb
35
37
  - lib/zeus/client.rb
36
- - lib/zeus/dsl.rb
38
+ - lib/zeus/client/winsize.rb
39
+ - lib/zeus/error_printer.rb
40
+ - lib/zeus/plan.rb
41
+ - lib/zeus/plan/acceptor.rb
42
+ - lib/zeus/plan/node.rb
43
+ - lib/zeus/plan/stage.rb
37
44
  - lib/zeus/server.rb
38
45
  - lib/zeus/server/acceptor.rb
39
46
  - lib/zeus/server/acceptor_registration_monitor.rb
@@ -41,14 +48,16 @@ files:
41
48
  - lib/zeus/server/command_runner.rb
42
49
  - lib/zeus/server/file_monitor.rb
43
50
  - lib/zeus/server/file_monitor/fsevent.rb
44
- - lib/zeus/server/forked_process.rb
45
51
  - lib/zeus/server/load_tracking.rb
46
52
  - lib/zeus/server/process_tree_monitor.rb
47
53
  - lib/zeus/server/stage.rb
54
+ - lib/zeus/server/stage/error_state.rb
55
+ - lib/zeus/server/stage/feature_notifier.rb
48
56
  - lib/zeus/templates/rails.rb
49
57
  - lib/zeus/ui.rb
50
58
  - lib/zeus/version.rb
51
59
  - spec/cli_spec.rb
60
+ - spec/error_printer_spec.rb
52
61
  - spec/integration_spec.rb
53
62
  - spec/server/file_monitor/fsevent_spec.rb
54
63
  - spec/server/load_tracking_spec.rb
@@ -84,6 +93,7 @@ summary: Zeus is an intelligent preloader for ruby applications. It allows norma
84
93
  development tasks to be run in a fraction of a second.
85
94
  test_files:
86
95
  - spec/cli_spec.rb
96
+ - spec/error_printer_spec.rb
87
97
  - spec/integration_spec.rb
88
98
  - spec/server/file_monitor/fsevent_spec.rb
89
99
  - spec/server/load_tracking_spec.rb
@@ -1,154 +0,0 @@
1
- require 'set'
2
-
3
- module Zeus
4
- module DSL
5
-
6
- class Evaluator
7
- def stage(name, &b)
8
- stage = DSL::Stage.new(name)
9
- stage.root = true
10
- stage.instance_eval(&b)
11
- stage
12
- end
13
- end
14
-
15
- class Node
16
- attr_reader :name, :stages, :features
17
- attr_accessor :pid, :root
18
-
19
- def initialize(name)
20
- @name = name
21
- @stages = []
22
- @features = Set.new # hash might be faster than ruby's inane impl of set.
23
- end
24
-
25
- def stage_has_feature(name, file)
26
- node_for_name(name).features << file
27
- end
28
-
29
- def stage_has_pid(name, pid)
30
- node_for_name(name).pid = pid
31
- end
32
-
33
- def kill_nodes_with_feature(file)
34
- if features.include?(file)
35
- if root
36
- Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
37
- else
38
- kill!
39
- end
40
- else
41
- stages.each do |child|
42
- child.kill_nodes_with_feature(file)
43
- end
44
- end
45
- end
46
-
47
- # We send STOP before actually killing the processes here.
48
- # This is to prevent parents from respawning before all the children
49
- # are killed. This prevents a race condition.
50
- def kill!
51
- Process.kill("STOP", pid) if pid
52
- # Protected methods don't work with each(&:m) notation.
53
- stages.each { |stage| stage.kill! }
54
- old_pid = pid
55
- self.pid = nil
56
- Process.kill("KILL", old_pid) if old_pid
57
- end
58
-
59
- private
60
-
61
- def node_for_name(name)
62
- @nodes_by_name ||= __nodes_by_name
63
- @nodes_by_name[name]
64
- end
65
-
66
- def __nodes_by_name
67
- nodes = {name => self}
68
- stages.each do |child|
69
- nodes.merge!(child.__nodes_by_name)
70
- end
71
- nodes
72
- end ; protected :__nodes_by_name
73
-
74
- end
75
-
76
- class Acceptor < Node
77
-
78
- attr_reader :name, :aliases, :description, :action
79
- def initialize(name, aliases, description, &b)
80
- super(name)
81
- @description = description
82
- @aliases = aliases
83
- @action = b
84
- end
85
-
86
- # ^ configuration
87
- # V later use
88
-
89
- def commands
90
- [name, *aliases].map(&:to_s)
91
- end
92
-
93
- def acceptors
94
- self
95
- end
96
-
97
- def to_process_object(server)
98
- Zeus::Server::Acceptor.new(server).tap do |stage|
99
- stage.name = @name
100
- stage.aliases = @aliases
101
- stage.action = @action
102
- stage.description = @description
103
- end
104
- end
105
-
106
- end
107
-
108
- class Stage < Node
109
-
110
- attr_reader :actions
111
- def initialize(name)
112
- super(name)
113
- @actions = []
114
- end
115
-
116
- def action(&b)
117
- @actions << b
118
- self
119
- end
120
-
121
- def desc(desc)
122
- @desc = desc
123
- end
124
-
125
- def stage(name, &b)
126
- @stages << DSL::Stage.new(name).tap { |s| s.instance_eval(&b) }
127
- self
128
- end
129
-
130
- def command(name, *aliases, &b)
131
- @stages << DSL::Acceptor.new(name, aliases, @desc, &b)
132
- @desc = nil
133
- self
134
- end
135
-
136
- # ^ configuration
137
- # V later use
138
-
139
- def acceptors
140
- stages.map(&:acceptors).flatten
141
- end
142
-
143
- def to_process_object(server)
144
- Zeus::Server::Stage.new(server).tap do |stage|
145
- stage.name = @name
146
- stage.stages = @stages.map { |stage| stage.to_process_object(server) }
147
- stage.actions = @actions
148
- end
149
- end
150
-
151
- end
152
-
153
- end
154
- end
@@ -1,98 +0,0 @@
1
- module Zeus
2
- class Server
3
- # base class for Stage and Acceptor
4
- class ForkedProcess
5
-
6
- attr_accessor :name
7
- attr_reader :pid
8
- def initialize(server)
9
- @server = server
10
- end
11
-
12
- def notify_feature(feature)
13
- @server.__CHILD__stage_has_feature(@name, feature)
14
- end
15
-
16
- def descendent_acceptors
17
- raise NotImplementedError
18
- end
19
-
20
- def process_type
21
- raise "NotImplementedError"
22
- end
23
-
24
- def notify_started
25
- @server.__CHILD__stage_starting_with_pid(@name, Process.pid)
26
- Zeus.ui.info("starting #{process_type} `#{@name}`")
27
- end
28
-
29
- def notify_terminated
30
- # @server.__CHILD__stage_terminating(@name)
31
- Zeus.ui.info("killing #{process_type} `#{@name}`")
32
- end
33
-
34
- def setup_forked_process(close_parent_sockets)
35
- @server.__CHILD__close_parent_sockets if close_parent_sockets
36
-
37
- notify_started
38
-
39
- $0 = "zeus #{process_type}: #{@name}"
40
-
41
- trap("INT") { exit }
42
- trap("TERM") {
43
- notify_terminated
44
- exit
45
- }
46
-
47
- defined?(ActiveRecord::Base) and ActiveRecord::Base.clear_all_connections!
48
-
49
- end
50
-
51
- def newly_loaded_features
52
- old_features = defined?($previously_loaded_features) ? $previously_loaded_features : []
53
- ($LOADED_FEATURES + @server.extra_features) - old_features
54
- end
55
-
56
- def runloop!
57
- raise NotImplementedError
58
- end
59
-
60
- def before_setup
61
- end
62
-
63
- def after_setup
64
- end
65
-
66
- def notify_new_features
67
- new_features = newly_loaded_features()
68
- $previously_loaded_features ||= []
69
- $previously_loaded_features |= new_features
70
- Thread.new {
71
- new_features.each { |f| notify_feature(f) }
72
- }
73
- end
74
-
75
- def after_notify
76
- end
77
-
78
- # TODO: This just got really ugly and needs a refactor more than ever.
79
- def run(close_parent_sockets = false)
80
- @pid = fork {
81
- before_setup
82
- setup_forked_process(close_parent_sockets)
83
-
84
- Zeus.run_after_fork!
85
-
86
- after_setup
87
- notify_new_features
88
-
89
- after_notify
90
- runloop!
91
- }
92
- end
93
-
94
- end
95
-
96
- end
97
- end
98
-