zeusd 0.0.1 → 0.1.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/.gitignore +2 -0
- data/Gemfile +2 -0
- data/Guardfile +1 -2
- data/README.md +27 -23
- data/Rakefile +1 -2
- data/bin/zeusd +13 -15
- data/lib/zeusd.rb +1 -1
- data/lib/zeusd/daemon.rb +80 -52
- data/lib/zeusd/{state_interpreter.rb → interpreter.rb} +18 -12
- data/lib/zeusd/process.rb +36 -25
- data/lib/zeusd/version.rb +1 -1
- data/spec/spec_helper.rb +3 -1
- data/spec/support/dummy_process.rb +10 -0
- data/spec/zeusd/daemon_spec.rb +37 -9
- data/spec/zeusd/process_spec.rb +76 -9
- data/zeusd.gemspec +1 -0
- metadata +23 -5
    
        data/.gitignore
    CHANGED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/Guardfile
    CHANGED
    
    | @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            guard :rspec, :failed_mode => :none do
         | 
| 2 2 | 
             
              watch(%r{^spec/.+_spec\.rb$})
         | 
| 3 | 
            -
              watch(%r{^lib/(.+)\.rb$})     { |m| "spec/ | 
| 4 | 
            -
              watch(%r{^lib/zeusd/(.+)\.rb$})     { |m| "spec/lib/zeusd/#{m[1]}_spec.rb" }
         | 
| 3 | 
            +
              watch(%r{^lib/zeusd/(.+)\.rb$})     { |m| "spec/zeusd/#{m[1]}_spec.rb" }
         | 
| 5 4 | 
             
              watch('spec/spec_helper.rb')  { "spec" }
         | 
| 6 5 | 
             
            end
         | 
| 7 6 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -1,48 +1,52 @@ | |
| 1 | 
            -
            # Zeusd
         | 
| 1 | 
            +
            # Zeusd [](http://badge.fury.io/rb/zeusd) [](https://travis-ci.org/veloper/zeusd) [](https://codeclimate.com/github/veloper/zeusd)
         | 
| 2 2 |  | 
| 3 | 
            -
            Run  | 
| 3 | 
            +
            *Run Zeus as a daemon.*
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 5 | 
            +
            ## Introduction
         | 
| 6 6 |  | 
| 7 | 
            +
            Zeus**d** lets you work with the [Zeus Gem](https://github.com/burke/zeus) like it's a daemon -- allowing greater control and easier scripting
         | 
| 7 8 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            Add this line to your application's Gemfile:
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                gem 'zeusd'
         | 
| 9 | 
            +
            ### Primary Commands
         | 
| 13 10 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
            Or install it yourself as:
         | 
| 11 | 
            +
            ```
         | 
| 12 | 
            +
            $ zeusd start   [--cwd=/path/to/rails/root]
         | 
| 13 | 
            +
                            [--block | -b]
         | 
| 14 | 
            +
                            [--verbose | -v]
         | 
| 19 15 |  | 
| 20 | 
            -
             | 
| 16 | 
            +
            $ zeusd restart [--cwd=/path/to/rails/root]
         | 
| 17 | 
            +
                            [--block | -b]
         | 
| 18 | 
            +
                            [--verbose | -v]
         | 
| 21 19 |  | 
| 22 | 
            -
             | 
| 20 | 
            +
            $ zeusd stop    [--cwd=/path/to/rails/root]
         | 
| 21 | 
            +
                            [--verbose | -v]
         | 
| 22 | 
            +
            ```
         | 
| 23 23 |  | 
| 24 | 
            -
            ### Commands
         | 
| 24 | 
            +
            ### Utility Commands
         | 
| 25 25 |  | 
| 26 26 | 
             
            ```
         | 
| 27 | 
            -
            zeusd  | 
| 28 | 
            -
             | 
| 29 | 
            -
            zeusd restart
         | 
| 27 | 
            +
            $ zeusd tail    [--cwd=/path/to/rails/root]
         | 
| 28 | 
            +
                            [--follow | -f]
         | 
| 30 29 | 
             
            ```
         | 
| 31 30 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
            * `--cwd=current/work/directory/of/rails/app` or alias `-d`
         | 
| 34 | 
            -
            * `--verbose` or `-v`
         | 
| 31 | 
            +
            ## Installation
         | 
| 35 32 |  | 
| 33 | 
            +
            ```
         | 
| 34 | 
            +
            $ gem install zeusd
         | 
| 35 | 
            +
            ```
         | 
| 36 36 |  | 
| 37 37 | 
             
            ## Contributing
         | 
| 38 38 |  | 
| 39 39 | 
             
            1. Fork it
         | 
| 40 40 | 
             
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 41 | 
            -
            3. Write  | 
| 41 | 
            +
            3. Write passing specs that test your changes
         | 
| 42 42 | 
             
            3. Commit your changes and specs (`git commit -am 'Add some feature'`)
         | 
| 43 43 | 
             
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 44 44 | 
             
            5. Create new Pull Request
         | 
| 45 45 |  | 
| 46 | 
            +
            ## Author
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            [Daniel Doezema](http://dan.doezema.com)
         | 
| 49 | 
            +
             | 
| 46 50 | 
             
            ## License
         | 
| 47 51 |  | 
| 48 52 | 
             
            * Zeusd is released under the New BSD license. http://dan.doezema.com/licenses/new-bsd/
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/bin/zeusd
    CHANGED
    
    | @@ -4,37 +4,35 @@ require "zeusd" | |
| 4 4 | 
             
            class ZeusdCLI < Thor
         | 
| 5 5 | 
             
              class_option :verbose,  :type => :boolean,  :aliases => :v
         | 
| 6 6 | 
             
              class_option :block,    :type => :boolean,  :aliases => :b
         | 
| 7 | 
            -
              class_option :cwd,      :type => :string | 
| 8 | 
            -
              class_option :log,      :type => :string,   :aliases => :l
         | 
| 7 | 
            +
              class_option :cwd,      :type => :string
         | 
| 9 8 |  | 
| 10 | 
            -
              desc "start", "."
         | 
| 9 | 
            +
              desc "start", "Start the daemon."
         | 
| 11 10 | 
             
              def start
         | 
| 12 | 
            -
                daemon.start! | 
| 11 | 
            +
                daemon.start!(:block => options[:block])
         | 
| 13 12 | 
             
              end
         | 
| 14 13 |  | 
| 15 | 
            -
              desc "restart", "."
         | 
| 14 | 
            +
              desc "restart", "Restart the daemon."
         | 
| 16 15 | 
             
              def restart
         | 
| 17 | 
            -
                 | 
| 18 | 
            -
                start
         | 
| 16 | 
            +
                daemon.restart!(:block => options[:block])
         | 
| 19 17 | 
             
              end
         | 
| 20 18 |  | 
| 21 | 
            -
              desc "stop", "."
         | 
| 19 | 
            +
              desc "stop", "Stop the daemon."
         | 
| 22 20 | 
             
              def stop
         | 
| 23 21 | 
             
                daemon.stop!
         | 
| 24 22 | 
             
              end
         | 
| 25 23 |  | 
| 26 | 
            -
              desc " | 
| 27 | 
            -
               | 
| 28 | 
            -
             | 
| 24 | 
            +
              desc "tail", "Tail the daemon's log."
         | 
| 25 | 
            +
              method_option :follow, :type => :boolean, :aliases => :f
         | 
| 26 | 
            +
              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)
         | 
| 29 30 | 
             
              end
         | 
| 30 31 |  | 
| 31 32 | 
             
              protected
         | 
| 32 33 |  | 
| 33 34 | 
             
              def daemon
         | 
| 34 | 
            -
                Zeusd::Daemon.new(
         | 
| 35 | 
            -
                  :verbose  => options[:verbose],
         | 
| 36 | 
            -
                  :cwd      => options[:cwd]
         | 
| 37 | 
            -
                )
         | 
| 35 | 
            +
                Zeusd::Daemon.new(:verbose => options[:verbose], :cwd => options[:cwd])
         | 
| 38 36 | 
             
              end
         | 
| 39 37 |  | 
| 40 38 | 
             
            end
         | 
    
        data/lib/zeusd.rb
    CHANGED
    
    
    
        data/lib/zeusd/daemon.rb
    CHANGED
    
    | @@ -1,78 +1,113 @@ | |
| 1 1 | 
             
            require 'thread'
         | 
| 2 2 | 
             
            require 'childprocess'
         | 
| 3 3 | 
             
            require 'pathname'
         | 
| 4 | 
            +
            require 'hooks'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Zeusd
         | 
| 6 7 | 
             
              class DaemonException < StandardError; end
         | 
| 7 8 |  | 
| 8 9 | 
             
              class Daemon
         | 
| 9 | 
            -
                 | 
| 10 | 
            -
                 | 
| 11 | 
            -
                attr_reader :state
         | 
| 12 | 
            -
                attr_reader :child_process, :reader, :writer
         | 
| 10 | 
            +
                include Hooks
         | 
| 11 | 
            +
                define_hooks :after_output, :after_start!, :after_stop!, :after_start_process!
         | 
| 13 12 |  | 
| 14 | 
            -
                 | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                  @queue   = Queue.new
         | 
| 18 | 
            -
                  @state   = StateInterpreter.new
         | 
| 19 | 
            -
                  on_update(&method(:puts)) if verbose
         | 
| 20 | 
            -
                end
         | 
| 13 | 
            +
                after_start!  { log(:start) }
         | 
| 14 | 
            +
                after_stop!   { log(:stop) }
         | 
| 15 | 
            +
                after_output  {|x| log(x, :zeus) }
         | 
| 21 16 |  | 
| 22 | 
            -
                 | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                    Zeusd::Process.kill!(processes.map(&:pid))
         | 
| 26 | 
            -
                  end
         | 
| 17 | 
            +
                after_start_process! :ensure_log_worker
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                after_stop! do
         | 
| 27 20 | 
             
                  (socket_file.delete rescue nil) if socket_file.exist?
         | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                   | 
| 21 | 
            +
                  @process = nil
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                after_output do |output|
         | 
| 25 | 
            +
                  interpreter.translate(output)
         | 
| 26 | 
            +
                  puts(output) if verbose?
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                attr_reader :cwd, :verbose, :log_file, :log_queue, :interpreter, :child_process
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def initialize(options = {})
         | 
| 32 | 
            +
                  @cwd         = Pathname.new(options.fetch(:cwd, Dir.pwd)).realpath
         | 
| 33 | 
            +
                  @verbose     = options.fetch(:verbose, false)
         | 
| 34 | 
            +
                  @interpreter = Interpreter.new
         | 
| 34 35 | 
             
                end
         | 
| 35 36 |  | 
| 36 37 | 
             
                def start!(options = {})
         | 
| 37 | 
            -
                  @process = Zeusd::Process.find( | 
| 38 | 
            +
                  @process = Zeusd::Process.find(start_process!.pid)
         | 
| 38 39 |  | 
| 39 40 | 
             
                  if options.fetch(:block, false)
         | 
| 40 | 
            -
                     | 
| 41 | 
            -
                      if loaded?
         | 
| 42 | 
            -
                        puts state.last_status
         | 
| 43 | 
            -
                        break
         | 
| 44 | 
            -
                      end
         | 
| 45 | 
            -
                      sleep(0.1)
         | 
| 46 | 
            -
                    end
         | 
| 41 | 
            +
                    sleep(0.1) until loaded?
         | 
| 47 42 | 
             
                  end
         | 
| 48 43 |  | 
| 49 | 
            -
                   | 
| 44 | 
            +
                  self
         | 
| 45 | 
            +
                ensure
         | 
| 46 | 
            +
                  run_hook :after_start!
         | 
| 50 47 | 
             
                end
         | 
| 51 48 |  | 
| 52 | 
            -
                def  | 
| 53 | 
            -
                   | 
| 54 | 
            -
             | 
| 49 | 
            +
                def restart!(options = {})
         | 
| 50 | 
            +
                  stop!.start!(options)
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def stop!
         | 
| 54 | 
            +
                  return self unless process
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  # Kill Pids and Wait
         | 
| 57 | 
            +
                  process.kill!(:recursive => true, :wait => true)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # Check for remaining processes
         | 
| 60 | 
            +
                  if[process, process.descendants].flatten.select(&:alive?).any?
         | 
| 61 | 
            +
                    raise DaemonException, "Unable to KILL processes: " + alive_processes.join(', ')
         | 
| 55 62 | 
             
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  self
         | 
| 65 | 
            +
                ensure
         | 
| 66 | 
            +
                  run_hook :after_stop!
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def process
         | 
| 70 | 
            +
                  @process ||= Process.all.find {|p| !!p.command[/zeus.*start$/] && p.cwd == cwd }
         | 
| 56 71 | 
             
                end
         | 
| 57 72 |  | 
| 58 73 | 
             
                def loaded?
         | 
| 59 | 
            -
                   | 
| 74 | 
            +
                  interpreter.complete?
         | 
| 60 75 | 
             
                end
         | 
| 61 76 |  | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
                   | 
| 77 | 
            +
             | 
| 78 | 
            +
                def log_file
         | 
| 79 | 
            +
                  cwd.join('log/zeusd.log')
         | 
| 65 80 | 
             
                end
         | 
| 66 81 |  | 
| 67 82 | 
             
                def socket_file
         | 
| 68 83 | 
             
                  cwd.join('.zeus.sock')
         | 
| 69 84 | 
             
                end
         | 
| 70 85 |  | 
| 86 | 
            +
                def verbose?
         | 
| 87 | 
            +
                  !!verbose
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 71 90 | 
             
                protected
         | 
| 72 91 |  | 
| 73 | 
            -
                def  | 
| 92 | 
            +
                def log(entry, type = :zeusd)
         | 
| 93 | 
            +
                  log_queue << "<#{type.to_s} utc='#{Time.now.utc}'>#{entry}</#{type.to_s}>\n"
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                def log_queue
         | 
| 97 | 
            +
                  @log_queue ||= Queue.new
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def ensure_log_worker
         | 
| 101 | 
            +
                  @log_worker ||= Thread.new do
         | 
| 102 | 
            +
                    while value = log_queue.shift
         | 
| 103 | 
            +
                      log_file.open("a+") {|f| f.write(value) }
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def start_process!
         | 
| 74 109 | 
             
                  @reader, @writer = IO.pipe
         | 
| 75 | 
            -
                  @child_process | 
| 110 | 
            +
                  @child_process   = ChildProcess.build("zeus", "start")
         | 
| 76 111 | 
             
                  @child_process.environment["BUNDLE_GEMFILE"] = cwd.join("Gemfile").to_path
         | 
| 77 112 | 
             
                  @child_process.io.stdout = @child_process.io.stderr = @writer
         | 
| 78 113 | 
             
                  @child_process.cwd       = cwd.to_path
         | 
| @@ -80,22 +115,15 @@ module Zeusd | |
| 80 115 | 
             
                  @child_process.start
         | 
| 81 116 | 
             
                  @writer.close
         | 
| 82 117 |  | 
| 83 | 
            -
                   | 
| 84 | 
            -
                    while (buffer = (reader.readpartial(10000) rescue nil)) do
         | 
| 85 | 
            -
                      state << buffer
         | 
| 86 | 
            -
                      queue << buffer
         | 
| 87 | 
            -
                    end
         | 
| 88 | 
            -
                  end
         | 
| 118 | 
            +
                  run_hook :after_start_process!
         | 
| 89 119 |  | 
| 90 | 
            -
                   | 
| 91 | 
            -
                     | 
| 92 | 
            -
                       | 
| 93 | 
            -
                        on_update.call(output)
         | 
| 94 | 
            -
                      end
         | 
| 120 | 
            +
                  Thread.new do
         | 
| 121 | 
            +
                    while (buffer = (@reader.readpartial(10000) rescue nil)) do
         | 
| 122 | 
            +
                      run_hook :after_output, buffer
         | 
| 95 123 | 
             
                    end
         | 
| 96 124 | 
             
                  end
         | 
| 97 125 |  | 
| 98 | 
            -
                  sleep | 
| 126 | 
            +
                  sleep 0.1 until interpreter.commands.any?
         | 
| 99 127 |  | 
| 100 128 | 
             
                  @child_process
         | 
| 101 129 | 
             
                end
         | 
| @@ -1,23 +1,22 @@ | |
| 1 | 
            -
            require 'stringio'
         | 
| 2 1 | 
             
            module Zeusd
         | 
| 3 | 
            -
              class  | 
| 2 | 
            +
              class Interpreter
         | 
| 4 3 | 
             
                STATES = %w[ready crashed running connecting waiting]
         | 
| 5 4 |  | 
| 6 5 | 
             
                attr_reader :lines
         | 
| 7 6 | 
             
                attr_reader :state_colors, :commands, :errors
         | 
| 7 | 
            +
                attr_reader :last_status
         | 
| 8 8 |  | 
| 9 9 | 
             
                def initialize(*args)
         | 
| 10 10 | 
             
                  @state_colors = Hash[STATES.zip([nil]*STATES.length)]
         | 
| 11 11 | 
             
                  @commands     = {}
         | 
| 12 12 | 
             
                  @errors       = []
         | 
| 13 13 | 
             
                  @lines        = []
         | 
| 14 | 
            -
                  super(*args)
         | 
| 15 14 | 
             
                end
         | 
| 16 15 |  | 
| 17 | 
            -
                def  | 
| 18 | 
            -
                   | 
| 16 | 
            +
                def translate(zeus_output)
         | 
| 17 | 
            +
                  zeus_output.split("\n").map{|x| Line.new(x) }.each do |line|
         | 
| 19 18 | 
             
                    # State Colors
         | 
| 20 | 
            -
                    if @state_colors.values.any?(&:nil?) && line. | 
| 19 | 
            +
                    if @state_colors.values.any?(&:nil?) && line.state_legend?
         | 
| 21 20 | 
             
                      STATES.each do |state|
         | 
| 22 21 | 
             
                        state_colors[state] = line.color_of(state)
         | 
| 23 22 | 
             
                      end
         | 
| @@ -32,15 +31,22 @@ module Zeusd | |
| 32 31 | 
             
                    # Add Line
         | 
| 33 32 | 
             
                    @lines << line
         | 
| 34 33 | 
             
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  # Garbage Collection
         | 
| 36 | 
            +
                  if @lines.length > 100
         | 
| 37 | 
            +
                    @lines = @lines.last(100)
         | 
| 38 | 
            +
                  end
         | 
| 35 39 | 
             
                end
         | 
| 36 40 |  | 
| 37 | 
            -
                def  | 
| 41 | 
            +
                def complete?
         | 
| 38 42 | 
             
                  return false if errors.any?
         | 
| 39 | 
            -
                  return true if commands.all? {|command, status| %[crashed  | 
| 43 | 
            +
                  return true if commands.all? {|command, status| %[crashed ready].include?(status)}
         | 
| 40 44 | 
             
                  false
         | 
| 41 45 | 
             
                end
         | 
| 42 46 |  | 
| 43 | 
            -
             | 
| 47 | 
            +
             | 
| 48 | 
            +
             | 
| 49 | 
            +
                def last_update
         | 
| 44 50 | 
             
                  @lines[@lines.rindex(&:update?)..-1].join("\n").to_s
         | 
| 45 51 | 
             
                end
         | 
| 46 52 |  | 
| @@ -55,12 +61,12 @@ module Zeusd | |
| 55 61 | 
             
                  end
         | 
| 56 62 |  | 
| 57 63 | 
             
                  def command
         | 
| 58 | 
            -
                    if match  | 
| 59 | 
            -
                      { :name => match[2], :color => match[1] }
         | 
| 64 | 
            +
                    if @match ||= self.match(/^(\e.*?)zeus\s(.*?)(\s|\e)/)
         | 
| 65 | 
            +
                      { :name => @match[2], :color => @match[1] }
         | 
| 60 66 | 
             
                    end
         | 
| 61 67 | 
             
                  end
         | 
| 62 68 |  | 
| 63 | 
            -
                  def  | 
| 69 | 
            +
                  def state_legend?
         | 
| 64 70 | 
             
                    STATES.all?{|state| !!self[state]}
         | 
| 65 71 | 
             
                  end
         | 
| 66 72 |  | 
    
        data/lib/zeusd/process.rb
    CHANGED
    
    | @@ -7,11 +7,15 @@ module Zeusd | |
| 7 7 | 
             
                  "stat"    => ->(x){x.to_s},
         | 
| 8 8 | 
             
                  "command" => ->(x){x.to_s}
         | 
| 9 9 | 
             
                }
         | 
| 10 | 
            -
                attr_accessor :attributes
         | 
| 11 | 
            -
                attr_accessor :children
         | 
| 10 | 
            +
                attr_accessor :attributes, :children
         | 
| 12 11 |  | 
| 13 | 
            -
                def initialize( | 
| 14 | 
            -
                   | 
| 12 | 
            +
                def initialize(attributes_or_pid)
         | 
| 13 | 
            +
                  if attributes_or_pid.is_a? Hash
         | 
| 14 | 
            +
                    self.attributes = attributes_or_pid
         | 
| 15 | 
            +
                  else
         | 
| 16 | 
            +
                    self.attributes = {"pid" => attributes_or_pid.to_i}
         | 
| 17 | 
            +
                    reload!
         | 
| 18 | 
            +
                  end
         | 
| 15 19 | 
             
                end
         | 
| 16 20 |  | 
| 17 21 | 
             
                def self.ps(options = {})
         | 
| @@ -35,17 +39,10 @@ module Zeusd | |
| 35 39 | 
             
                  end
         | 
| 36 40 | 
             
                end
         | 
| 37 41 |  | 
| 38 | 
            -
                # Note: Non-chinable, AND joined, Proc allowed for value
         | 
| 39 | 
            -
                # {"attr" => value}
         | 
| 40 42 | 
             
                def self.where(criteria, options = {})
         | 
| 41 43 | 
             
                  all(options).select do |process|
         | 
| 42 44 | 
             
                    criteria.all? do |key, value|
         | 
| 43 | 
            -
                       | 
| 44 | 
            -
                      when Array then value.include?(process.send(key))
         | 
| 45 | 
            -
                      when Proc  then !!value.call(process)
         | 
| 46 | 
            -
                      else
         | 
| 47 | 
            -
                        process.send(key) == value
         | 
| 48 | 
            -
                      end
         | 
| 45 | 
            +
                      process.send(key) == value
         | 
| 49 46 | 
             
                    end
         | 
| 50 47 | 
             
                  end
         | 
| 51 48 | 
             
                end
         | 
| @@ -56,12 +53,24 @@ module Zeusd | |
| 56 53 | 
             
                  end
         | 
| 57 54 | 
             
                end
         | 
| 58 55 |  | 
| 56 | 
            +
                def self.wait(pids = [])
         | 
| 57 | 
            +
                  pids = Array(pids).map(&:to_s)
         | 
| 58 | 
            +
                  loop do
         | 
| 59 | 
            +
                    break if (self.ps.map{|x| x["pid"]} & pids).length.zero?
         | 
| 60 | 
            +
                    sleep(0.1)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 59 64 | 
             
                def self.kill!(pids, options = {})
         | 
| 60 | 
            -
                   | 
| 61 | 
            -
                   | 
| 62 | 
            -
                   | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            +
                  pids      = Array(pids).map(&:to_i).select{|x| x > 0}
         | 
| 66 | 
            +
                  processes = pids.map{|pid| self.new(pid)}
         | 
| 67 | 
            +
                  signal    = options.fetch(:signal, "TERM")
         | 
| 68 | 
            +
                  wait      = options.fetch(:wait, false)
         | 
| 69 | 
            +
                  return false if processes.any?(&:dead?)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  if system("kill -#{signal} #{processes.map(&:pid).join(' ')}")
         | 
| 72 | 
            +
                    self.wait(pids) if wait
         | 
| 73 | 
            +
                    true
         | 
| 65 74 | 
             
                  else
         | 
| 66 75 | 
             
                    false
         | 
| 67 76 | 
             
                  end
         | 
| @@ -69,7 +78,7 @@ module Zeusd | |
| 69 78 |  | 
| 70 79 | 
             
                def reload!
         | 
| 71 80 | 
             
                  self.attributes = self.class.ps(:pid => pid).first || {}
         | 
| 72 | 
            -
                  @children | 
| 81 | 
            +
                  @children       = nil
         | 
| 73 82 | 
             
                  !attributes.empty?
         | 
| 74 83 | 
             
                end
         | 
| 75 84 |  | 
| @@ -112,8 +121,14 @@ module Zeusd | |
| 112 121 | 
             
                end
         | 
| 113 122 |  | 
| 114 123 | 
             
                def kill!(options = {})
         | 
| 115 | 
            -
                   | 
| 116 | 
            -
                   | 
| 124 | 
            +
                  return false if dead?
         | 
| 125 | 
            +
                  opts = options.dup
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  pids = [pid].tap do |x|
         | 
| 128 | 
            +
                    x.concat(descendants.map(&:pid)) if !!opts.delete(:recursive)
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  self.class.kill!(pids, opts)
         | 
| 117 132 | 
             
                end
         | 
| 118 133 |  | 
| 119 134 | 
             
                def descendants(options = {})
         | 
| @@ -123,11 +138,7 @@ module Zeusd | |
| 123 138 | 
             
                end
         | 
| 124 139 |  | 
| 125 140 | 
             
                def children(options = {})
         | 
| 126 | 
            -
                  @children = self.class.where("ppid" => pid) | 
| 127 | 
            -
                    if options.fetch(:recursive, false)
         | 
| 128 | 
            -
                      processes.each{|p| p.children(options)}
         | 
| 129 | 
            -
                    end
         | 
| 130 | 
            -
                  end
         | 
| 141 | 
            +
                  @children = self.class.where("ppid" => pid)
         | 
| 131 142 | 
             
                end
         | 
| 132 143 |  | 
| 133 144 | 
             
                def attributes=(hash)
         | 
    
        data/lib/zeusd/version.rb
    CHANGED
    
    
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        data/spec/zeusd/daemon_spec.rb
    CHANGED
    
    | @@ -3,23 +3,51 @@ require 'spec_helper' | |
| 3 3 | 
             
            describe Zeusd::Daemon do
         | 
| 4 4 | 
             
              let(:daemon) { Zeusd::Daemon.new(:cwd => DUMMY_APP_PATH) }
         | 
| 5 5 | 
             
              after(:each) { daemon.stop! }
         | 
| 6 | 
            +
             | 
| 6 7 | 
             
              describe ".start!" do
         | 
| 8 | 
            +
                subject { daemon.start!(:verbose => true) }
         | 
| 9 | 
            +
                it { should be daemon }
         | 
| 10 | 
            +
                it { should_not be_loaded }
         | 
| 7 11 |  | 
| 8 | 
            -
                 | 
| 12 | 
            +
                describe ":block option" do
         | 
| 9 13 | 
             
                  subject { daemon.start!(:block => true) }
         | 
| 10 | 
            -
                  it { should  | 
| 14 | 
            +
                  it { should be_loaded }
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 11 17 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 18 | 
            +
              describe ".stop!" do
         | 
| 19 | 
            +
                subject { daemon.start!.stop! }
         | 
| 20 | 
            +
                it { should be daemon }
         | 
| 21 | 
            +
                it { should_not be_loaded }
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              describe ".restart!" do
         | 
| 25 | 
            +
                subject { daemon }
         | 
| 26 | 
            +
                context "when daemon is already running" do
         | 
| 27 | 
            +
                  before { subject.start!.restart!(:block => true) }
         | 
| 28 | 
            +
                  it { should be_loaded }
         | 
| 16 29 | 
             
                end
         | 
| 30 | 
            +
              end
         | 
| 17 31 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
                   | 
| 32 | 
            +
              describe ".process" do
         | 
| 33 | 
            +
                context "before start" do
         | 
| 34 | 
            +
                  subject { daemon.process }
         | 
| 35 | 
            +
                  it { should be_nil }
         | 
| 21 36 | 
             
                end
         | 
| 22 37 |  | 
| 38 | 
            +
                context "after start" do
         | 
| 39 | 
            +
                  subject { daemon.start!.process }
         | 
| 40 | 
            +
                  it { should be_a Zeusd::Process}
         | 
| 41 | 
            +
                  it { should be_alive }
         | 
| 42 | 
            +
                  its "a zeus start process" do
         | 
| 43 | 
            +
                    subject.command.should match(/zeus.*?start$/)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                context "after start and stop" do
         | 
| 48 | 
            +
                  subject { daemon.start!.stop!.process }
         | 
| 49 | 
            +
                  it { should be_nil }
         | 
| 50 | 
            +
                end
         | 
| 23 51 | 
             
              end
         | 
| 24 52 |  | 
| 25 53 | 
             
            end
         | 
    
        data/spec/zeusd/process_spec.rb
    CHANGED
    
    | @@ -1,16 +1,83 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'benchmark'
         | 
| 2 3 | 
             
            require 'zeusd'
         | 
| 3 4 |  | 
| 4 5 | 
             
            describe Zeusd::Process do
         | 
| 5 6 |  | 
| 6 | 
            -
              def spawn_dummy_process( | 
| 7 | 
            -
                 | 
| 7 | 
            +
              def spawn_dummy_process(ttl = 1, usr1delay = 0.5, options = {})
         | 
| 8 | 
            +
                ttl       = ttl.to_f.to_s
         | 
| 9 | 
            +
                usr1delay = usr1delay.to_f.to_s
         | 
| 10 | 
            +
                script    = File.expand_path("../../support/dummy_process.rb", __FILE__)
         | 
| 11 | 
            +
                p         = ChildProcess.build(script, ttl, usr1delay)
         | 
| 12 | 
            +
                p.cwd     = options[:cwd] if options[:cwd]
         | 
| 13 | 
            +
                p.detach  = true
         | 
| 14 | 
            +
                p.start
         | 
| 15 | 
            +
                p
         | 
| 8 16 | 
             
              end
         | 
| 9 17 |  | 
| 18 | 
            +
              before(:each) do
         | 
| 19 | 
            +
                @dummy_ttl       = 1
         | 
| 20 | 
            +
                @dummy_usr1delay = 0.5
         | 
| 21 | 
            +
                @dummy_process   = spawn_dummy_process(@dummy_ttl, @dummy_usr1delay)
         | 
| 22 | 
            +
              end
         | 
| 10 23 | 
             
              subject(:model) { Zeusd::Process }
         | 
| 11 24 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 25 | 
            +
             | 
| 26 | 
            +
              describe "process instance" do
         | 
| 27 | 
            +
                subject { model.find(@dummy_process.pid) }
         | 
| 28 | 
            +
                it { should be_a Zeusd::Process }
         | 
| 29 | 
            +
                it { should be_alive }
         | 
| 30 | 
            +
                it { should_not be_dead }
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              describe "instance methods" do
         | 
| 34 | 
            +
                subject { model.find(@dummy_process.pid) }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                describe ".cwd" do
         | 
| 37 | 
            +
                  it "return the current working directory" do
         | 
| 38 | 
            +
                    subject.cwd.to_path == Dir.pwd
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                describe ".kill!" do
         | 
| 43 | 
            +
                  context "when process exists" do
         | 
| 44 | 
            +
                    it "kills the process and returns true" do
         | 
| 45 | 
            +
                      subject.kill!(:wait => true).should be_true
         | 
| 46 | 
            +
                      subject.should be_dead
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  context "when processes does not exists" do
         | 
| 51 | 
            +
                    it "returns false" do
         | 
| 52 | 
            +
                      subject.kill!(:wait => true).should be_true
         | 
| 53 | 
            +
                      subject.kill!.should be_false
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
             | 
| 60 | 
            +
              describe "class methods" do
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                describe "::kill!" do
         | 
| 63 | 
            +
                  it "kills a process in under 0.1 seconds" do
         | 
| 64 | 
            +
                    model.kill!(@dummy_process.pid)
         | 
| 65 | 
            +
                    sleep 0.1
         | 
| 66 | 
            +
                    model.find(@dummy_process.pid).should be_nil
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  it "blocks until the process exits" do
         | 
| 70 | 
            +
                    Benchmark.realtime do
         | 
| 71 | 
            +
                      model.kill!(@dummy_process.pid, :signal => "USR1", :wait => true).should be_true
         | 
| 72 | 
            +
                    end.should be < 1.0
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                describe "::wait" do
         | 
| 77 | 
            +
                  it "blocks until the process exits" do
         | 
| 78 | 
            +
                    Benchmark.realtime { model.wait(@dummy_process.pid) }.should be > 1
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
                end
         | 
| 14 81 |  | 
| 15 82 | 
             
                describe "::ps" do
         | 
| 16 83 | 
             
                  subject { model.ps }
         | 
| @@ -19,7 +86,7 @@ describe Zeusd::Process do | |
| 19 86 | 
             
                    subject.all?{|x| x.is_a?(Hash)}.should be_true
         | 
| 20 87 | 
             
                  end
         | 
| 21 88 | 
             
                  it "includes the dummy process" do
         | 
| 22 | 
            -
                    subject.map{|x|x["pid"]}.should include(@ | 
| 89 | 
            +
                    subject.map{|x|x["pid"]}.should include(@dummy_process.pid.to_s)
         | 
| 23 90 | 
             
                  end
         | 
| 24 91 | 
             
                  it "allows additional -o keywords" do
         | 
| 25 92 | 
             
                    model.ps(:keywords => "user").first.should include("user")
         | 
| @@ -60,7 +127,7 @@ describe Zeusd::Process do | |
| 60 127 | 
             
                      it { should have_at_least(2).items }
         | 
| 61 128 | 
             
                    end
         | 
| 62 129 | 
             
                    context "criteria not met" do
         | 
| 63 | 
            -
                      subject {model.where(:pid => @ | 
| 130 | 
            +
                      subject {model.where(:pid => @dummy_process.pid, :pgid => 9999999999999)}
         | 
| 64 131 | 
             
                      it { should be_an Array }
         | 
| 65 132 | 
             
                      it { should be_empty }
         | 
| 66 133 | 
             
                    end
         | 
| @@ -68,14 +135,14 @@ describe Zeusd::Process do | |
| 68 135 | 
             
                end
         | 
| 69 136 |  | 
| 70 137 | 
             
                describe "::find" do
         | 
| 71 | 
            -
                  subject { model.find(@ | 
| 138 | 
            +
                  subject { model.find(@dummy_process.pid) }
         | 
| 72 139 | 
             
                  context "existing process" do
         | 
| 73 140 | 
             
                    it { should be_a Zeusd::Process }
         | 
| 74 141 | 
             
                    it { should be_alive }
         | 
| 75 | 
            -
                    it { subject.pid.should eq(@ | 
| 142 | 
            +
                    it { subject.pid.should eq(@dummy_process.pid) }
         | 
| 76 143 | 
             
                  end
         | 
| 77 144 | 
             
                  context "non-existant process" do
         | 
| 78 | 
            -
                    before(:each) {  | 
| 145 | 
            +
                    before(:each) { @dummy_process.stop }
         | 
| 79 146 | 
             
                    it { should be_nil }
         | 
| 80 147 | 
             
                  end
         | 
| 81 148 | 
             
                end
         | 
    
        data/zeusd.gemspec
    CHANGED
    
    
    
        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.0 | 
| 4 | 
            +
              version: 0.1.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-02- | 
| 12 | 
            +
            date: 2014-02-16 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: thor
         | 
| @@ -43,6 +43,22 @@ dependencies: | |
| 43 43 | 
             
                - - ! '>='
         | 
| 44 44 | 
             
                  - !ruby/object:Gem::Version
         | 
| 45 45 | 
             
                    version: '0'
         | 
| 46 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 47 | 
            +
              name: hooks
         | 
| 48 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 49 | 
            +
                none: false
         | 
| 50 | 
            +
                requirements:
         | 
| 51 | 
            +
                - - ! '>='
         | 
| 52 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            +
                    version: '0'
         | 
| 54 | 
            +
              type: :runtime
         | 
| 55 | 
            +
              prerelease: false
         | 
| 56 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 | 
            +
                none: false
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ! '>='
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '0'
         | 
| 46 62 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 47 63 | 
             
              name: zeus
         | 
| 48 64 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -126,8 +142,8 @@ files: | |
| 126 142 | 
             
            - bin/zeusd
         | 
| 127 143 | 
             
            - lib/zeusd.rb
         | 
| 128 144 | 
             
            - lib/zeusd/daemon.rb
         | 
| 145 | 
            +
            - lib/zeusd/interpreter.rb
         | 
| 129 146 | 
             
            - lib/zeusd/process.rb
         | 
| 130 | 
            -
            - lib/zeusd/state_interpreter.rb
         | 
| 131 147 | 
             
            - lib/zeusd/version.rb
         | 
| 132 148 | 
             
            - spec/dummy/.gitignore
         | 
| 133 149 | 
             
            - spec/dummy/Gemfile
         | 
| @@ -180,6 +196,7 @@ files: | |
| 180 196 | 
             
            - spec/dummy/vendor/plugins/.gitkeep
         | 
| 181 197 | 
             
            - spec/dummy/zeus.json
         | 
| 182 198 | 
             
            - spec/spec_helper.rb
         | 
| 199 | 
            +
            - spec/support/dummy_process.rb
         | 
| 183 200 | 
             
            - spec/zeusd/daemon_spec.rb
         | 
| 184 201 | 
             
            - spec/zeusd/process_spec.rb
         | 
| 185 202 | 
             
            - zeusd.gemspec
         | 
| @@ -198,7 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 198 215 | 
             
                  version: '0'
         | 
| 199 216 | 
             
                  segments:
         | 
| 200 217 | 
             
                  - 0
         | 
| 201 | 
            -
                  hash: - | 
| 218 | 
            +
                  hash: -3204481942119935445
         | 
| 202 219 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 203 220 | 
             
              none: false
         | 
| 204 221 | 
             
              requirements:
         | 
| @@ -207,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 207 224 | 
             
                  version: '0'
         | 
| 208 225 | 
             
                  segments:
         | 
| 209 226 | 
             
                  - 0
         | 
| 210 | 
            -
                  hash: - | 
| 227 | 
            +
                  hash: -3204481942119935445
         | 
| 211 228 | 
             
            requirements: []
         | 
| 212 229 | 
             
            rubyforge_project: 
         | 
| 213 230 | 
             
            rubygems_version: 1.8.25
         | 
| @@ -266,5 +283,6 @@ test_files: | |
| 266 283 | 
             
            - spec/dummy/vendor/plugins/.gitkeep
         | 
| 267 284 | 
             
            - spec/dummy/zeus.json
         | 
| 268 285 | 
             
            - spec/spec_helper.rb
         | 
| 286 | 
            +
            - spec/support/dummy_process.rb
         | 
| 269 287 | 
             
            - spec/zeusd/daemon_spec.rb
         | 
| 270 288 | 
             
            - spec/zeusd/process_spec.rb
         |