zeus 0.2.0.beta1 → 0.2.0.beta2

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/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in zeus.gemspec
4
4
  gemspec
5
+
6
+ gem 'pry'
data/README.md CHANGED
@@ -17,7 +17,6 @@ Soon? You can use Zeus now, but don't expect it to be perfect. I'm working hard
17
17
  ## Ugly bits
18
18
 
19
19
  * Not battle-tested yet
20
- * Creates a bunch of sockets
21
20
  * Uses an obscene number of file descriptors
22
21
 
23
22
  ## Installation
@@ -26,7 +25,17 @@ Install the gem.
26
25
 
27
26
  gem install zeus
28
27
 
29
- Copy `examples/rails.rb` to `{your app}/.zeus.rb`
28
+ Run the project initializer.
29
+
30
+ zeus init
31
+
32
+ ## Upgrading from initial release
33
+
34
+ Since zeus is super-brand-new, the config file format changed already.
35
+
36
+ gem install zeus
37
+ rm .zeus.rb
38
+ zeus init
30
39
 
31
40
  ## Usage
32
41
 
@@ -45,23 +54,25 @@ Run some commands:
45
54
 
46
55
  ## TODO (roughly prioritized)
47
56
 
48
- * Fix all the bugs I added when I switched to singlesocket...
49
- * Make the code less terrible
50
- * Figure out how to run full test suites without multiple env loads
51
- * Support other frameworks?
52
- * Use fsevent instead of kqueue to reduce the obscene number of file descriptors.
53
- * Support inotify on linux
57
+ * Make sure client connection requests are handled immediately
58
+ * Acceptors booting should not be dependent on passing all loaded features to the file monitor
59
+ * Route all logging output through Zeus.ui
54
60
  * Handle connections for not-yet-started sockets
61
+ * Refactor, refactor, refactor...
62
+ * Support other frameworks?
63
+ * Periodically re-attempt to add lost files to KQueue
64
+ * Figure out how to run full test suites without multiple env loads
55
65
  * Don't replace a socket with changed deps until the new one is ready
66
+
67
+ ## Ideas (not quite TODOs)
68
+
56
69
  * (maybe) Start the preloader as a daemon transparently when any command is run, then wait for it to finish
70
+ * Use fsevent instead of kqueue to reduce the obscene number of file descriptors.
71
+ * Support inotify on linux
57
72
 
58
73
  ## Contributing
59
74
 
60
- 1. Fork it
61
- 2. Create your feature branch (`git checkout -b my-new-feature`)
62
- 3. Commit your changes (`git commit -am 'Added some feature'`)
63
- 4. Push to the branch (`git push origin my-new-feature`)
64
- 5. Create new Pull Request
75
+ Fork, Branch, Pull Request.
65
76
 
66
77
  ## Thanks...
67
78
 
