jefe 1.0.1 → 1.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/bin/jefe CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'jefe'
3
4
  require 'jefe/cli'
4
5
  Jefe::CLI.start
data/lib/jefe/cli.rb CHANGED
@@ -1,4 +1,8 @@
1
- require 'jefe'
1
+ require 'thor'
2
+
3
+ require 'jefe/color_printer'
4
+ require 'jefe/em'
5
+ require 'jefe/loader'
2
6
  require 'jefe/version'
3
7
 
4
8
  class Jefe::CLI < Thor
@@ -8,23 +12,25 @@ class Jefe::CLI < Thor
8
12
  desc "start [COMMAND...]", "Start the application"
9
13
 
10
14
  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"'
15
+ method_option :port, :type => :numeric, :aliases => "-p"
16
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
13
17
 
14
18
  def start(*args)
15
19
  error("#{procfile} does not exist") unless File.exists? procfile
16
20
 
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
+ loader = Jefe::Loader.new File.read procfile
22
+ engine = Jefe::EM.new(Jefe::ColorPrinter.new)
21
23
 
22
- names = args.empty? ? engine.process_types.keys : args
23
24
  trap("INT") do
24
25
  puts
25
26
  engine.stop
26
27
  end
27
- engine.start concurrency(names), port
28
+ names = args.empty? ? loader.process_types.keys : args
29
+ engine.start do
30
+ loader.tasks(concurrency(names), port).each do |(name, command)|
31
+ engine.add name, command
32
+ end
33
+ end
28
34
  end
29
35
 
30
36
  def help(*args)
@@ -40,18 +46,18 @@ private
40
46
  end
41
47
 
42
48
  def concurrency names
49
+ ret = {}
43
50
  if options[:concurrency]
44
- options[:concurrency].split(",").reduce({}) do |tot, kv|
51
+ options[:concurrency].split(",").each do |kv|
45
52
  k, v = kv.split "="
46
- tot[k] = v.to_i
47
- tot
53
+ ret[k] = v.to_i
48
54
  end
49
55
  else
50
- names.reduce({}) do |tot, name|
51
- tot[name] = 1
52
- tot
56
+ names.each do |name|
57
+ ret[name] = 1
53
58
  end
54
59
  end
60
+ ret
55
61
  end
56
62
 
57
63
  def procfile
