rrails 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changes.md CHANGED
@@ -1,6 +1,18 @@
1
- v0.3.1 2012-10-13 18:08:39 +0900
1
+ v0.4.0 2012-10-15 09:56:00 +0900
2
2
  ------------------------------------------------------------------------
3
- - FIX: PAGE_SIZE constants define constants outside RemoteRails::Server namespace.
3
+ - MOD: pty support. So you can use rails console/server from rrails. (thanks quark-zju)
4
+ - MOD: pry support. (thansk quark-zju)
5
+ - MOD: add --host option. (thanks quark-zju)
6
+ - MOD: Change UNIXDomainSocket to IO.pipe. (thanks quark-zju)
7
+
8
+ v0.3.1 2012-10-13 18:22:08 +0900
9
+ ------------------------------------------------------------------------
10
+ - (tag: v0.3.1) Regenerate gemspec for version 0.3.1
11
+ - Version bump to 0.3.1
12
+ - update changes.
13
+ - it should not define constants outside RemoteRails::Server namespace.
14
+ - restyle again.
15
+ - use markdown style.
4
16
  - update doc.
5
17
 
6
18
  v0.3.0 2012-10-13 14:58:02 +0900
data/LICENSE.txt CHANGED
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2012 Keiji, Yoshimi
2
+ Copyright (c) 2012 Wu Jun <quark@lihdd.net>
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining
4
5
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # rrails
2
+
3
+ Preload rails environment in Remote server to make rails/rake commands faster.
4
+
5
+ ## Requirements
6
+
7
+ * non-Windows OS
8
+ * Ruby 1.9.3
9
+
10
+ ## Usage
11
+
12
+ Start server:
13
+
14
+ $ cd ~/rails_project
15
+ $ export RAILS_ENV=development # optionally
16
+ $ bundle exec rrails-server
17
+
18
+ Run rails/rake commands using rrails:
19
+
20
+ $ export RAILS_ENV=development # optionally
21
+ $ rrails -- rails generate model Yakiniku
22
+ $ rrails -- rake db:migrate
23
+ $ rrails -- rake routes
24
+ $ rrails -- rails server
25
+ $ rrails -- rails console
26
+ $ rrails -- pry # start pry as rails console
27
+
28
+ # If you need an interactive console for non rails *console
29
+ # commands, you may want to add '--pty' option.
30
+ # This makes sure that interactive things (like line editing)
31
+ # work correctly, but it also redirect all STDERR to STDOUT
32
+ # and keys like ^C may not work correctly.
33
+ $ rrails --pty -- rails server # use debugger
34
+
35
+ You may want to add following code to your shell rc file:
36
+
37
+ rrails-exec() {
38
+ if pgrep -f rrails-server >/dev/null && grep -q rrails Gemfile.lock &>/dev/null; then
39
+ rrails -- "$@"
40
+ else
41
+ command "$@"
42
+ fi
43
+ }
44
+ alias rails='rrails-exec rails'
45
+ alias rake='rrails-exec rake'
46
+
47
+ ## Description
48
+
49
+ rails command is too slow. and rake command is too slow under rails environment.
50
+ So, rrails can run rails/rake commands by preloaded daemon.
51
+
52
+ rails-sh is very good solution for this issue. But
53
+
54
+ * it can't run "rake -T"
55
+ * it can't use zsh's histroy.
56
+
57
+ So I wrote rrails.
58
+
59
+ ## See Also
60
+
61
+ * guard-rrails: https://github.com/walf443/guard-rrails
62
+ * rails-sh: https://github.com/jugyo/rails-sh
63
+
64
+ ## Contributing to rrails
65
+
66
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
67
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
68
+ * Fork the project.
69
+ * Start a feature/bugfix branch.
70
+ * Commit and push until you are happy with your contribution.
71
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
72
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
73
+
74
+ ## Copyright
75
+
76
+ Copyright (c) 2012 Keiji, Yoshimi. <br>
77
+ Copyright (c) 2012 Wu Jun.
78
+
79
+ See LICENSE.txt for further details.
80
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.4.0
data/lib/rrails/client.rb CHANGED
@@ -2,6 +2,8 @@ require 'socket'
2
2
  require 'rrails'