data/bin/zeus CHANGED
@@ -1,43 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'zeus'
4
- require 'zeus/cli'
5
- Zeus::CLI.start
6
-
7
- begin
8
- rescue Zeus::ZeusError => e
9
- Zeus.ui.error e.message
10
- Zeus.ui.debug e.backtrace.join("\n")
11
- exit e.status_code
12
- rescue Interrupt => e
13
- Zeus.ui.error e.message
14
- Zeus.ui.debug e.backtrace.join("\n")
15
- exit 1
16
- rescue SystemExit => e
17
- exit e.status
18
- rescue Exception => e
19
- Zeus.ui.error("A fatal error has occurred. Please see the github issues at:\n" \
20
- "http://github.com/burke/zeus/issues")
21
- end
22
- exit 0
23
-
24
-
25
- case ARGV[0]
26
- when "start"
27
- require 'zeus/server'
28
- begin
29
- require './.zeus.rb'
30
- rescue LoadError
31
- puts "\x1b[31mYour project is missing a config file (.zeus.rb), or you are not in the project root."
32
- puts "You can run `zeus init` to generate a config file.\x1b[0m"
33
- exit 1
34
- end
35
- Zeus::Server.run
36
- when "init"
37
- require 'zeus/init'
38
- ARGV.shift # get "init" off the list
39
- Zeus::Init.run(ARGV)
40
- else
41
- require 'zeus/client'
42
- Zeus::Client.run
43
- end
4
+ Zeus::CLI.start
Binary file
@@ -0,0 +1,133 @@
1
+ //
2
+ // main.m
3
+ // fsevents-wrapper
4
+ //
5
+ // Created by Burke Libbey on 2012-07-30.
6
+ // Copyright (c) 2012 Burke Libbey. All rights reserved.
7
+ //
8
+
9
+ #import <Foundation/Foundation.h>
10
+ #include <CoreServices/CoreServices.h>
11
+ #include <sys/stat.h>
12
+
13
+
14
+ static CFMutableArrayRef _watchedFiles;
15
+ static FSEventStreamRef _activeStream;
16
+ static NSMutableDictionary *_fileIsWatched;
17
+
18
+ static int flagsWorthReloadingFor = \
19
+ kFSEventStreamEventFlagItemRemoved | \
20
+ kFSEventStreamEventFlagItemRenamed | \
21
+ kFSEventStreamEventFlagItemModified;
22
+
23
+ void myCallbackFunction(
24
+ ConstFSEventStreamRef streamRef,
25
+ void *clientCallBackInfo,
26
+ size_t numEvents,
27
+ void *eventPaths,
28
+ const FSEventStreamEventFlags eventFlags[],
29
+ const FSEventStreamEventId eventIds[])
30
+ {
31
+ int i, flags;
32
+ char **paths = eventPaths;
33
+
34
+ for (i = 0; i < numEvents; i++) {
35
+ flags = eventFlags[i];
36
+
37
+ if (flags & (kFSEventStreamEventFlagItemIsFile | flagsWorthReloadingFor)) {
38
+ printf("%s\n", paths[i]);
39
+ fflush(stdout);
40
+ }
41
+ }
42
+ }
43
+
44
+ void configureStream()
45
+ {
46
+ if (CFArrayGetCount(_watchedFiles) == 0) return;
47
+
48
+ if (_activeStream) {
49
+ FSEventStreamStop(_activeStream);
50
+ FSEventStreamUnscheduleFromRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
51
+ //CFRelease(_activeStream);
52
+ }
53
+
54
+ _activeStream = FSEventStreamCreate(NULL,
55
+ &myCallbackFunction,
56
+ NULL,
57
+ _watchedFiles,
58
+ kFSEventStreamEventIdSinceNow,
59
+ 1.0, // latency
60
+ kFSEventStreamCreateFlagFileEvents);
61
+
62
+ FSEventStreamScheduleWithRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
63
+
64
+ FSEventStreamStart(_activeStream);
65
+
66
+ }
67
+
68
+ int maybeAddFileToWatchList(char *line)
69
+ {
70
+ CFStringRef file = CFStringCreateWithCString(NULL, line, kCFStringEncodingASCII);
71
+ struct stat buf;
72
+
73
+ if ([_fileIsWatched valueForKey:(__bridge NSString *)file]) {
74
+ return 0;
75
+ } else if (stat(line, &buf) == 0) {
76
+ [_fileIsWatched setValue:@"yes" forKey:(__bridge NSString *)file];
77
+ CFArrayAppendValue(_watchedFiles, file);
78
+ return 1;
79
+ } else {
80
+ return 0;
81
+ }
82
+ }
83
+
84
+ void handleInputFiles()
85
+ {
86
+ int anyChanges = 0;
87
+
88
+ char line[2048];
89
+
90
+ fd_set fdset;
91
+
92
+ FD_ZERO(&fdset);
93
+ FD_SET(STDIN_FILENO, &fdset);
94
+ struct timeval timeout;
95
+ timeout.tv_sec = 0;
96
+ timeout.tv_usec = 0;
97
+
98
+ select(STDIN_FILENO+1, &fdset, NULL, NULL, &timeout);
99
+ while (FD_ISSET(STDIN_FILENO, &fdset) && fgets(line, 2048, stdin) != NULL) {
100
+ line[strlen(line)-1] = 0;
101
+ anyChanges |= maybeAddFileToWatchList(line);
102
+ select(STDIN_FILENO+1, &fdset, NULL, NULL, &timeout);
103
+ }
104
+
105
+ if (anyChanges) {
106
+ configureStream();
107
+ }
108
+ }
109
+
110
+ void configureTimerAndRun()
111
+ {
112
+ CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL,
113
+ 0,
114
+ 0.5,
115
+ 0,
116
+ 0,
117
+ &handleInputFiles,
118
+ NULL);
119
+
120
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
121
+ CFRunLoopRun();
122
+ }
123
+
124
+ int main(int argc, const char * argv[])
125
+ {
126
+ @autoreleasepool {
127
+ _watchedFiles = CFArrayCreateMutable(NULL, 0, NULL);
128
+ _fileIsWatched = [[NSMutableDictionary alloc] initWithCapacity:500];
129
+
130
+ configureTimerAndRun();
131
+ }
132
+ return 0;
133
+ }
@@ -1,4 +1,4 @@
1
- class TinyThor
1
+ class Thrud
2
2
 