@@ -0,0 +1,29 @@
1
+ require 'thor/shell/color'
2
+
3
+ class Jefe::ColorPrinter
4
+ Color = Thor::Shell::Color
5
+ COLORS = %w{CYAN YELLOW GREEN MAGENTA RED}
6
+ def initialize
7
+ @colors = {"system" => "WHITE"}
8
+ @longest_seen = 0
9
+ end
10
+ def colored color, string
11
+ color = Color.const_get color
12
+ "#{color}#{string}#{Color::CLEAR}"
13
+ end
14
+ def color_for type
15
+ @colors[type] ||= COLORS.shift.tap {|c| COLORS.push c}
16
+ end
17
+ def padded name
18
+ @longest_seen = name.size if name.size > @longest_seen
19
+ name.ljust(@longest_seen)
20
+ end
21
+ def datetime
22
+ Time.now.strftime '%H:%M:%S'
23
+ end
24
+ def out name, msg
25
+ type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
26
+ color = color_for type
27
+ puts colored(color, "#{datetime} #{padded name} | ") + msg.chomp
28
+ end
29
+ end
data/lib/jefe/em.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'eventmachine'
2
+ require 'jefe/emitter'
3
+ require 'micromachine'
4
+ require 'pty'
5
+
6
+ class Jefe::EM
7
+ include Jefe::Emitter
8
+
9
+ def initialize(printer)
10
+ @printer = printer
11
+ @fsm = MicroMachine.new(:started).tap do |m|
12
+ m.when(:stop, :started => :stopped)
13
+ end
14
+ end
15
+
16
+ def start
17
+ @printer.out "system", "starting"
18
+ EM.run do
19
+ yield self
20
+ end
21
+ end
22
+
23
+ def stop
24
+ if @fsm.trigger(:stop)
25
+ @printer.out "system", "stopping"
26
+ emit(:stop)
27
+ EM.stop
28
+ end
29
+ end
30
+
31
+ def add name, command
32
+ @printer.out name, "starting #{command}"
33
+
34
+ PTY.spawn(command) do |output, input, pid|
35
+ input.close
36
+
37
+ @printer.out(name, "started with pid #{pid}")
38
+
39
+ c = EM.attach output, ProcessHandler
40
+ m = MicroMachine.new(:started).tap do |m|
41
+ m.when(:stop, :started => :stopped)
42
+ end
43
+
44
+ c.on(:data) do |data|
45
+ @printer.out(name, data)
46
+ end
47
+
48
+ c.on(:unbind) do
49
+ @printer.out(name, "process terminated")
50
+ m.trigger(:stop)
51
+ self.stop
52
+ end
53
+
54
+ self.on(:stop) do
55
+ if m.trigger(:stop)
56
+ @printer.out("system", "killing #{name} in #{pid}")
57
+ Process.kill("INT", pid)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ class ProcessHandler < EM::Connection
64
+ include EM::Protocols::LineText2
65
+ include Jefe::Emitter
66
+
67
+ emits(:data, :from => :receive_line)
68
+ emits(:unbind)
69
+ end
70
+ end
@@ -0,0 +1,24 @@
1
+ module Jefe::Emitter
2
+ def self.included(klass)
3
+ klass.extend(ClassMethods)
4
+ end
5
+ def callbacks
6
+ @callbacks ||= {}
7
+ end
8
+ def on(name, &cb)
9
+ callbacks[name] ||= []
10
+ callbacks[name] << cb
11
+ end
12
+ def emit(name, *args)
13
+ callbacks[name] ||= []
14
+ callbacks[name].each { |cb| cb.call(*args) }
15
+ end
16
+ module ClassMethods
17
+ def emits(name, opts={})
18
+ from = opts[:from] || name
19
+ define_method(from) do |*args|
20
+ emit(name, *args)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ class Jefe::Loader
2
+ attr_reader :process_types
3
+ def initialize(file)
4
+ @process_types = {}
5
+ file.split("\n").each do |line|
6
+ if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
7
+ @process_types[$1] = $2
8
+ else
9
+ raise ArgumentError
10
+ end
11
+ end
12
+ end
13
+ def tasks(concurrency, port)
14
+ tasks = []
15
+ concurrency.each do |(name, num)|
16
+ num.times do |i|
17
+ env = { "PORT" => (port + i) }
18
+ command = @process_types[name].gsub(/\$(\w+)/) { env[$1] || ENV[$1] }
19
+ tasks.push ["#{name}.#{i}", command]
20
+ end
21
+ port += 100
22
+ end
23
+ tasks
24
+ end
25
+ end
data/lib/jefe/version.rb CHANGED
@@ -1 +1 @@
1
- Jefe::VERSION = "1.0.1"
1
+ Jefe::VERSION = "1.1.0"
data/lib/jefe.rb CHANGED
@@ -1,149 +1,2 @@
1
- require 'eventmachine'
2
- require 'pty'
3
- require 'thor'
4
- require 'thor/shell/color'
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 = Thor::Shell::Color
51
- COLORS = [:cyan, :yellow, :green, :magenta, :red]
52
- def initialize
53
- @colors ||= {"system" => :white}
54
- @longest_seen = 0
55
- end
56
- def set_color color, string
57
- color = Color.const_get color.to_s.upcase
58
- "#{color}#{string}#{Color::CLEAR}"
59
- end
60
- def color_for type
61
- @colors[type] ||= COLORS.shift.tap {|c| COLORS.push c}
62
- end
63
- def padding_for name
64
- @longest_seen = name.size if name.size > @longest_seen
65
- " " * (@longest_seen - name.length)
66
- end
67
- def datetime
68
- Time.now.strftime '%H:%M:%S'
69
- end
70
- def out name, command
71
- type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
72
- color = color_for type
73
- puts set_color(color, "#{datetime} #{name}#{padding_for name} | ") + command.chomp
74
- end
75
- end
76
-
77
- class Jefe::EM
78
- def initialize(printer)
79
- @printer = printer
80
- @connections = []
81
- end
82
-
83
- def start
84
- EM.run do
85
- yield self
86
- end
87
- end
88
-
89
- def add name, command
90
- PTY.spawn(command) do |output, input, pid|
91
- input.close
92
-
93
- EM.attach output, ProcessHandler do |c|
94
- c.init name, pid, @printer
95
- bind(c)
96
- end
97
- end
98
- end
99
-
100
- def stop
101
- @connections.each &:stop
102
- EM.stop
103
- end
104
-
105
- def bind c
106
- @connections.push c
107
- c.engine = self
108
- end
109
-
110
- def unbind c
111
- @connections.delete c
112
- stop if @connections.empty?
113
- end
114
-
115
- class ProcessHandler < EM::Connection
116
- include EM::Protocols::LineText2
117
- attr_accessor :engine, :name, :pid, :printer
118
-
119
- def init name, pid, printer
120
- self.name = name
121
- self.pid = pid
122
- self.printer = printer
123
-
124
- out "started with pid #{self.pid}"
125
- end
126
-
127
- def receive_line data
128
- out data
129
- end
130
-
131
- def unbind
132
- out "process terminated"
133
- self.engine.unbind self
134
- end
135
-
136
- def out msg
137
- self.printer.out self.name, msg
138
- end
139
-
140
- def to_s
141
- "#{self.name} in pid #{self.pid}"
142
- end
143
-
144
- def stop
145
- self.printer.out "system", "killing #{self}"
146
- Process.kill("INT", self.pid)
147
- end
148
- end
1
+ module Jefe
149
2
  end
