zeusd 0.2.5 → 0.3.0

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/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