Skip to content

Instantly share code, notes, and snippets.

@gbuesing
Last active December 10, 2015 02:09
Show Gist options
  • Select an option

  • Save gbuesing/4365756 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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
Copy Markdown
Author

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

@gbuesing
Copy link
Copy Markdown
Author

gbuesing commented Jan 3, 2013

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