3
3
  require 'shellwords'
4
4
  require 'optparse'
5
+ require 'io/console'
6
+
5
7
  module RemoteRails
6
8
  #
7
9
  # client for RemoteRails::Server.
@@ -9,17 +11,26 @@ module RemoteRails
9
11
  # @example
10
12
  # client = RemoteRails::Client.new({
11
13
  # :cmd => "rails generate model Sushi",
12
- # :rails_env => "development",
14
+ # :port => 5656,
15
+ # :host => 'localhost'
13
16
  # })
14
17
  # client.run
15
18
  #
16
19
  class Client
17
- def self.new_with_options(argv)
18
- options = {}
20
+ def self.opts_parser(options = {})
19
21
  opts = OptionParser.new
22
+ opts.banner = 'Usage: rrails [options] [[--] commands]'
23
+ opts.on('-h', '--help', 'This help.') {|v| options[:help] = v }
24
+ opts.on('--host=s', 'RRails server hostname. Default value is "localhost".') {|v| options[:host] = v }
20
25
  opts.on('-E', '--rails_env=s') {|v| options[:rails_env] = v }
21
- opts.on('-p', '--port=i') {|v| options[:port] = v }
22
- opts.parse!(argv)
26
+ opts.on('-p', '--port=i', 'RRails server port. Default value is decided from RAILS_ENV.') {|v| options[:port] = v }
27
+ opts.on('-t', '--[no-]pty', "Prepare a PTY. Default value is decided by commands.") {|v| options[:pty] = v }
28
+ return opts
29
+ end
30
+
31
+ def self.new_with_options(argv)
32
+ options = {}
33
+ opts_parser(options).parse!(argv)
23
34
 
24
35
  cmd = Shellwords.join(argv)
25
36
  options[:cmd] = cmd == "" ? nil : cmd
@@ -27,25 +38,66 @@ module RemoteRails
27
38
  end
28
39
 
29
40
  def initialize(options={})
30
- @cmd = options[:cmd] || "rails"
31
- @rails_env = options[:rails_env] || 'development'
41
+ @cmd = options[:cmd] || ""
32
42
  @host = options[:host] || 'localhost'
43
+ @rails_env = options[:rails_env] || ENV['RAILS_ENV'] || 'development'
33
44
  @port = options[:port] || DEFAULT_PORT[@rails_env]
45
+ @use_pty = options[:pty]
46
+ if @use_pty.nil?
47
+ # decide use_pty from cmd
48
+ case @cmd
49
+ when /^rails (?:c(?:onsole)?|db(?:console)?)$/, 'pry'
50
+ @use_pty = true
51
+ end
52
+ end
34
53
  end
35
54
 
36
55
  def run
56
+ if @cmd.empty?
57
+ STDERR.puts Client.opts_parser
58
+ return
59
+ end
60
+
37
61
  sock = TCPSocket.open(@host, @port)
38
- sock.puts(@cmd)
39
- while line = sock.gets
40
- if line =~ /^FINISHED\t(.+)/
41
- $stdout.puts("\nfinished (#{$1}sec)")
42
- return
43
- elsif line =~ /^OUT\t(.+)$/
44
- $stdout.puts($1)
45
- elsif line =~ /^ERROR\t(.+)$/
46
- $stderr.puts($1)
62
+ sock.puts("#{@use_pty ? 'P' : ' '}#@cmd")
63
+ running = true
64
+
65
+ begin
66
+ # input thread
67
+ thread = Thread.start do
68
+ while running do
69
+ begin
70
+ input = @use_pty ? STDIN.getch : STDIN.gets
71
+ sock.write(input)
72
+ sock.flush
73
+ rescue
74
+ running = false
75
+ sock.close unless sock.closed?
76
+ end
77
+ end
78
+ end
79
+
80
+ while running && line = sock.gets
81
+ case line.chomp
82
+ when /^EXIT\t(.+)$/
83
+ exit($1.to_i)
84
+ when /^OUT\t(.+)$/
85
+ STDOUT.write($1.split(',').map(&:to_i).pack('c*'))
86
+ when /^ERR\t(.+)$/
87
+ STDERR.write($1.split(',').map(&:to_i).pack('c*'))
88
+ end
47
89
  end
