Skip to content

Instantly share code, notes, and snippets.

@gbuesing
Last active December 10, 2015 02:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gbuesing/4365756 to your computer and use it in GitHub Desktop.
Save gbuesing/4365756 to your computer and use it in GitHub Desktop.
Rails commands multi-process demo
#!/usr/bin/env ruby
# Rails commands multi-process demo
#
# For use with https://github.com/rails/commands
#
# Put this file in an app in script/superconsole (or whatever name) and chmod +x
# or just put this in lib/superconsole.rb.
#
# This script opens the console in the development env, and forks a child process
# with the app in the test env.
#
# Console can send commands to the test env via the "testenv" method, e.g.:
#
# testenv.rake 'about'
#
# This message is sent to the testenv via a Unix socket
require 'socket'
# create unix socket to enable message passing between parent process (dev env) and child (test env)
CHILD_SOCKET, PARENT_SOCKET = Socket.pair(:UNIX, :DGRAM, 0)
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
# load gems before forking
Bundler.require(:default, :assets)
# fork for test environment
child_pid = fork do
ENV['RAILS_ENV'] = Rails.env = 'test'
require APP_PATH
Rails.application.require_environment!
trap(:INT) { exit(0) } # close silently
commander = Rails::Commands::Commander.new
loop do
msg = CHILD_SOCKET.recv(1000)
# puts "[#{Rails.env}] received message: #{msg.inspect}\n"
argv = msg.split(' ')
command = argv.shift
args = argv.join(' ')
args = nil unless args.match(/\S/)
retval = if commander.respond_to?(command)
commander.send(command, args)
else
'Unrecognized command'
end
CHILD_SOCKET.write(retval)
end
end
# cleanup: kill child process at exit
at_exit do
Process.kill(:INT, child_pid)
end
# enable communication between dev console and test env child process
module TestEnv
def self.send msg
PARENT_SOCKET.write(msg)
PARENT_SOCKET.recv(1000)
end
def self.method_missing(m, *args, &block)
send "#{m} #{args.join(' ')}"
end
end
module ConsoleDelegationOverride
def testenv
TestEnv
end
def test *args
testenv.test *args
end
end
require 'rails/commands/console'
require APP_PATH
Rails.application.require_environment!
Rails::ConsoleMethods.send :include, ConsoleDelegationOverride
Rails::Console.start(Rails.application)
@gbuesing
Copy link
Author

Should be easy enough to have the bare "test" command delegate to testenv.test in the parent process, and actually run the tests in the child.

@gbuesing
Copy link
Author

Added ConsoleDelegationOverride, so that the bare test command now runs in child process

@gbuesing
Copy link
Author

gbuesing commented Jan 3, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment