zeusd 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  Zeusd aims to provide greater control and easier scripting of the [Zeus Gem](https://github.com/burke/zeus) via daemonization.
4
4
 
5
- ## Feautres
5
+ ## Features
6
6
 
7
7
  * Run the [Zeus Gem](https://github.com/burke/zeus) as a daemon.
8
- * Optionally block execution until Zeus has finished loaded using the `--block` flag.
8
+ * Block execution until after Zeus has loaded using the `--block` flag.
9
9
  * Manage multiple zeusd daemons using the `--cwd` flag.
10
+ * Show or follow the live Status Shart using the `zeusd status` command.
10
11
 
11
12
  ## Usage
12
13
 
@@ -23,6 +24,9 @@ $ zeusd restart [--cwd=/path/to/rails/root]
23
24
 
24
25
  $ zeusd stop [--cwd=/path/to/rails/root]
25
26
  [--verbose | -v]
27
+
28
+ $ zeusd status [--cwd=/path/to/rails/root]
29
+ [--follow | -f]
26
30
  ```
27
31
 
28
32
  ### Utility Commands
data/bin/zeusd CHANGED
@@ -21,12 +21,20 @@ class ZeusdCLI < Thor
21
21
  daemon.stop!
22
22
  end
23
23
 
24
+ desc "status", "Get the current status."
25
+ method_option :follow, :type => :boolean, :aliases => :f
26
+ def status
27
+ trap("SIGINT") { exit! }
28
+ queue = daemon.status_queue
29
+ while output = queue.shift
30
+ puts output
31
+ end
32
+ end
33
+
24
34
  desc "tail", "Tail the daemon's log."
25
35
  method_option :follow, :type => :boolean, :aliases => :f
26
36
  def tail
27
- cmd = "tail#{options[:follow] ? ' -f' : ''} -n 25 #{daemon.log_file.to_path}"
28
- puts "\n\n[Zeusd] exec(#{cmd})\n\n"
29
- exec(cmd)
37
+ exec "tail#{options[:follow] ? ' -f' : ''} -n 25 #{daemon.log_file.to_path}"
30
38
  end
31
39
 
32
40
  protected
@@ -1,6 +1,18 @@
1
+ # Standard Lib
2
+ require 'thread'
3
+ require 'pathname'
4
+ require 'logger'
5
+ require 'json'
6
+
7
+ # 3rd Party Gems
8
+ require 'childprocess'
9
+ require 'file-tail'
10
+
11
+ # Components
1
12
  require 'zeusd/version'
2
13
  require 'zeusd/process'
3
- require 'zeusd/interpreter'
14
+ require 'zeusd/log'
15
+ require 'zeusd/daemon_tracker'
4
16
  require 'zeusd/daemon'
5
17
 
6
18
  module Zeusd
@@ -1,18 +1,10 @@
1
- require 'thread'
2
- require 'childprocess'
3
- require 'pathname'
4
- require 'file-tail'
5
-
6
- require 'zeusd/daemon_logging'
7
-
8
1
  module Zeusd
9
2
  class Daemon
10
- attr_reader :cwd, :verbose, :interpreter, :child_process
3
+ attr_reader :cwd, :verbose, :child_process, :status
11
4
 
12
5
  def initialize(options = {})
13
- @cwd = Pathname.new(options[:cwd] || Dir.pwd).realpath
14
- @verbose = !!options[:verbose]
15
- @interpreter = Interpreter.new
6
+ @cwd = Pathname.new(options[:cwd] || Dir.pwd).realpath
7
+ @verbose = !!options[:verbose]
16
8
  end
17
9
 
18
10
  def start!(options = {})
@@ -21,7 +13,7 @@ module Zeusd
21
13
  @process = Zeusd::Process.find(child_process.pid)
22
14
 
23
15
  if options.fetch(:block, false)
24
- sleep(0.1) until loaded?
16
+ sleep(3) until status.finished?
25
17
  end
26
18
 
27
19
  self
@@ -38,35 +30,46 @@ module Zeusd
38
30
  process.kill!(:recursive => true, :signal => "KILL", :wait => true)
39
31
 
40
32
  # Clean up socket file if stil exists
41
- (zeus_socket_file.delete rescue nil) if zeus_socket_file.exist?
33
+ (zeus_socket.delete rescue nil) if zeus_socket.exist?
42
34
 
43
35
  @process = nil
44
36
 
45
37
  self
46
38
  end
47
39
 
48
- def processes
49
- process ? [process, process.descendants].flatten : []
40
+ def status_queue
41
+ queue = Queue.new
42
+ status = Log::Status.new(File.new(zeus_log.to_path, 'r'))
43
+
44
+ queue << status.to_cli
45
+ status.on_update {|x| queue << x.to_cli }
46
+ status.record!
47
+
48
+ queue
50
49
  end
51
50
 
52
51
  def process
53
52
  @process ||= Process.all.find {|p| !!p.command[/zeus.*start$/] && p.cwd == cwd }
54
53
  end
55
54
 
56
- def loaded?
57
- interpreter.complete?
55
+ def finished?
56
+ status.finished?
58
57
  end
59
58
 
60
- def zeus_socket_file
59
+ def zeus_socket
61
60
  cwd.join('.zeus.sock')
62
61
  end
63
62
 
64
- def zeus_log_file
63
+ def zeus_log
65
64
  cwd.join('log', 'zeus.log').tap do |path|
66
65
  FileUtils.touch(path.to_path)
67
66
  end
68
67
  end
69
68
 
69
+ def verbose?
70
+ !!verbose
71
+ end
72
+
70
73
  def to_json(*args)
71
74
  {
72
75
  :class => self.class.name,
@@ -79,9 +82,9 @@ module Zeusd
79
82
  protected
80
83
 
81
84
  def start_child_process!
82
- # Truncate and cast to File instance
83
- zeus_log_file.open("w") {}
84
- std_file = File.new(zeus_log_file, 'w+')
85
+ # Truncate and cast to File instance
86
+ zeus_log.open("w") {}
87
+ std_file = File.new(zeus_log.to_path, 'w+')
85
88
  std_file.sync = true
86
89
 
87
90
  # Prep and Start child process
@@ -93,25 +96,17 @@ module Zeusd
93
96
  @child_process.detach = true
94
97
  @child_process.start
95
98
 
96
- # Start tailing child process output
97
- Thread.new do
98
- File.open(std_file.to_path) do |log|
99
- log.extend(File::Tail)
100
- log.interval = 0.1
101
- log.backward(100)
102
- log.tail do |line|
103
- interpreter.translate(line)
104
- puts line if verbose
105
- end
106
- end
99
+ @status = Log::Status.new(std_file)
100
+ @status.on_update do |status, line|
101
+ puts status.to_cli if verbose?
107
102
  end
103
+ @status.record!
108
104
 
109
- # Block until the first zeus command has been registered
110
- sleep 0.1 until interpreter.commands.any?
105
+ sleep 0.1 until @status.started?
111
106
 
112
107
  @child_process
113
108
  end
114
109
 
115
- include Zeusd::DaemonLogging
110
+ include Zeusd::DaemonTracker
116
111
  end
117
112
  end
@@ -1,8 +1,5 @@
1
- require 'logger'
2
- require 'json'
3
-
4
1
  module Zeusd
5
- module DaemonLogging
2
+ module DaemonTracker
6
3
 
7
4
  def log_file
8
5
  cwd.join('log', 'zeusd.log')
@@ -0,0 +1,28 @@
1
+ require 'zeusd/log/last_line_array'
2
+ require 'zeusd/log/line'
3
+ require 'zeusd/log/status'
4
+ require 'zeusd/log/tailer'
5
+
6
+ module Zeusd
7
+ module Log
8
+
9
+ COLOR_TO_ANSI = {
10
+ :red => "\e[31m",
11
+ :green => "\e[32m",
12
+ :yellow => "\e[33m",
13
+ :blue => "\e[34m",
14
+ :magenta => "\e[35m"
15
+ }
16
+
17
+ STATUS_TO_COLOR = {
18
+ :ready => :green,
19
+ :crashed => :red,
20
+ :waiting => :yellow,
21
+ :running => :blue,
22
+ :connecting => :magenta
23
+ }
24
+
25
+ STATUS_TO_ANSI = Hash[STATUS_TO_COLOR.map{|status, color| [status, COLOR_TO_ANSI[color]]}]
26
+
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ require 'forwardable'
2
+ module Zeusd
3
+ class LastLineArray
4
+ extend Forwardable
5
+
6
+ def_delegators *[:to_a].concat(Array.instance_methods.select{|x| x[/^[a-z]+.*?[^!]{1}$/]} - instance_methods(true))
7
+
8
+ def initialize(*args)
9
+ @hash = Hash[args.zip(args)]
10
+ end
11
+
12
+ def <<(line)
13
+ @hash[line.id] = line
14
+ end
15
+
16
+ def to_a
17
+ @hash.values
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'zeusd/log/line/base'
4
+ require 'zeusd/log/line/command'
5
+ require 'zeusd/log/line/process'
6
+ require 'zeusd/log/line/update'
7
+ require 'zeusd/log/line/error'
8
+
9
+ module Zeusd
10
+ module Log
11
+ module Line
12
+
13
+ class << self
14
+
15
+ def create(line)
16
+ [Command, Process, Update, Error, Base].each do |klass|
17
+ return klass.new(line) if klass.matches_line?(line)
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+ module Zeusd
3
+ module Log
4
+ module Line
5
+
6
+ class Base < String
7
+
8
+ def self.matches_line?(line)
9
+ true
10
+ end
11
+
12
+ def status_substring
13
+ ansi_color
14
+ end
15
+
16
+ def id
17
+ self
18
+ end
19
+
20
+ def status?(status)
21
+ ansi_color_of(status_substring) == Log::STATUS_TO_ANSI[status]
22
+ end
23
+
24
+ def ready?
25
+ status? :ready
26
+ end
27
+
28
+ def crashed?
29
+ status? :crashed
30
+ end
31
+
32
+ def waiting?
33
+ status? :waiting
34
+ end
35
+
36
+ def running?
37
+ status? :running
38
+ end
39
+
40
+ def connecting?
41
+ status? :connecting
42
+ end
43
+
44
+ def loading?
45
+ [:waiting, :connecting, :running].any? {|x| status? x}
46
+ end
47
+
48
+ def done?
49
+ [:crashed, :ready].any? {|x| status? x}
50
+ end
51
+
52
+ def ansi_color_of(substring)
53
+ if stop_point = index(substring) + (substring.length - 1)
54
+ if color_start = rindex(/\e/, stop_point)
55
+ color_end = index('m', color_start)
56
+ self[color_start..color_end]
57
+ end
58
+ end
59
+ end
60
+
61
+ def ansi_color
62
+ if self[0] == "\e" && !self.index('m').nil?
63
+ self[0..self.index('m')]
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module Zeusd
3
+ module Log
4
+ module Line
5
+
6
+ class Command < Base
7
+ def self.matches_line?(line)
8
+ line[/^\e.*?zeus\s(.*?)(\s|\e)/]
9
+ end
10
+
11
+ def id
12
+ name
13
+ end
14
+
15
+ def name
16
+ self[/^\e.*?zeus\s(.*?)(\s|\e)/, 1]
17
+ end
18
+
19
+ def status_substring
20
+ name
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module Zeusd
3
+ module Log
4
+ module Line
5
+
6
+ class Error < Base
7
+
8
+ def self.matches_line?(line)
9
+ !!line[0..5]["\e[31m"]
10
+ end
11
+
12
+ def id
13
+ message
14
+ end
15
+
16
+ alias_method :message, :to_s
17
+
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module Zeusd
3
+ module Log
4
+ module Line
5
+
6
+ class Process < Base
7
+ def self.matches_line?(line)
8
+ !!line[/(boot|─)/]
9
+ end
10
+
11
+ def id
12
+ name
13
+ end
14
+
15
+ def name
16
+ self[/(\e\[[0-9]{1,2}m)([a-z_]+)/, 2]
17
+ end
18
+
19
+ def status_substring
20
+ name
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Zeusd
3
+ module Log
4
+ module Line
5
+
6
+ class Update < Base
7
+
8
+ attr_reader :time
9
+
10
+ def self.matches_line?(line)
11
+ !!line[/\s=====$/]
12
+ end
13
+
14
+ def time
15
+ @time ||= Time.parse(self[/UPDATED\s(.*?)\s=/, 1])
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,99 @@
1
+ module Zeusd
2
+ module Log
3
+
4
+ class Status
5
+
6
+ attr_reader :file, :tailer, :updated_at, :line_print_count
7
+ attr_reader :errors
8
+ attr_reader :commands
9
+ attr_reader :processes
10
+
11
+ def initialize(file)
12
+ @line_print_count = 0
13
+ @file = File.new(file)
14
+ @updated_at = Time.now
15
+ @errors = LastLineArray.new
16
+ @commands = LastLineArray.new
17
+ @processes = LastLineArray.new
18
+ @tailer = Tailer.new(file)
19
+
20
+ file.each {|line| process(line)} unless file.size.zero?
21
+ end
22
+
23
+ def record!
24
+ tailer.on_update {|line| process(line)}
25
+ tailer.start!
26
+ self
27
+ end
28
+
29
+ def pause!
30
+ tailer.stop!
31
+ self
32
+ end
33
+
34
+ def paused?
35
+ !tailer.following?
36
+ end
37
+
38
+ def recording?
39
+ tailer.following?
40
+ end
41
+
42
+ def process(line)
43
+ line = Line.create(line)
44
+ case line
45
+ when Line::Command then @commands << line
46
+ when Line::Process then @processes << line
47
+ when Line::Error then @errors << line
48
+ end
49
+ @updated_at = Time.now
50
+ on_update.call(self, line) if on_update
51
+ end
52
+
53
+ def on_update(&block)
54
+ @on_update = block if block_given?
55
+ @on_update
56
+ end
57
+
58
+ def started?
59
+ commands.any?
60
+ end
61
+
62
+ def finished?
63
+ return true if errors.any?
64
+ return true if [commands, processes].map(&:to_a).flatten.all?(&:done?)
65
+ false
66
+ end
67
+
68
+ def empty?
69
+ [errors, commands, processes].all?(&:empty?)
70
+ end
71
+
72
+ def to_cli
73
+ output = [
74
+ "\e[36mZeusd Status\e[0m - Updated: #{updated_at.to_s}\e[K\e[0m\n",
75
+ "\e[K\e[0m\n",
76
+ "Legend: \e[32m[ready] \e[31m[crashed] \e[34m[running] \e[35m[connecting] \e[33m[waiting]\e[K\e[0m\n",
77
+ "\e[K\e[0m\n",
78
+ "\e[4mProcess Tree\e[K\e[0m\n",
79
+ [processes.map(&:to_s)],
80
+ "\e[K\e[0m\n",
81
+ "\e[4mCommands\e[K\e[0m\n",
82
+ [commands.map(&:to_s)],
83
+ ].tap do |x|
84
+ if errors.any?
85
+ x << "\e[K"
86
+ x << "\e[4mErrors\e[K\e[0m\n"
87
+ x << errors.map(&:to_s)
88
+ end
89
+ end.flatten
90
+ if line_print_count > 0
91
+ output = output.unshift("\e[#{line_print_count}A\e[K\e[0m\n")
92
+ end
93
+ @line_print_count = output.length
94
+ output
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,49 @@
1
+ module Zeusd
2
+ module Log
3
+ class Tailer
4
+ attr_reader :file, :lines
5
+
6
+ def initialize(file)
7
+ @file = file
8
+ @lines = []
9
+ end
10
+
11
+ def on_update(&block)
12
+ @on_update = block if block_given?
13
+ @on_update
14
+ end
15
+
16
+ def following?
17
+ !!@thread
18
+ end
19
+
20
+ def restart!
21
+ stop!.start!
22
+ end
23
+
24
+ def start!
25
+ @thread = Thread.new do
26
+ File.open(file) do |log|
27
+ log.extend(File::Tail)
28
+ log.interval = 0.1
29
+ log.backward(0)
30
+ log.tail do |line|
31
+ @lines << line
32
+ on_update.call(line) if on_update
33
+ end
34
+ end
35
+ end
36
+ self
37
+ end
38
+
39
+ def stop!
40
+ if @thread
41
+ @thread.terminate
42
+ @thread = nil
43
+ end
44
+ self
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Zeusd
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,10 +1,14 @@
1
+ # encoding: utf-8
1
2
  require 'simplecov'
2
3
  SimpleCov.start
3
4
 
4
- require 'zeusd'
5
+ require 'tempfile'
5
6
 
6
- DUMMY_APP_PATH = File.expand_path('../dummy', __FILE__)
7
+ require File.expand_path('../support/helpers.rb', __FILE__)
8
+ require File.expand_path('../support/constants.rb', __FILE__)
7
9
 
8
- RSpec.configure do |config|
10
+ require 'zeusd'
9
11
 
12
+ RSpec.configure do |c|
13
+ c.include Helpers
10
14
  end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ DUMMY_APP_PATH = File.expand_path('../../dummy', __FILE__)
4
+
5
+ DUMMY_ZEUS_LOG_PATH = File.expand_path('../dummy_zeus.log', __FILE__)
6
+
7
+ ZEUS_LOG_LINES = {
8
+ :commands => [
9
+ "\e[32mzeus destroy (alias: d)\e[K\e[0m\n",
10
+ "\e[32mzeus server (alias: s)\e[K\e[0m\n",
11
+ "\e[32mzeus console (alias: c)\e[K\e[0m\n",
12
+ "\e[32mzeus dbconsole\e[K\e[0m\n",
13
+ "\e[32mzeus runner (alias: r)\e[K\e[0m\n",
14
+ "\e[32mzeus generate (alias: g)\e[K\e[0m\n",
15
+ "\e[32mzeus rake\e[K\e[0m\n",
16
+ "\e[32mzeus test (alias: rspec, testrb)\e[K\e[0m\n"
17
+ ],
18
+ :processes => [
19
+ "\e[32mboot\e[K\e[0m\n",
20
+ "\e[33m└── \e[32mdefault_bundle\e[K\e[0m\n",
21
+ "\e[33m \e[33m├── \e[32mdevelopment_environment\e[K\e[0m\n",
22
+ "\e[33m \e[33m│  \e[33m└── \e[32mprerake\e[K\e[0m\n",
23
+ "\e[33m \e[33m└── \e[32mtest_environment\e[K\e[0m\n",
24
+ "\e[33m \e[33m \e[33m└── \e[32mtest_helper\e[K\e[0m\n"
25
+ ],
26
+ :all => [
27
+ "\e[4m\e[32m[ready] \e[31m[crashed] \e[34m[running] \e[35m[connecting] \e[33m[waiting]\e[K\e[0m\n",
28
+ "\e[32mboot\e[K\e[0m\n",
29
+ "\e[33m└── \e[32mdefault_bundle\e[K\e[0m\n",
30
+ "\e[33m \e[33m├── \e[32mdevelopment_environment\e[K\e[0m\n",
31
+ "\e[33m \e[33m│  \e[33m└── \e[32mprerake\e[K\e[0m\n",
32
+ "\e[33m \e[33m└── \e[32mtest_environment\e[K\e[0m\n",
33
+ "\e[33m \e[33m \e[33m└── \e[32mtest_helper\e[K\e[0m\n",
34
+ "\e[K\n",
35
+ "\e[4mAvailable Commands: \e[33m[waiting] \e[31m[crashed] \e[32m[ready]\e[K\e[0m\n",
36
+ "\e[32mzeus destroy (alias: d)\e[K\e[0m\n",
37
+ "\e[32mzeus server (alias: s)\e[K\e[0m\n",
38
+ "\e[32mzeus console (alias: c)\e[K\e[0m\n",
39
+ "\e[32mzeus dbconsole\e[K\e[0m\n",
40
+ "\e[32mzeus runner (alias: r)\e[K\e[0m\n",
41
+ "\e[32mzeus generate (alias: g)\e[K\e[0m\n",
42
+ "\e[32mzeus rake\e[K\e[0m\n",
43
+ "\e[32mzeus test (alias: rspec, testrb)\e[K\e[0m\n"
44
+ ]
45
+ }
@@ -0,0 +1,12 @@
1
+ module Helpers
2
+
3
+ def file_write(file, data, options = {})
4
+ file.write(data)
5
+ file.flush
6
+ file.fsync
7
+ sleep(0.1)
8
+ file.rewind if options.fetch(:rewind, false)
9
+ file
10
+ end
11
+
12
+ end
@@ -5,27 +5,27 @@ describe Zeusd::Daemon do
5
5
  after(:each) { daemon.stop! }
6
6
 
7
7
  describe ".start!" do
8
- subject { daemon.start!(:verbose => true) }
8
+ subject { daemon.start! }
9
9
  it { should be daemon }
10
- it { should_not be_loaded }
10
+ it { should_not be_finished }
11
11
 
12
12
  describe ":block option" do
13
13
  subject { daemon.start!(:block => true) }
14
- it { should be_loaded }
14
+ it { should be_finished }
15
15
  end
16
16
  end
17
17
 
18
18
  describe ".stop!" do
19
19
  subject { daemon.start!.stop! }
20
20
  it { should be daemon }
21
- it { should_not be_loaded }
21
+ it { should_not be_finished }
22
22
  end
23
23
 
24
24
  describe ".restart!" do
25
25
  subject { daemon }
26
26
  context "when daemon is already running" do
27
27
  before { subject.start!.restart!(:block => true) }
28
- it { should be_loaded }
28
+ it { should be_finished }
29
29
  end
30
30
  end
31
31
 
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+
7
+ describe Zeusd::Log::Line::Base do
8
+
9
+ class ExampleLine < Zeusd::Log::Line::Base
10
+ def status_substring
11
+ "example"
12
+ end
13
+ end
14
+
15
+ RED = "\e[31m"
16
+ GREEN = "\e[32m"
17
+ YELLOW = "\e[33m"
18
+
19
+ let(:line) { Zeusd::Log::Line::Base.new("#{RED}This line starts with red but then#{GREEN}changes to green after a bit") }
20
+
21
+
22
+ describe ".ansi_color" do
23
+ subject { line.ansi_color }
24
+ it { should eq RED }
25
+ end
26
+
27
+
28
+ describe ".ansi_color_of" do
29
+ context "far from setting of color" do
30
+ subject { line.ansi_color_of("bit") }
31
+ it { should eq GREEN }
32
+ end
33
+
34
+ context "near setting of color" do
35
+ subject { line.ansi_color_of("This") }
36
+ it { should eq RED }
37
+ end
38
+
39
+ context "color itself" do
40
+ subject { line.ansi_color_of(GREEN) }
41
+ it { should eq GREEN }
42
+ end
43
+ end
44
+
45
+ describe ".status_substring" do
46
+ subject { line }
47
+ it { should be_crashed }
48
+ end
49
+
50
+ describe "checking status of substring" do
51
+ Zeusd::Log::STATUS_TO_ANSI.each do |status, color|
52
+ context "when in a #{status} state" do
53
+ subject { ExampleLine.new "#{color}zeus example\e[K\e[0m" }
54
+ it { should send("be_#{status}") }
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Line::Command do
7
+
8
+ NAME = "dbconsole"
9
+ let(:crashed) { Zeusd::Log::Line::Command.new "\e[31mzeus #{NAME}\e[K\e[0m" }
10
+ let(:ready) { Zeusd::Log::Line::Command.new "\e[32mzeus #{NAME}\e[K\e[0m" }
11
+ let(:waiting) { Zeusd::Log::Line::Command.new "\e[33mzeus #{NAME}\e[K\e[0m" }
12
+
13
+ describe ".name" do
14
+ subject { ready.name }
15
+ it { should eq NAME }
16
+ end
17
+
18
+ describe "checking status of command" do
19
+ context ".ready?" do
20
+ subject { ready }
21
+ it { should be_ready }
22
+ end
23
+ context ".waiting?" do
24
+ subject { waiting }
25
+ it { should be_waiting }
26
+ end
27
+ context ".crashed?" do
28
+ subject { crashed }
29
+ it { should be_crashed }
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Line::Error do
7
+
8
+ ERROR_STRING = "\e[31This would be a typical \e[33mZeus\e[31 error to see.\e[0m"
9
+
10
+ let(:error) { Zeusd::Log::Line::Error.new ERROR_STRING }
11
+ describe ".message" do
12
+ subject { error.message }
13
+ it { should eq ERROR_STRING }
14
+ end
15
+
16
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Line::Process do
7
+
8
+ let(:crashed) { Zeusd::Log::Line::Process.new "\e[31mboot\e[K\e[0m" }
9
+ let(:ready) { Zeusd::Log::Line::Process.new "\e[32m└── \e[32mdefault_bundle\e[K\e[0m" }
10
+ let(:waiting) { Zeusd::Log::Line::Process.new "\e[33m \e[33m├── \e[33mdevelopment_environment\e[K\e[0m" }
11
+ let(:running) { Zeusd::Log::Line::Process.new "\e[34m \e[34m└── \e[34mtest_environment\e[K\e[0m" }
12
+ let(:connecting) { Zeusd::Log::Line::Process.new "\e[35m \e[35m \e[35m└── \e[35mtest_helper\e[K\e[0m" }
13
+
14
+ describe ".name" do
15
+ context "when deeply nested" do
16
+ subject { waiting.name }
17
+ it { should eq "development_environment" }
18
+ end
19
+ context "when boot" do
20
+ subject { crashed.name }
21
+ it { should eq "boot" }
22
+ end
23
+ end
24
+
25
+ describe "checking status of command" do
26
+ context ".ready?" do
27
+ subject { ready }
28
+ it { should be_ready }
29
+ end
30
+ context ".waiting?" do
31
+ subject { waiting }
32
+ it { should be_waiting }
33
+ end
34
+ context ".crashed?" do
35
+ subject { crashed }
36
+ it { should be_crashed }
37
+ end
38
+ context ".running?" do
39
+ subject { running }
40
+ it { should be_running }
41
+ end
42
+ context ".connecting?" do
43
+ subject { connecting }
44
+ it { should be_connecting }
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Line::Update do
7
+
8
+ let(:update) { Zeusd::Log::Line::Update.new "==== UPDATED Sat Mar 1 00:07:24 EST 2014 =====" }
9
+
10
+ describe ".time" do
11
+ subject { update.time }
12
+ it { should eq Time.parse("1/3/2014 00:07:24 EST") }
13
+ end
14
+
15
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Line do
7
+
8
+ describe "determining line type" do
9
+
10
+ context "update" do
11
+ subject { Zeusd::Log::Line.create("==== UPDATED Sat Mar 1 00:07:24 EST 2014 =====") }
12
+ it { should be_a Zeusd::Log::Line::Update }
13
+ end
14
+
15
+ context "error" do
16
+ subject { Zeusd::Log::Line.create("\e[31mThis would be a typical zeus error") }
17
+ it { should be_a Zeusd::Log::Line::Error }
18
+ end
19
+
20
+ context "process" do
21
+ subject { Zeusd::Log::Line.create("\e[33m \e[33m└── \e[33mtest_environment\e[K\e[0m") }
22
+ it { should be_a Zeusd::Log::Line::Process }
23
+ end
24
+
25
+ context "command" do
26
+ subject { Zeusd::Log::Line.create("\e[33mzeus generate (alias: g)\e[K\e[0m") }
27
+ it { should be_a Zeusd::Log::Line::Command }
28
+ end
29
+
30
+ context "unknown" do
31
+ subject { Zeusd::Log::Line.create("!#!@$$@#%$#%%!$43154}") }
32
+ it { should be_a Zeusd::Log::Line::Base }
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Status do
7
+
8
+ let(:log_file) { Tempfile.new("Zeusd_Log_Status").tap {|f| f.sync = true} }
9
+ let(:status) { Zeusd::Log::Status.new(log_file) }
10
+
11
+ describe "after initialization" do
12
+ subject { status }
13
+ it { should be_paused }
14
+ its(:tailer) { should be_a Zeusd::Log::Tailer }
15
+ its(:errors) { should be_empty }
16
+
17
+ context "when the log file is empty" do
18
+ its(:commands) { should be_empty }
19
+ its(:processes) { should be_empty }
20
+ end
21
+
22
+ context "when log file has data" do
23
+ before(:each) { file_write(log_file, ZEUS_LOG_LINES[:all].join, :rewind => true) }
24
+ its(:commands) { should_not be_empty }
25
+ its(:processes) { should_not be_empty }
26
+ end
27
+ end
28
+
29
+ describe ".process" do
30
+ subject do
31
+ lines.each {|line| status.process(line)}
32
+ status
33
+ end
34
+
35
+ context "command" do
36
+ let(:lines) { ZEUS_LOG_LINES[:commands] }
37
+ its(:commands) { should have(lines.length).items }
38
+ end
39
+
40
+ context "process" do
41
+ let(:lines) { ZEUS_LOG_LINES[:processes] }
42
+ its(:processes) { have(lines.length).items }
43
+ end
44
+ end
45
+
46
+ describe "recording" do
47
+ subject { status.record! }
48
+ it { should be status }
49
+ it { should be_recording }
50
+ end
51
+
52
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'zeusd'
5
+
6
+ describe Zeusd::Log::Tailer do
7
+
8
+ let(:log_file) { Tempfile.new("Zeusd_Log_Tailer") }
9
+ let(:tailer) { Zeusd::Log::Tailer.new(log_file) }
10
+
11
+ describe "after initialization" do
12
+ subject { tailer }
13
+ it { should_not be_following }
14
+ it { should respond_to :start! }
15
+ it { should respond_to :stop! }
16
+ it { should respond_to :restart! }
17
+ it { should respond_to :following? }
18
+ it { should respond_to :lines }
19
+ it { should respond_to :file }
20
+ end
21
+
22
+ end
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.add_runtime_dependency "thor" , "~> 0.18.1"
22
22
  spec.add_runtime_dependency "childprocess" , ">= 0.5.1"
23
23
  spec.add_runtime_dependency "file-tail" , "~> 1.0.12"
24
- spec.add_runtime_dependency "hooks" , "~> 0.3.3"
25
24
  spec.add_runtime_dependency "zeus" , "~> 0.13.3"
26
25
 
27
26
  spec.add_development_dependency "bundler", "~> 1.3"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zeusd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
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: 2014-03-01 00:00:00.000000000 Z
12
+ date: 2014-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -59,22 +59,6 @@ dependencies:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: 1.0.12
62
- - !ruby/object:Gem::Dependency
63
- name: hooks
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 0.3.3
70
- type: :runtime
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 0.3.3
78
62
  - !ruby/object:Gem::Dependency
79
63
  name: zeus
80
64
  requirement: !ruby/object:Gem::Requirement
@@ -158,8 +142,17 @@ files:
158
142
  - bin/zeusd
159
143
  - lib/zeusd.rb
160
144
  - lib/zeusd/daemon.rb
161
- - lib/zeusd/daemon_logging.rb
162
- - lib/zeusd/interpreter.rb
145
+ - lib/zeusd/daemon_tracker.rb
146
+ - lib/zeusd/log.rb
147
+ - lib/zeusd/log/last_line_array.rb
148
+ - lib/zeusd/log/line.rb
149
+ - lib/zeusd/log/line/base.rb
150
+ - lib/zeusd/log/line/command.rb
151
+ - lib/zeusd/log/line/error.rb
152
+ - lib/zeusd/log/line/process.rb
153
+ - lib/zeusd/log/line/update.rb
154
+ - lib/zeusd/log/status.rb
155
+ - lib/zeusd/log/tailer.rb
163
156
  - lib/zeusd/process.rb
164
157
  - lib/zeusd/version.rb
165
158
  - spec/dummy/.gitignore
@@ -213,8 +206,18 @@ files:
213
206
  - spec/dummy/vendor/plugins/.gitkeep
214
207
  - spec/dummy/zeus.json
215
208
  - spec/spec_helper.rb
209
+ - spec/support/constants.rb
216
210
  - spec/support/dummy_process.rb
211
+ - spec/support/helpers.rb
217
212
  - spec/zeusd/daemon_spec.rb
213
+ - spec/zeusd/log/line/base_spec.rb
214
+ - spec/zeusd/log/line/command_spec.rb
215
+ - spec/zeusd/log/line/error_spec.rb
216
+ - spec/zeusd/log/line/process_spec.rb
217
+ - spec/zeusd/log/line/update_spec.rb
218
+ - spec/zeusd/log/line_spec.rb
219
+ - spec/zeusd/log/status_spec.rb
220
+ - spec/zeusd/log/tailer_spec.rb
218
221
  - spec/zeusd/process_spec.rb
219
222
  - zeusd.gemspec
220
223
  homepage: https://github.com/veloper/zeusd
@@ -232,7 +235,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
232
235
  version: '0'
233
236
  segments:
234
237
  - 0
235
- hash: -1735692127611811224
238
+ hash: -4218968737058463992
236
239
  required_rubygems_version: !ruby/object:Gem::Requirement
237
240
  none: false
238
241
  requirements:
@@ -241,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
244
  version: '0'
242
245
  segments:
243
246
  - 0
244
- hash: -1735692127611811224
247
+ hash: -4218968737058463992
245
248
  requirements: []
246
249
  rubyforge_project:
247
250
  rubygems_version: 1.8.25
@@ -300,6 +303,16 @@ test_files:
300
303
  - spec/dummy/vendor/plugins/.gitkeep
301
304
  - spec/dummy/zeus.json
302
305
  - spec/spec_helper.rb
306
+ - spec/support/constants.rb
303
307
  - spec/support/dummy_process.rb
308
+ - spec/support/helpers.rb
304
309
  - spec/zeusd/daemon_spec.rb
310
+ - spec/zeusd/log/line/base_spec.rb
311
+ - spec/zeusd/log/line/command_spec.rb
312
+ - spec/zeusd/log/line/error_spec.rb
313
+ - spec/zeusd/log/line/process_spec.rb
314
+ - spec/zeusd/log/line/update_spec.rb
315
+ - spec/zeusd/log/line_spec.rb
316
+ - spec/zeusd/log/status_spec.rb
317
+ - spec/zeusd/log/tailer_spec.rb
305
318
  - spec/zeusd/process_spec.rb
@@ -1,93 +0,0 @@
1
- module Zeusd
2
- class Interpreter
3
- STATES = %w[ready crashed running connecting waiting]
4
-
5
- attr_reader :lines
6
- attr_reader :state_colors, :commands, :errors
7
- attr_reader :last_status
8
-
9
- def initialize(*args)
10
- @state_colors = Hash[STATES.zip([nil]*STATES.length)]
11
- @commands = {}
12
- @errors = []
13
- @lines = []
14
- end
15
-
16
- def translate(zeus_output)
17
- zeus_output.split("\n").map{|x| Line.new(x) }.each do |line|
18
- # State Colors
19
- if @state_colors.values.any?(&:nil?) && line.state_legend?
20
- STATES.each do |state|
21
- state_colors[state] = line.color_of(state)
22
- end
23
- end
24
-
25
- # Errors
26
- @errors << line if line.color == state_colors["crashed"]
27
-
28
- # Commands
29
- @commands[line.command[:name]] = state_colors.invert[line.command[:color]] if line.command?
30
-
31
- # Add Line
32
- @lines << line
33
- end
34
-
35
- # Garbage Collection
36
- if @lines.length > 100
37
- @lines = @lines.last(100)
38
- end
39
- end
40
-
41
- def complete?
42
- return false if errors.any?
43
- return true if commands.all? {|command, status| %[crashed ready].include?(status)}
44
- false
45
- end
46
-
47
- def last_update
48
- @lines[@lines.rindex(&:update?)..-1].join("\n").to_s
49
- end
50
-
51
- class Line < String
52
-
53
- def update?
54
- self =~ /\=\=\=\=$/
55
- end
56
-
57
- def command?
58
- !!command
59
- end
60
-
61
- def command
62
- if @match ||= self.match(/^(\e.*?)zeus\s(.*?)(\s|\e)/)
63
- { :name => @match[2], :color => @match[1] }
64
- end
65
- end
66
-
67
- def state_legend?
68
- STATES.all?{|state| !!self[state]}
69
- end
70
-
71
- def color_of(substring)
72
- if stop_point = index(substring) + substring.length
73
- if color_start = rindex(/\e/, stop_point)
74
- color_end = index('m', color_start)
75
- self[color_start..color_end]
76
- end
77
- end
78
- end
79
-
80
- def color
81
- if self[0] == "\e" && !self.index('m').nil?
82
- self[0..self.index('m')]
83
- end
84
- end
85
-
86
- def color?
87
- !!color
88
- end
89
-
90
- end
91
-
92
- end
93
- end