90
+
91
+ rescue EOFError
92
+ running = false
93
+ rescue Interrupt
94
+ running = false
95
+ exit 130
48
96
  end
97
+
98
+ STDERR.puts "\nERROR: RRails server disconnected"
99
+ exit -1
49
100
  end
101
+
50
102
  end
51
103
  end
data/lib/rrails/server.rb CHANGED
@@ -4,6 +4,8 @@ require 'logger'
4
4
  require 'rake'
5
5
  require 'stringio'
6
6
  require 'shellwords'
7
+ require 'pty'
8
+ require 'benchmark'
7
9
 
8
10
  # FIXME: rails command require APP_PATH constants.
9
11
  APP_PATH = File.expand_path('./config/application')
@@ -18,7 +20,7 @@ module RemoteRails
18
20
  PAGE_SIZE = 4096
19
21
 
20
22
  def initialize(options={})
21
- @rails_env = options[:rails_env] || "development"
23
+ @rails_env = options[:rails_env] || ENV['RAILS_ENV'] || "development"
22
24
  @app_path = File.expand_path('./config/application')
23
25
  # should not access to outside
24
26
  @host = 'localhost'
@@ -30,14 +32,14 @@ module RemoteRails
30
32
  self.boot_rails
31
33
  server = TCPServer.open(@host, @port)
32
34
  @logger.info("starting rrails server on #{@host}:#{@port}")
33
- trap(:INT) do
34
- @logger.info("SIGINT recieved. shutdown...")
35
- exit
36
- end
37
- trap(:TERM) do
38
- @logger.info("SIGTERM recieved. shutdown...")
39
- exit
35
+
36
+ [:INT, :TERM].each do |sig|
37
+ trap(sig) do
38
+ @logger.info("SIG#{sig} recieved. shutdown...")
39
+ exit
40
+ end
40
41
  end
42
+
41
43
  trap(:HUP) do
42
44
  @logger.info("SIGHUP recieved. reload...")
43
45
  ActionDispatch::Callbacks.new(Proc.new {}).call({})
@@ -46,31 +48,29 @@ module RemoteRails
46
48
  Thread.abort_on_exception = true
47
49
  loop do
48
50
  Thread.start(server.accept) do |s|
49
- childpids = []
50
51
  begin
51
- while line = s.gets
52
- line.chomp!
53
- @logger.info("invoke: #{line}")
54
- start = Time.now
55
- self.dispatch(s, line) { |pid| childpids << pid }
56
- finish = Time.now
57
- s.puts("FINISHED\t#{ finish - start }")
58
- @logger.info("finished: #{line}")
52
+ line = s.gets.chomp
53
+ pty, line = (line[0] == 'P'), line[1..-1]
54
+ @logger.info("invoke: #{line} (pty=#{pty})")
55
+ status = nil
56
+ time = Benchmark.realtime do
57
+ status = dispatch(s, line, pty)
59
58
  end
60
- rescue Errno::EPIPE => e
61
- Process.kill 'TERM', *childpids unless childpids.empty?
62
- @logger.error("client disconnect: " + e.message)
59
+ exitcode = status ? status.exitstatus || (status.termsig + 128) : 0
60
+ s.puts("EXIT\t#{exitcode}")
61
+ @logger.info("finished: #{line} (#{time} seconds)")
62
+ rescue Errno::EPIPE
63
+ @logger.info("disconnected: #{line}")
63
64
  end
64
65
  end
65
66
  end
66
67
  end
67
68
 
68
69
  def boot_rails
69
- @logger.info("prepare rails environment")
70
+ @logger.info("prepare rails environment (#{@rails_env})")
70
71
  ENV["RAILS_ENV"] = @rails_env