3
3
  class Task < Struct.new(:method_name, :desc, :long_desc, :method_options)
4
4
  def arity(obj)
@@ -78,7 +78,7 @@ BANNER
78
78
  taskname = ARGV.shift
79
79
  arguments = ARGV
80
80
 
81
- taskname == "" and taskname = "help"
81
+ taskname ||= "help"
82
82
 
83
83
  unless task = @tasks[taskname.to_s]
84
84
  Zeus.ui.error "Could not find task \"#{taskname}\""
@@ -1,8 +1,13 @@
1
- require "zeus/version"
2
- require 'zeus/server'
3
- require "zeus/ui"
4
-
5
1
  module Zeus
2
+ SOCKET_NAME = '.zeus.sock'
3
+
4
+ autoload :UI, 'zeus/ui'
5
+ autoload :CLI, 'zeus/cli'
6
+ autoload :DSL, 'zeus/dsl'
7
+ autoload :Server, 'zeus/server'
8
+ autoload :Client, 'zeus/client'
9
+ autoload :Version, 'zeus/version'
10
+
6
11
  class ZeusError < StandardError
7
12
  def self.status_code(code)
8
13
  define_method(:status_code) { code }
@@ -1,8 +1,7 @@
1
- require 'zeus'
2
- require 'tiny_thor'
1
+ require 'thrud'
3
2
 
4
3
  module Zeus
5
- class CLI < TinyThor
4
+ class CLI < Thrud
6
5
 
7
6
  def initialize(*)
8
7
  super
@@ -34,7 +33,6 @@ module Zeus
34
33
  use the zeus runner commands (see `zeus help` for a list of available commands).
35
34
  D
36
35
  def start
37
- require 'zeus/server'
38
36
  begin
39
37
  require './.zeus.rb'
40
38
  rescue LoadError
@@ -60,7 +58,6 @@ module Zeus
60
58
  Zeus::Server.acceptors.each do |acc|
61
59
  desc acc.name, (acc.description || "#{acc.name} task defined in .zeus.rb")
62
60
  define_method(acc.name) { |*args|
63
- require 'zeus/client'
64
61
  Zeus::Client.run(acc.name, args)
65
62
  }
66
63
  map acc.aliases => acc.name
@@ -6,61 +6,90 @@ require "socket"
6
6
  module Zeus
7
7
  class Client
8
8
 
9
+ attr_accessor :pid
10
+
9
11
  SIGNALS = {
10
12
  "\x03" => "TERM",
11
13
  "\x1C" => "QUIT"
12
14
  }
13
15
  SIGNAL_REGEX = Regexp.union(SIGNALS.keys)
14
16
 
15
- def self.maybe_raw(&b)
16
- if $stdout.tty?
17
- $stdout.raw(&b)
18
- else
19
- b.call
20
- end
17
+ def self.run(command, args)
18
+ new.run(command, args)
21
19
  end
22
20
 
23
- def self.run(command, args)
21
+ def run(command, args)
24
22
  maybe_raw do
25
23
  PTY.open do |master, slave|
26
- $stdout.tty? and master.winsize = $stdout.winsize
27
- winch, winch_ = IO.pipe
28
- trap("WINCH") { winch_ << "\0" }
24
+ @master = master
25
+ set_winsize
29
26
 
30
- socket = UNIXSocket.new(".zeus.sock")
31
- socket << {command: command, arguments: args}.to_json << "\n"
32
- socket.send_io(slave)
33
- slave.close
34
-
35
- pid = socket.readline.chomp.to_i
27
+ @winch = make_winch_channel
28
+ pid = connect_to_server(command, args, slave)
36
29
 
30
+ buffer = ""
37
31
  begin
