appraisal 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+ gem "cucumber"
3
+ gem "aruba"
4
+ gem "rake"
5
+ gem "rspec"
@@ -0,0 +1,38 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ aruba (0.2.4)
5
+ background_process
6
+ cucumber (~> 0.9.3)
7
+ background_process (1.2)
8
+ builder (2.1.2)
9
+ cucumber (0.9.4)
10
+ builder (~> 2.1.2)
11
+ diff-lcs (~> 1.1.2)
12
+ gherkin (~> 2.2.9)
13
+ json (~> 1.4.6)
14
+ term-ansicolor (~> 1.0.5)
15
+ diff-lcs (1.1.2)
16
+ gherkin (2.2.9)
17
+ json (~> 1.4.6)
18
+ term-ansicolor (~> 1.0.5)
19
+ json (1.4.6)
20
+ rake (0.8.7)
21
+ rspec (2.1.0)
22
+ rspec-core (~> 2.1.0)
23
+ rspec-expectations (~> 2.1.0)
24
+ rspec-mocks (~> 2.1.0)
25
+ rspec-core (2.1.0)
26
+ rspec-expectations (2.1.0)
27
+ diff-lcs (~> 1.1.2)
28
+ rspec-mocks (2.1.0)
29
+ term-ansicolor (1.0.5)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ aruba
36
+ cucumber
37
+ rake
38
+ rspec
@@ -0,0 +1,97 @@
1
+ h1. Appraisal
2
+
3
+ Find out what your Ruby gems are worth.
4
+
5
+ h2. Synopsis
6
+
7
+ Appraisal integrates with bundler and rake to test your library against
8
+ different versions of dependencies in repeatable scenarios called "appraisals."
9
+ Appraisal is designed to make is easy to check for regressions in your library
10
+ without interfering with day-to-day development using bundler.
11
+
12
+ h2. Installation
13
+
14
+ <pre>
15
+ gem install appraisal
16
+ </pre>
17
+
18
+ h2. Setup
19
+
20
+ Setting up appraisal requires an Appraisals file (similar to a Gemfile) in your
21
+ project root, and some slight changes to your project's Rakefile.
22
+
23
+ An Appraisals file consists of several appraisal definitions. An appraisal
24
+ definition is simply a list of gem dependencies. For example, to test with a
25
+ few versions of Rails:
26
+
27
+ <pre>
28
+ appraise "rails2" do
29
+ gem "rails", "2.3.9"
30
+ end
31
+
32
+ appraise "rails3" do
33
+ gem "rails", "3.0.0"
34
+ end
35
+ </pre>
36
+
37
+ The dependencies in your Appraisals file are combined with dependencies in your
38
+ Gemfile, so you don't need to repeat anything that's the same for each
39
+ appraisal. If something is specified in both the Gemfile and an appraisal, the
40
+ version from the appraisal takes precedence.
41
+
42
+ Once you have an Appraisals file set up, just require appraisal in your Rakefile:
43
+
44
+ <pre>
45
+ require 'appraisal'
46
+ </pre>
47
+
48
+ It's also recommended that you setup bundler at the very top of your Rakefile,
49
+ so that you don't need to constantly run bundle exec:
50
+
51
+ <pre>
52
+ require 'rubygems'
53
+ require 'bundler/setup'
54
+ </pre>
55
+
56
+ h2. Usage
57
+
58
+ Once you've configured the appraisals you want to use, you need to install the
59
+ dependencies for each appraisal:
60
+
61
+ <pre>
62
+ rake appraisal:install
63
+ </pre>
64
+
65
+ This will resolve, install, and lock the dependencies for that appraisal using
66
+ bundler. Once you have your dependencies setup, you can run any rake task in a
67
+ single appraisal:
68
+
69
+ <pre>
70
+ rake appraisal:rails2 test
71
+ </pre>
72
+
73
+ This will run your "test" rake task using the dependencies configured for Rails
74
+ 2. You can also run each appraisal in turn:
75
+
76
+ <pre>
77
+ rake appraisal test
78
+ </pre>
79
+
80
+ If you want to use only the dependencies from your Gemfile, just run "rake
81
+ test" as normal. This allows you to keep running with the latest versions of
82
+ your dependencies in quick test runs, but keep running the tests in older
83
+ versions to check for regressions.
84
+
85
+ h2. Under the hood
86
+
87
+ Running "rake appraisal:install" generates a Gemfile for each appraisal by
88
+ combining your root Gemfile with the specific requirements for each appraisal.
89
+ These are stored in the "gemfiles" directory, and should be added to version
90
+ control to ensure that the same versions are always used. When running rake
91
+ tasks for an appraisal, the rake task is run with the appropriate Gemfile for
92
+ that appraisal, ensuring the correct dependencies are used.
93
+
94
+ h2. Author
95
+
96
+ Copyright 2010 Joe Ferris
97
+
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake/gempackagetask'
4
+ require 'cucumber/rake/task'
5
+
6
+ eval("$specification = #{IO.read('appraisal.gemspec')}")
7
+ Rake::GemPackageTask.new($specification) do |package|
8
+ package.need_zip = true
9
+ package.need_tar = true
10
+ end
11
+
12
+ Cucumber::Rake::Task.new(:cucumber) do |t|
13
+ t.fork = true
14
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')]
15
+ end
16
+
17
+ desc "Default: run the cucumber scenarios"
18
+ task :default => :cucumber
19
+
@@ -0,0 +1,75 @@
1
+ Feature: run a rake task through several appraisals
2
+
3
+ Background:
4
+ Given a directory named "projecto"
5
+ When I cd to "projecto"
6
+ And I write to "Gemfile" with:
7
+ """
8
+ source "http://rubygems.org"
9
+ gem "rake"
10
+ gem "factory_girl"
11
+ """
12
+ When I add "appraisal" from this project as a dependency
13
+ And I write to "Appraisals" with:
14
+ """
15
+ appraise "1.3.2" do
16
+ gem "factory_girl", "1.3.2"
17
+ end
18
+ appraise "1.3.0" do
19
+ gem "factory_girl", "1.3.0"
20
+ end
21
+ """
22
+ When I write to "Rakefile" with:
23
+ """
24
+ require 'rubygems'
25
+ require 'bundler/setup'
26
+ require 'appraisal'
27
+ task :version do
28
+ require 'factory_girl'
29
+ puts "Loaded #{Factory::VERSION}"
30
+ end
31
+ task :fail do
32
+ require 'factory_girl'
33
+ puts "Fail #{Factory::VERSION}"
34
+ raise
35
+ end
36
+ task :default => :version
37
+ """
38
+ When I successfully run "rake appraisal:install --trace"
39
+
40
+ @disable-bundler
41
+ Scenario: run a specific task with one appraisal
42
+ When I successfully run "rake appraisal:1.3.0 version --trace"
43
+ Then the output should contain "Loaded 1.3.0"
44
+
45
+ @disable-bundler
46
+ Scenario: run a specific task with all appraisals
47
+ When I successfully run "rake appraisal version --trace"
48
+ Then the output should contain "Loaded 1.3.0"
49
+ And the output should contain "Loaded 1.3.2"
50
+ And the output should not contain "Invoke version"
51
+
52
+ @disable-bundler
53
+ Scenario: run the default task with one appraisal
54
+ When I successfully run "rake appraisal:1.3.0 --trace"
55
+ Then the output should contain "Loaded 1.3.0"
56
+
57
+ @disable-bundler
58
+ Scenario: run the default task with all appraisals
59
+ When I successfully run "rake appraisal --trace"
60
+ Then the output should contain "Loaded 1.3.0"
61
+ And the output should contain "Loaded 1.3.2"
62
+
63
+ @disable-bundler
64
+ Scenario: run a failing task with one appraisal
65
+ When I run "rake appraisal:1.3.0 fail --trace"
66
+ Then the output should contain "Fail 1.3.0"
67
+ And the exit status should be 1
68
+
69
+ @disable-bundler
70
+ Scenario: run a failing task with all appraisals
71
+ When I run "rake appraisal fail --trace"
72
+ Then the output should contain "Fail 1.3.2"
73
+ But the output should not contain "Fail 1.3.0"
74
+ And the exit status should be 1
75
+
@@ -0,0 +1,4 @@
1
+ When /^I add "([^"]*)" from this project as a dependency$/ do |gem_name|
2
+ append_to_file('Gemfile', %{\ngem "#{gem_name}", :path => "#{PROJECT_ROOT}"})
3
+ end
4
+
@@ -0,0 +1,4 @@
1
+ require 'aruba'
2
+
3
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
4
+
@@ -0,0 +1,4 @@
1
+ require 'appraisal/task'
2
+
3
+ Appraisal::Task.new
4
+
@@ -0,0 +1,46 @@
1
+ require 'appraisal/gemfile'
2
+ require 'appraisal/command'
3
+ require 'fileutils'
4
+
5
+ module Appraisal
6
+ # Represents one appraisal and its dependencies
7
+ class Appraisal
8
+ attr_reader :name, :gemfile
9
+
10
+ def initialize(name, source_gemfile)
11
+ @name = name
12
+ @gemfile = source_gemfile.dup
13
+ end
14
+
15
+ def gem(name, *requirements)
16
+ gemfile.gem(name, *requirements)
17
+ end
18
+
19
+ def write_gemfile
20
+ ::File.open(gemfile_path, "w") do |file|
21
+ file.puts("# This file was generated by Appraisal")
22
+ file.puts
23
+ file.write(gemfile.to_s)
24
+ end
25
+ end
26
+
27
+ def install
28
+ Command.new("bundle install --gemfile=#{gemfile_path}").run
29
+ end
30
+
31
+ def gemfile_path
32
+ unless ::File.exist?(gemfile_root)
33
+ FileUtils.mkdir(gemfile_root)
34
+ end
35
+
36
+ ::File.join(gemfile_root, "#{name}.gemfile")
37
+ end
38
+
39
+ private
40
+
41
+ def gemfile_root
42
+ "gemfiles"
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,57 @@
1
+ module Appraisal
2
+ # Executes commands with a clean environment
3
+ class Command
4
+ BUNDLER_ENV_VARS = %w(RUBYOPT BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE).freeze
5
+
6
+ def self.from_args(gemfile)
7
+ command = ([$0] + ARGV.slice(1, ARGV.size)).join(' ')
8
+ new(command, gemfile)
9
+ end
10
+
11
+ def initialize(command, gemfile = nil)
12
+ @original_env = {}
13
+ @gemfile = gemfile
14
+ @command = command
15
+ end
16
+
17
+ def run
18
+ announce
19
+ with_clean_env do
20
+ unless Kernel.system(@command)
21
+ exit(1)
22
+ end
23
+ end
24
+ end
25
+
26
+ def exec
27
+ announce
28
+ with_clean_env { Kernel.exec(@command) }
29
+ end
30
+
31
+ private
32
+
33
+ def with_clean_env
34
+ unset_bundler_env_vars
35
+ ENV['BUNDLE_GEMFILE'] = @gemfile
36
+ yield
37
+ ensure
38
+ restore_env
39
+ end
40
+
41
+ def announce
42
+ puts ">> BUNDLE_GEMFILE=#{@gemfile} #{@command}"
43
+ end
44
+
45
+ def unset_bundler_env_vars
46
+ BUNDLER_ENV_VARS.each do |key|
47
+ @original_env[key] = ENV[key]
48
+ ENV[key] = nil
49
+ end
50
+ end
51
+
52
+ def restore_env
53
+ @original_env.each { |key, value| ENV[key] = value }
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,27 @@
1
+ module Appraisal
2
+ # Dependency on a gem and optional version requirements
3
+ class Dependency
4
+ attr_reader :name, :requirements
5
+
6
+ def initialize(name, requirements)
7
+ @name = name
8
+ @requirements = requirements
9
+ end
10
+
11
+ def to_s
12
+ gem_name = %{gem "#{name}"}
13
+ if requirements.nil? || requirements.empty?
14
+ gem_name
15
+ else
16
+ "#{gem_name}, #{inspect_requirements}"
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def inspect_requirements
23
+ requirements.map { |requirement| requirement.inspect }.join(", ")
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,41 @@
1
+ require 'appraisal/appraisal'
2
+ require 'appraisal/gemfile'
3
+
4
+ module Appraisal
5
+ # Loads and parses Appraisal files
6
+ class File
7
+ attr_reader :appraisals, :gemfile
8
+
9
+ def self.each(&block)
10
+ new.each(&block)
11
+ end
12
+
13
+ def initialize
14
+ @appraisals = []
15
+ @gemfile = Gemfile.new
16
+ @gemfile.load('Gemfile')
17
+ run(IO.read(path))
18
+ end
19
+
20
+ def each(&block)
21
+ appraisals.each(&block)
22
+ end
23
+
24
+ def appraise(name, &block)
25
+ @appraisals << Appraisal.new(name, gemfile).tap do |appraisal|
26
+ appraisal.instance_eval(&block)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def run(definitions)
33
+ instance_eval definitions, __FILE__, __LINE__
34
+ end
35
+
36
+ def path
37
+ 'Appraisals'
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,42 @@
1
+ require 'appraisal/dependency'
2
+
3
+ module Appraisal
4
+ # Load bundler Gemfiles and merge dependencies
5
+ class Gemfile
6
+ attr_reader :dependencies
7
+
8
+ def initialize
9
+ @dependencies = {}
10
+ end
11
+
12
+ def load(path)
13
+ run(IO.read(path))
14
+ end
15
+
16
+ def run(definitions)
17
+ instance_eval(definitions, __FILE__, __LINE__)
18
+ end
19
+
20
+ def gem(name, *requirements)
21
+ @dependencies[name] = Dependency.new(name, requirements)
22
+ end
23
+
24
+ def source(source)
25
+ @source = source
26
+ end
27
+
28
+ def to_s
29
+ %{source "#{@source}"\n} <<
30
+ dependencies.values.map { |dependency| dependency.to_s }.join("\n")
31
+ end
32
+
33
+ def dup
34
+ Gemfile.new.tap do |gemfile|
35
+ gemfile.source @source
36
+ dependencies.values.each do |dependency|
37
+ gemfile.gem(dependency.name, *dependency.requirements)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ require 'appraisal/file'
2
+ require 'rake/tasklib'
3
+
4
+ module Appraisal
5
+ # Defines tasks for installing appraisal dependencies and running other tasks
6
+ # for a given appraisal.
7
+ class Task < Rake::TaskLib
8
+ def initialize
9
+ namespace :appraisal do
10
+ desc "Generate a Gemfile for each appraisal"
11
+ task :gemfiles do
12
+ File.each do |appraisal|
13
+ appraisal.write_gemfile
14
+ end
15
+ end
16
+
17
+ desc "Resolve and install dependencies for each appraisal"
18
+ task :install => :gemfiles do
19
+ File.each do |appraisal|
20
+ appraisal.install
21
+ end
22
+ end
23
+
24
+ File.each do |appraisal|
25
+ desc "Run the given task for appraisal #{appraisal.name}"
26
+ task appraisal.name do
27
+ Command.from_args(appraisal.gemfile_path).exec
28
+ end
29
+ end
30
+
31
+ task :all do
32
+ File.each do |appraisal|
33
+ Command.from_args(appraisal.gemfile_path).run
34
+ end
35
+ exit
36
+ end
37
+ end
38
+
39
+ desc "Run the given task for all appraisals"
40
+ task :appraisal => "appraisal:all"
41
+ end
42
+ end
43
+ end
44
+
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appraisal
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Joe Ferris
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-11 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :development
22
+ prerelease: false
23
+ name: cucumber
24
+ version_requirements: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ requirement: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ type: :development
36
+ prerelease: false
37
+ name: aruba
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ requirement: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ type: :runtime
50
+ prerelease: false
51
+ name: rake
52
+ version_requirements: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirement: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ type: :runtime
64
+ prerelease: false
65
+ name: bundler
66
+ version_requirements: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirement: *id004
76
+ description: Appraisal integrates with bundler and rake to test your library against different versions of dependencies in repeatable scenarios called "appraisals."
77
+ email: jferris@thoughtbot.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files: []
83
+
84
+ files:
85
+ - Gemfile
86
+ - Gemfile.lock
87
+ - Rakefile
88
+ - README.textile
89
+ - lib/appraisal/appraisal.rb
90
+ - lib/appraisal/command.rb
91
+ - lib/appraisal/dependency.rb
92
+ - lib/appraisal/file.rb
93
+ - lib/appraisal/gemfile.rb
94
+ - lib/appraisal/task.rb
95
+ - lib/appraisal.rb
96
+ - features/appraisals.feature
97
+ - features/step_definitions/dependency_steps.rb
98
+ - features/support/env.rb
99
+ has_rdoc: false
100
+ homepage: http://github.com/thoughtbot/appraisal
101
+ licenses: []
102
+
103
+ post_install_message:
104
+ rdoc_options: []
105
+
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ requirements: []
127
+
128
+ rubyforge_project:
129
+ rubygems_version: 1.3.7
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Find out what your Ruby gems are worth
133
+ test_files:
134
+ - features/appraisals.feature
135
+ - features/step_definitions/dependency_steps.rb
136
+ - features/support/env.rb