From 702ecadbb8f84e3972485d51a3953496bfd93e86 Mon Sep 17 00:00:00 2001 From: mnussbaum Date: Fri, 25 Oct 2013 11:57:14 -0500 Subject: [PATCH] Adds '--choose-env' flag to use rbenv if available, otherwise rvm --- .gitignore | 1 + README.md | 17 ++++++++++- lib/subcontractor/cli.rb | 30 ++++++++----------- lib/subcontractor/command.rb | 40 +++++++++++++++++++++++++ spec/spec_helper.rb | 6 ++++ spec/subcontractor/cli_spec.rb | 54 ++++++++++++++++++++++++++++++++++ subcontractor.gemspec | 2 ++ 7 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 lib/subcontractor/command.rb create mode 100644 spec/spec_helper.rb create mode 100644 spec/subcontractor/cli_spec.rb diff --git a/.gitignore b/.gitignore index 4040c6c..b5a68ee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .bundle Gemfile.lock pkg/* +/vendor/bundle diff --git a/README.md b/README.md index d00d6f2..91a73a3 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ The gem provides an executable called ```subcontract``` that you will use from y ``` USAGE: subcontract [options] -- executable -r, --rvm RVM run in a specific RVM (use `.` for ruby from `PATH`) - -b, --rbenv RBENV run in a specific RBENV + -b, --rbenv RBENV run in a specific RBENV (use `.` for local rbenv) + -c, --choose-env ENV run in either a specified RBENV or RVM, whichever is present -d, --chdir PATH chdir to PATH before starting process -s, --signal SIGNAL signal to send to process to kill it, default TERM ``` @@ -53,6 +54,19 @@ You can use specific rbenv version. rbenv_app: bundle exec subcontract --rbenv 'ree-1.8.7-2012.02' --chdir ~/rbenv_app -- bundle exec rails server -p 3001 ``` +Or you can use whatever the local rbenv settings are for a project. This will load whatever `rbenv local` returns out of the current folder once it has been changed to the folder specified by --chdir + +``` +rbenv_app: bundle exec subcontract --rbenv . --chdir ~/rbenv_app -- bundle exec rails server -p 3001 +``` + +If you have team members using both rvm and rbenv on a project then use --choose-env to use whatever version manager is present on the system. If both are present rbenv is chosen. + +``` +mixed_env_manager_app: bundle exec subcontract --choose-env . --chdir ~/mixed_env_manager_app -- bundle exec rails server -p 3001 +``` + + ### Contributions * Fork the project * Make your change @@ -65,3 +79,4 @@ rbenv_app: bundle exec subcontract --rbenv 'ree-1.8.7-2012.02' --chdir ~/rbenv_a * Paul Gross [github](http://github.com/pgr0ss) [blog](http://www.pgrs.net) [twitter](http://twitter.com/pgr0ss) * Rune Skjoldborg Madsen [github](https://github.com/runemadsen) * Masahiro Ihara [github](http://github.com/ihara2525) [twitter](http://twitter.com/ihara2525) +* Michael Nussbaum [github](https://github.com/mnussbaum) diff --git a/lib/subcontractor/cli.rb b/lib/subcontractor/cli.rb index bdd30c1..0e62ded 100644 --- a/lib/subcontractor/cli.rb +++ b/lib/subcontractor/cli.rb @@ -1,11 +1,12 @@ -require 'optparse' -require 'pty' +require "optparse" +require "pty" +require "subcontractor/command" $stdout.sync = true module SafePty def self.spawn command, &block - if Object.const_defined?('Bundler') + if Object.const_defined?("Bundler") Bundler.with_clean_env do self.clear_more_env self.spawn_internal command, &block @@ -29,16 +30,15 @@ def self.spawn_internal command, &block end def self.clear_more_env - ['GEM_HOME', 'GEM_PATH', 'RUBYOPT', 'RBENV_DIR'].each { |e| ENV.delete(e) } + ["GEM_HOME", "GEM_PATH", "RUBYOPT", "RBENV_DIR"].each { |e| ENV.delete(e) } end end module Subcontractor class CLI - def run options = parse_options(ARGV) - command = build_command(ARGV.dup, options) + command = Subcontractor::Command.build(ARGV, options) Dir.chdir(options[:chdir]) if options[:chdir] signal = options[:signal] || "TERM" SafePty.spawn(command) do |stdin, stdout, pid| @@ -74,26 +74,23 @@ def find_child_pids(pids) end.map(&:first).map(&:to_i) end - def build_command(parts, options) - parts.unshift("rvm #{options[:rvm]} exec") if options.has_key?(:rvm) - parts.unshift("env RBENV_VERSION=#{options[:rbenv]} rbenv exec") if options.has_key?(:rbenv) - parts.join(' ') - end - def parse_options(argv) options = {} parser = OptionParser.new do |opt| opt.banner = "USAGE: subcontract [options] -- executable" - opt.on('-r', '--rvm RVM', 'run in a specific RVM') do |rvm| + opt.on("-r", "--rvm RVM", "run in a specific RVM") do |rvm| options[:rvm] = rvm end - opt.on('-b', '--rbenv RBENV', 'run in a specific RBENV') do |rbenv| + opt.on("-b", "--rbenv RBENV", "run in a specific RBENV") do |rbenv| options[:rbenv] = rbenv end - opt.on('-d', '--chdir PATH', 'chdir to PATH before starting process') do |path| + opt.on("-c", "--choose-env ENV", "run in either specified RBENV or RVM, whichever is present") do |env| + options[:choose_env] = env + end + opt.on("-d", "--chdir PATH", "chdir to PATH before starting process") do |path| options[:chdir] = path end - opt.on('-s', '--signal SIGNAL', 'signal to send to process to kill it, default TERM') do |signal| + opt.on("-s", "--signal SIGNAL", "signal to send to process to kill it, default TERM") do |signal| options[:signal] = signal end end @@ -101,6 +98,5 @@ def parse_options(argv) parser.parse! argv options end - end end diff --git a/lib/subcontractor/command.rb b/lib/subcontractor/command.rb new file mode 100644 index 0000000..fd408e0 --- /dev/null +++ b/lib/subcontractor/command.rb @@ -0,0 +1,40 @@ +module Subcontractor + class Command + def self.build(parts, options) + new(parts.dup, options).build + end + + def initialize(parts, options) + @parts = parts + @options = options + end + + def build + if _use_command?(:rbenv) + @parts.unshift("#{_set_rbenv_version} rbenv exec") + elsif _use_command?(:rvm) + @parts.unshift("rvm #{_env_specifier(:rvm)} exec") + end + + @parts.join(" ") + end + + def _use_command?(command) + @options.has_key?(command) || _choose_env_and_command_present?(command) + end + + def _choose_env_and_command_present?(command) + @options.has_key?(:choose_env) && system("which #{command} > /dev/null 2>&1") + end + + def _set_rbenv_version + env_specifier = _env_specifier(:rbenv) + env_specifier = "`rbenv local`" if env_specifier == "." + "env RBENV_VERSION=#{env_specifier}" + end + + def _env_specifier(command) + @options[command] || @options[:choose_env] + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..7f905e6 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require "subcontractor" +require "subcontractor/cli" + +RSpec.configure do |config| + config.color = true +end diff --git a/spec/subcontractor/cli_spec.rb b/spec/subcontractor/cli_spec.rb new file mode 100644 index 0000000..e2b6ac7 --- /dev/null +++ b/spec/subcontractor/cli_spec.rb @@ -0,0 +1,54 @@ +require "spec_helper" + +describe Subcontractor::CLI do + before(:each) do + Object.instance_eval{ remove_const(:ARGV) } + end + + describe "#run" do + it "uses rvm with --rvm" do + ARGV = ["--rvm", ".", "test"] + SafePty.should_receive(:spawn).with("rvm . exec test") + Subcontractor::CLI.new.run + end + + context "with --rbenv" do + it "specifies a rbenv" do + ARGV = ["--rbenv", "1.9.3", "test"] + SafePty.should_receive(:spawn).with("env RBENV_VERSION=1.9.3 rbenv exec test") + Subcontractor::CLI.new.run + end + + it "uses 'rbenv local' if a '.' is given as the rbenv version" do + ARGV = ["--rbenv", ".", "test"] + SafePty.should_receive(:spawn).with("env RBENV_VERSION=`rbenv local` rbenv exec test") + Subcontractor::CLI.new.run + end + end + + it "creates a valid command if no environment manager is specifed" do + ARGV = ["test"] + SafePty.should_receive(:spawn).with("test") + Subcontractor::CLI.new.run + end + + context "with --choose-env" do + it "uses rbenv when rbenv is present" do + ARGV = ["--choose-env", "1.9.3", "test"] + SafePty.should_receive(:spawn).with("env RBENV_VERSION=1.9.3 rbenv exec test") + command = Subcontractor::Command.any_instance + command.should_receive(:system).with("which rbenv > /dev/null 2>&1").and_return(true) + Subcontractor::CLI.new.run + end + + it "uses rvm when rvm is present and rbenv isn't" do + ARGV = ["--choose-env", ".", "test"] + SafePty.should_receive(:spawn).with("rvm . exec test") + command = Subcontractor::Command.any_instance + command.should_receive(:system).with("which rbenv > /dev/null 2>&1").and_return(false) + command.should_receive(:system).with("which rvm > /dev/null 2>&1").and_return(true) + Subcontractor::CLI.new.run + end + end + end +end diff --git a/subcontractor.gemspec b/subcontractor.gemspec index 5c2b993..353f37e 100644 --- a/subcontractor.gemspec +++ b/subcontractor.gemspec @@ -14,6 +14,8 @@ Gem::Specification.new do |s| s.rubyforge_project = "subcontractor" + s.add_development_dependency("rspec") + s.files = `git ls-files`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"]