38
- buffer = ""
39
-
40
- while ready = select([winch, master, $stdin])[0]
41
- if ready.include?(winch)
42
- winch.read(1)
43
- $stdout.tty? and master.winsize = $stdout.winsize
44
- Process.kill("WINCH", pid)
45
- end
46
-
47
- if ready.include?($stdin)
48
- input = $stdin.readpartial(4096, buffer)
49
- input.scan(SIGNAL_REGEX).each { |signal|
50
- Process.kill(SIGNALS[signal], pid)
51
- }
52
- master << input
53
- end
54
-
55
- if ready.include?(master)
56
- $stdout << master.readpartial(4096, buffer)
57
- end
32
+ while ready = select([@winch, @master, $stdin])[0]
33
+ handle_winch if ready.include?(@winch)
34
+ handle_stdin(buffer) if ready.include?($stdin)
35
+ handle_master(buffer) if ready.include?(@master)
58
36
  end
59
37
  rescue EOFError
60
38
  end
61
39
  end
62
40
  end
63
41
  end
42
+
43
+ private
44
+
45
+ def connect_to_server(command, arguments, slave, socket_path = Zeus::SOCKET_NAME)
46
+ socket = UNIXSocket.new(socket_path)
47
+ socket << {command: command, arguments: arguments}.to_json << "\n"
48
+ socket.send_io(slave)
49
+ slave.close
50
+
51
+ pid = socket.readline.chomp.to_i
52
+ rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ECONNRESET
53
+ Zeus.ui.error "Zeus doesn't seem to be running, try 'zeus start`"
54
+ abort
55
+ end
56
+
57
+ def make_winch_channel
58
+ winch, winch_ = IO.pipe
59
+ trap("WINCH") { winch_ << "\0" }
60
+ winch
61
+ end
62
+
63
+ def handle_winch
64
+ @winch.read(1)
65
+ set_winsize
66
+ Process.kill("WINCH", pid) if pid
67
+ end
68
+
69
+ def handle_stdin(buffer)
70
+ input = $stdin.readpartial(4096, buffer)
71
+ input.scan(SIGNAL_REGEX).each { |signal|
72
+ Process.kill(SIGNALS[signal], pid)
73
+ }
74
+ @master << input
75
+ end
76
+
77
+ def handle_master(buffer)
78
+ $stdout << @master.readpartial(4096, buffer)
79
+ end
80
+
81
+ def set_winsize
82
+ $stdout.tty? and @master.winsize = $stdout.winsize
83
+ end
84
+
85
+ def maybe_raw(&b)
86
+ if $stdout.tty?
87
+ $stdout.raw(&b)
88
+ else
89
+ b.call
90
+ end
91
+ end
92
+
64
93
  end
65
94
  end
66
95
 
@@ -1,6 +1,3 @@
1
- require 'zeus/server/stage'
2
- require 'zeus/server/acceptor'
3
-
4
1
  module Zeus
5
2
  module DSL
6
3
 
@@ -2,18 +2,18 @@ require 'json'
2
2
  require 'socket'
3
3
 
4
4
  require 'rb-kqueue'
5
-
6
5
  require 'zeus/process'
7
- require 'zeus/dsl'
8
- require 'zeus/server/file_monitor'
9
- require 'zeus/server/client_handler'
10
- require 'zeus/server/process_tree_monitor'
11
- require 'zeus/server/acceptor_registration_monitor'
12
- require 'zeus/server/acceptor'
13
6
 
14
7
  module Zeus
15
8
  class Server
16
9
 
10
+ autoload :Stage, 'zeus/server/stage'
11
+ autoload :Acceptor, 'zeus/server/acceptor'
12
+ autoload :FileMonitor, 'zeus/server/file_monitor'
13
+ autoload :ClientHandler, 'zeus/server/client_handler'
14
+ autoload :ProcessTreeMonitor, 'zeus/server/process_tree_monitor'
15
+ autoload :AcceptorRegistrationMonitor, 'zeus/server/acceptor_registration_monitor'
16
+
17
17
  def self.define!(&b)
18
18
  @@definition = Zeus::DSL::Evaluator.new.instance_eval(&b)
19
19
  end
@@ -24,7 +24,7 @@ module Zeus
24
24
 
25
25
  attr_reader :client_handler, :acceptor_registration_monitor
26
26
  def initialize
27
- @file_monitor = FileMonitor.new(&method(:dependency_did_change))
27
+ @file_monitor = FileMonitor::FSEvent.new(&method(:dependency_did_change))
28
28
  @acceptor_registration_monitor = AcceptorRegistrationMonitor.new