71
- require File.expand_path('./config/boot')
72
- require @app_path
73
- Rails.application.require_environment!
72
+ require File.expand_path('./config/environment')
73
+
74
74
  unless Rails.application.config.cache_classes
75
75
  ActionDispatch::Reloader.cleanup!
76
76
  ActionDispatch::Reloader.prepare!
@@ -78,38 +78,89 @@ module RemoteRails
78
78
  @logger.info("finished preparing rails environment")
79
79
  end
80
80
 
81
- def dispatch(sock, line)
82
- servsock_out, clisock_out = UNIXSocket.pair
83
- servsock_err, clisock_err = UNIXSocket.pair
81
+ def dispatch(sock, line, pty=false)
82
+
83
+ if pty
84
+ m_out, c_out = PTY.open
85
+ c_in = c_err = c_out
86
+ m_fds = [m_out, c_out]
87
+ c_fds = [c_out]
88
+ clisocks = {in: m_out, out: m_out}
89
+ else
90
+ c_in, m_in = IO.pipe
91
+ m_out, c_out = IO.pipe
92
+ m_err, c_err = IO.pipe
93
+ m_fds = [m_in, m_out, m_err]
94
+ c_fds = [c_in, c_out, c_err]
95
+ clisocks = {in: m_in, out: m_out, err: m_err}
96
+ end
97
+
98
+ running = true
84
99
  pid = fork do
85
- clisock_out.close
86
- clisock_err.close
100
+ m_fds.map(&:close) if not pty
101
+ STDIN.reopen(c_in)
102
+ STDOUT.reopen(c_out)
103
+ STDERR.reopen(c_err)
87
104
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
88
- STDOUT.reopen(servsock_out)
89
- STDERR.reopen(servsock_err)
90
105
  execute *Shellwords.shellsplit(line)
91
106
  end
92
- yield pid
93
- servsock_out.close
94
- servsock_err.close
95
- buffers = {out: '', error: ''}
96
- clisocks = {out: clisock_out, error: clisock_err}
107
+
108
+ c_fds.map(&:close) if not pty
109
+
110
+ # pump input. since it will block, make it in another thread
111
+ thread = Thread.start do
112
+ while running do
113
+ begin
114
+ input = sock.__send__(pty ? :getc : :gets)
115
+ rescue => ex
116
+ @logger.debug "input thread got #{ex}"
117
+ running = false
118
+ end
119
+ clisocks[:in].write(input) rescue nil
120
+ end
121
+ end
122
+
97
123
  loop do
98
- [:out, :error].each do |channel|
124
+ [:out, :err].each do |channel|
125
+ next if not clisocks[channel]
99
126
  begin
100
- buffers[channel] << clisocks[channel].read_nonblock(PAGE_SIZE)
101
- while buffers[channel][/[\n\r]/]
102
- line, buffers[channel] = buffers[channel].split(/[\n\r]/, 2)
103
- sock.puts("#{channel.upcase}\t#{line}")
127
+ loop do
128
+ response = clisocks[channel].read_nonblock(PAGE_SIZE)
129
+ sock.puts("#{channel.upcase}\t#{response.bytes.to_a.join(',')}")
130
+ sock.flush
104
131
  end
105
132
  rescue Errno::EAGAIN, EOFError => ex
106
- sleep 0.01
133
+ next
107
134
  end
108
135
  end
109
- if Process.waitpid(pid, Process::WNOHANG)
110
- return
136
+
137
+ if running
138
+ _, stat = Process.waitpid2(pid, Process::WNOHANG)
139
+ if stat
140
+ @logger.debug "child exits. #{stat}"
141
+ return stat
142
+ end
111
143
  end
144
+
145
+ # send heartbeat so that we got EPIPE immediately when client dies
146
+ sock.puts("PING")
147
+ sock.flush
148
+
149
+ # do not make CPU hot
150
+ sleep 0.1
112
151
  end
152
+ ensure
153
+ running = false
154
+ [*c_fds, *m_fds].each {|io| io.close unless io.closed?}
155
+ if pid
156
+ begin
157
+ Process.kill 0, pid
158
+ @logger.debug "killing pid #{pid}"
159
+ Process.kill 'TERM', pid rescue nil
160
+ rescue Errno::ESRCH
161
+ end
162
+ end
163
+ thread.kill if thread
113
164
  end
