jefe 1.0.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 ADDED
@@ -0,0 +1,3 @@
1
+ # [Gaffer](http://en.wikipedia.org/wiki/Gaffer_(boss)), the featherweight Procfile manager
2
+
3
+ Through the magic of [Thor](https://github.com/wycats/thor) and [EventMachine](http://rubyeventmachine.com/), we give you the equivalent to [Foreman](https://github.com/ddollar/foreman) in just 200 lines of sweet unadulterated Ruby.
data/bin/jefe ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jefe/cli'
4
+ Jefe::CLI.start
data/lib/jefe/cli.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'jefe'
2
+ require 'jefe/version'
3
+
4
+ class Jefe::CLI < Thor
5
+
6
+ class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
7
+
8
+ desc "start [COMMAND...]", "Start the application"
9
+
10
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
11
+ method_option :port, :type => :numeric, :aliases => "-p"
12
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
13
+
14
+ def start(*args)
15
+ error("#{procfile} does not exist") unless File.exists? procfile
16
+
17
+ engine = Jefe.new
18
+ engine.load File.read(procfile)
19
+ engine.printer = Jefe::ColorPrinter.new
20
+ engine.backend = Jefe::EM.new(engine.printer)
21
+
22
+ names = args.empty? ? engine.process_types.keys : args
23
+ trap("INT") do
24
+ puts
25
+ engine.stop
26
+ end
27
+ engine.start concurrency(names), port
28
+ end
29
+
30
+ def help(*args)
31
+ puts "Jefe #{Jefe::VERSION}, the featherweight Procfile manager"
32
+ puts
33
+ super
34
+ end
35
+
36
+ private
37
+
38
+ def port
39
+ options[:port] || 5000
40
+ end
41
+
42
+ def concurrency names
43
+ if options[:concurrency]
44
+ options[:concurrency].split(",").reduce({}) do |tot, kv|
45
+ k, v = kv.split "="
46
+ tot[k] = v.to_i
47
+ tot
48
+ end
49
+ else
50
+ names.reduce({}) do |tot, name|
51
+ tot[name] = 1
52
+ tot
53
+ end
54
+ end
55
+ end
56
+
57
+ def procfile
58
+ options[:procfile] || "Procfile"
59
+ end
60
+
61
+ def error(message)
62
+ puts "ERROR: #{message}"
63
+ exit 1
64
+ end
65
+
66
+ end
@@ -0,0 +1 @@
1
+ Jefe::VERSION = "1.0.0"
data/lib/jefe.rb ADDED
@@ -0,0 +1,135 @@
1
+ require 'eventmachine'
2
+ require 'pty'
3
+ require 'term/ansicolor'
4
+ require 'thor'
5
+
6
+ class Jefe
7
+ def initialize
8
+ @process_types = {}
9
+ end
10
+ attr_accessor :backend, :printer, :process_types
11
+ def load file
12
+ file.split("\n").each do |line|
13
+ if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
14
+ add_process_type $1, $2
15
+ end
16
+ end
17
+ end
18
+ def add_process_type name, command
19
+ @process_types[name] = command
20
+ end
21
+
22
+ def start concurrency, port
23
+ processes = []
24
+ concurrency.each do |(name, num)|
25
+ num.times do |i|
26
+ env = { "PORT" => (port + i) }
27
+ command = self.process_types[name].gsub(/\$(\w+)/) { env[$1] || ENV[$1] }
28
+ processes.push ["#{name}.#{i}", command]
29
+ end
30
+ port += 100
31
+ end
32
+
33
+ self.printer.out "system", "starting"
34
+ self.backend.start do |b|
35
+ processes.each do |(name, command)|
36
+ self.printer.out name, "starting #{command}"
37
+ b.add name, command
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ def stop
44
+ self.printer.out "system", "stopping"
45
+ self.backend.stop
46
+ end
47
+ end
48
+
49
+ class Jefe::ColorPrinter
50
+ Color = Term::ANSIColor
51
+ COLORS = [:cyan, :yellow, :green, :magenta, :red]
52
+ def initialize
53
+ @colors ||= {"system" => :white}
54
+ end
55
+ def out name, command
56
+ type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
57
+ @colors[type] = COLORS.shift.tap {|c| COLORS.push c} unless @colors[type]
58
+ color = @colors[type]
59
+ puts "#{Color.send color} #{Time.now.strftime '%H:%M:%S'} #{name}#{' ' * (9 - name.length)} | #{Color.reset}#{command.chomp}"
60
+ end
61
+ end
62
+
63
+ class Jefe::EM
64
+ def initialize(printer)
65
+ @printer = printer
66
+ @connections = []
67
+ end
68
+
69
+ def start
70
+ EM.run do
71
+ yield self
72
+ end
73
+ end
74
+
75
+ def add name, command
76
+ PTY.spawn(command) do |output, input, pid|
77
+ input.close
78
+
79
+ EM.attach output, ProcessHandler do |c|
80
+ c.init name, pid, @printer
81
+ bind(c)
82
+ end
83
+ end
84
+ end
85
+
86
+ def stop
87
+ @connections.each &:stop
88
+ EM.stop
89
+ end
90
+
91
+ def bind c
92
+ @connections.push c
93
+ c.engine = self
94
+ end
95
+
96
+ def unbind c
97
+ @connections.delete c
98
+ stop if @connections.empty?
99
+ end
100
+
101
+ class ProcessHandler < EM::Connection
102
+ include EM::Protocols::LineText2
103
+ attr_accessor :engine, :name, :pid, :printer
104
+
105
+ def init name, pid, printer
106
+ self.name = name
107
+ self.pid = pid
108
+ self.printer = printer
109
+
110
+ out "started with pid #{self.pid}"
111
+ end
112
+
113
+ def receive_line data
114
+ out data
115
+ end
116
+
117
+ def unbind
118
+ out "process terminated"
119
+ self.engine.unbind self
120
+ end
121
+
122
+ def out msg
123
+ self.printer.out self.name, msg
124
+ end
125
+
126
+ def to_s
127
+ "#{self.name} in pid #{self.pid}"
128
+ end
129
+
130
+ def stop
131
+ self.printer.out "system", "killing #{self}"
132
+ Process.kill("INT", self.pid)
133
+ end
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jefe
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Michael Maltese
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-14 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: eventmachine
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: term-ansicolor
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: thor
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: Through the magic of Thor and EventMachine, we give you the equivalent to Foreman in just 200 lines of sweet unadulterated Ruby.
64
+ email: michael.maltese@pomona.edu
65
+ executables:
66
+ - jefe
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - lib/jefe/cli.rb
73
+ - lib/jefe/version.rb
74
+ - lib/jefe.rb
75
+ - bin/jefe
76
+ - README.md
77
+ has_rdoc: true
78
+ homepage: http://github.com/mikemaltese/jefe
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project:
107
+ rubygems_version: 1.3.9.3
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: The featherweight Procfile manager
111
+ test_files: []
112
+