29
29
  @process_tree_monitor = ProcessTreeMonitor.new
30
30
  @client_handler = ClientHandler.new(acceptor_registration_monitor)
@@ -39,22 +39,18 @@ module Zeus
39
39
 
40
40
  PID_TYPE = "P"
41
41
  def w_pid line
42
- begin
43
- @w_msg.send(PID_TYPE + line, 0)
44
- rescue Errno::ENOBUFS
45
- sleep 0.2
46
- retry
47
- end
42
+ @w_msg.send(PID_TYPE + line, 0)
43
+ rescue Errno::ENOBUFS
44
+ sleep 0.2
45
+ retry
48
46
  end
49
47
 
50
48
  FEATURE_TYPE = "F"
51
49
  def w_feature line
52
- begin
53
- @w_msg.send(FEATURE_TYPE + line, 0)
54
- rescue Errno::ENOBUFS
55
- sleep 0.2
56
- retry
57
- end
50
+ @w_msg.send(FEATURE_TYPE + line, 0)
51
+ rescue Errno::ENOBUFS
52
+ sleep 0.2
53
+ retry
58
54
  end
59
55
 
60
56
  def run
@@ -69,48 +65,38 @@ module Zeus
69
65
  @w_msg.close
70
66
 
71
67
  loop do
72
- @file_monitor.process_events
73
-
74
- datasources = [@r_msg,
75
- @acceptor_registration_monitor.datasource, @client_handler.datasource]
76
-
77
- # TODO: It would be really nice if we could put the queue poller in the select somehow.
78
- # --investigate kqueue. Is this possible?
79
- begin
80
- rs, _, _ = IO.select(datasources, [], [], 1)
81
- rescue Errno::EBADF
82
- puts "EBADF" unless defined?($asdf)
83
- sleep 1
84
- $asdf = true
68
+ monitors = [@file_monitor, @acceptor_registration_monitor, @client_handler]
69
+ # TODO: Make @r_msg a Monitor instead. All that logic should be its own thing.
70
+ datasources = [@r_msg, *monitors.map(&:datasource)]
71
+
72
+ ready, _, _ = IO.select(datasources, [], [], 1)
73
+ next unless ready
74
+ monitors.each do |m|
75
+ m.on_datasource_event if ready.include?(m.datasource)
85
76
  end
86
- rs.each do |r|
87
- case r
88
- when @acceptor_registration_monitor.datasource
89
- @acceptor_registration_monitor.on_datasource_event
90
- when @r_msg ; handle_messages
91
- when @client_handler.datasource
92
- @client_handler.on_datasource_event
93
- end
94
- end if rs
77
+ handle_messages if ready.include?(@r_msg)
95
78
  end
96
79
 
80
+ ensure
81
+ File.unlink(Zeus::SOCKET_NAME)
97
82
  end
98
83
 
99
84
  def handle_messages
100
85
  loop do
101
- begin
102
- data = @r_msg.recv_nonblock(1024)
103
- case data[0]
104
- when FEATURE_TYPE
105
- handle_feature_message(data[1..-1])
106
- when PID_TYPE
107
- handle_pid_message(data[1..-1])
108
- else
109
- raise "Unrecognized message"
110
- end
111
- rescue Errno::EAGAIN
112
- break
113
- end
86
+ handle_message
87
+ end
88
+ rescue Errno::EAGAIN
89
+ end
90
+
91
+ def handle_message
92
+ data = @r_msg.recv_nonblock(1024)
93
+ case data[0]
94
+ when FEATURE_TYPE
95
+ handle_feature_message(data[1..-1])
96
+ when PID_TYPE
97
+ handle_pid_message(data[1..-1])
98
+ else
99
+ raise "Unrecognized message"
114
100
  end
115
101
  end
116
102
 
@@ -36,9 +36,9 @@ module Zeus
36
36
 
37
37
  @server.w_pid "#{pid}:#{Process.ppid}"
38
38
 
39
- puts "\x1b[35m[zeus] starting acceptor `#{@name}`\x1b[0m"
39
+ Zeus.ui.as_zeus "starting acceptor `#{@name}`"
40
40
  trap("INT") {
41
- puts "\x1b[35m[zeus] killing acceptor `#{@name}`\x1b[0m"
41
+ Zeus.ui.as_zeus "killing acceptor `#{@name}`"
42
42
  exit 0
43
43
  }