114
165
 
115
166
  def execute(cmd, *args)
@@ -120,9 +171,14 @@ module RemoteRails
120
171
  require 'rails/commands'
121
172
  when 'rake'
122
173
  ::Rake.application.run
174
+ when 'pry'
175
+ begin
176
+ ::Pry::CLI.parse_options
177
+ rescue NameError
178
+ STDERR.puts "if you want to use pry. you should add 'pry' to Gemfile."
179
+ end
123
180
  else
124
- @logger.warn "#{cmd} not supported"
125
- raise RuntimeError.new("#{cmd} is not supported in rrails.")
181
+ STDERR.puts "#{cmd} is not supported in RRails."
126
182
  end
127
183
  end
128
184
 
data/rrails.gemspec CHANGED
@@ -5,17 +5,17 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "rrails"
8
- s.version = "0.3.1"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Keiji, Yoshimi"]
12
- s.date = "2012-10-13"
12
+ s.date = "2012-10-15"
13
13
  s.description = "remote run rails/rake command"
14
14
  s.email = "walf443@gmail.com"
15
15
  s.executables = ["rrails", "rrails-server"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
- "README.rdoc"
18
+ "README.md"
19
19
  ]
20
20
  s.files = [
21
21
  ".document",
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  "Gemfile",
24
24
  "Gemfile.lock",
25
25
  "LICENSE.txt",
26
- "README.rdoc",
26
+ "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
29
  "bin/rrails",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rrails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-13 00:00:00.000000000 Z
12
+ date: 2012-10-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: shoulda
@@ -99,14 +99,14 @@ executables:
99
99
  extensions: []
100
100
  extra_rdoc_files:
101
101
  - LICENSE.txt
102
- - README.rdoc
102
+ - README.md
103
103
  files:
104
104
  - .document
105
105
  - Changes.md
106
106
  - Gemfile
107
107
  - Gemfile.lock
108
108
  - LICENSE.txt
109
- - README.rdoc
109
+ - README.md
110
110
  - Rakefile
111
111
  - VERSION
112
112
  - bin/rrails
@@ -132,7 +132,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
132
  version: '0'
133
133
  segments:
134
134
  - 0
135
- hash: -2945796966926352412
135
+ hash: 3003535707070342098
136
136
  required_rubygems_version: !ruby/object:Gem::Requirement
137
137
  none: false
138
138
  requirements:
data/README.rdoc DELETED
@@ -1,51 +0,0 @@
1
- = rrails
2
-
3
- remote run rails/rake command.
4
-
5
- == Usage
6
-
7
- # run server
8
- $ cd ~/rails_project
9
- $ bundle exec rrails-server
10
-
11
- # on another console.
12
- $ rrails -- rails generate model Yakiniku
13
- $ rrails -E test -- rake db:migrate
14
-
15
- == Description
16
-
17
- rails command is too slow. and rake command is too slow under rails environment.
18
- So, rrails can run rails/rake commands by preloaded daemon.
19
-
20
- rails-sh is very good solution for this issue. But
21
- * it can't run "rake -T"
22
- * it can't use zsh's histroy.
23
-
24
- So I wrote rrails.
25
-
26
- == Problem
27
-
28
- rrails can't work folloing commands:
29
- * rake (console|dbconsole): please use rails-sh
30
- * rake server
31
-
32
- == SEE ALSO
33
-
34
- * guard-rrails: https://github.com/walf443/guard-rrails
35
- * rails-sh: https://github.com/jugyo/rails-sh
36
-
37
- == Contributing to rrails
38
-
39
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
40
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
41
- * Fork the project.
42
- * Start a feature/bugfix branch.
43
- * Commit and push until you are happy with your contribution.
44
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
45
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
46
-
47
- == Copyright
48
-
49
- Copyright (c) 2012 Keiji, Yoshimi. See LICENSE.txt for
50
- further details.
51
-