theine 0.0.10.rc1 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 44a70acdfb48ffa54a63ec3720c61082f30342d2
4
+ data.tar.gz: 3a3c2dd8947da2b1f637f4950c08ca5511dea769
5
+ SHA512:
6
+ metadata.gz: d57a7544fd0f61d71a851aba79decded6ef277652a1e3f779c914d32dcf1ea7f12f0f805650714b5f11b597dab272dacb59afea8e671a15028b86ce81ecb15d4
7
+ data.tar.gz: fe22098198bb33413f763bd98de9117c2754c6ad3b00ccc4ae7fca432e73e9d1d1159b9e49e4115af076d4aa32d4dbfc0d3a5a7416518ab7dcfd0e30890ac185
data/lib/theine/client.rb CHANGED
@@ -2,79 +2,6 @@ require 'drb/drb'
2
2
  require 'readline'
3
3
  require_relative './config'
4
4
 
5
- class IOUndumpedProxy
6
- include DRb::DRbUndumped
7
-
8
- def initialize(obj)
9
- @obj = obj
10
- create_method_proxies
11
- end
12
-
13
- def completion_proc=(val)
14
- if @obj.respond_to? :completion_proc=
15
- @obj.completion_proc = val
16
- end
17
- end
18
-
19
- def completion_proc
20
- @obj.completion_proc if @obj.respond_to? :completion_proc
21
- end
22
-
23
- def readline(prompt)
24
- if ::Readline == @obj
25
- @obj.readline(prompt, true)
26
- elsif @obj.method(:readline).arity == 1
27
- @obj.readline(prompt)
28
- else
29
- $stdout.print prompt
30
- @obj.readline
31
- end
32
- end
33
-
34
- def readline_arity
35
- method(:readline).arity
36
- rescue NameError
37
- 0
38
- end
39
-
40
- def <<(data)
41
- @obj << data
42
- self
43
- end
44
-
45
- # Some versions of Pry expect $stdout or its output objects to respond to
46
- # this message.
47
- def tty?
48
- false
49
- end
50
-
51
- private
52
- # http://www.ruby-doc.org/core-1.9.3/IO.html
53
- # Creating method proxies. We take this approach so that method.arity will
54
- # give the correct result (if we just used *args it would always return -1).
55
- # Can't use SimpleDelegator, won't work over DRb
56
- def create_method_proxies
57
- (@obj.public_methods - public_methods).each do |meth|
58
- next unless @obj.respond_to?(meth)
59
- arity = @obj.method(meth).arity
60
- if arity >= 0
61
- args = arity.times.map { |i| "a#{i+1}" }
62
- else
63
- args = (arity.abs - 1).times.map { |i| "a#{i+1}" }
64
- args << "*args"
65
- args = args
66
- end
67
- args = (args + ["&block"]).join(", ")
68
-
69
- singleton_class.class_eval <<-EOS
70
- def #{meth}(#{args})
71
- @obj.send(:#{meth}, #{args})
72
- end
73
- EOS
74
- end
75
- end
76
- end
77
-
78
5
  module Theine
79
6
  class Client
80
7
  def self.start
@@ -85,29 +12,20 @@ module Theine
85
12
 
86
13
  def initialize
87
14
  @config = ConfigReader.new(Dir.pwd)
88
- reset_argv!
89
- trap_signals
15
+ @argv = ARGV.dup
90
16
  begin
91
17
  connect_worker
92
- redirect_io
93
18
  run_command
94
- ensure
95
- stop
19
+ attach_screen
20
+ exit_prompt
96
21
  end
97
22
  end
98
23
 
99
- def stop(sleep_for = 0.1)
100
- begin
101
- if @worker
102
- %x[kill -2 #{@worker.pid}] # TODO: if client was term-ed, term worker (maybe term)
103
- sleep(sleep_for) if sleep_for > 0 # to finish receiving IO
104
- end
105
- rescue DRb::DRbConnError
106
- end
107
- exit(0)
24
+ private
25
+ def attach_screen
26
+ exec("screen -r theine#{@port}")
108
27
  end
109
28
 
110
- private
111
29
  def run_command
112
30
  case @argv[0]
113
31
  when "rake"
@@ -117,51 +35,22 @@ module Theine
117
35
  @argv.shift
118
36
  @worker.command_rspec(@argv)
119
37
  else
120
- if ["c", "console"].include?(@argv[0])
121
- load_pry_history
122
- end
123
38
  @worker.command_rails(@argv)
124
39
  end
125
40
  rescue DRb::DRbConnError
126
41
  $stderr.puts "\nTheine closed the connection."
127
42
  end
128
43
 
129
- def load_pry_history
130
- history_file = File.expand_path("~/.pry_history")
131
- if File.exist?(history_file)
132
- File.readlines(history_file).pop(100).each do |line|
133
- Readline::HISTORY << line[0, line.size-1]
134
- end
135
- end
136
- end
137
-
138
- def reset_argv!
139
- @argv = ARGV.dup
140
- ARGV.clear
141
- end
142
-
143
- def trap_signals
144
- trap('INT') { exit(0) } # TODO: is this needed?
145
- trap('TERM') { exit(0) }
146
- end
147
-
148
- def redirect_io
149
- # Need to be careful that these don't get garbage collected
150
- $stdin_undumped = @worker.stdin = IOUndumpedProxy.new(Readline)
151
- $stdout_undumped = @worker.stdout = IOUndumpedProxy.new($stdout)
152
- $stderr_undumped = @worker.stderr = IOUndumpedProxy.new($stderr)
153
- end
154
-
155
44
  def connect_worker
156
45
  balancer = wait_until_result("Cannot connect to theine server. Waiting") do
157
46
  object = DRbObject.new_with_uri("druby://localhost:#{config.base_port}")
158
47
  object.respond_to?(:get_port) # test if connected
159
48
  object
160
49
  end
161
- port = wait_until_result("Waiting for Theine worker...") do
50
+ @port = wait_until_result("Waiting for Theine worker...") do
162
51
  balancer.get_port
163
52
  end
164
- @worker = DRbObject.new_with_uri("druby://localhost:#{port}")
53
+ @worker = DRbObject.new_with_uri("druby://localhost:#{@port}")
165
54
  end
166
55
 
167
56
  WaitResultNoResultError = Class.new(StandardError)
data/lib/theine/server.rb CHANGED
@@ -12,7 +12,9 @@ module Theine
12
12
  @config = ConfigReader.new(Dir.pwd)
13
13
 
14
14
  @workers = []
15
- @spawning = []
15
+ @workers_in_use = []
16
+ @worker_pids = {}
17
+ @spawning_workers = []
16
18
 
17
19
  @available_ports = ((config.base_port + 1)..config.max_port).to_a
18
20
  @check_mutex = Mutex.new
@@ -23,33 +25,48 @@ module Theine
23
25
 
24
26
  def add_worker
25
27
  path = File.expand_path('../worker.rb', __FILE__)
26
- port = @available_ports.shift
28
+ port = @workers_mutex.synchronize { @available_ports.shift }
27
29
  puts "(spawn #{port})"
28
- spawn("ruby", path, config.base_port.to_s, port.to_s, config.rails_root)
29
- @workers_mutex.synchronize { @spawning << 1 }
30
+ spawn("screen", "-d", "-m", "-S", worker_session_name(port),
31
+ "sh", "-c",
32
+ "ruby #{path} #{config.base_port.to_s} #{port.to_s} #{config.rails_root}; read -p 'Press [Enter] to exit...\n'")
33
+ @workers_mutex.synchronize { @spawning_workers << port }
34
+ end
35
+
36
+ def worker_session_name(port)
37
+ "theine#{port}"
38
+ end
39
+
40
+ def set_worker_pid(port, pid)
41
+ @workers_mutex.synchronize do
42
+ @worker_pids[port] = pid
43
+ end
30
44
  end
31
45
 
32
46
  def worker_boot(port)
33
47
  puts "+ worker #{port}"
34
48
 
35
49
  @workers_mutex.synchronize do
36
- @spawning.pop
50
+ @spawning_workers.delete(port)
37
51
  @workers << port
38
52
  end
39
53
  end
40
54
 
41
55
  def worker_done(port)
42
56
  puts "- worker #{port}"
57
+ @workers_mutex.synchronize do
58
+ @workers_in_use.delete(port)
59
+ @available_ports << port
60
+ end
43
61
  end
44
62
 
45
- def get_port
46
- add_worker if all_size == 0
63
+ def get_port(spawn_new = true)
64
+ add_worker if spawn_new && all_size == 0
47
65
 
48
- port = @workers_mutex.synchronize do
49
- @workers.shift
50
- end
66
+ port = @workers_mutex.synchronize { @workers.shift }
67
+ @workers_mutex.synchronize { @workers_in_use << port } if port
51
68
 
52
- Thread.new { check_min_free_workers }
69
+ Thread.new { check_min_free_workers } if spawn_new
53
70
 
54
71
  port
55
72
  end
@@ -60,7 +77,7 @@ module Theine
60
77
  # do this in thread
61
78
  while all_size < config.min_free_workers
62
79
  unless config.spawn_parallel
63
- sleep 0.1 until @workers_mutex.synchronize { @spawning.empty? }
80
+ sleep 0.1 until @workers_mutex.synchronize { @spawning_workers.empty? }
64
81
  end
65
82
  add_worker
66
83
  end
@@ -69,10 +86,43 @@ module Theine
69
86
  end
70
87
 
71
88
  def all_size
72
- @workers_mutex.synchronize { @workers.size + @spawning.size }
89
+ @workers_mutex.synchronize { @workers.size + @spawning_workers.size }
90
+ end
91
+
92
+ def stop!
93
+ if spawning_worker_pids.include?(nil)
94
+ puts "Waiting for workers to quit..."
95
+ sleep 0.1 while spawning_worker_pids.include?(nil)
96
+ end
97
+
98
+ @workers_mutex.synchronize do
99
+ (@spawning_workers + @workers_in_use + @workers).each do |port|
100
+ kill_worker(port)
101
+ end
102
+ end
103
+ exit(0)
73
104
  end
74
105
  private
106
+ def kill_worker(port)
107
+ print "- worker #{port}"
108
+ worker_pid = @worker_pids[port]
109
+ worker_pid ||= DRbObject.new_with_uri("druby://localhost:#{port}").pid
110
+ system("kill -9 #{worker_pid} > /dev/null 2>&1")
111
+ session_name = worker_session_name(port)
112
+ system("screen -S #{session_name} -X quit > /dev/null 2>&1")
113
+ puts "."
114
+ rescue
115
+ end
116
+
117
+ def spawning_worker_pids
118
+ @spawning_workers.map { |port| @worker_pids[port] }
119
+ end
120
+
75
121
  def run
122
+ trap("INT") { stop! }
123
+ trap("TERM") { stop! }
124
+ system("screen -wipe > /dev/null 2>&1")
125
+
76
126
  DRb.start_service("druby://localhost:#{config.base_port}", self)
77
127
  check_min_free_workers
78
128
  DRb.thread.join
@@ -80,5 +130,4 @@ module Theine
80
130
  end
81
131
  end
82
132
 
83
- server = Theine::Server.new
84
-
133
+ Theine::Server.new
data/lib/theine/worker.rb CHANGED
@@ -1,139 +1,82 @@
1
- root_path = ARGV[2]
2
- APP_PATH = "#{root_path}/config/application"
3
- require "#{root_path}/config/boot"
4
- require "#{root_path}/config/environment"
1
+ RAILS_ROOT_PATH = ARGV[2]
2
+ APP_PATH = "#{RAILS_ROOT_PATH}/config/application"
5
3
  require 'drb/drb'
6
- require 'delegate'
7
-
8
- $real_stdout = $stdout
9
- $real_stderr = $stderr
10
4
 
11
5
  module Theine
12
- class InputProxy < SimpleDelegator
13
- # Reads a line from the input
14
- def readline(prompt)
15
- case readline_arity
16
- when 1 then __getobj__.readline(prompt)
17
- else __getobj__.readline
18
- end
19
- end
20
- end
21
-
22
6
  class Worker
23
- attr_reader :stdin, :stdout, :stderr
7
+ attr_reader :command_proc
24
8
 
25
9
  def initialize
26
10
  @pumps = []
11
+ @command_proc = proc { }
27
12
  end
28
13
 
29
- def command_rails(argv)
30
- rails_reload!
14
+ def boot
15
+ require "#{RAILS_ROOT_PATH}/config/boot"
16
+ require "#{RAILS_ROOT_PATH}/config/environment"
17
+ end
31
18
 
19
+ def command_rails(argv)
32
20
  ARGV.clear
33
21
  ARGV.concat(argv)
34
22
 
35
- require 'pry'
36
- ::Rails.application.config.console = ::Pry
37
- pry_setup
38
-
39
- require 'rails/commands'
40
- sleep 0.1 # allow Pumps to finish
41
- DRb.stop_service
23
+ set_command do
24
+ require 'rails/commands'
25
+ end
42
26
  end
43
27
 
44
28
  def command_rake(argv)
45
- pry_setup
46
- ::Rails.application.load_tasks
47
- argv.each do |task|
48
- ::Rake::Task[task].invoke
29
+ set_command do
30
+ ::Rails.application.load_tasks
31
+ argv.each do |task|
32
+ ::Rake::Task[task].invoke
33
+ end
49
34
  end
50
35
  end
51
36
 
52
37
  def command_rspec(argv)
53
- pry_setup
54
- require 'rspec/core'
55
- ::RSpec::Core::Runner.run(argv, $stderr, $stdout)
56
- end
57
-
58
- def pry_setup
59
- ::Pry.config.input = stdin
60
- ::Pry.config.output = stdout
61
- end
62
-
63
- def stdin=(value)
64
- @stdin = InputProxy.new(value)
65
- $stdin = @stdin
66
- end
67
-
68
- def stdout=(value)
69
- patch_out_io($stdout, value)
70
- @stdout = value
71
- r, w = IO.pipe
72
- $stdout = w
73
- @pumps << Pump.new(r, @stdout)
74
- end
75
-
76
- def stderr=(value)
77
- patch_out_io($stderr, value)
78
- @stderr = value
79
- r, w = IO.pipe
80
- $stderr = w
81
- @pumps << Pump.new(r, @stderr)
38
+ set_command do
39
+ require 'rspec/core'
40
+ ::RSpec::Core::Runner.run(argv, $stderr, $stdout)
41
+ end
82
42
  end
83
43
 
84
44
  def pid
85
45
  ::Process.pid
86
46
  end
87
47
 
48
+ def stop!
49
+ exit(1)
50
+ end
88
51
  private
89
- def patch_out_io(io, write_to)
90
- # This is done because Rails 'remembers' $stdout in some places when it's
91
- # loaded, for example for logging SQL in the Rails console.
92
- # We have to pre-load Rails and at that point we do not know what to change
93
- # $stdout to, so this is why we patch it here.
94
- io.singleton_class.send :define_method, :write do |*args, &block|
95
- write_to.write(*args, &block)
96
- end
52
+ def set_command(&block)
53
+ rails_reload!
54
+ @command_proc = block
55
+ DRb.stop_service
97
56
  end
98
57
 
99
58
  def rails_reload!
100
59
  ActionDispatch::Reloader.cleanup!
101
60
  ActionDispatch::Reloader.prepare!
102
61
  end
103
-
104
- class Pump < Thread
105
- def initialize(input, output)
106
- if output
107
- @input = input
108
- @output = output
109
- super(&method(:main))
110
- else
111
- close_stream(input)
112
- end
113
- end
114
-
115
- private
116
- def main
117
- while buf = @input.sysread(1024)
118
- @output.print(buf)
119
- @output.flush
120
- end
121
- ensure
122
- @output.close
123
- end
124
- end
125
62
  end
126
63
  end
127
64
 
128
65
  base_port = ARGV[0]
129
- worker_port = ARGV[1]
130
- DRb.start_service("druby://localhost:#{worker_port}", Theine::Worker.new)
66
+ worker_port = ARGV[1].to_i
67
+
68
+ worker = Theine::Worker.new
131
69
 
132
70
  balancer = DRbObject.new_with_uri("druby://localhost:#{base_port}")
71
+ balancer.set_worker_pid(worker_port, worker.pid)
72
+
73
+ worker.boot
74
+ DRb.start_service("druby://localhost:#{worker_port}", worker)
133
75
  balancer.worker_boot(worker_port)
134
76
 
135
77
  begin
136
78
  DRb.thread.join
79
+ worker.command_proc.call
137
80
  ensure
138
81
  balancer.worker_done(worker_port)
139
82
  end
metadata CHANGED
@@ -1,32 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theine
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: 7
5
- version: 0.0.10.rc1
4
+ version: 0.0.10
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jan Berdajs
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-10-17 00:00:00.000000000 Z
11
+ date: 2013-10-20 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: pry
16
- version_requirements: !ruby/object:Gem::Requirement
15
+ requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
17
  - - '>='
19
18
  - !ruby/object:Gem::Version
20
19
  version: '0'
21
- none: false
22
- requirement: !ruby/object:Gem::Requirement
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- none: false
28
- prerelease: false
29
- type: :runtime
30
27
  description: A Rails preloader for JRuby
31
28
  email: mrbrdo@mrbrdo.net
32
29
  executables:
@@ -49,7 +46,8 @@ files:
49
46
  homepage: https://github.com/mrbrdo/theine
50
47
  licenses:
51
48
  - MIT
52
- post_install_message:
49
+ metadata: {}
50
+ post_install_message:
53
51
  rdoc_options: []
54
52
  require_paths:
55
53
  - lib
@@ -58,17 +56,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
56
  - - '>='
59
57
  - !ruby/object:Gem::Version
60
58
  version: '0'
61
- none: false
62
59
  required_rubygems_version: !ruby/object:Gem::Requirement
63
60
  requirements:
64
- - - '>'
61
+ - - '>='
65
62
  - !ruby/object:Gem::Version
66
- version: 1.3.1
67
- none: false
63
+ version: '0'
68
64
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 1.8.24
71
- signing_key:
72
- specification_version: 3
65
+ rubyforge_project:
66
+ rubygems_version: 2.0.6
67
+ signing_key:
68
+ specification_version: 4
73
69
  summary: Theine
74
70
  test_files: []