44
44
 
@@ -1,8 +1,6 @@
1
1
  require 'socket'
2
2
  require 'json'
3
3
 
4
- require 'zeus/server/acceptor'
5
-
6
4
  module Zeus
7
5
  class Server
8
6
  # The model here is kind of convoluted, so here's an explanation of what's
@@ -26,23 +24,16 @@ module Zeus
26
24
  # 4. ClientHandler forwards the pid to the client over S_CLI.
27
25
  #
28
26
  class ClientHandler
29
- SERVER_SOCK = ".zeus.sock"
30
-
31
27
  def datasource ; @server ; end
32
28
  def on_datasource_event ; handle_server_connection ; end
33
29
 
34
30
  def initialize(registration_monitor)
35
31
  @reg_monitor = registration_monitor
36
- begin
37
- @server = UNIXServer.new(SERVER_SOCK)
38
- @server.listen(10)
39
- rescue Errno::EADDRINUSE
40
- Zeus.ui.error "Zeus appears to be already running in this project. If not, remove .zeus.sock and try again."
41
- exit 1
42
- # ensure
43
- # @server.close rescue nil
44
- # File.unlink(SERVER_SOCK)
45
- end
32
+ @server = UNIXServer.new(Zeus::SOCKET_NAME)
33
+ @server.listen(10)
34
+ rescue Errno::EADDRINUSE
35
+ Zeus.ui.error "Zeus appears to be already running in this project. If not, remove .zeus.sock and try again."
36
+ exit 1
46
37
  end
47
38
 
48
39
  def handle_server_connection
@@ -71,7 +62,7 @@ module Zeus
71
62
  # TODO handle nothing found
72
63
  usock.send_io(client_terminal)
73
64
 
74
- puts "accepting connection for #{command}"
65
+ Zeus.ui.info "accepting connection for #{command}"
75
66
 
76
67
  # 4
77
68
  acceptor.socket.puts arguments.to_json
@@ -2,56 +2,8 @@ require 'rb-kqueue'
2
2
 
3
3
  module Zeus
4
4
  class Server
5
- class FileMonitor
6
-
7
- TARGET_FD_LIMIT = 8192
8
-
9
- def initialize(&change_callback)
10
- configure_file_descriptor_resource_limit
11
- @queue = KQueue::Queue.new
12
- @watched_files = {}
13
- @deleted_files = []
14
- @change_callback = change_callback
15
- end
16
-
17
- def process_events
18
- @queue.poll
19
- end
20
-
21
- def watch(file)
22
- return if @watched_files[file]
23
- @watched_files[file] = true
24
- @queue.watch_file(file, :write, :extend, :rename, :delete, &method(:file_did_change))
25
- rescue Errno::ENOENT
26
- Zeus.ui.debug("No file found at #{file}")
27
- end
28
-
29
- private
30
-
31
- def file_did_change(event)
32
- Zeus.ui.info("Dependency change at #{event.watcher.path}")
33
- resubscribe_deleted_file(event) if event.flags.include?(:delete)
34
- @change_callback.call(event.watcher.path)
35
- end
36
-
37
- def configure_file_descriptor_resource_limit
38
- limit = Process.getrlimit(Process::RLIMIT_NOFILE)
39
- if limit[0] < TARGET_FD_LIMIT && limit[1] >= TARGET_FD_LIMIT
40
- Process.setrlimit(Process::RLIMIT_NOFILE, TARGET_FD_LIMIT)
41
- else
42
- puts "\x1b[33m[zeus] Warning: increase the max number of file descriptors. If you have a large project, this max cause a crash in about 10 seconds.\x1b[0m"
43
- end
44
- end
45
-
46
- def resubscribe_deleted_file(event)
47
- event.watcher.disable!
48
- begin
49
- @queue.watch_file(event.watcher.path, :write, :extend, :rename, :delete, &method(:file_did_change))
50
- rescue Errno::ENOENT
51
- @deleted_files << event.watcher.path
52
- end
53
- end
54
-
5
+ module FileMonitor
6
+ autoload :FSEvent, 'zeus/server/file_monitor/fsevent'
55
7
  end
56
8
  end
57
9
  end
