From 1a5f0142dfae6d2d0558aac2427c9830d0ced071 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Tue, 5 Jun 2012 15:29:27 -0400 Subject: [PATCH] yeah it's all consolidated now --- README.md | 47 ++++---- bin/penchant | 4 +- features/gemfile.feature | 26 +---- features/ruby_gemfile.feature | 1 - lib/penchant/gemfile.rb | 204 ++++++++++++++++++---------------- 5 files changed, 144 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 372f8ad..50b6cef 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ I like to do these things in all my projects: * Have all my tests run before committing. I don't like buying ice cream for the team on test failures. -* If I'm developing gems alongside this project, I use a `Gemfile.erb` to get around the "one gem, one source" issue in +* If I'm developing gems alongside this project, I use a `Gemfile.penchant` to get around the "one gem, one source" issue in current versions of Bundler. * If I'm moving to different machines or (heaven forbid!) having other developers work on the project, I want to make getting all those local gems as easy as possible. @@ -14,27 +14,36 @@ This gem makes that easier! Installs a bunch of scripts into the `scripts` directory of your project: -* `gemfile` which switches between `Gemfile.erb` environments +* `gemfile` which switches between `Gemfile.penchant` environments * `install-git-hooks` which will do just what it says * `hooks`, several git hooks that the prior script symlinks into .git/hooks for you * `initialize-environment`, which bootstraps your local environment so you can get up and running -## Gemfile.erb?! +## Gemfile.penchant?! -Yeah, it's a `Gemfile` with ERB in it: +Yeah, it's a `Gemfile` with some extras: -``` erb -<% env :local do %> - gem 'guard', :path => '../guard' -<% end %> +``` ruby +source :rubygems -<% env :remote do %> - gem 'guard', :git => 'git://github.com/johnbintz/guard.git' -<% end %> +gem 'rails', '3.2.3' +gems 'rake', 'nokogiri', 'rack-rewrite' -<% no_deployment do %> - gem 'os-specific-things' -<% end %> +no_deployment do + group :development, :test do + gem 'rspec', '~> 2.6.0' + + dev_gems = %w{flowerbox guard-flowerbox} + + env :local do + gems dev_gems, :path => '../%s' + end + + env :remote do + gems dev_gems, :git => 'git://github.com/johnbintz/%s.git' + end + end +end ``` Use `script/gemfile local` to get at the local ones, and `script/gemfile remote` to get at the remote ones. @@ -49,8 +58,8 @@ remote servers. *Very* helpful when you have OS-specific gems and are developing and deploying on another, or if you don't want to deal with the dependencies for your testing frameworks: -``` erb -<% no_deployment do %> +``` ruby +no_deployment do require 'rbconfig' case RbConfig::CONFIG['host_os'] when /darwin/ @@ -64,7 +73,7 @@ frameworks: group :test do # ... all your testing libraries you won't need on the deployed end ... end -<% end %> +end ``` Run `penchant gemfile ENV --deployment` to get this behavior. This is run by default when the @@ -74,8 +83,8 @@ pre-commit git hook runs, but only after the default Rake task passes. Get new developers up to speed fast! `script/initialize-environment` does the following when run: -* Check out any remote repos found in `Gemfile.erb` to the same directory where your current project lives. - That way, you can have your `Gemfile.erb` set up as above and everything works cleanly. +* Check out any remote repos found in `Gemfile.penchant` to the same directory where your current project lives. + That way, you can have your `Gemfile.penchant` set up as above and everything works cleanly. * Runs `script/gemfile remote` to set your project to using remote repositories. * Runs `rake bootstrap` for the project if it exists. diff --git a/bin/penchant b/bin/penchant index ca6fffb..8861946 100755 --- a/bin/penchant +++ b/bin/penchant @@ -30,7 +30,7 @@ class PenchantCLI < Thor method_options :dir => 'script' def convert install - FileUtils.mv 'Gemfile', 'Gemfile.erb' + FileUtils.mv 'Gemfile', 'Gemfile.penchant' gemfile(:remote) end @@ -52,7 +52,7 @@ class PenchantCLI < Thor gemfile = Penchant::Gemfile.new if !gemfile.has_gemfile? - puts "No Gemfile or Gemfile.erb, exiting." + puts "No Gemfile or Gemfile.penchant, exiting." exit 1 end system %{bundle} diff --git a/features/gemfile.feature b/features/gemfile.feature index 890cf84..290db10 100644 --- a/features/gemfile.feature +++ b/features/gemfile.feature @@ -49,7 +49,7 @@ Feature: Gemfiles Scenario: Use a gem list for an operation Given I have the file "Gemfile.erb" with the content: """ - <% with_gem_list 'test' do %> + <% gems 'test' do %> <% env :local, :path => '../%s' do %> <%= gem %> <% end %> @@ -65,7 +65,7 @@ Feature: Gemfiles Scenario: Let gem get additional info Given I have the file "Gemfile.erb" with the content: """ - <% with_gem_list 'test' do %> + <% gems 'test' do %> <%= gem :path => '../%s' %> <% end %> """ @@ -74,38 +74,24 @@ Feature: Gemfiles """ # generated by penchant, environment: local - - gem 'test', :path => %{../test} + gem 'test', {:path=>"../test"} """ Scenario: Use a gem list without a block Given I have the file "Gemfile.erb" with the content: """ - <% with_gem_list 'test', :path => '../%s' %> + <% gems 'test', :path => '../%s' %> """ When I rebuild the Gemfile for "local" mode Then the file "Gemfile" should have the following content: """ # generated by penchant, environment: local - gem 'test', :path => %{../test} + gem 'test', {:path=>"../test"} """ Scenario: Use a gem list with an array - Given I have the file "Gemfile.erb" with the content: - """ - <% with_gem_list [ 'test' ], :path => '../%s' %> - """ - When I rebuild the Gemfile for "local" mode - Then the file "Gemfile" should have the following content: - """ - # generated by penchant, environment: local - gem 'test', :path => %{../test} - - """ - - Scenario: Use another gem list with an array Given I have the file "Gemfile.erb" with the content: """ <% gems [ 'test' ], :path => '../%s' %> @@ -114,7 +100,7 @@ Feature: Gemfiles Then the file "Gemfile" should have the following content: """ # generated by penchant, environment: local - gem 'test', :path => %{../test} + gem 'test', {:path=>"../test"} """ diff --git a/features/ruby_gemfile.feature b/features/ruby_gemfile.feature index da0460a..f9391e8 100644 --- a/features/ruby_gemfile.feature +++ b/features/ruby_gemfile.feature @@ -49,7 +49,6 @@ Feature: Gemfiles gem "three", {:path=>"../three"} """ - @wip Scenario: Use an env block Given I have the file "Gemfile.penchant" with the content: """ diff --git a/lib/penchant/gemfile.rb b/lib/penchant/gemfile.rb index 96ac61b..4e504f6 100644 --- a/lib/penchant/gemfile.rb +++ b/lib/penchant/gemfile.rb @@ -74,31 +74,128 @@ module Penchant gemfile_header['deployment mode'] != nil end - class ERBFile - end + class FileProcessor + attr_reader :environment, :is_deployment - class PenchantFile def self.result(data, *args) new(data).result(*args) end + def self.handle_result(&block) + if block + @handle_result = block + else + @handle_result + end + end + def initialize(data) @data = data end - attr_reader :environment, :is_deployment - def result(_env, _is_deployment) @environment = _env.to_s.to_sym @is_deployment = _is_deployment @output = [] - instance_eval(@data) + handle_result(@data) @output.join("\n") end + def env(*args) + yield if args.include?(environment) + end + + def no_deployment + yield if !is_deployment + end + + protected + def args_to_string(args) + args.inspect[1..-2] + end + + def split_args(args) + template = {} + + while args.last.instance_of?(Hash) + template.merge!(args.pop) + end + + [ args, template ] + end + + def call_and_indent_output(block) + index = @output.length + block.call + index.upto(@output.length - 1) do |i| + @output[i] = " " + @output[i] + end + end + + def process_options(gem_name, template = {}) + Hash[ + template.collect { |key, value| + value = value % gem_name if value.respond_to?(:%) + + [ key, value ] + }.sort + ] + end + end + + class ERBFile < FileProcessor + def handle_result(data) + @output << ERB.new(data, nil, nil, '@_erbout').result(binding) + end + + def env(check, template = {}, &block) + if check.to_s == @env.to_s + original_erbout = @_erbout.dup + + output = instance_eval(&block).lines.to_a + + output.each do |line| + if gem_name = line[%r{gem ['"]([^'"]+)['"]}, 1] + line.replace(line.rstrip + process_options(gem_name, template) + "\n") + end + end + + @_erbout = original_erbout + output.join + end + end + + def gems(*gems) + template = {} + template = gems.pop if gems.last.instance_of?(Hash) + + gems.flatten.each do |gem| + @_current_gem = gem + if block_given? + yield + else + @_erbout += gem(template) + "\n" + end + end + end + + def gem(template = {}) + output = "gem '#{@_current_gem}'" + options = process_options(@_current_gem, template) + if !options.empty? + output += ", #{options.inspect}" + end + output + end + end + + class PenchantFile < FileProcessor + def handle_result(data) + instance_eval(data) + end + def gem(*args) gem_name, template = split_args(args) @@ -135,47 +232,6 @@ module Penchant def source(*args) @output << %{source #{args_to_string(args)}} end - - def env(*args) - yield if args.include?(environment) - end - - def no_deployment - yield if !is_deployment - end - - private - def args_to_string(args) - args.inspect[1..-2] - end - - def split_args(args) - template = {} - - while args.last.instance_of?(Hash) - template.merge!(args.pop) - end - - [ args, template ] - end - - def call_and_indent_output(block) - index = @output.length - block.call - index.upto(@output.length - 1) do |i| - @output[i] = " " + @output[i] - end - end - - def process_options(gem_name, template = {}) - Hash[ - template.collect { |key, value| - value = value % gem_name if value.respond_to?(:%) - - [ key, value ] - }.sort - ] - end end def switch_to!(gemfile_env = nil, deployment = false) @@ -214,64 +270,20 @@ module Penchant end def process(template) - case File.extname(processable_gemfile_path) + builder = case File.extname(processable_gemfile_path) when '.penchant' - PenchantFile.result(template, @env, @is_deployment) + PenchantFile when '.erb' - ERB.new(template, nil, nil, '@_erbout').result(binding).lines.to_a + ERBFile end + + builder.result(template, @env, @is_deployment) end def template File.read(processable_gemfile_path) end - def env(check, template = {}, &block) - if check.to_s == @env.to_s - original_erbout = @_erbout.dup - - output = instance_eval(&block).lines.to_a - - output.each do |line| - if gem_name = line[%r{gem ['"]([^'"]+)['"]}, 1] - line.replace(line.rstrip + options_to_string(gem_name, template) + "\n") - end - end - - @_erbout = original_erbout + output.join - end - end - - def with_gem_list(*gems) - template = {} - template = gems.pop if gems.last.instance_of?(Hash) - - gems.flatten.each do |gem| - @_current_gem = gem - if block_given? - yield - else - @_erbout += gem(template) + "\n" - end - end - end - - alias :gems :with_gem_list - - def gem(template = {}) - "gem '#{@_current_gem}'" + options_to_string(@_current_gem, template) - end - - def options_to_string(gem_name, template = {}) - template.collect do |key, value| - ", #{key.inspect} => %{#{value % gem_name}}" - end.join - end - - def no_deployment(&block) - instance_eval(&block) if !@is_deployment - end - def gemfile_header (has_gemfile? and File.readlines(gemfile_path).first) or "" end