metadata CHANGED
@@ -1,73 +1,105 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: jefe
3
- version: !ruby/object:Gem::Version
4
- version: 1.0.1
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 1
8
+ - 0
9
+ version: 1.1.0
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Michael Maltese
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2012-01-17 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
16
+
17
+ date: 2012-04-01 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: eventmachine
16
- requirement: &70096979689180 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
22
30
  type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: micromachine
23
34
  prerelease: false
24
- version_requirements: *70096979689180
25
- - !ruby/object:Gem::Dependency
26
- name: thor
27
- requirement: &70096979688760 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
33
42
  type: :runtime
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: thor
34
46
  prerelease: false
35
- version_requirements: *70096979688760
36
- description: Through the magic of Thor and EventMachine, we give you the equivalent
37
- to Foreman in just 200 lines of sweet unadulterated Ruby.
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :runtime
55
+ version_requirements: *id003
56
+ description: Through the magic of Thor and EventMachine, we give you a Foreman substitute in just 200 lines of sweet unadulterated Ruby.
38
57
  email: michael.maltese@pomona.edu
39
- executables:
58
+ executables:
40
59
  - jefe
41
60
  extensions: []
61
+
42
62
  extra_rdoc_files: []
43
- files:
63
+
64
+ files:
44
65
  - lib/jefe/cli.rb
66
+ - lib/jefe/color_printer.rb
67
+ - lib/jefe/em.rb
68
+ - lib/jefe/emitter.rb
69
+ - lib/jefe/loader.rb
45
70
  - lib/jefe/version.rb
46
71
  - lib/jefe.rb
47
72
  - bin/jefe
48
73
  - README.md
74
+ has_rdoc: true
49
75
  homepage: http://github.com/mikemaltese/jefe
50
76
  licenses: []
77
+
51
78
  post_install_message:
52
79
  rdoc_options: []
53
- require_paths:
80
+
81
+ require_paths:
54
82
  - - lib
55
- required_ruby_version: !ruby/object:Gem::Requirement
56
- none: false
57
- requirements:
58
- - - ! '>='
59
- - !ruby/object:Gem::Version
60
- version: '0'
61
- required_rubygems_version: !ruby/object:Gem::Requirement
62
- none: false
63
- requirements:
64
- - - ! '>='
65
- - !ruby/object:Gem::Version
66
- version: '0'
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
67
97
  requirements: []
98
+
68
99
  rubyforge_project:
69
- rubygems_version: 1.8.11
100
+ rubygems_version: 1.3.6
70
101
  signing_key:
71
102
  specification_version: 3
72
103
  summary: The featherweight Procfile manager
73
104
  test_files: []
105
+