@@ -0,0 +1,63 @@
1
+ require 'open3'
2
+
3
+ module Zeus
4
+ class Server
5
+ module FileMonitor
6
+ class FSEvent
7
+ WRAPPER_PATH = File.expand_path("../../../../../ext/fsevents-wrapper/fsevents-wrapper", __FILE__)
8
+
9
+ def datasource ; @io_out ; end
10
+ def on_datasource_event ; handle_changed_files ; end
11
+
12
+ def initialize(&change_callback)
13
+ @change_callback = change_callback
14
+ @io_in, @io_out, _ = Open3.popen2e(WRAPPER_PATH)
15
+ @watched_files = {}
16
+ @buffer = ""
17
+ end
18
+
19
+ def handle_changed_files
20
+ loop do
21
+ begin
22
+ read_and_notify_files
23
+ rescue Errno::EAGAIN
24
+ break
25
+ end
26
+ end
27
+ end
28
+
29
+ def read_and_notify_files
30
+ lines = @io_out.read_nonblock(30000)
31
+ files = lines.split("\n")
32
+ files[0] = "#{@buffer}#{files[0]}" unless @buffer == ""
33
+ unless lines[-1] == "\n"
34
+ @buffer = files.pop
35
+ end
36
+
37
+ files.each do |file|
38
+ file_did_change(file)
39
+ end
40
+ end
41
+
42
+ def watch(file)
43
+ return false if @watched_files[file]
44
+ @watched_files[file] = true
45
+ File.open('a.log', 'a') { |f| f.puts file }
46
+ @io_in.puts file
47
+ true
48
+ end
49
+
50
+ private
51
+
52
+ def file_did_change(file)
53
+ Zeus.ui.info("Dependency change at #{file}")
54
+ @change_callback.call(file)
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+
63
+
@@ -76,7 +76,7 @@ module Zeus
76
76
  def kill_nodes_with_feature(file, base = @root)
77
77
  if base.has_feature?(file)
78
78
  if base == @root.children[0] || base == @root
79
- puts "\x1b[31mOne of zeus's dependencies changed. Not killing zeus. You may have to restart the server.\x1b[0m"
79
+ Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
80
80
  return false
81
81
  end
82
82
  kill_node(base)
@@ -1,6 +1,7 @@
1
1
  module Zeus
2
2
  class Server
3
3
  class Stage
4
+ HasNoChildren = Class.new(Exception)
4
5
 
5
6
  attr_accessor :name, :stages, :actions
6
7
  attr_reader :pid
@@ -17,9 +18,10 @@ module Zeus
17
18
  $0 = "zeus spawner: #{@name}"
18
19
  pid = Process.pid
19
20
  @server.w_pid "#{pid}:#{Process.ppid}"
20
- puts "\x1b[35m[zeus] starting spawner `#{@name}`\x1b[0m"
21
+
22
+ Zeus.ui.as_zeus("starting spawner `#{@name}`")
21
23
  trap("INT") {
22
- puts "\x1b[35m[zeus] killing spawner `#{@name}`\x1b[0m"
24
+ Zeus.ui.as_zeus("killing spawner `#{@name}`")
23
25
  exit 0
24
26
  }
25
27
 
@@ -38,7 +40,7 @@ module Zeus
38
40
  begin
39
41
  pid = Process.wait
40
42
  rescue Errno::ECHILD
41
- raise "Stage `#{@name}` has no children. All terminal nodes must be acceptors"
43
+ raise HasNoChildren.new("Stage `#{@name}` - All terminal nodes must be acceptors")
42
44
  end
43
45
  if (status = $?.exitstatus) > 0
44
46
  exit status
@@ -19,7 +19,7 @@ Zeus::Server.define! do
19
19
  stage :dev do
20
20
  action do
21
21
  Bundler.require(:development)
22
- ENV['RAILS_ENV'] = "development"
22
+ Rails.env = ENV['RAILS_ENV'] = "development"
23
23
  require APP_PATH
24
24
  Rails.application.require_environment!
25
25
  end
@@ -57,22 +57,23 @@ Zeus::Server.define! do
57
57
  end
58
58
  end
59
59
 
60
- stage :test do
61
- action do
62
- ENV['RAILS_ENV'] = "test"
63
- Bundler.require(:test)
64
- require APP_PATH
65
- Rails.application.require_environment!
66
- end
60
+ # stage :test do
61
+ # action do
62
+ # Rails.env = ENV['RAILS_ENV'] = "test"
63
+ # Bundler.require(:test)
64
+ # require APP_PATH
65
+ # Rails.application.require_environment!
66
+ # end
67
67
 
