theine 0.0.10.rc1 → 0.0.10

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.
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: []