appraisal 0.1

Sign up to get free protection for your applications and to get access to all the features.
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