68
- command :testrb do
69
- (r = Test::Unit::AutoRunner.new(true)).process_args(ARGV) or
70
- abort r.options.banner + " tests..."
71
- exit r.run
72
- end
68
+ # command :testrb do
69
+ # (r = Test::Unit::AutoRunner.new(true)).process_args(ARGV) or
70
+ # abort r.options.banner + " tests..."
71
+ # exit r.run
72
+ # end
73
73
 
74
- end
74
+ # end
75
75
 
76
76
  end
77
77
  end
78
78
  end
79
+
@@ -6,6 +6,10 @@ module Zeus
6
6
  @debug = ENV['DEBUG']
7
7
  end
8
8
 
9
+ def as_zeus(msg)
10
+ tell_me("[zeus] #{msg}",:purple)
11
+ end
12
+
9
13
  def info(msg)
10
14
  tell_me(msg, nil) if !@quiet
11
15
  end
@@ -45,6 +49,7 @@ module Zeus
45
49
  when :red ; "\x1b[31m#{msg}\x1b[0m"
46
50
  when :green ; "\x1b[32m#{msg}\x1b[0m"
47
51
  when :yellow ; "\x1b[33m#{msg}\x1b[0m"
52
+ when :purple ; "\x1b[35m#{msg}\x1b[0m"
48
53
  else ; msg
49
54
  end
50
55
  puts msg
@@ -1,3 +1,3 @@
1
1
  module Zeus
2
- VERSION = "0.2.0.beta1"
2
+ VERSION = "0.2.0.beta2"
3
3
  end
@@ -8,12 +8,10 @@ Gem::Specification.new do |gem|
8
8
  gem.summary = %q{Zeus is an alpha-quality application preloader with terrible documentation.}
9
9
  gem.homepage = "http://github.com/burke/zeus"
10
10
 
11
- gem.files = `git ls-files`.split($\)
11
+ gem.files = `git ls-files`.split("\n").reject{ |f| f =~ /xcodeproj/ }
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
13
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
14
  gem.name = "zeus"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Zeus::VERSION
17
-
18
- gem.add_dependency "rb-kqueue-burke", "~> 0.1.0"
19
17
  end
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.2.0.beta1
4
+ version: 0.2.0.beta2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,19 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-28 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rb-kqueue-burke
16
- requirement: &70116037263980 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: 0.1.0
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: *70116037263980
12
+ date: 2012-07-31 00:00:00.000000000 Z
13
+ dependencies: []
25
14
  description: Zeus preloads pretty much everything you'll ever want to use in development.
26
15
  email:
27
16
  - burke@libbey.me
@@ -36,18 +25,21 @@ files:
36
25
  - README.md
37
26
  - Rakefile
38
27
  - bin/zeus
39
- - lib/tiny_thor.rb
28
+ - ext/.DS_Store
29
+ - ext/fsevents-wrapper/fsevents-wrapper
30
+ - ext/fsevents-wrapper/main.m
31
+ - lib/thrud.rb
40
32
  - lib/zeus.rb
41
33
  - lib/zeus/cli.rb
42
34
  - lib/zeus/client.rb
43
35
  - lib/zeus/dsl.rb
44
- - lib/zeus/init.rb
45
36
  - lib/zeus/process.rb
46
37
  - lib/zeus/server.rb
47
38
  - lib/zeus/server/acceptor.rb
48
39
  - lib/zeus/server/acceptor_registration_monitor.rb
49
40
  - lib/zeus/server/client_handler.rb
50
41
  - lib/zeus/server/file_monitor.rb
42
+ - lib/zeus/server/file_monitor/fsevent.rb
51
43
  - lib/zeus/server/process_tree_monitor.rb
52
44
  - lib/zeus/server/stage.rb
53
45
  - lib/zeus/templates/rails.rb
@@ -1,17 +0,0 @@
1
- module Zeus
2
- module Init
3
-
4
- def self.run
5
- if looks_like_rails?
6
- copy_rails_template!
7
- else
8
- puts
9
- end
10
- end
11
-
12
- def self.looks_like_rails?
13
- File.exist?('Gemfile') && File.read('Gemfile').include?('rails')
14
- end
15
-
16
- end
17
- end