jefe 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+