├── .gemtest
├── .rspec
├── .yardopts
├── lib
├── cape
│ ├── version.rb
│ ├── core_ext.rb
│ ├── core_ext
│ │ ├── hash.rb
│ │ └── symbol.rb
│ ├── util.rb
│ ├── hash_list.rb
│ ├── recipe_definition.rb
│ ├── rake.rb
│ ├── capistrano.rb
│ ├── dsl.rb
│ └── xterm.rb
└── cape.rb
├── .gitignore
├── features
├── support
│ └── env.rb
├── dsl
│ ├── mirror_rake_tasks
│ │ ├── with_undefined_task_or_namespace.feature
│ │ ├── with_valid_options.feature
│ │ ├── with_valid_options_and_cd.feature
│ │ ├── with_rename.feature
│ │ ├── with_cd_and_environment_variables.feature
│ │ ├── with_valid_options_and_environment_variables.feature
│ │ ├── with_rename_and_cd.feature
│ │ ├── with_rename_and_valid_options.feature
│ │ ├── with_environment_variables.feature
│ │ ├── with_valid_options_and_cd_and_environment_variables.feature
│ │ ├── with_rename_and_environment_variables.feature
│ │ ├── with_rename_and_valid_options_and_cd.feature
│ │ ├── inside_capistrano_namespace.feature
│ │ ├── with_rename_and_cd_and_environment_variables.feature
│ │ ├── with_rename_and_valid_options_and_environment_variables.feature
│ │ ├── with_rename_and_valid_options_and_cd_and_environment_variables.feature
│ │ ├── with_defined_task.feature
│ │ ├── with_defined_task_and_cd.feature
│ │ ├── with_defined_task_and_valid_options.feature
│ │ ├── with_defined_task_and_environment_variables.feature
│ │ ├── with_defined_task_and_valid_options_and_cd.feature
│ │ ├── with_defined_task_and_rename.feature
│ │ ├── with_defined_task_and_cd_and_environment_variables.feature
│ │ ├── with_defined_task_and_valid_options_and_environment_variables.feature
│ │ ├── with_defined_task_and_rename_and_cd.feature
│ │ ├── with_defined_task_and_rename_and_valid_options.feature
│ │ ├── with_defined_task_and_valid_options_and_cd_and_environment_variables.feature
│ │ ├── with_defined_task_and_rename_and_environment_variables.feature
│ │ ├── with_defined_task_and_rename_and_valid_options_and_cd.feature
│ │ ├── with_defined_task_and_rename_and_cd_and_environment_variables.feature
│ │ ├── with_defined_task_and_rename_and_valid_options_and_environment_variables.feature
│ │ ├── with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature
│ │ ├── with_cd.feature
│ │ ├── with_defined_namespace.feature
│ │ └── unqualified.feature
│ ├── each_rake_task
│ │ ├── with_undefined_task_or_namespace.feature
│ │ ├── with_defined_task.feature
│ │ ├── with_defined_namespace.feature
│ │ └── unqualified.feature
│ └── rake_executable.feature
└── step_definitions.rb
├── gemfiles
├── rake_v0.9.3.gemfile
├── rake_v10.x.gemfile
├── capistrano_v2.x.gemfile
├── rake_v0.9.3.gemfile.lock
├── rake_v10.x.gemfile.lock
└── capistrano_v2.x.gemfile.lock
├── spec
├── cape
│ ├── version_spec.rb
│ ├── core_ext
│ │ ├── hash_spec.rb
│ │ └── symbol_spec.rb
│ ├── capistrano_spec.rb
│ ├── util_spec.rb
│ ├── recipe_definition_spec.rb
│ ├── hash_list_spec.rb
│ ├── xterm_spec.rb
│ ├── rake_spec.rb
│ └── dsl_spec.rb
├── cape_spec.rb
└── spec_helper.rb
├── Appraisals
├── .travis.yml
├── Gemfile
├── License.markdown
├── Guardfile
├── cape.gemspec
├── History.markdown
├── Rakefile
└── README.markdown
/.gemtest:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 |
--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | --file History.markdown --file License.markdown --no-private --protected --title "Cape"
2 |
--------------------------------------------------------------------------------
/lib/cape/version.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # The version of Cape.
4 | VERSION = '1.8.0'
5 |
6 | end
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle
2 | .rbx
3 | .ruby-gemset
4 | .ruby-version
5 | .yardoc
6 | Gemfile.lock
7 | doc
8 | pkg
9 |
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | require 'aruba/cucumber'
2 |
3 | Before do
4 | @aruba_timeout_seconds = 10
5 | end
6 |
--------------------------------------------------------------------------------
/gemfiles/rake_v0.9.3.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "http://rubygems.org"
4 |
5 | gem "rake", "0.9.3"
6 |
7 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/gemfiles/rake_v10.x.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "http://rubygems.org"
4 |
5 | gem "rake", "~> 10"
6 |
7 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/gemfiles/capistrano_v2.x.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "http://rubygems.org"
4 |
5 | gem "capistrano", "~> 2"
6 |
7 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/spec/cape/version_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/version'
3 |
4 | describe 'Cape::VERSION' do
5 | specify { expect(Cape::VERSION).to match(/^\d+\.\d+\.\d+/) }
6 | end
7 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise 'capistrano-v2.x' do
2 | gem 'capistrano', '~> 2'
3 | end
4 |
5 | appraise 'rake-v0.9.3' do
6 | gem 'rake', '0.9.3'
7 | end
8 |
9 | appraise 'rake-v10.x' do
10 | gem 'rake', '~> 10'
11 | end
12 |
--------------------------------------------------------------------------------
/lib/cape/core_ext.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # Contains extensions to core types.
4 | #
5 | # @api private
6 | module CoreExt
7 |
8 | autoload :Hash, 'cape/core_ext/hash'
9 | autoload :Symbol, 'cape/core_ext/symbol'
10 |
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/spec/cape/core_ext/hash_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/core_ext/hash'
3 |
4 | describe Hash do
5 | subject(:hash) { {:foo => 'bar', :baz => 'qux', :quux => 'corge'} }
6 |
7 | describe '#slice with keys that are present and those that are not' do
8 | it 'returns the expected subset hash' do
9 | expect(hash.slice(:baz, :fizzle, :quux)).to eq(:baz => 'qux',
10 | :quux => 'corge')
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/cape/core_ext/symbol_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/core_ext/symbol'
3 |
4 | describe Symbol do
5 | describe '#<=>' do
6 | describe 'with a lower symbol' do
7 | specify { expect(:foo <=> :bar).to eq(1) }
8 | end
9 |
10 | describe 'with a higher symbol' do
11 | specify { expect(:baz <=> :qux).to eq(-1) }
12 | end
13 |
14 | describe 'with itself' do
15 | specify { expect(:quux <=> :quux).to eq(0) }
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | bundler_args: --without debug doc tooling
3 | gemfile:
4 | - gemfiles/capistrano_v2.x.gemfile
5 | - gemfiles/rake_v0.9.3.gemfile
6 | - gemfiles/rake_v10.x.gemfile
7 | rvm:
8 | - 1.8.7
9 | - 1.9.2
10 | - 1.9.3
11 | - 2.0.0
12 | - ruby-head
13 | - ree
14 | - jruby-18mode
15 | - jruby-19mode
16 | - jruby-head
17 | script: "bundle exec rake test"
18 | matrix:
19 | allow_failures:
20 | - rvm: ruby-head
21 | - rvm: jruby-18mode
22 | - rvm: jruby-19mode
23 | - rvm: jruby-head
24 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_undefined_task_or_namespace.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with an undefined task or namespace
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: do not mirror any Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks 'lon'
13 | end
14 | """
15 | When I run `cap -vT`
16 | Then the output should not contain "lon"
17 |
--------------------------------------------------------------------------------
/lib/cape/core_ext/hash.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | module CoreExt
4 |
5 | # Adds methods missing from Ruby's Hash core class.
6 | module Hash
7 |
8 | # Returns a copy of the Hash containing values only for the specified
9 | # _keys_.
10 | #
11 | # @param [Array] keys zero or more hash keys
12 | #
13 | # @return [Hash] a subset of the Hash
14 | def slice(*keys)
15 | ::Hash[select { |key, value| keys.include? key }]
16 | end
17 |
18 | end
19 |
20 | end
21 |
22 | end
23 |
24 | unless ::Hash.instance_methods.collect(&:to_s).include?('slice')
25 | ::Hash.class_eval do
26 | include Cape::CoreExt::Hash
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/cape/capistrano_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/capistrano'
3 | require 'cape/rake'
4 |
5 | describe Cape::Capistrano do
6 | subject(:capistrano) { capistrano_class.new }
7 |
8 | let(:capistrano_class) { described_class }
9 |
10 | describe 'without specified attributes' do
11 | describe '#rake' do
12 | specify { expect(capistrano.rake).to eq(Cape::Rake.new) }
13 | end
14 | end
15 |
16 | describe 'with specified attributes' do
17 | subject(:capistrano) {
18 | capistrano_class.new :rake => 'the specified value of #rake'
19 | }
20 |
21 | describe '#rake' do
22 | specify { expect(capistrano.rake).to eq('the specified value of #rake') }
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/cape/core_ext/symbol.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | module CoreExt
4 |
5 | # Adds methods missing from Ruby's Symbol core class.
6 | module Symbol
7 |
8 | # Compares the String representation of the Symbol to that of another.
9 | #
10 | # @param [Symbol] other
11 | #
12 | # @return [0] the Symbol is equal to _other_
13 | # @return [-1] the Symbol is lesser than _other_
14 | # @return [1] the Symbol is greater than _other_
15 | def <=>(other)
16 | to_s <=> other.to_s
17 | end
18 |
19 | end
20 |
21 | end
22 |
23 | end
24 |
25 | unless ::Symbol.instance_methods.collect(&:to_s).include?('<=>')
26 | ::Symbol.class_eval do
27 | include Cape::CoreExt::Symbol
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/cape_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape'
3 | require 'cape/dsl'
4 |
5 | describe Cape do
6 | let(:cape_module) { described_class }
7 |
8 | Cape::DSL.public_instance_methods.each do |m|
9 | specify { expect(cape_module).to respond_to(m) }
10 | end
11 | end
12 |
13 | describe '#Cape' do
14 | it 'yields the Cape module if given a unary block' do
15 | yielded = nil
16 | Cape do |c|
17 | yielded = c
18 | end
19 | expect(yielded).to eq(Cape)
20 | end
21 |
22 | it 'accepts a nullary block' do
23 | Cape do
24 | end
25 | end
26 |
27 | it 'expires the Rake tasks cache when leaving the block' do
28 | Cape do
29 | expect(rake).to receive(:expire_cache!).once
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/features/dsl/each_rake_task/with_undefined_task_or_namespace.feature:
--------------------------------------------------------------------------------
1 | Feature: The #each_rake_task DSL method with an undefined task or namespace
2 |
3 | In order to use the metadata of Rake tasks in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: do not enumerate any Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | each_rake_task 'lon' do |t|
13 | $stdout.puts '', "Name: #{t[:name].inspect}"
14 | if t[:parameters]
15 | $stdout.puts "Parameters: #{t[:parameters].inspect}"
16 | end
17 | if t[:description]
18 | $stdout.puts "Description: #{t[:description].inspect}"
19 | end
20 | end
21 | end
22 | """
23 | When I run `cap -vT`
24 | Then the output should not contain "lon"
25 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_valid_options.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with valid options
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.options[:roles] = :app
16 | end
17 | end
18 | """
19 | When I run `cap long`
20 | Then the output should contain:
21 | """
22 | * executing `long'
23 | """
24 | And the output should contain:
25 | """
26 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
27 | """
28 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | gemspec
4 |
5 | group :debug do
6 | gem 'ruby-debug', :require => false, :platforms => [:mri_18, :jruby]
7 | gem 'debugger', :require => false, :platforms => [:mri_19, :mri_20]
8 | end
9 |
10 | group :doc do
11 | gem 'yard', '~> 0', :require => false, :platforms => [:ruby, :mswin, :mingw]
12 | gem 'rdiscount', :require => false, :platforms => [:ruby, :mswin, :mingw]
13 |
14 | gem 'relish', '~> 0', :require => false, :platforms => :mri_19
15 | end
16 |
17 | group :tooling do
18 | gem 'appraisal', '~> 0', :require => false
19 | gem 'guard-cucumber', '~> 1', :require => false, :platforms => :mri_19
20 | gem 'guard-rspec', '~> 4', :require => false, :platforms => :mri_19
21 | if RUBY_PLATFORM =~ /darwin/i
22 | gem 'rb-fsevent', '~> 0', :require => false
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # This file was generated by the `rspec --init` command. Conventionally, all
2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3 | # Require this file using `require "spec_helper"` to ensure that it is only
4 | # loaded once.
5 | #
6 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7 | RSpec.configure do |config|
8 | config.treat_symbols_as_metadata_keys_with_true_values = true
9 | config.run_all_when_everything_filtered = true
10 | config.filter_run :focus
11 |
12 | # Run specs in random order to surface order dependencies. If you find an
13 | # order dependency and want to debug it, you can fix the order by providing
14 | # the seed, which is printed after each run.
15 | # --seed 1234
16 | config.order = 'random'
17 |
18 | config.alias_it_should_behave_like_to :behaves_like, 'behaves like'
19 |
20 | config.expect_with :rspec do |c|
21 | c.syntax = :expect
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_valid_options_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with valid options and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.options[:roles] = :app
16 | recipes.cd { release_path }
17 | end
18 | end
19 | """
20 | When I run `cap long`
21 | Then the output should contain:
22 | """
23 | * executing `long'
24 | """
25 | And the output should contain:
26 | """
27 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
28 | """
29 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.rename do |task_name|
16 | "do_#{task_name}"
17 | end
18 | end
19 | end
20 | """
21 | When I run `cap do_load`
22 | Then the output should contain:
23 | """
24 | * executing `do_load'
25 | """
26 | And the output should contain:
27 | """
28 | `do_load' is only run for servers matching {}, but no servers matched
29 | """
30 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a different directory and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.cd { release_path }
17 | recipes.env['RAILS_ENV'] = lambda { rails_env }
18 | end
19 | end
20 | """
21 | When I run `cap long`
22 | Then the output should contain:
23 | """
24 | * executing `long'
25 | """
26 | And the output should contain:
27 | """
28 | `long' is only run for servers matching {}, but no servers matched
29 | """
30 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with valid options and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.options[:roles] = :app
17 | recipes.env['RAILS_ENV'] = lambda { rails_env }
18 | end
19 | end
20 | """
21 | When I run `cap long`
22 | Then the output should contain:
23 | """
24 | * executing `long'
25 | """
26 | And the output should contain:
27 | """
28 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
29 | """
30 |
--------------------------------------------------------------------------------
/lib/cape/util.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # Provides utility functions.
4 | #
5 | # @api private
6 | module Util
7 |
8 | # Conditionally transforms the specified _noun_ into its plural form.
9 | #
10 | # @param [String] singular_noun a singular noun
11 | # @param [Fixnum] count the quantity of _singular_noun_
12 | #
13 | # @return [String] the plural of _singular_noun_, unless _count_ is +1+
14 | def self.pluralize(singular_noun, count=2)
15 | return singular_noun if count == 1
16 |
17 | "#{singular_noun}s"
18 | end
19 |
20 | # Builds a list phrase from the elements of the specified _array_.
21 | #
22 | # @param [Array of String] array zero or more nouns
23 | #
24 | # @return [String] the elements of _array_, joined with commas and "and", as
25 | # appropriate
26 | def self.to_list_phrase(array)
27 | return array.join(' and ') if (array.length <= 2)
28 |
29 | [array[0...-1].join(', '), array[-1]].join ', and '
30 | end
31 |
32 | end
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.rename do |task_name|
16 | "do_#{task_name}"
17 | end
18 | recipes.cd { release_path }
19 | end
20 | end
21 | """
22 | When I run `cap do_load`
23 | Then the output should contain:
24 | """
25 | * executing `do_load'
26 | """
27 | And the output should contain:
28 | """
29 | `do_load' is only run for servers matching {}, but no servers matched
30 | """
31 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_valid_options.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic and valid options
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.rename do |task_name|
16 | "do_#{task_name}"
17 | end
18 | recipes.options[:roles] = :app
19 | end
20 | end
21 | """
22 | When I run `cap do_load`
23 | Then the output should contain:
24 | """
25 | * executing `do_load'
26 | """
27 | And the output should contain:
28 | """
29 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
30 | """
31 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.env['RAILS_ENV'] = lambda { rails_env }
17 | recipes.env[nil] = 'foo'
18 | recipes.env['FOO'] = nil
19 | recipes.env['SOME_OTHER'] = 'var'
20 | end
21 | end
22 | """
23 | When I run `cap long`
24 | Then the output should contain:
25 | """
26 | * executing `long'
27 | """
28 | And the output should contain:
29 | """
30 | `long' is only run for servers matching {}, but no servers matched
31 | """
32 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_valid_options_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with valid options, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror the matching Rake task with its implementation
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.options[:roles] = :app
17 | recipes.cd { release_path }
18 | recipes.env['RAILS_ENV'] = lambda { rails_env }
19 | end
20 | end
21 | """
22 | When I run `cap long`
23 | Then the output should contain:
24 | """
25 | * executing `long'
26 | """
27 | And the output should contain:
28 | """
29 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
30 | """
31 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.rename do |task_name|
17 | "do_#{task_name}"
18 | end
19 | recipes.env['RAILS_ENV'] = lambda { rails_env }
20 | end
21 | end
22 | """
23 | When I run `cap do_load`
24 | Then the output should contain:
25 | """
26 | * executing `do_load'
27 | """
28 | And the output should contain:
29 | """
30 | `do_load' is only run for servers matching {}, but no servers matched
31 | """
32 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.rename do |task_name|
16 | "do_#{task_name}"
17 | end
18 | recipes.options[:roles] = :app
19 | recipes.cd { release_path }
20 | end
21 | end
22 | """
23 | When I run `cap do_load`
24 | Then the output should contain:
25 | """
26 | * executing `do_load'
27 | """
28 | And the output should contain:
29 | """
30 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
31 | """
32 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/inside_capistrano_namespace.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method, inside a Capistrano namespace
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror all Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | namespace :ns do
12 | Cape do |cape|
13 | cape.mirror_rake_tasks
14 | end
15 | end
16 | """
17 | When I run `cap -vT`
18 | Then the output should contain:
19 | """
20 | cap ns:long # My long task -- it has a ve...
21 | """
22 |
23 | Scenario: mirror a Rake task with its implementation
24 | Given a full-featured Rakefile
25 | And a Capfile with:
26 | """
27 | set :current_path, '/current/path'
28 |
29 | namespace :ns do
30 | Cape do |cape|
31 | cape.mirror_rake_tasks
32 | end
33 | end
34 | """
35 | When I run `cap ns:long`
36 | Then the output should contain:
37 | """
38 | * executing `ns:long'
39 | """
40 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.rename do |task_name|
17 | "do_#{task_name}"
18 | end
19 | recipes.cd { release_path }
20 | recipes.env['RAILS_ENV'] = lambda { rails_env }
21 | end
22 | end
23 | """
24 | When I run `cap do_load`
25 | Then the output should contain:
26 | """
27 | * executing `do_load'
28 | """
29 | And the output should contain:
30 | """
31 | `do_load' is only run for servers matching {}, but no servers matched
32 | """
33 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, and enviroment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :current_path, '/current/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.rename do |task_name|
17 | "do_#{task_name}"
18 | end
19 | recipes.options[:roles] = :app
20 | recipes.env['RAILS_ENV'] = lambda { rails_env }
21 | end
22 | end
23 | """
24 | When I run `cap do_load`
25 | Then the output should contain:
26 | """
27 | * executing `do_load'
28 | """
29 | And the output should contain:
30 | """
31 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
32 | """
33 |
--------------------------------------------------------------------------------
/License.markdown:
--------------------------------------------------------------------------------
1 | # The MIT License
2 |
3 | Source code for _Cape_ is Copyright © 2011–2015 [Nils Jonsson](mailto:cape@nilsjonsson.com) and [contributors](http://github.com/njonsson/cape/contributors "Cape contributors at GitHub").
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/features/dsl/each_rake_task/with_defined_task.feature:
--------------------------------------------------------------------------------
1 | Feature: The #each_rake_task DSL method with an argument of a defined task
2 |
3 | In order to use the metadata of Rake tasks in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: enumerate only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | each_rake_task 'long' do |t|
13 | $stdout.puts '', "Name: #{t[:name].inspect}"
14 | if t[:parameters]
15 | $stdout.puts "Parameters: #{t[:parameters].inspect}"
16 | end
17 | if t[:description]
18 | $stdout.puts "Description: #{t[:description].inspect}"
19 | end
20 | end
21 | end
22 | """
23 | When I run `cap -vT`
24 | Then the output should contain:
25 | """
26 |
27 | Name: "long"
28 | Description: "My long task -- it has a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long description"
29 | """
30 | And the output should not contain "with_one_arg"
31 | And the output should not contain "my_namespace"
32 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, a different directory, and enviroment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Ruby-method-shadowing Rake task with its implementation
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 | set :rails_env, 'rails-env'
13 |
14 | Cape do
15 | mirror_rake_tasks do |recipes|
16 | recipes.rename do |task_name|
17 | "do_#{task_name}"
18 | end
19 | recipes.options[:roles] = :app
20 | recipes.cd { release_path }
21 | recipes.env['RAILS_ENV'] = lambda { rails_env }
22 | end
23 | end
24 | """
25 | When I run `cap do_load`
26 | Then the output should contain:
27 | """
28 | * executing `do_load'
29 | """
30 | And the output should contain:
31 | """
32 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
33 | """
34 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long
13 | end
14 | """
15 | When I run `cap -vT`
16 | Then the output should contain:
17 | """
18 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
19 | """
20 | And the output should not contain "with_one_arg"
21 | And the output should not contain "my_namespace"
22 |
23 | Scenario: mirror the matching Rake task with its implementation
24 | Given a full-featured Rakefile
25 | And a Capfile with:
26 | """
27 | set :current_path, '/current/path'
28 |
29 | Cape do
30 | mirror_rake_tasks 'long'
31 | end
32 | """
33 | When I run `cap long`
34 | Then the output should contain:
35 | """
36 | * executing `long'
37 | """
38 | And the output should contain:
39 | """
40 | `long' is only run for servers matching {}, but no servers matched
41 | """
42 |
--------------------------------------------------------------------------------
/features/dsl/each_rake_task/with_defined_namespace.feature:
--------------------------------------------------------------------------------
1 | Feature: The #each_rake_task DSL method with an argument of a defined namespace
2 |
3 | In order to use the metadata of Rake tasks in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: enumerate only the Rake tasks in the matching namespace
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | each_rake_task :my_namespace do |t|
13 | $stdout.puts '', "Name: #{t[:name].inspect}"
14 | if t[:parameters]
15 | $stdout.puts "Parameters: #{t[:parameters].inspect}"
16 | end
17 | if t[:description]
18 | $stdout.puts "Description: #{t[:description].inspect}"
19 | end
20 | $stdout.puts 'Default' if t[:default]
21 | end
22 | end
23 | """
24 | When I run `cap -vT`
25 | Then the output should contain:
26 | """
27 |
28 | Name: "my_namespace"
29 | Description: "A task that shadows a namespace"
30 | Default
31 |
32 | Name: "my_namespace:in_a_namespace"
33 | Description: "My task in a namespace"
34 |
35 | Name: "my_namespace:my_nested_namespace:in_a_nested_namespace"
36 | Description: "My task in a nested namespace"
37 | """
38 | And the output should not contain "long"
39 |
--------------------------------------------------------------------------------
/spec/cape/util_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/util'
3 |
4 | describe Cape::Util do
5 | describe '.pluralize' do
6 | it "transforms 'foo' as expected" do
7 | expect(Cape::Util.pluralize('foo')).to eq('foos')
8 | end
9 |
10 | it "transforms 'foo' as expected for a count of 2" do
11 | expect(Cape::Util.pluralize('foo', 2)).to eq('foos')
12 | end
13 |
14 | it "does not transform 'foo' for a count of 1" do
15 | expect(Cape::Util.pluralize('foo', 1)).to eq('foo')
16 | end
17 |
18 | it "transforms 'foo' as expected for a count of 0" do
19 | expect(Cape::Util.pluralize('foo', 0)).to eq('foos')
20 | end
21 |
22 | it "transforms 'foo' as expected for a count of -1" do
23 | expect(Cape::Util.pluralize('foo', -1)).to eq('foos')
24 | end
25 | end
26 |
27 | describe '.to_list_phrase' do
28 | it 'makes the expected phrase of an empty array' do
29 | expect(Cape::Util.to_list_phrase([])).to eq('')
30 | end
31 |
32 | it 'makes the expected phrase of a 1-element array' do
33 | expect(Cape::Util.to_list_phrase(%w(foo))).to eq('foo')
34 | end
35 |
36 | it 'makes the expected phrase of a 2-element array' do
37 | expect(Cape::Util.to_list_phrase(%w(foo bar))).to eq('foo and bar')
38 | end
39 |
40 | it 'makes the expected phrase of a 3-element array' do
41 | array = %w(foo bar baz)
42 | expect(Cape::Util.to_list_phrase(array)).to eq('foo, bar, and baz')
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.cd { release_path }
14 | end
15 | end
16 | """
17 | When I run `cap -vT`
18 | Then the output should contain:
19 | """
20 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
21 | """
22 | And the output should not contain "with_one_arg"
23 | And the output should not contain "my_namespace"
24 |
25 | Scenario: mirror the matching Rake task with its implementation
26 | Given a full-featured Rakefile
27 | And a Capfile with:
28 | """
29 | set :release_path, '/release/path'
30 |
31 | Cape do
32 | mirror_rake_tasks :long do |recipes|
33 | recipes.cd { release_path }
34 | end
35 | end
36 | """
37 | When I run `cap long`
38 | Then the output should contain:
39 | """
40 | * executing `long'
41 | """
42 | And the output should contain:
43 | """
44 | `long' is only run for servers matching {}, but no servers matched
45 | """
46 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task and valid options
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.options[:roles] = :app
14 | end
15 | end
16 | """
17 | When I run `cap -vT`
18 | Then the output should contain:
19 | """
20 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
21 | """
22 | And the output should not contain "with_one_arg"
23 | And the output should not contain "my_namespace"
24 |
25 | Scenario: mirror the matching Rake task with its implementation
26 | Given a full-featured Rakefile
27 | And a Capfile with:
28 | """
29 | set :current_path, '/current/path'
30 |
31 | Cape do
32 | mirror_rake_tasks 'long' do |recipes|
33 | recipes.options[:roles] = :app
34 | end
35 | end
36 | """
37 | When I run `cap long`
38 | Then the output should contain:
39 | """
40 | * executing `long'
41 | """
42 | And the output should contain:
43 | """
44 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
45 | """
46 |
--------------------------------------------------------------------------------
/gemfiles/rake_v0.9.3.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../
3 | specs:
4 | cape (1.8.0)
5 |
6 | GEM
7 | remote: http://rubygems.org/
8 | specs:
9 | aruba (0.5.4)
10 | childprocess (>= 0.3.6)
11 | cucumber (>= 1.1.1)
12 | rspec-expectations (>= 2.7.0)
13 | builder (3.2.2)
14 | capistrano (2.15.5)
15 | highline
16 | net-scp (>= 1.0.0)
17 | net-sftp (>= 2.0.0)
18 | net-ssh (>= 2.0.14)
19 | net-ssh-gateway (>= 1.1.0)
20 | childprocess (0.3.9)
21 | ffi (~> 1.0, >= 1.0.11)
22 | cucumber (1.3.10)
23 | builder (>= 2.1.2)
24 | diff-lcs (>= 1.1.3)
25 | gherkin (~> 2.12)
26 | multi_json (>= 1.7.5, < 2.0)
27 | multi_test (>= 0.0.2)
28 | diff-lcs (1.2.5)
29 | ffi (1.9.3)
30 | gherkin (2.12.2)
31 | multi_json (~> 1.3)
32 | highline (1.6.20)
33 | json_pure (1.8.1)
34 | multi_json (1.8.4)
35 | multi_test (0.0.3)
36 | net-scp (1.1.2)
37 | net-ssh (>= 2.6.5)
38 | net-sftp (2.1.2)
39 | net-ssh (>= 2.6.5)
40 | net-ssh (2.7.0)
41 | net-ssh-gateway (1.2.0)
42 | net-ssh (>= 2.6.5)
43 | rake (0.9.3)
44 | rspec (2.14.1)
45 | rspec-core (~> 2.14.0)
46 | rspec-expectations (~> 2.14.0)
47 | rspec-mocks (~> 2.14.0)
48 | rspec-core (2.14.7)
49 | rspec-expectations (2.14.4)
50 | diff-lcs (>= 1.1.3, < 2.0)
51 | rspec-mocks (2.14.4)
52 |
53 | PLATFORMS
54 | ruby
55 |
56 | DEPENDENCIES
57 | aruba (~> 0)
58 | cape!
59 | capistrano (~> 2)
60 | json_pure
61 | rake (= 0.9.3)
62 | rspec (~> 2)
63 |
--------------------------------------------------------------------------------
/gemfiles/rake_v10.x.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../
3 | specs:
4 | cape (1.8.0)
5 |
6 | GEM
7 | remote: http://rubygems.org/
8 | specs:
9 | aruba (0.5.4)
10 | childprocess (>= 0.3.6)
11 | cucumber (>= 1.1.1)
12 | rspec-expectations (>= 2.7.0)
13 | builder (3.2.2)
14 | capistrano (2.15.5)
15 | highline
16 | net-scp (>= 1.0.0)
17 | net-sftp (>= 2.0.0)
18 | net-ssh (>= 2.0.14)
19 | net-ssh-gateway (>= 1.1.0)
20 | childprocess (0.3.9)
21 | ffi (~> 1.0, >= 1.0.11)
22 | cucumber (1.3.10)
23 | builder (>= 2.1.2)
24 | diff-lcs (>= 1.1.3)
25 | gherkin (~> 2.12)
26 | multi_json (>= 1.7.5, < 2.0)
27 | multi_test (>= 0.0.2)
28 | diff-lcs (1.2.5)
29 | ffi (1.9.3)
30 | gherkin (2.12.2)
31 | multi_json (~> 1.3)
32 | highline (1.6.20)
33 | json_pure (1.8.1)
34 | multi_json (1.8.4)
35 | multi_test (0.0.3)
36 | net-scp (1.1.2)
37 | net-ssh (>= 2.6.5)
38 | net-sftp (2.1.2)
39 | net-ssh (>= 2.6.5)
40 | net-ssh (2.7.0)
41 | net-ssh-gateway (1.2.0)
42 | net-ssh (>= 2.6.5)
43 | rake (10.1.1)
44 | rspec (2.14.1)
45 | rspec-core (~> 2.14.0)
46 | rspec-expectations (~> 2.14.0)
47 | rspec-mocks (~> 2.14.0)
48 | rspec-core (2.14.7)
49 | rspec-expectations (2.14.4)
50 | diff-lcs (>= 1.1.3, < 2.0)
51 | rspec-mocks (2.14.4)
52 |
53 | PLATFORMS
54 | ruby
55 |
56 | DEPENDENCIES
57 | aruba (~> 0)
58 | cape!
59 | capistrano (~> 2)
60 | json_pure
61 | rake (~> 10)
62 | rspec (~> 2)
63 |
--------------------------------------------------------------------------------
/gemfiles/capistrano_v2.x.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../
3 | specs:
4 | cape (1.8.0)
5 |
6 | GEM
7 | remote: http://rubygems.org/
8 | specs:
9 | aruba (0.5.4)
10 | childprocess (>= 0.3.6)
11 | cucumber (>= 1.1.1)
12 | rspec-expectations (>= 2.7.0)
13 | builder (3.2.2)
14 | capistrano (2.15.5)
15 | highline
16 | net-scp (>= 1.0.0)
17 | net-sftp (>= 2.0.0)
18 | net-ssh (>= 2.0.14)
19 | net-ssh-gateway (>= 1.1.0)
20 | childprocess (0.3.9)
21 | ffi (~> 1.0, >= 1.0.11)
22 | cucumber (1.3.10)
23 | builder (>= 2.1.2)
24 | diff-lcs (>= 1.1.3)
25 | gherkin (~> 2.12)
26 | multi_json (>= 1.7.5, < 2.0)
27 | multi_test (>= 0.0.2)
28 | diff-lcs (1.2.5)
29 | ffi (1.9.3)
30 | gherkin (2.12.2)
31 | multi_json (~> 1.3)
32 | highline (1.6.20)
33 | json_pure (1.8.1)
34 | multi_json (1.8.4)
35 | multi_test (0.0.3)
36 | net-scp (1.1.2)
37 | net-ssh (>= 2.6.5)
38 | net-sftp (2.1.2)
39 | net-ssh (>= 2.6.5)
40 | net-ssh (2.7.0)
41 | net-ssh-gateway (1.2.0)
42 | net-ssh (>= 2.6.5)
43 | rake (10.1.1)
44 | rspec (2.14.1)
45 | rspec-core (~> 2.14.0)
46 | rspec-expectations (~> 2.14.0)
47 | rspec-mocks (~> 2.14.0)
48 | rspec-core (2.14.7)
49 | rspec-expectations (2.14.4)
50 | diff-lcs (>= 1.1.3, < 2.0)
51 | rspec-mocks (2.14.4)
52 |
53 | PLATFORMS
54 | ruby
55 |
56 | DEPENDENCIES
57 | aruba (~> 0)
58 | cape!
59 | capistrano (~> 2)
60 | json_pure
61 | rake (>= 0.9.3)
62 | rspec (~> 2)
63 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.env['RAILS_ENV'] = lambda { rails_env }
14 | end
15 | end
16 | """
17 | When I run `cap -vT`
18 | Then the output should contain:
19 | """
20 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
21 | """
22 | And the output should not contain "with_one_arg"
23 | And the output should not contain "my_namespace"
24 |
25 | Scenario: mirror the matching Rake task with its implementation
26 | Given a full-featured Rakefile
27 | And a Capfile with:
28 | """
29 | set :current_path, '/current/path'
30 | set :rails_env, 'rails-env'
31 |
32 | Cape do
33 | mirror_rake_tasks 'long' do |recipes|
34 | recipes.env['RAILS_ENV'] = lambda { rails_env }
35 | end
36 | end
37 | """
38 | When I run `cap long`
39 | Then the output should contain:
40 | """
41 | * executing `long'
42 | """
43 | And the output should contain:
44 | """
45 | `long' is only run for servers matching {}, but no servers matched
46 | """
47 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | interactor :off
2 |
3 | guard :rspec, :all_after_pass => true,
4 | :all_on_start => false,
5 | :keep_failed => false,
6 | :cmd => "bundle exec rspec --debug --format progress" do
7 | # Run the corresponding spec (or all specs) when code changes.
8 | watch(%r{^lib/(.+)\.rb$}) do |match|
9 | Dir["spec/#{match[1]}_spec.rb"].first || 'spec'
10 | end
11 |
12 | # Run a spec when it changes.
13 | watch %r{^spec/.+_spec\.rb$}
14 |
15 | # Run all specs when the RSpec configuration changes.
16 | watch('.rspec' ) { 'spec' }
17 | watch('spec/spec_helper.rb') { 'spec' }
18 |
19 | # Run all specs when the bundle changes.
20 | watch('Gemfile.lock') { 'spec' }
21 | end
22 |
23 | guard :cucumber, :all_after_pass => true,
24 | :all_on_start => false,
25 | :keep_failed => false,
26 | :focus_on => 'focus' do
27 | # Run run all features when code changes.
28 | watch(%r{^lib/(.+)\.rb$}) { 'features' }
29 |
30 | # Run the corresponding feature (or all features) when a step definition
31 | # changes.
32 | watch(%r{^features/step_definitions\.rb$}) { 'features' }
33 | watch(%r{^features/step_definitions/(.+)_steps\.rb$}) do |match|
34 | Dir["features/**/#{match[1]}.feature"].first || 'features'
35 | end
36 |
37 | # Run a feature when it changes.
38 | watch %r{^features/.+\.feature$}
39 |
40 | # Run all features when the Cucumber configuration changes.
41 | watch(%r{^features/support/.+$}) { 'features' }
42 |
43 | # Run all features when the bundle changes.
44 | watch('Gemfile.lock') { 'features' }
45 | end
46 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.options[:roles] = :app
14 | recipes.cd { release_path }
15 | end
16 | end
17 | """
18 | When I run `cap -vT`
19 | Then the output should contain:
20 | """
21 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
22 | """
23 | And the output should not contain "with_one_arg"
24 | And the output should not contain "my_namespace"
25 |
26 | Scenario: mirror the matching Rake task with its implementation
27 | Given a full-featured Rakefile
28 | And a Capfile with:
29 | """
30 | set :release_path, '/release/path'
31 |
32 | Cape do
33 | mirror_rake_tasks :long do |recipes|
34 | recipes.options[:roles] = :app
35 | recipes.cd { release_path }
36 | end
37 | end
38 | """
39 | When I run `cap long`
40 | Then the output should contain:
41 | """
42 | * executing `long'
43 | """
44 | And the output should contain:
45 | """
46 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
47 | """
48 |
--------------------------------------------------------------------------------
/lib/cape.rb:
--------------------------------------------------------------------------------
1 | # Contains the implementation of Cape.
2 | module Cape
3 |
4 | autoload :Capistrano, 'cape/capistrano'
5 | autoload :CoreExt, 'cape/core_ext'
6 | autoload :DSL, 'cape/dsl'
7 | autoload :HashList, 'cape/hash_list'
8 | autoload :Rake, 'cape/rake'
9 | autoload :RecipeDefinition, 'cape/recipe_definition'
10 | autoload :Util, 'cape/util'
11 | autoload :VERSION, 'cape/version'
12 | autoload :XTerm, 'cape/xterm'
13 |
14 | extend DSL
15 |
16 | end
17 |
18 | # The method used to group Cape statements.
19 | #
20 | # @param [Proc] block Cape and Capistrano statements
21 | #
22 | # @return [Cape] the Cape module
23 | #
24 | # @yield [cape] a block containing Cape statements
25 | # @yieldparam [Cape::DSL] cape the Cape DSL; optional
26 | #
27 | # @example Basic Cape usage
28 | # # config/deploy.rb
29 | #
30 | # require 'cape'
31 | #
32 | # Cape do
33 | # mirror_rake_tasks
34 | # end
35 | #
36 | # @example Combining Cape statements with Capistrano statements
37 | # # config/deploy.rb
38 | #
39 | # require 'cape'
40 | #
41 | # namespace :rake_tasks do
42 | # # Use an argument with the Cape block, if you want to or need to.
43 | # Cape do |cape|
44 | # cape.mirror_rake_tasks
45 | # end
46 | # end
47 | def Cape(&block)
48 | Cape.module_eval do
49 | @outer_self = block.binding.eval('self', __FILE__, __LINE__)
50 | begin
51 | if 0 < block.arity
52 | block.call self
53 | else
54 | module_eval(&block)
55 | end
56 | ensure
57 | rake.expire_cache!
58 | end
59 | end
60 | Cape
61 | end
62 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task and renaming logic
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | end
17 | end
18 | """
19 | When I run `cap -vT`
20 | Then the output should contain:
21 | """
22 | cap do_load # A task that shadows a Ruby method.
23 | """
24 | And the output should not contain "long"
25 | And the output should not contain "my_namespace"
26 |
27 | Scenario: mirror the matching Rake task with its implementation
28 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
29 | And a Capfile with:
30 | """
31 | set :current_path, '/current/path'
32 |
33 | Cape do
34 | mirror_rake_tasks :load do |recipes|
35 | recipes.rename do |task_name|
36 | "do_#{task_name}"
37 | end
38 | end
39 | end
40 | """
41 | When I run `cap do_load`
42 | Then the output should contain:
43 | """
44 | * executing `do_load'
45 | """
46 | And the output should contain:
47 | """
48 | `do_load' is only run for servers matching {}, but no servers matched
49 | """
50 |
--------------------------------------------------------------------------------
/cape.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path('../lib', __FILE__)
3 | require 'cape/version'
4 |
5 | Gem::Specification.new do |s|
6 | s.name = 'cape'
7 | s.version = Cape::VERSION
8 | s.summary = 'Dynamically generates Capistrano recipes for Rake tasks'
9 | s.description = <<-end_description.gsub(/^\s+/, '').chomp
10 | Cape dynamically generates Capistrano recipes for Rake tasks.
11 | It provides a DSL for reflecting on Rake tasks and mirroring
12 | them as documented Capistrano recipes. You can pass Rake task
13 | arguments via environment variables.
14 | end_description
15 | s.platform = Gem::Platform::RUBY
16 | s.authors = ['Nils Jonsson']
17 | s.email = %w(cape@nilsjonsson.com)
18 | s.homepage = 'http://njonsson.github.io/cape'
19 | s.license = 'MIT'
20 |
21 | s.rubyforge_project = 'cape'
22 |
23 | s.required_ruby_version = '>= 1.8.7'
24 |
25 | s.files = `git ls-files`.split("\n")
26 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27 | s.executables = `git ls-files -- bin/*`.split("\n").map do |f|
28 | File.basename f
29 | end
30 | s.require_paths = %w(lib)
31 | s.has_rdoc = true
32 |
33 | s.add_development_dependency 'aruba', '~> 0'
34 | s.add_development_dependency 'capistrano', '~> 2'
35 | s.add_development_dependency 'rake', '>= 0.9.3'
36 | s.add_development_dependency 'rspec', '~> 2'
37 |
38 | # Avoid Cucumber's warnings about JSON parser performance.
39 | s.add_development_dependency 'json_pure'
40 | end
41 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.cd { release_path }
14 | recipes.env['RAILS_ENV'] = lambda { rails_env }
15 | end
16 | end
17 | """
18 | When I run `cap -vT`
19 | Then the output should contain:
20 | """
21 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
22 | """
23 | And the output should not contain "with_one_arg"
24 | And the output should not contain "my_namespace"
25 |
26 | Scenario: mirror the matching Rake task with its implementation
27 | Given a full-featured Rakefile
28 | And a Capfile with:
29 | """
30 | set :release_path, '/release/path'
31 | set :rails_env, 'rails-env'
32 |
33 | Cape do
34 | mirror_rake_tasks :long do |recipes|
35 | recipes.cd { release_path }
36 | recipes.env['RAILS_ENV'] = lambda { rails_env }
37 | end
38 | end
39 | """
40 | When I run `cap long`
41 | Then the output should contain:
42 | """
43 | * executing `long'
44 | """
45 | And the output should contain:
46 | """
47 | `long' is only run for servers matching {}, but no servers matched
48 | """
49 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.options[:roles] = :app
14 | recipes.env['RAILS_ENV'] = lambda { rails_env }
15 | end
16 | end
17 | """
18 | When I run `cap -vT`
19 | Then the output should contain:
20 | """
21 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
22 | """
23 | And the output should not contain "with_one_arg"
24 | And the output should not contain "my_namespace"
25 |
26 | Scenario: mirror the matching Rake task with its implementation
27 | Given a full-featured Rakefile
28 | And a Capfile with:
29 | """
30 | set :current_path, '/current/path'
31 | set :rails_env, 'rails-env'
32 |
33 | Cape do
34 | mirror_rake_tasks 'long' do |recipes|
35 | recipes.options[:roles] = :app
36 | recipes.env['RAILS_ENV'] = lambda { rails_env }
37 | end
38 | end
39 | """
40 | When I run `cap long`
41 | Then the output should contain:
42 | """
43 | * executing `long'
44 | """
45 | And the output should contain:
46 | """
47 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
48 | """
49 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.cd { release_path }
17 | end
18 | end
19 | """
20 | When I run `cap -vT`
21 | Then the output should contain:
22 | """
23 | cap do_load # A task that shadows a Ruby method.
24 | """
25 | And the output should not contain "long"
26 | And the output should not contain "my_namespace"
27 |
28 | Scenario: mirror the matching Rake task with its implementation
29 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
30 | And a Capfile with:
31 | """
32 | set :release_path, '/release/path'
33 |
34 | Cape do
35 | mirror_rake_tasks :load do |recipes|
36 | recipes.rename do |task_name|
37 | "do_#{task_name}"
38 | end
39 | recipes.cd { release_path }
40 | end
41 | end
42 | """
43 | When I run `cap do_load`
44 | Then the output should contain:
45 | """
46 | * executing `do_load'
47 | """
48 | And the output should contain:
49 | """
50 | `do_load' is only run for servers matching {}, but no servers matched
51 | """
52 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and valid options
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.options[:roles] = :app
17 | end
18 | end
19 | """
20 | When I run `cap -vT`
21 | Then the output should contain:
22 | """
23 | cap do_load # A task that shadows a Ruby method.
24 | """
25 | And the output should not contain "long"
26 | And the output should not contain "my_namespace"
27 |
28 | Scenario: mirror the matching Rake task with its implementation
29 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
30 | And a Capfile with:
31 | """
32 | set :current_path, '/current/path'
33 |
34 | Cape do
35 | mirror_rake_tasks :load do |recipes|
36 | recipes.rename do |task_name|
37 | "do_#{task_name}"
38 | end
39 | recipes.options[:roles] = :app
40 | end
41 | end
42 | """
43 | When I run `cap do_load`
44 | Then the output should contain:
45 | """
46 | * executing `do_load'
47 | """
48 | And the output should contain:
49 | """
50 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
51 | """
52 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :long do |recipes|
13 | recipes.options[:roles] = :app
14 | recipes.cd { release_path }
15 | recipes.env['RAILS_ENV'] = lambda { rails_env }
16 | end
17 | end
18 | """
19 | When I run `cap -vT`
20 | Then the output should contain:
21 | """
22 | cap long # My long task -- it has a very, very, very, very, very, very, ver...
23 | """
24 | And the output should not contain "with_one_arg"
25 | And the output should not contain "my_namespace"
26 |
27 | Scenario: mirror the matching Rake task with its implementation
28 | Given a full-featured Rakefile
29 | And a Capfile with:
30 | """
31 | set :release_path, '/release/path'
32 | set :rails_env, 'rails-env'
33 |
34 | Cape do
35 | mirror_rake_tasks :long do |recipes|
36 | recipes.options[:roles] = :app
37 | recipes.cd { release_path }
38 | recipes.env['RAILS_ENV'] = lambda { rails_env }
39 | end
40 | end
41 | """
42 | When I run `cap long`
43 | Then the output should contain:
44 | """
45 | * executing `long'
46 | """
47 | And the output should contain:
48 | """
49 | `long' is only run for servers matching {:roles=>:app}, but no servers matched
50 | """
51 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.env['RAILS_ENV'] = lambda { rails_env }
17 | end
18 | end
19 | """
20 | When I run `cap -vT`
21 | Then the output should contain:
22 | """
23 | cap do_load # A task that shadows a Ruby method.
24 | """
25 | And the output should not contain "long"
26 | And the output should not contain "my_namespace"
27 |
28 | Scenario: mirror the matching Rake task with its implementation
29 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
30 | And a Capfile with:
31 | """
32 | set :current_path, '/current/path'
33 | set :rails_env, 'rails-env'
34 |
35 | Cape do
36 | mirror_rake_tasks :load do |recipes|
37 | recipes.rename do |task_name|
38 | "do_#{task_name}"
39 | end
40 | recipes.env['RAILS_ENV'] = lambda { rails_env }
41 | end
42 | end
43 | """
44 | When I run `cap do_load`
45 | Then the output should contain:
46 | """
47 | * executing `do_load'
48 | """
49 | And the output should contain:
50 | """
51 | `do_load' is only run for servers matching {}, but no servers matched
52 | """
53 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, and a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.options[:roles] = :app
17 | recipes.cd { release_path }
18 | end
19 | end
20 | """
21 | When I run `cap -vT`
22 | Then the output should contain:
23 | """
24 | cap do_load # A task that shadows a Ruby method.
25 | """
26 | And the output should not contain "long"
27 | And the output should not contain "my_namespace"
28 |
29 | Scenario: mirror the matching Rake task with its implementation
30 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
31 | And a Capfile with:
32 | """
33 | set :release_path, '/release/path'
34 |
35 | Cape do
36 | mirror_rake_tasks :load do |recipes|
37 | recipes.rename do |task_name|
38 | "do_#{task_name}"
39 | end
40 | recipes.options[:roles] = :app
41 | recipes.cd { release_path }
42 | end
43 | end
44 | """
45 | When I run `cap do_load`
46 | Then the output should contain:
47 | """
48 | * executing `do_load'
49 | """
50 | And the output should contain:
51 | """
52 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
53 | """
54 |
--------------------------------------------------------------------------------
/features/dsl/each_rake_task/unqualified.feature:
--------------------------------------------------------------------------------
1 | Feature: The #each_rake_task DSL method
2 |
3 | In order to use the metadata of Rake tasks in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: enumerate all Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | each_rake_task do |t|
13 | $stdout.puts '', "Name: #{t[:name].inspect}"
14 | if t[:parameters]
15 | $stdout.puts "Parameters: #{t[:parameters].inspect}"
16 | end
17 | if t[:description]
18 | $stdout.puts "Description: #{t[:description].inspect}"
19 | end
20 | $stdout.puts 'Default' if t[:default]
21 | end
22 | end
23 | """
24 | When I run `cap -vT`
25 | Then the output should contain:
26 | """
27 |
28 | Name: "hidden_task"
29 | Description: ""
30 |
31 | Name: "long"
32 | Description: "My long task -- it has a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long description"
33 |
34 | Name: "my_namespace"
35 | Description: "A task that shadows a namespace"
36 | Default
37 |
38 | Name: "my_namespace:in_a_namespace"
39 | Description: "My task in a namespace"
40 |
41 | Name: "my_namespace:my_nested_namespace:in_a_nested_namespace"
42 | Description: "My task in a nested namespace"
43 |
44 | Name: "with_one_arg"
45 | Parameters: ["the_arg"]
46 | Description: "My task with one argument"
47 |
48 | Name: "with_three_args"
49 | Parameters: ["an_arg1", "an_arg2", "an_arg3"]
50 | Description: "My task with three arguments"
51 |
52 | Name: "with_two_args"
53 | Parameters: ["my_arg1", "my_arg2"]
54 | Description: "My task with two arguments"
55 | """
56 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.cd { release_path }
17 | recipes.env['RAILS_ENV'] = lambda { rails_env }
18 | end
19 | end
20 | """
21 | When I run `cap -vT`
22 | Then the output should contain:
23 | """
24 | cap do_load # A task that shadows a Ruby method.
25 | """
26 | And the output should not contain "long"
27 | And the output should not contain "my_namespace"
28 |
29 | Scenario: mirror the matching Rake task with its implementation
30 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
31 | And a Capfile with:
32 | """
33 | set :release_path, '/release/path'
34 | set :rails_env, 'rails-env'
35 |
36 | Cape do
37 | mirror_rake_tasks :load do |recipes|
38 | recipes.rename do |task_name|
39 | "do_#{task_name}"
40 | end
41 | recipes.cd { release_path }
42 | recipes.env['RAILS_ENV'] = lambda { rails_env }
43 | end
44 | end
45 | """
46 | When I run `cap do_load`
47 | Then the output should contain:
48 | """
49 | * executing `do_load'
50 | """
51 | And the output should contain:
52 | """
53 | `do_load' is only run for servers matching {}, but no servers matched
54 | """
55 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.options[:roles] = :app
17 | recipes.env['RAILS_ENV'] = lambda { rails_env }
18 | end
19 | end
20 | """
21 | When I run `cap -vT`
22 | Then the output should contain:
23 | """
24 | cap do_load # A task that shadows a Ruby method.
25 | """
26 | And the output should not contain "long"
27 | And the output should not contain "my_namespace"
28 |
29 | Scenario: mirror the matching Rake task with its implementation
30 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
31 | And a Capfile with:
32 | """
33 | set :current_path, '/current/path'
34 | set :rails_env, 'rails-env'
35 |
36 | Cape do
37 | mirror_rake_tasks :load do |recipes|
38 | recipes.rename do |task_name|
39 | "do_#{task_name}"
40 | end
41 | recipes.options[:roles] = :app
42 | recipes.env['RAILS_ENV'] = lambda { rails_env }
43 | end
44 | end
45 | """
46 | When I run `cap do_load`
47 | Then the output should contain:
48 | """
49 | * executing `do_load'
50 | """
51 | And the output should contain:
52 | """
53 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
54 | """
55 |
--------------------------------------------------------------------------------
/spec/cape/recipe_definition_spec.rb:
--------------------------------------------------------------------------------
1 | require 'cape/recipe_definition'
2 |
3 | describe Cape::RecipeDefinition do
4 | subject(:recipe_definition) { recipe_definition_class.new }
5 |
6 | let(:recipe_definition_class) { described_class }
7 |
8 | describe '#cd' do
9 | specify { expect(recipe_definition.cd).to be_nil }
10 |
11 | it 'is mutable' do
12 | recipe_definition.cd '/foo/bar'
13 | expect(recipe_definition.cd).to eq('/foo/bar')
14 |
15 | recipe_definition.cd lambda { '/foo/bar' }
16 | expect(recipe_definition.cd.call).to eq('/foo/bar')
17 |
18 | recipe_definition.cd { '/foo/bar' }
19 | expect(recipe_definition.cd.call).to eq('/foo/bar')
20 | end
21 |
22 | it 'complains about wrong arity' do
23 | expect {
24 | recipe_definition.cd do |foo, bar|
25 | end
26 | }.to raise_error(ArgumentError, 'Must have 0 parameters but has 2')
27 | end
28 | end
29 |
30 | describe '#env' do
31 | specify { expect(recipe_definition.env).to eq({}) }
32 |
33 | it 'is mutable' do
34 | recipe_definition.env['FOO'] = 'bar'
35 | expect(recipe_definition.env).to eq('FOO' => 'bar')
36 | end
37 | end
38 |
39 | describe '#options' do
40 | specify { expect(recipe_definition.options).to eq({}) }
41 |
42 | it 'is mutable' do
43 | recipe_definition.options[:some_option] = 'foo'
44 | expect(recipe_definition.options).to eq(:some_option => 'foo')
45 | end
46 | end
47 |
48 | describe '#rename' do
49 | specify { expect(recipe_definition.rename).to be_nil }
50 |
51 | it 'is mutable' do
52 | recipe_definition.rename do |task_name|
53 | "#{task_name}_recipe"
54 | end
55 | expect(recipe_definition.rename.call(:foo)).to eq('foo_recipe')
56 | end
57 |
58 | it 'complains about wrong arity' do
59 | expect {
60 | recipe_definition.rename do |foo, bar|
61 | end
62 | }.to raise_error(ArgumentError, 'Must have 1 parameter but has 2')
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/spec/cape/hash_list_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/hash_list'
3 |
4 | describe Cape::HashList do
5 | subject(:hash_list) { hash_list_class.new }
6 |
7 | let(:hash_list_class) { described_class }
8 |
9 | describe 'that is empty' do
10 | specify { expect(hash_list).to be_empty }
11 |
12 | describe '#inspect' do
13 | specify { expect(hash_list.inspect).to eq('{}') }
14 | end
15 |
16 | describe '#to_a' do
17 | specify { expect(hash_list.to_a).to eq([]) }
18 | end
19 |
20 | describe '#to_hash' do
21 | specify { expect(hash_list.to_hash).to eq({}) }
22 | end
23 |
24 | describe 'when values are added out of order' do
25 | before :each do
26 | hash_list['foo'] = 'xxx'
27 | hash_list['foo'] = 'bar'
28 | hash_list['baz'] = 'qux'
29 | end
30 |
31 | specify { expect(hash_list).to eq({'foo' => 'bar', 'baz' => 'qux'}) }
32 |
33 | describe '#inspect' do
34 | specify {
35 | expect(hash_list.inspect).to eq('{"foo"=>"bar", "baz"=>"qux"}')
36 | }
37 | end
38 |
39 | describe '#to_a' do
40 | specify { expect(hash_list.to_a).to eq([%w(foo bar), %w(baz qux)]) }
41 | end
42 |
43 | describe '#to_hash' do
44 | specify {
45 | expect(hash_list.to_hash).to eq({'foo' => 'bar', 'baz' => 'qux'})
46 | }
47 | end
48 | end
49 | end
50 |
51 | describe 'that has values out of order' do
52 | subject(:hash_list) { hash_list_class.new 'foo' => 'bar', 'baz' => 'qux' }
53 |
54 | specify { expect(hash_list).to eq({'foo' => 'bar', 'baz' => 'qux'}) }
55 |
56 | it 'indexes the values as expected' do
57 | expect(hash_list['foo']).to eq('bar')
58 | expect(hash_list['baz']).to eq('qux')
59 | expect(hash_list['not-found']).to be_nil
60 | end
61 |
62 | describe '#clear' do
63 | before :each do
64 | hash_list.clear
65 | end
66 |
67 | specify { expect(hash_list).to be_empty }
68 | end
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, a different directory, and environment variables
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the matching Rake task
8 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :load do |recipes|
13 | recipes.rename do |task_name|
14 | "do_#{task_name}"
15 | end
16 | recipes.options[:roles] = :app
17 | recipes.cd { release_path }
18 | recipes.env['RAILS_ENV'] = lambda { rails_env }
19 | end
20 | end
21 | """
22 | When I run `cap -vT`
23 | Then the output should contain:
24 | """
25 | cap do_load # A task that shadows a Ruby method.
26 | """
27 | And the output should not contain "long"
28 | And the output should not contain "my_namespace"
29 |
30 | Scenario: mirror the matching Rake task with its implementation
31 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
32 | And a Capfile with:
33 | """
34 | set :release_path, '/release/path'
35 | set :rails_env, 'rails-env'
36 |
37 | Cape do
38 | mirror_rake_tasks :load do |recipes|
39 | recipes.rename do |task_name|
40 | "do_#{task_name}"
41 | end
42 | recipes.options[:roles] = :app
43 | recipes.cd { release_path }
44 | recipes.env['RAILS_ENV'] = lambda { rails_env }
45 | end
46 | end
47 | """
48 | When I run `cap do_load`
49 | Then the output should contain:
50 | """
51 | * executing `do_load'
52 | """
53 | And the output should contain:
54 | """
55 | `do_load' is only run for servers matching {:roles=>:app}, but no servers matched
56 | """
57 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_cd.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a different directory
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror a Rake task with its implementation, using a Capistrano variable inside a lambda
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | set :release_path, '/release/path'
12 |
13 | Cape do
14 | mirror_rake_tasks do |recipes|
15 | recipes.cd lambda { release_path }
16 | end
17 | end
18 | """
19 | When I run `cap long`
20 | Then the output should contain:
21 | """
22 | * executing `long'
23 | """
24 | And the output should contain:
25 | """
26 | `long' is only run for servers matching {}, but no servers matched
27 | """
28 |
29 | Scenario: mirror a Rake task with its implementation, using a Capistrano variable inside a block
30 | Given a full-featured Rakefile
31 | And a Capfile with:
32 | """
33 | set :release_path, '/release/path'
34 |
35 | Cape do
36 | mirror_rake_tasks do |recipes|
37 | recipes.cd { release_path }
38 | end
39 | end
40 | """
41 | When I run `cap long`
42 | Then the output should contain:
43 | """
44 | * executing `long'
45 | """
46 | And the output should contain:
47 | """
48 | `long' is only run for servers matching {}, but no servers matched
49 | """
50 |
51 | Scenario: mirror a Rake task with its implementation, using a string
52 | Given a full-featured Rakefile
53 | And a Capfile with:
54 | """
55 | Cape do
56 | mirror_rake_tasks do |recipes|
57 | recipes.cd '/release/path'
58 | end
59 | end
60 | """
61 | When I run `cap long`
62 | Then the output should contain:
63 | """
64 | * executing `long'
65 | """
66 | And the output should contain:
67 | """
68 | `long' is only run for servers matching {}, but no servers matched
69 | """
70 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/with_defined_namespace.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method with a defined namespace
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror only the Rake tasks in the matching namespace
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks :my_namespace
13 | end
14 | """
15 | When I run `cap -vT`
16 | Then the output should contain:
17 | """
18 | cap my_namespace # A task that shadows a names...
19 | """
20 | And the output should contain:
21 | """
22 | cap my_namespace:in_a_namespace # My task in a namespace.
23 | """
24 | And the output should contain:
25 | """
26 | cap my_namespace:my_nested_namespace:in_a_nested_namespace # My task in a nested namespace.
27 | """
28 | And the output should not contain "long"
29 |
30 | Scenario: mirror a Rake task that shadows the matching namespace with its implementation
31 | Given a full-featured Rakefile
32 | And a Capfile with:
33 | """
34 | set :current_path, '/current/path'
35 |
36 | Cape do
37 | mirror_rake_tasks 'my_namespace'
38 | end
39 | """
40 | When I run `cap my_namespace`
41 | Then the output should contain:
42 | """
43 | * executing `my_namespace'
44 | """
45 | And the output should contain:
46 | """
47 | `my_namespace' is only run for servers matching {}, but no servers matched
48 | """
49 |
50 | Scenario: mirror a Rake task in the matching namespace with its implementation
51 | Given a full-featured Rakefile
52 | And a Capfile with:
53 | """
54 | set :current_path, '/current/path'
55 |
56 | Cape do
57 | mirror_rake_tasks :my_namespace
58 | end
59 | """
60 | When I run `cap my_namespace:my_nested_namespace:in_a_nested_namespace`
61 | Then the output should contain:
62 | """
63 | * executing `my_namespace:my_nested_namespace:in_a_nested_namespace'
64 | """
65 | And the output should contain:
66 | """
67 | `my_namespace:my_nested_namespace:in_a_nested_namespace' is only run for servers matching {}, but no servers matched
68 | """
69 |
--------------------------------------------------------------------------------
/features/step_definitions.rb:
--------------------------------------------------------------------------------
1 | Given 'a Capfile with:' do |content|
2 | preamble = <<-end_preamble
3 | $:.unshift #{File.expand_path('../../lib', __FILE__).inspect}
4 | require 'cape'
5 |
6 | end_preamble
7 | step('a file named "Capfile" with:', (preamble + content))
8 | end
9 |
10 | Given 'a full-featured Rakefile' do
11 | step 'a file named "Rakefile" with:', <<-end_step
12 | desc 'My long task -- it has a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long description'
13 | task :long
14 |
15 | desc 'My task with one argument'
16 | task :with_one_arg, [:the_arg]
17 |
18 | desc 'A task that shadows a namespace'
19 | task :my_namespace
20 |
21 | namespace :my_namespace do
22 | desc 'My task in a namespace'
23 | task :in_a_namespace
24 |
25 | namespace :my_nested_namespace do
26 | desc 'My task in a nested namespace'
27 | task :in_a_nested_namespace
28 | end
29 | end
30 |
31 | desc 'My task with two arguments'
32 | task :with_two_args, [:my_arg1, :my_arg2]
33 |
34 | desc 'My task with three arguments'
35 | task :with_three_args, [:an_arg1, :an_arg2, :an_arg3]
36 |
37 | task :hidden_task
38 | end_step
39 | end
40 |
41 | Given 'a full-featured Rakefile defining a Ruby-method-shadowing task' do
42 | step 'a file named "Rakefile" with:', <<-end_step
43 | desc 'A task that shadows a Ruby method'
44 | task :load
45 |
46 | desc 'My long task -- it has a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long description'
47 | task :long
48 |
49 | desc 'My task with one argument'
50 | task :with_one_arg, [:the_arg]
51 |
52 | desc 'A task that shadows a namespace'
53 | task :my_namespace
54 |
55 | namespace :my_namespace do
56 | desc 'My task in a namespace'
57 | task :in_a_namespace
58 |
59 | namespace :my_nested_namespace do
60 | desc 'My task in a nested namespace'
61 | task :in_a_nested_namespace
62 | end
63 | end
64 |
65 | desc 'My task with two arguments'
66 | task :with_two_args, [:my_arg1, :my_arg2]
67 |
68 | desc 'My task with three arguments'
69 | task :with_three_args, [:an_arg1, :an_arg2, :an_arg3]
70 |
71 | task :hidden_task
72 | end_step
73 | end
74 |
--------------------------------------------------------------------------------
/History.markdown:
--------------------------------------------------------------------------------
1 | # Version history for the _Cape_ project
2 |
3 | ## v1.8.0, Mon 11/18/2013
4 |
5 | * Add official support for MRI v1.9.2 and v2.0
6 | * Add official support for Rake v10.x
7 | * Don’t crash if the user attempts to mirror a task whose name collides with a Ruby method
8 |
9 | ## v1.7.0, Thu 3/07/2013
10 |
11 | * Introduce a new DSL that supports task renaming and path switching, and deprecate the old one
12 |
13 | ## v1.6.2, Fri 2/22/2013
14 |
15 | * Correctly update environment variables when set more than once
16 |
17 | ## v1.6.1, Mon 2/18/2013
18 |
19 | * Respect the specified order of environment variables in the remote command line
20 |
21 | ## v1.6.0, Wed 11/14/2012
22 |
23 | * Enable mirroring and enumeration of hidden Rake tasks for versions of Rake that make it possible
24 |
25 | ## v1.5.0, Tue 10/09/2012
26 |
27 | * Automatically detect and use Bundler when running Rake
28 |
29 | ## v1.4.0, Mon 2/06/2012
30 |
31 | * Cache the Rake task list to improve Capistrano responsiveness
32 |
33 | ## v1.3.0, Fri 2/03/2012
34 |
35 | * Don’t allow `nil` environment variables to pass through to the remote command line
36 |
37 | ## v1.2.0, Wed 2/01/2012
38 |
39 | * Add support in the DSL for specifying remote environment variables and Capistrano recipe options
40 | * Add support for Rake tasks that overlap with (have the same full name as) namespaces
41 | * Match Rake tasks properly: by the full name of the task rather than a substring thereof
42 | * Don’t choke on unexpected output from Rake
43 | * Silence Rake stderr output while enumerating Rake tasks
44 | * Tweak the wording of generated Capistrano recipe descriptions
45 | * Tighten RubyGem dependency specifications in an effort to avoid potential compatibility issues
46 |
47 | ## v1.1.0, Thu 12/29/2011
48 |
49 | * Allow environment variables for Rake task arguments to be optional
50 |
51 | ## v1.0.3, Thu 12/29/2011
52 |
53 | * Run Cucumber features from `gem test cape`
54 |
55 | ## v1.0.2, Thu 12/29/2011
56 |
57 | * Support Rake task arguments that contain whitespace
58 |
59 | ## v1.0.1, Tue 11/29/2011
60 |
61 | * Don’t run Cucumber features from `gem test cape` because they fail
62 |
63 | ## v1.0.0, Mon 11/28/2011
64 |
65 | (First release)
66 |
--------------------------------------------------------------------------------
/lib/cape/hash_list.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # A HashList is a collection of key-value pairs. It is similar to an Array,
4 | # except that indexing is done via arbitrary keys of any object type, not an
5 | # integer index. Hashes enumerate their values in the order that the
6 | # corresponding keys were inserted.
7 | #
8 | # This class exists because in Ruby v1.8.7 and earlier, Hash did not preserve
9 | # the insertion order of keys.
10 | #
11 | # @api private
12 | class HashList < ::Array
13 |
14 | # Constructs a new HashList using the specified _arguments_.
15 | #
16 | # @param [Hash] arguments attribute values
17 | def initialize(*arguments)
18 | super Hash[*arguments].to_a
19 | end
20 |
21 | # Compares a HashList to another object.
22 | #
23 | # @param [Object] other another object
24 | #
25 | # @return [true] the HashList has the same number of keys as _other_, and
26 | # each key-value pair is equal (according to Object#==)
27 | # @return [false] the HashList is not equal to _other_
28 | def ==(other)
29 | other.is_a?(::Hash) ? (other == to_hash) : super(other)
30 | end
31 |
32 | # Retrieves a value from the HashList.
33 | #
34 | # @param [Object] key a key
35 | #
36 | # @return [Object] the value for _key_
37 | # @return [nil] if there is no value for _key_
38 | def [](key)
39 | entry = find do |pair|
40 | Array(pair).first == key
41 | end
42 | entry ? Array(entry).last : nil
43 | end
44 |
45 | # Sets a value in the HashList.
46 | #
47 | # @param [Object] key a key
48 | # @param [Object] value a value for _key_
49 | #
50 | # @return [HashList] the HashList
51 | def []=(key, value)
52 | index = find_index do |pair|
53 | Array(pair).first == key
54 | end
55 | if index
56 | super(index, [key, value])
57 | else
58 | self << [key, value]
59 | end
60 | self
61 | end
62 |
63 | # Provides a string representation of the HashList.
64 | #
65 | # @return [String] a string representation of the HashList
66 | def inspect
67 | entries = collect do |pair|
68 | Array(pair).collect(&:inspect).join '=>'
69 | end
70 | "{#{entries.join ', '}}"
71 | end
72 |
73 | # Converts the HashList to a Hash.
74 | #
75 | # @return [Hash] a hash
76 | def to_hash
77 | ::Hash[to_a]
78 | end
79 |
80 | end
81 |
82 | end
83 |
--------------------------------------------------------------------------------
/features/dsl/rake_executable.feature:
--------------------------------------------------------------------------------
1 | Feature: The #local_rake_executable and #remote_rake_executable DSL attributes
2 |
3 | In order to control which Rake executables are used locally and remotely,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: use a different Rake executable to enumerate Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape.local_rake_executable = 'echo "rake this-comes-from-overridden-rake # This comes from overridden Rake" #'
12 |
13 | Cape do
14 | $stdout.puts "We changed the local Rake executable to #{local_rake_executable.inspect}."
15 | $stdout.puts "We left the remote Rake executable as #{remote_rake_executable.inspect}."
16 | each_rake_task do |t|
17 | $stdout.puts '', "Name: #{t[:name].inspect}"
18 | if t[:parameters]
19 | $stdout.puts "Parameters: #{t[:parameters].inspect}"
20 | end
21 | if t[:description]
22 | $stdout.puts "Description: #{t[:description].inspect}"
23 | end
24 | end
25 | end
26 | """
27 | When I run `cap -vT`
28 | Then the output should contain:
29 | """
30 | We changed the local Rake executable to "echo \"rake this-comes-from-overridden-rake # This comes from overridden Rake\" #"
31 | """
32 | And the output should contain:
33 | """
34 | We left the remote Rake executable as "/usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake"
35 | """
36 | And the output should contain:
37 | """
38 |
39 | Name: "this-comes-from-overridden-rake"
40 | Description: "This comes from overridden Rake"
41 | """
42 |
43 | Scenario: use a different Rake executable to execute Rake tasks
44 | Given a full-featured Rakefile
45 | And a Capfile with:
46 | """
47 | set :current_path, '/current/path'
48 |
49 | Cape.remote_rake_executable = 'echo "This comes from overridden Rake" #'
50 |
51 | Cape do
52 | $stdout.puts "We changed the remote Rake executable to #{remote_rake_executable.inspect}."
53 | $stdout.puts "We left the local Rake executable as #{local_rake_executable.inspect}."
54 | mirror_rake_tasks
55 | end
56 | """
57 | When I run `cap long`
58 | Then the output should contain:
59 | """
60 | We changed the remote Rake executable to "echo \"This comes from overridden Rake\" #"
61 | """
62 | And the output should contain:
63 | """
64 | We left the local Rake executable as "/usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake"
65 | """
66 |
--------------------------------------------------------------------------------
/spec/cape/xterm_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/xterm'
3 |
4 | describe Cape::XTerm do
5 | let(:xterm_module) { described_class }
6 |
7 | describe '.format' do
8 | it 'does not try to format a nil argument' do
9 | expect(xterm_module.format(nil)).to be_nil
10 | end
11 |
12 | it 'does not try to format a nil argument with a recognized format' do
13 | expect(xterm_module.format(nil, :bold)).to be_nil
14 | end
15 |
16 | specify do
17 | expect {
18 | xterm_module.format nil, :this_does_not_exist
19 | }.to raise_error(ArgumentError, 'Unrecognized format :this_does_not_exist')
20 | end
21 |
22 | specify do
23 | expect {
24 | xterm_module.format 'foo', :this_does_not_exist
25 | }.to raise_error(ArgumentError, 'Unrecognized format :this_does_not_exist')
26 | end
27 |
28 | described_class::FORMATS.each do |format, code|
29 | it "formats a String argument with the #{format.inspect} format" do
30 | expect(xterm_module.format('foo',
31 | format)).to eq("\e[#{code}mfoo\e[0m")
32 | end
33 | end
34 |
35 | it "formats a String argument with the :bold and :foreground_red formats" do
36 | expect(xterm_module.format('foo',
37 | :bold,
38 | :foreground_red)).to eq("\e[1;31mfoo\e[0m")
39 | end
40 | end
41 |
42 | specify { expect(xterm_module).not_to respond_to(:this_does_not_exist) }
43 |
44 | specify do
45 | expect {
46 | xterm_module.this_does_not_exist
47 | }.to raise_error(NoMethodError,
48 | "undefined method `this_does_not_exist' for #{xterm_module.name}:Module")
49 | end
50 |
51 | described_class::FORMATS.each do |format, code|
52 | specify { expect(xterm_module).to respond_to(format) }
53 |
54 | describe ".#{format}" do
55 | it "formats a String argument with the #{format.inspect} format" do
56 | expect(xterm_module.send(format,
57 | 'foo')).to eq(xterm_module.format('foo',
58 | format))
59 | end
60 | end
61 | end
62 |
63 | specify { expect(xterm_module).to respond_to(:bold_and_foreground_red) }
64 |
65 | describe '.bold_and_foreground_red' do
66 | it "formats a String argument with the :bold and :foreground_red formats" do
67 | expect(xterm_module.bold_and_foreground_red('foo')).to eq(xterm_module.format('foo',
68 | :bold,
69 | :foreground_red))
70 | end
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/lib/cape/recipe_definition.rb:
--------------------------------------------------------------------------------
1 | require 'cape/hash_list'
2 |
3 | module Cape
4 |
5 | # Determines how a Capistrano recipe will be defined.
6 | class RecipeDefinition
7 |
8 | # The remote directory from which Rake tasks will be executed.
9 | #
10 | # @overload cd
11 | # The remote directory from which Rake tasks will be executed.
12 | #
13 | # @return [String, Proc] the value or a block that returns the value
14 | #
15 | # @overload cd(path)
16 | # Sets the remote directory from which Rake tasks will be executed.
17 | #
18 | # @param [String, Proc] path the value or a callable object that returns
19 | # the value
20 | #
21 | # @return [String, Proc] _path_
22 | #
23 | # @raise [ArgumentError] _path_ is a callable object that has parameters
24 | #
25 | # @overload cd(&block)
26 | # Sets the remote directory from which Rake tasks will be executed.
27 | #
28 | # @yieldreturn [String] the value
29 | #
30 | # @return [String, Proc] _block_
31 | #
32 | # @raise [ArgumentError] _block_ has parameters
33 | def cd(path=nil, &block)
34 | if (cd = (path || block))
35 | if cd.respond_to?(:arity)
36 | case cd.arity
37 | when -1
38 | # Lambda: good
39 | when 0
40 | # Good
41 | else
42 | raise ::ArgumentError, "Must have 0 parameters but has #{cd.arity}"
43 | end
44 | end
45 |
46 | @cd = cd
47 | else
48 | @cd
49 | end
50 | end
51 |
52 | # A hash of remote environment variables.
53 | #
54 | # @return [HashList] the desired environment of the remote computer
55 | def env
56 | @env ||= HashList.new
57 | end
58 |
59 | # A hash of Capistrano recipe options to pass to the Capistrano +task+
60 | # method.
61 | #
62 | # @return [HashList] the desired Capistrano recipe options
63 | #
64 | # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano 'task' method options
65 | def options
66 | @options ||= HashList.new
67 | end
68 |
69 | # How Rake tasks will be named when they are mirrored as Capistrano recipes.
70 | #
71 | # @overload rename
72 | # How Rake tasks will be named when they are mirrored as Capistrano
73 | # recipes.
74 | #
75 | # @return [Proc] the block that generates a Capistrano recipe name from a
76 | # Rake task name
77 | #
78 | # @overload rename(&block)
79 | # Determines how Rake tasks will be named when they are mirrored as
80 | # Capistrano recipes.
81 | #
82 | # @yield [task_name]
83 | # @yieldparam task_name [String] the name of a Rake task
84 | # @yieldreturn [String] a name for the Capistrano recipe
85 | #
86 | # @return [Proc] _block_
87 | #
88 | # @raise [ArgumentError] _block_ does not have exactly one parameter
89 | def rename(&block)
90 | if block
91 | unless (block.arity == 1)
92 | raise ::ArgumentError, "Must have 1 parameter but has #{block.arity}"
93 | end
94 |
95 | @rename = block
96 | else
97 | @rename
98 | end
99 | end
100 |
101 | end
102 |
103 | end
104 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require 'appraisal'
3 | rescue LoadError
4 | end
5 | begin
6 | require 'bundler/gem_tasks'
7 | rescue LoadError
8 | end
9 |
10 | begin
11 | require 'yard'
12 | rescue LoadError
13 | else
14 | namespace :build do
15 | YARD::Rake::YardocTask.new :doc
16 | end
17 | end
18 |
19 | def define_features_task(name, options)
20 | begin
21 | require 'cucumber/rake/task'
22 | rescue LoadError
23 | else
24 | Cucumber::Rake::Task.new name, options[:desc] do |t|
25 | t.bundler = false
26 |
27 | cucumber_opts = [t.cucumber_opts]
28 | cucumber_opts << "--backtrace" if options[:backtrace]
29 | if options.key?(:format)
30 | cucumber_opts << "--format #{options[:format]}"
31 | else
32 | cucumber_opts << '--format pretty'
33 | end
34 | cucumber_opts << "--tags #{options[:tags]}" if options.key?(:tags)
35 | t.cucumber_opts = cucumber_opts.join(' ')
36 | end
37 | end
38 | end
39 |
40 | tags = `grep -Ehr "^\\s*@\\S+\\s*$" features`.split("\n").
41 | collect(&:strip).
42 | uniq.
43 | sort
44 | options = {:desc => 'Test features'}
45 | options[:tags] = '@focus' if tags.delete('@focus')
46 | define_features_task :features, options
47 |
48 | unless tags.empty?
49 | namespace :features do
50 | tags.each do |t|
51 | define_features_task t.gsub(/^@/, ''),
52 | :desc => "Test features tagged #{t}", :tags => t
53 | end
54 | end
55 | end
56 |
57 | def define_spec_task(name, options={})
58 | desc options[:desc]
59 | begin
60 | require 'rspec/core/rake_task'
61 | rescue LoadError
62 | else
63 | RSpec::Core::RakeTask.new name do |t|
64 | t.rspec_opts ||= []
65 | t.rspec_opts << "--backtrace" if options[:backtrace]
66 | unless options[:debug] == false
67 | available = %w(debugger ruby-debug).detect do |debugger_library|
68 | begin
69 | require debugger_library
70 | rescue LoadError
71 | false
72 | else
73 | true
74 | end
75 | end
76 | if available
77 | t.rspec_opts << '--debug'
78 | else
79 | require 'cape/xterm'
80 | $stderr.puts Cape::XTerm.bold('*** Debugging tools not installed')
81 | end
82 | end
83 | t.rspec_opts << "--format #{options[:format]}" if options.key?(:format)
84 | t.pattern = options[:pattern] || %w(spec/*_spec.rb spec/**/*_spec.rb)
85 | end
86 | end
87 | end
88 |
89 | define_spec_task :spec, :desc => 'Run specs'
90 |
91 | namespace :spec do
92 | uncommitted_specs = `git ls-files --modified --others *_spec.rb`.split("\n")
93 | desc = 'Run uncommitted specs'
94 | desc += ' (none)' if uncommitted_specs.empty?
95 | define_spec_task :uncommitted, :desc => desc, :pattern => uncommitted_specs
96 | end
97 |
98 | desc 'Run specs and test features'
99 | task '' => [:spec, :features]
100 | task :default => [:spec, :features]
101 |
102 | # Support the 'gem test' command.
103 | namespace :test do
104 | define_spec_task :spec, :desc => '', :backtrace => true,
105 | :debug => false,
106 | :format => :progress
107 |
108 | define_features_task :features, :desc => '', :backtrace => true,
109 | :format => :progress
110 | end
111 | task :test => %w(test:spec test:features)
112 |
--------------------------------------------------------------------------------
/spec/cape/rake_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/rake'
3 |
4 | describe Cape::Rake do
5 | subject(:rake) { rake_class.new }
6 |
7 | let(:rake_class) { described_class }
8 |
9 | describe '::DEFAULT_EXECUTABLE' do
10 | subject(:constant) { rake_class::DEFAULT_EXECUTABLE }
11 |
12 | specify { expect(constant).to be_frozen }
13 | end
14 |
15 | describe '#==' do
16 | it('recognizes equivalent instances to be equal') {
17 | expect(rake_class.new).to eq(rake_class.new)
18 | }
19 |
20 | it('compares using #local_executable') {
21 | expect(rake_class.new).not_to eq(rake_class.new(:local_executable => 'foo'))
22 | }
23 |
24 | it('compares using #remote_executable') {
25 | expect(rake_class.new).not_to eq(rake_class.new(:remote_executable => 'foo'))
26 | }
27 | end
28 |
29 | describe '-- without specified attributes --' do
30 | describe '#local_executable' do
31 | specify { expect(rake.local_executable).to eq(rake_class::DEFAULT_EXECUTABLE) }
32 | end
33 |
34 | describe '#remote_executable' do
35 | specify { expect(rake.remote_executable).to eq(rake_class::DEFAULT_EXECUTABLE) }
36 | end
37 | end
38 |
39 | describe '-- with specified attributes --' do
40 | subject(:rake) {
41 | rake_class.new :local_executable => ('the specified value of ' +
42 | '#local_executable'),
43 | :remote_executable => ('the specified value of ' +
44 | '#remote_executable')
45 | }
46 |
47 | describe '#local_executable' do
48 | specify { expect(rake.local_executable).to eq('the specified value of #local_executable') }
49 | end
50 |
51 | describe '#remote_executable' do
52 | specify { expect(rake.remote_executable).to eq('the specified value of #remote_executable') }
53 | end
54 | end
55 |
56 | describe '-- with respect to caching --' do
57 | before :each do
58 | allow(rake).to receive(:fetch_output).and_return(output)
59 | end
60 |
61 | let(:output) {
62 | <<-end_output
63 | rake foo # foo
64 | rake bar # bar
65 | rake baz # baz
66 | end_output
67 | }
68 |
69 | describe '#each_task' do
70 | it 'builds and uses a cache' do
71 | expect(rake).to receive(:fetch_output).once.and_return(output)
72 | rake.each_task do |t|
73 | end
74 | rake.each_task do |t|
75 | end
76 | end
77 |
78 | it 'does not expire the cache' do
79 | expect(rake).not_to receive(:expire_cache!)
80 | rake.each_task do |t|
81 | end
82 | end
83 |
84 | it 'expires the cache in the event of an error' do
85 | expect(rake).to receive(:expire_cache!).once
86 | begin
87 | rake.each_task do |t|
88 | raise 'pow!'
89 | end
90 | rescue
91 | end
92 | end
93 |
94 | it 'does not swallow errors' do
95 | expect {
96 | rake.each_task do |t|
97 | raise ZeroDivisionError, 'pow!'
98 | end
99 | }.to raise_error(ZeroDivisionError, 'pow!')
100 | end
101 | end
102 |
103 | describe '#expire_cache!' do
104 | it 'expires the cache' do
105 | expect(rake).to receive(:fetch_output).twice.and_return(output)
106 | rake.each_task do |t|
107 | end
108 | rake.expire_cache!
109 | rake.each_task do |t|
110 | end
111 | end
112 | end
113 |
114 | describe '#local_executable=' do
115 | describe 'with the same value,' do
116 | it 'does not expire the cache' do
117 | expect(rake).not_to receive(:expire_cache!)
118 | rake.local_executable = rake.local_executable
119 | end
120 | end
121 |
122 | describe 'with a different value,' do
123 | it 'expires the cache' do
124 | expect(rake).to receive(:expire_cache!).once
125 | rake.local_executable = rake.local_executable + ' foo'
126 | end
127 | end
128 | end
129 | end
130 | end
131 |
--------------------------------------------------------------------------------
/lib/cape/rake.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # An abstraction of the Rake installation and available tasks.
4 | class Rake
5 |
6 | # The default command used to run Rake. We use `bundle check` to detect the
7 | # presence of Bundler and a Bundler configuration. If Bundler is installed on
8 | # the computer and configured, we prepend `rake` with `bundle exec`.
9 | DEFAULT_EXECUTABLE = (
10 | '/usr/bin/env ' +
11 | '`' +
12 | '/usr/bin/env bundle check >/dev/null 2>&1; ' +
13 | 'case $? in ' +
14 | # Exit code 0: bundle is defined and installed
15 | # Exit code 1: bundle is defined but not installed
16 | '0|1 ) ' +
17 | 'echo bundle exec ' +
18 | ';; ' +
19 | 'esac' +
20 | '` ' +
21 | 'rake'
22 | ).freeze
23 |
24 | # Sets the command used to run Rake on remote computers.
25 | #
26 | # @param [String] value the command used to run Rake on remote computers
27 | #
28 | # @return [String] _value_
29 | attr_writer :remote_executable
30 |
31 | # Constructs a new Rake object with the specified _attributes_.
32 | #
33 | # @param [Hash] attributes attribute values
34 | def initialize(attributes={})
35 | attributes.each do |name, value|
36 | send "#{name}=", value
37 | end
38 | end
39 |
40 | # Compares the Rake object to another.
41 | #
42 | # @param [Object] other another object
43 | #
44 | # @return [true] the {Rake} object is equal to _other_
45 | # @return [false] the {Rake} object is unequal to _other_
46 | def ==(other)
47 | other.kind_of?(Rake) &&
48 | (other.local_executable == local_executable) &&
49 | (other.remote_executable == remote_executable)
50 | end
51 |
52 | # Enumerates Rake tasks.
53 | #
54 | # @param [String, Symbol] task_expression the full name of a task or
55 | # namespace to filter
56 | #
57 | # @yield [task] a block that processes tasks
58 | # @yieldparam [Hash] task metadata on a task
59 | #
60 | # @return [Rake] the object
61 | def each_task(task_expression=nil)
62 | previous_task, this_task = nil, nil
63 | task_expression = task_expression ?
64 | ::Regexp.escape(task_expression.to_s) :
65 | '.+?'
66 | regexp = /^rake (#{task_expression}(?::.+?)?)(?:\[(.+?)\])?\s+#\s*(.*)/
67 | each_output_line do |l|
68 | unless (matches = l.chomp.match(regexp))
69 | next
70 | end
71 |
72 | previous_task = this_task
73 | this_task = {}.tap do |t|
74 | t[:name] = matches[1].strip
75 | t[:parameters] = matches[2].split(',') if matches[2]
76 | t[:description] = matches[3]
77 | end
78 | if previous_task
79 | all_but_last_segment = this_task[:name].split(':')[0...-1].join(':')
80 | previous_task[:default] = all_but_last_segment ==
81 | previous_task[:name]
82 | yield previous_task
83 | end
84 | end
85 | yield this_task if this_task
86 | self
87 | end
88 |
89 | # Forces cached Rake task metadata (if any) to be discarded.
90 | def expire_cache!
91 | @output_lines = nil
92 | self
93 | end
94 |
95 | # The command used to run Rake on the local computer.
96 | #
97 | # @return [String] the command used to run Rake on the local computer
98 | #
99 | # @see DEFAULT_EXECUTABLE
100 | def local_executable
101 | @local_executable ||= DEFAULT_EXECUTABLE
102 | end
103 |
104 | # Sets the command used to run Rake on the local computer and discards any
105 | # cached Rake task metadata.
106 | #
107 | # @param [String] value the command used to run Rake on the local computer
108 | #
109 | # @return [String] _value_
110 | #
111 | # @see #expire_cache!
112 | def local_executable=(value)
113 | unless @local_executable == value
114 | @local_executable = value
115 | expire_cache!
116 | end
117 | value
118 | end
119 |
120 | # The command used to run Rake on remote computers.
121 | #
122 | # @return [String] the command used to run Rake on remote computers
123 | #
124 | # @see DEFAULT_EXECUTABLE
125 | def remote_executable
126 | @remote_executable ||= DEFAULT_EXECUTABLE
127 | end
128 |
129 | private
130 |
131 | def each_output_line(&block)
132 | if @output_lines
133 | @output_lines.each(&block)
134 | return self
135 | end
136 |
137 | @output_lines = []
138 | begin
139 | fetch_output.each_line do |l|
140 | @output_lines << l
141 | block.call(l)
142 | end
143 | rescue
144 | expire_cache!
145 | raise
146 | end
147 | self
148 | end
149 |
150 | def fetch_output
151 | `#{local_executable} --all --tasks 2>/dev/null || #{local_executable} --tasks 2>/dev/null`
152 | end
153 | end
154 |
155 | end
156 |
--------------------------------------------------------------------------------
/spec/cape/dsl_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'cape/dsl'
3 | require 'cape/capistrano'
4 | require 'cape/core_ext/hash'
5 | require 'cape/core_ext/symbol'
6 | require 'cape/rake'
7 |
8 | describe Cape::DSL do
9 | subject(:dsl) {
10 | Object.new.tap do |o|
11 | o.extend dsl_module
12 | end
13 | }
14 |
15 | let(:dsl_module) { described_class }
16 |
17 | let(:capistrano) { double(Cape::Capistrano).as_null_object }
18 |
19 | let(:rake) { double(Cape::Rake).as_null_object }
20 |
21 | let(:task_expression) { 'task:expression' }
22 |
23 | before :each do
24 | allow(Cape::Capistrano).to receive(:new).and_return(capistrano)
25 | allow(Cape::Rake ).to receive(:new).and_return(rake)
26 | end
27 |
28 | describe '#each_rake_task' do
29 | def do_each_rake_task(&block)
30 | dsl.each_rake_task(task_expression, &block)
31 | end
32 |
33 | it 'delegates to Rake#each_task' do
34 | expect(rake).to receive(:each_task).
35 | with(task_expression).
36 | and_yield({:name => task_expression})
37 | do_each_rake_task do |t|
38 | expect(t).to eq(:name => task_expression)
39 | end
40 | end
41 |
42 | it 'returns itself' do
43 | expect(do_each_rake_task).to eq(dsl)
44 | end
45 | end
46 |
47 | describe '#mirror_rake_tasks' do
48 | before :each do
49 | allow(rake).to receive(:each_task).and_yield({:name => task_expression})
50 | allow(dsl ).to receive(:raise_unless_capistrano)
51 | end
52 |
53 | def do_mirror_rake_tasks(*arguments, &block)
54 | dsl.mirror_rake_tasks(*arguments, &block)
55 | end
56 |
57 | describe 'with two scalar arguments --' do
58 | specify do
59 | expect {
60 | do_mirror_rake_tasks task_expression, task_expression
61 | }.to raise_error(ArgumentError, /^wrong number of arguments/)
62 | end
63 | end
64 |
65 | shared_examples_for "a successful call (#{Cape::DSL.name})" do |task_expression_in_use,
66 | options_in_use|
67 | specify 'by collecting Rake#each_task' do
68 | expect(rake).to receive(:each_task).with(task_expression_in_use)
69 | do_mirror_rake_tasks
70 | end
71 |
72 | specify 'by verifying that Capistrano is in use' do
73 | expect(dsl).to receive(:raise_unless_capistrano)
74 | do_mirror_rake_tasks
75 | end
76 |
77 | describe 'by sending Capistrano#define_rake_wrapper for Rake#each_task' do
78 | specify 'with the expected task' do
79 | expect(capistrano).to receive(:define_rake_wrapper).
80 | with { |task, named_arguments| task == {:name => task_expression} }
81 | do_mirror_rake_tasks
82 | end
83 |
84 | specify 'with the expected named options' do
85 | expect(capistrano).to receive(:define_rake_wrapper).
86 | with { |task, named_arguments| named_arguments.keys.sort == ([:binding] + options_in_use.keys).sort }
87 | do_mirror_rake_tasks
88 | end
89 |
90 | specify 'with a :binding option' do
91 | expect(capistrano).to receive(:define_rake_wrapper).
92 | with { |task, named_arguments| named_arguments[:binding].is_a? Binding }
93 | do_mirror_rake_tasks
94 | end
95 |
96 | specify 'with any provided extra options' do
97 | expect(capistrano).to receive(:define_rake_wrapper).
98 | with { |task, named_arguments| named_arguments.slice(*options_in_use.keys) == options_in_use }
99 | do_mirror_rake_tasks
100 | end
101 | end
102 |
103 | specify 'by returning itself' do
104 | expect(do_mirror_rake_tasks).to eq(dsl)
105 | end
106 | end
107 |
108 | describe 'with one scalar argument and a block --' do
109 | def do_mirror_rake_tasks
110 | super 'task:expression' do |env|
111 | env['AN_ENV_VAR'] = 'an environment variable'
112 | end
113 | end
114 |
115 | behaves_like("a successful call (#{Cape::DSL.name})",
116 | 'task:expression',
117 | {})
118 | end
119 |
120 | describe 'with one scalar argument --' do
121 | def do_mirror_rake_tasks
122 | super 'task:expression'
123 | end
124 |
125 | behaves_like("a successful call (#{Cape::DSL.name})",
126 | 'task:expression',
127 | {})
128 | end
129 |
130 | describe 'without arguments --' do
131 | def do_mirror_rake_tasks
132 | super
133 | end
134 |
135 | behaves_like("a successful call (#{Cape::DSL.name})", nil, {})
136 | end
137 | end
138 |
139 | describe '#local_rake_executable' do
140 | before :each do
141 | allow(rake).to receive(:local_executable).and_return('foo')
142 | end
143 |
144 | it 'delegates to Rake#local_executable' do
145 | expect(rake).to receive(:local_executable)
146 | dsl.local_rake_executable
147 | end
148 |
149 | it 'returns the result of Rake#local_executable' do
150 | expect(dsl.local_rake_executable).to eq('foo')
151 | end
152 | end
153 |
154 | describe '#remote_rake_executable' do
155 | before :each do
156 | allow(rake).to receive(:remote_executable).and_return('foo')
157 | end
158 |
159 | it 'delegates to Rake#remote_executable' do
160 | expect(rake).to receive(:remote_executable)
161 | dsl.remote_rake_executable
162 | end
163 |
164 | it 'returns the result of Rake#remote_executable' do
165 | expect(dsl.remote_rake_executable).to eq('foo')
166 | end
167 | end
168 | end
169 |
--------------------------------------------------------------------------------
/lib/cape/capistrano.rb:
--------------------------------------------------------------------------------
1 | require 'cape/rake'
2 | require 'cape/recipe_definition'
3 | require 'cape/util'
4 | require 'cape/xterm'
5 |
6 | module Cape
7 |
8 | # An abstraction of the Capistrano installation.
9 | class Capistrano
10 |
11 | # A Cape abstraction of the Rake installation.
12 | attr_accessor :rake
13 |
14 | # Constructs a new Capistrano object with the specified _attributes_.
15 | #
16 | # @param [Hash] attributes attribute values
17 | def initialize(attributes={})
18 | attributes.each do |name, value|
19 | send "#{name}=", value
20 | end
21 | self.rake ||= new_rake
22 | end
23 |
24 | # Defines a wrapper in Capistrano around the specified Rake _task_.
25 | #
26 | # @param [Hash] task metadata for a Rake task
27 | # @param [Hash] named_arguments
28 | #
29 | # @option task [String] :name the name of the Rake task
30 | # @option task [Array of String, nil] :parameters the names of the Rake
31 | # task's parameters, if any
32 | # @option task [String] :description documentation for the Rake
33 | # task
34 | #
35 | # @option named_arguments [Binding] :binding the Binding of your Capistrano
36 | # recipes file
37 | #
38 | # @yield [recipes] a block that customizes the Capistrano recipe(s)
39 | # generated for the Rake task(s); optional
40 | # @yieldparam [RecipeDefinition] recipes an interface for customizing the
41 | # Capistrano recipe(s) generated for
42 | # the Rake task(s)
43 | #
44 | # @return [Capistrano] the object
45 | #
46 | # @raise [ArgumentError] +named_arguments[:binding]+ is missing
47 | #
48 | # @note Any parameters that the Rake task has are integrated via environment variables, since Capistrano does not support recipe parameters per se.
49 | def define_rake_wrapper(task, named_arguments, &block)
50 | unless (binding = named_arguments[:binding])
51 | raise ::ArgumentError, ':binding named argument is required'
52 | end
53 |
54 | capistrano_context = binding.eval('self', __FILE__, __LINE__)
55 | describe task, capistrano_context
56 | implement(task, capistrano_context, &block)
57 | end
58 |
59 | private
60 |
61 | def build_capistrano_description(task)
62 | return nil unless task[:description]
63 |
64 | description = [task[:description]]
65 | unless task[:description].empty? || task[:description].end_with?('.')
66 | description << '.'
67 | end
68 |
69 | unless (parameters = Array(task[:parameters])).empty?
70 | noun = Util.pluralize('variable', parameters.length)
71 | parameters_list = Util.to_list_phrase(parameters.collect(&:upcase))
72 | singular = 'Rake task argument'
73 | noun_phrase = (parameters.length == 1) ?
74 | "a #{singular}" :
75 | Util.pluralize(singular)
76 | description << <<-end_fragment
77 |
78 |
79 | Set environment #{noun} #{parameters_list} if you want to pass #{noun_phrase}.
80 | end_fragment
81 | end
82 |
83 | description.join
84 | end
85 |
86 | def capture_recipe_definition(recipe_definition, &recipe_definition_block)
87 | recipe_definition_block.call(recipe_definition) if recipe_definition_block
88 | true
89 | end
90 |
91 | def describe(task, capistrano_context)
92 | if (description = build_capistrano_description(task))
93 | capistrano_context.desc description
94 | end
95 | self
96 | end
97 |
98 | def implement(task, capistrano_context, &recipe_definition_block)
99 | name_tokens = tokenize_name(task)
100 | recipe_definition = new_recipe_definition
101 | capture_recipe_definition(recipe_definition, &recipe_definition_block)
102 | env = nil
103 | rake = self.rake
104 | block = lambda { |context|
105 | recipe_name = name_tokens.last
106 | if recipe_definition.rename
107 | recipe_name = recipe_definition.rename.call(name_tokens.last)
108 | end
109 | begin
110 | context.task recipe_name, recipe_definition.options do
111 | arguments = Array(task[:parameters]).collect do |a|
112 | if (value = ENV[a.upcase])
113 | value = value.inspect
114 | end
115 | value
116 | end
117 | if arguments.empty?
118 | arguments = nil
119 | else
120 | arguments = "[#{arguments.join ','}]"
121 | end
122 |
123 | unless env
124 | env_strings = recipe_definition.env.collect do |var_name, var_value|
125 | if var_name.nil? || var_value.nil?
126 | nil
127 | else
128 | if var_value.is_a?(Proc)
129 | var_value = context.instance_eval do
130 | var_value.call
131 | end
132 | end
133 | "#{var_name}=#{var_value.inspect}"
134 | end
135 | end.compact
136 | env = env_strings.empty? ? nil : (' ' + env_strings.join(' '))
137 | end
138 |
139 | path = recipe_definition.cd || context.current_path
140 | path = path.call if path.respond_to?(:call)
141 | command = "cd #{path} && #{rake.remote_executable} " +
142 | "#{task[:name]}#{arguments}#{env}"
143 | context.run command
144 | end
145 | rescue => e
146 | $stderr.puts XTerm.bold_and_foreground_red('*** WARNING:') +
147 | ' ' +
148 | XTerm.bold("You must use Cape's renaming API in order " +
149 | 'to mirror Rake task ') +
150 | XTerm.bold_and_underlined(task[:name]) +
151 | XTerm.bold(" (#{e.message})")
152 | end
153 | }
154 | # Nest the recipe inside its containing namespaces.
155 | name_tokens[0...-1].reverse.each do |namespace_token|
156 | inner_block = block
157 | block = lambda { |context|
158 | context.namespace(namespace_token, &inner_block)
159 | }
160 | end
161 | block.call capistrano_context
162 | self
163 | end
164 |
165 | def new_rake(*arguments)
166 | Rake.new(*arguments)
167 | end
168 |
169 | def new_recipe_definition(*arguments)
170 | RecipeDefinition.new(*arguments)
171 | end
172 |
173 | def tokenize_name(task)
174 | task[:name].split(':').tap do |result|
175 | result << 'default' if task[:default]
176 | end
177 | end
178 |
179 | end
180 |
181 | end
182 |
--------------------------------------------------------------------------------
/lib/cape/dsl.rb:
--------------------------------------------------------------------------------
1 | require 'cape/capistrano'
2 | require 'cape/rake'
3 |
4 | module Cape
5 |
6 | # Provides methods for integrating Capistrano and Rake.
7 | module DSL
8 |
9 | # Enumerates Rake tasks.
10 | #
11 | # @param [String, Symbol] task_expression the full name of a task or
12 | # namespace to filter
13 | # @param [Proc] block a block that processes tasks
14 | #
15 | # @yield [task] a block that processes tasks
16 | # @yieldparam [Hash] task metadata on a task
17 | #
18 | # @return [DSL] the object
19 | #
20 | # @example Enumerating all Rake tasks
21 | # # config/deploy.rb
22 | #
23 | # require 'cape'
24 | #
25 | # Cape do
26 | # each_rake_task do |t|
27 | # # Do something interesting with this hash:
28 | # # * t[:name] -- the full name of the task
29 | # # * t[:parameters] -- the names of task arguments
30 | # # * t[:description] -- documentation on the task, including
31 | # # parameters
32 | # end
33 | # end
34 | #
35 | # @example Enumerating some Rake tasks
36 | # # config/deploy.rb
37 | #
38 | # require 'cape'
39 | #
40 | # Cape do
41 | # each_rake_task :foo do |t|
42 | # # Do something interesting with this hash:
43 | # # * t[:name] -- the full name of the task
44 | # # * t[:parameters] -- the names of task arguments
45 | # # * t[:description] -- documentation on the task, including
46 | # # parameters
47 | # end
48 | # end
49 | def each_rake_task(task_expression=nil, &block)
50 | rake.each_task(task_expression, &block)
51 | self
52 | end
53 |
54 | # The command used to run Rake on the local computer.
55 | #
56 | # @return [String] the command used to run Rake on the local computer
57 | #
58 | # @see Rake::DEFAULT_EXECUTABLE
59 | def local_rake_executable
60 | rake.local_executable
61 | end
62 |
63 | # Sets the command used to run Rake on the local computer.
64 | #
65 | # @param [String] value the command used to run Rake on the local computer
66 | #
67 | # @return [String] _value_
68 | #
69 | # @example Changing the local Rake executable
70 | # require 'cape'
71 | #
72 | # Cape do
73 | # self.local_rake_executable = '/path/to/rake'
74 | # $stdout.puts 'We changed the local Rake executable to ' +
75 | # "#{local_rake_executable.inspect}."
76 | # end
77 | def local_rake_executable=(value)
78 | rake.local_executable = value
79 | end
80 |
81 | # Makes the use of a Cape block parameter optional by forwarding non-Cape
82 | # method calls to the containing binding.
83 | #
84 | # @param [Symbol, String] method the method called
85 | # @param [Array] args the arguments passed to _method_
86 | # @param [Proc] block the block passed to _method_
87 | #
88 | # @return the result of the forwarded method call
89 | def method_missing(method, *args, &block)
90 | @outer_self.send(method, *args, &block)
91 | end
92 |
93 | # Defines Rake tasks as Capistrano recipes.
94 | #
95 | # @param [String, Symbol] task_expression the full name of a Rake task or
96 | # namespace to filter
97 | #
98 | # @yield [recipes] a block that customizes the Capistrano recipe(s)
99 | # generated for the Rake task(s); optional
100 | # @yieldparam [RecipeDefinition] recipes an interface for customizing the
101 | # Capistrano recipe(s) generated for
102 | # the Rake task(s)
103 | #
104 | # @return [DSL] the object
105 | #
106 | # @note Any parameters that the Rake tasks have are integrated via environment variables, since Capistrano does not support recipe parameters per se.
107 | #
108 | # @example Mirroring all Rake tasks
109 | # # config/deploy.rb
110 | #
111 | # require 'cape'
112 | #
113 | # Cape do
114 | # # Create Capistrano recipes for all Rake tasks.
115 | # mirror_rake_tasks
116 | # end
117 | #
118 | # @example Mirroring some Rake tasks, but not others
119 | # # config/deploy.rb
120 | #
121 | # require 'cape'
122 | #
123 | # Cape do
124 | # # Create Capistrano recipes for the Rake task 'foo' and/or for the
125 | # # tasks in the 'foo' namespace.
126 | # mirror_rake_tasks :foo
127 | # end
128 | #
129 | # @example Mirroring Rake tasks that require renaming, Capistrano recipe options, path switching, and/or environment variables
130 | # # config/deploy.rb
131 | #
132 | # require 'cape'
133 | #
134 | # Cape do
135 | # # Display defined Rails routes on application server remote machines
136 | # # only.
137 | # mirror_rake_tasks :routes do |recipes|
138 | # recipes.options[:roles] = :app
139 | # end
140 | #
141 | # # Execute database migration on application server remote machines
142 | # # only, and set the 'RAILS_ENV' environment variable to the value of
143 | # # the Capistrano variable 'rails_env'.
144 | # mirror_rake_tasks 'db:migrate' do |recipes|
145 | # recipes.options[:roles] = :app
146 | # recipes.env['RAILS_ENV'] = lambda { rails_env }
147 | # end
148 | #
149 | # # Support a Rake task that must be run on application server remote
150 | # # machines only, and in the remote directory 'release_path' instead of
151 | # # the default, 'current_path'.
152 | # before 'deploy:symlink', :spec
153 | # mirror_rake_tasks :spec do |recipes|
154 | # recipes.cd { release_path }
155 | # recipes.options[:roles] = :app
156 | # end
157 | #
158 | # # Avoid collisions with the existing Ruby method #test, run tests on
159 | # # application server remote machines only, and set the 'RAILS_ENV'
160 | # # environment variable to the value of the Capistrano variable
161 | # # 'rails_env'.
162 | # mirror_rake_tasks :test do |recipes|
163 | # recipes.rename do |rake_task_name|
164 | # "#{rake_task_name}_task"
165 | # end
166 | # recipes.options[:roles] = :app
167 | # recipes.env['RAILS_ENV'] = lambda { rails_env }
168 | # end
169 | # end
170 | #
171 | # @example Mirroring Rake tasks into a Capistrano namespace
172 | # # config/deploy.rb
173 | #
174 | # require 'cape'
175 | #
176 | # namespace :rake_tasks do
177 | # Cape do |cape|
178 | # cape.mirror_rake_tasks
179 | # end
180 | # end
181 | def mirror_rake_tasks(task_expression=nil, &block)
182 | rake.each_task task_expression do |t|
183 | deployment_library.define_rake_wrapper(t, :binding => binding, &block)
184 | end
185 | self
186 | end
187 |
188 | # The command used to run Rake on remote computers.
189 | #
190 | # @return [String] the command used to run Rake on remote computers
191 | #
192 | # @see Rake::DEFAULT_EXECUTABLE
193 | def remote_rake_executable
194 | rake.remote_executable
195 | end
196 |
197 | # Sets the command used to run Rake on remote computers.
198 | #
199 | # @param [String] value the command used to run Rake on remote computers
200 | #
201 | # @return [String] _value_
202 | #
203 | # @example Changing the remote Rake executable
204 | # require 'cape'
205 | #
206 | # Cape do
207 | # self.remote_rake_executable = '/path/to/rake'
208 | # $stdout.puts 'We changed the remote Rake executable to ' +
209 | # "#{remote_rake_executable.inspect}."
210 | # end
211 | def remote_rake_executable=(value)
212 | rake.remote_executable = value
213 | end
214 |
215 | protected
216 |
217 | # Returns an abstraction of the Rake installation and available tasks.
218 | def rake
219 | @rake ||= new_rake
220 | end
221 |
222 | private
223 |
224 | def deployment_library
225 | return @deployment_library if @deployment_library
226 |
227 | raise_unless_capistrano
228 | @deployment_library = new_capistrano(:rake => rake)
229 | end
230 |
231 | def new_capistrano(*arguments)
232 | Capistrano.new(*arguments)
233 | end
234 |
235 | def new_rake(*arguments)
236 | Rake.new(*arguments)
237 | end
238 |
239 | def raise_unless_capistrano
240 | if @outer_self.method(:task).owner.name !~ /^Capistrano::/
241 | raise 'Use this in the context of Capistrano recipes'
242 | end
243 | end
244 |
245 | end
246 |
247 | end
248 |
--------------------------------------------------------------------------------
/lib/cape/xterm.rb:
--------------------------------------------------------------------------------
1 | module Cape
2 |
3 | # Formats output for an xterm console.
4 | #
5 | # Convenience class methods are made available dynamically that permit multiple
6 | # formats to be applied.
7 | #
8 | # @example Apply bold and red-foreground formatting to a string
9 | # Cape::XTerm.bold_and_foreground_red 'foo'
10 | #
11 | # @api private
12 | module XTerm
13 |
14 | # The xterm formatting codes.
15 | FORMATS = {:bold => '1',
16 | :underlined => '4',
17 | :blinking => '5',
18 | :inverse => '7',
19 | :foreground_black => '30',
20 | :foreground_red => '31',
21 | :foreground_green => '32',
22 | :foreground_yellow => '33',
23 | :foreground_blue => '34',
24 | :foreground_magenta => '35',
25 | :foreground_cyan => '36',
26 | :foreground_gray => '37',
27 | :foreground_default => '39',
28 | :background_black => '40',
29 | :background_red => '41',
30 | :background_green => '42',
31 | :background_yellow => '43',
32 | :background_blue => '44',
33 | :background_magenta => '45',
34 | :background_cyan => '46',
35 | :background_gray => '47',
36 | :background_default => '49'}
37 |
38 | # @!method bold(string)
39 | # Formats the specified _string_ in bold type.
40 | #
41 | # @param [String] string the string to format in bold type
42 | #
43 | # @return [String] the formatted _string_
44 | #
45 | # @!scope class
46 | #
47 | # @api private
48 |
49 | # @!method underlined(string)
50 | # Formats the specified _string_ in underlined type.
51 | #
52 | # @param [String] string the string to underline
53 | #
54 | # @return [String] the formatted _string_
55 | #
56 | # @!scope class
57 | #
58 | # @api private
59 |
60 | # @!method blinking(string)
61 | # Formats the specified _string_ in blinking type.
62 | #
63 | # @param [String] string the string to blink
64 | #
65 | # @return [String] the formatted _string_
66 | #
67 | # @!scope class
68 | #
69 | # @api private
70 |
71 | # @!method inverse(string)
72 | # Formats the specified _string_ in inverse colors.
73 | #
74 | # @param [String] string the string whose colors are to be inverted
75 | #
76 | # @return [String] the formatted _string_
77 | #
78 | # @!scope class
79 | #
80 | # @api private
81 |
82 | # @!method foreground_black(string)
83 | # Formats the specified _string_ in black.
84 | #
85 | # @param [String] string the string to color
86 | #
87 | # @return [String] the formatted _string_
88 | #
89 | # @!scope class
90 | #
91 | # @api private
92 |
93 | # @!method foreground_red(string)
94 | # Formats the specified _string_ in red.
95 | #
96 | # @param [String] string the string to color
97 | #
98 | # @return [String] the formatted _string_
99 | #
100 | # @!scope class
101 | #
102 | # @api private
103 |
104 | # @!method foreground_green(string)
105 | # Formats the specified _string_ in green.
106 | #
107 | # @param [String] string the string to color
108 | #
109 | # @return [String] the formatted _string_
110 | #
111 | # @!scope class
112 | #
113 | # @api private
114 |
115 | # @!method foreground_yellow(string)
116 | # Formats the specified _string_ in yellow.
117 | #
118 | # @param [String] string the string to color
119 | #
120 | # @return [String] the formatted _string_
121 | #
122 | # @!scope class
123 | #
124 | # @api private
125 |
126 | # @!method foreground_blue(string)
127 | # Formats the specified _string_ in blue.
128 | #
129 | # @param [String] string the string to color
130 | #
131 | # @return [String] the formatted _string_
132 | #
133 | # @!scope class
134 | #
135 | # @api private
136 |
137 | # @!method foreground_magenta(string)
138 | # Formats the specified _string_ in magenta.
139 | #
140 | # @param [String] string the string to color
141 | #
142 | # @return [String] the formatted _string_
143 | #
144 | # @!scope class
145 | #
146 | # @api private
147 |
148 | # @!method foreground_cyan(string)
149 | # Formats the specified _string_ in cyan.
150 | #
151 | # @param [String] string the string to color
152 | #
153 | # @return [String] the formatted _string_
154 | #
155 | # @!scope class
156 | #
157 | # @api private
158 |
159 | # @!method foreground_gray(string)
160 | # Formats the specified _string_ in gray.
161 | #
162 | # @param [String] string the string to color
163 | #
164 | # @return [String] the formatted _string_
165 | #
166 | # @!scope class
167 | #
168 | # @api private
169 |
170 | # @!method foreground_default(string)
171 | # Formats the specified _string_ in the default foreground color.
172 | #
173 | # @param [String] string the string to color
174 | #
175 | # @return [String] the formatted _string_
176 | #
177 | # @!scope class
178 | #
179 | # @api private
180 |
181 | # @!method background_black(string)
182 | # Formats the specified _string_ with a black background.
183 | #
184 | # @param [String] string the string to color
185 | #
186 | # @return [String] the formatted _string_
187 | #
188 | # @!scope class
189 | #
190 | # @api private
191 |
192 | # @!method background_red(string)
193 | # Formats the specified _string_ with a red background.
194 | #
195 | # @param [String] string the string to color
196 | #
197 | # @return [String] the formatted _string_
198 | #
199 | # @!scope class
200 | #
201 | # @api private
202 |
203 | # @!method background_green(string)
204 | # Formats the specified _string_ with a green background.
205 | #
206 | # @param [String] string the string to color
207 | #
208 | # @return [String] the formatted _string_
209 | #
210 | # @!scope class
211 | #
212 | # @api private
213 |
214 | # @!method background_yellow(string)
215 | # Formats the specified _string_ with a yellow background.
216 | #
217 | # @param [String] string the string to color
218 | #
219 | # @return [String] the formatted _string_
220 | #
221 | # @!scope class
222 | #
223 | # @api private
224 |
225 | # @!method background_blue(string)
226 | # Formats the specified _string_ with a blue background.
227 | #
228 | # @param [String] string the string to color
229 | #
230 | # @return [String] the formatted _string_
231 | #
232 | # @!scope class
233 | #
234 | # @api private
235 |
236 | # @!method background_magenta(string)
237 | # Formats the specified _string_ with a magenta background.
238 | #
239 | # @param [String] string the string to color
240 | #
241 | # @return [String] the formatted _string_
242 | #
243 | # @!scope class
244 | #
245 | # @api private
246 |
247 | # @!method background_cyan(string)
248 | # Formats the specified _string_ with a cyan background.
249 | #
250 | # @param [String] string the string to color
251 | #
252 | # @return [String] the formatted _string_
253 | #
254 | # @!scope class
255 | #
256 | # @api private
257 |
258 | # @!method background_gray(string)
259 | # Formats the specified _string_ with a gray background.
260 | #
261 | # @param [String] string the string to color
262 | #
263 | # @return [String] the formatted _string_
264 | #
265 | # @!scope class
266 | #
267 | # @api private
268 |
269 | # @!method background_default(string)
270 | # Formats the specified _string_ in the default background color.
271 | #
272 | # @param [String] string the string to color
273 | #
274 | # @return [String] the formatted _string_
275 | #
276 | # @!scope class
277 | #
278 | # @api private
279 |
280 | # Applies the specified _formats_ to _string_.
281 | #
282 | # @param [String] string the string to format
283 | # @param [Array of Symbol] formats the formats to apply
284 | #
285 | # @return [String] the formatted _string_
286 | def self.format(string, *formats)
287 | formatting_codes = formats.collect do |f|
288 | unless FORMATS.key?(f)
289 | raise ::ArgumentError, "Unrecognized format #{f.inspect}"
290 | end
291 |
292 | FORMATS[f]
293 | end
294 |
295 | return string if formatting_codes.empty? || string.nil?
296 |
297 | "\e[#{formatting_codes.join ';'}m#{string}\e[0m"
298 | end
299 |
300 | private
301 |
302 | def self.method_missing(method, *arguments, &block)
303 | unless respond_to_missing?(method, false)
304 | return super(method, *arguments, &block)
305 | end
306 |
307 | formats = method.to_s.split('_and_').collect(&:to_sym)
308 | format(*(arguments + formats))
309 | end
310 |
311 | def self.respond_to_missing?(method, include_private)
312 | formats = method.to_s.split('_and_').collect(&:to_sym)
313 | formats.all? do |f|
314 | FORMATS.key? f
315 | end
316 | end
317 |
318 | if RUBY_VERSION <= '1.8.7'
319 | def self.respond_to?(method, include_private=false)
320 | respond_to_missing? method, include_private
321 | end
322 | end
323 |
324 | end
325 |
326 | end
327 |
--------------------------------------------------------------------------------
/features/dsl/mirror_rake_tasks/unqualified.feature:
--------------------------------------------------------------------------------
1 | Feature: The #mirror_rake_tasks DSL method
2 |
3 | In order to include Rake tasks with descriptions in my Capistrano recipes,
4 | As a developer using Cape,
5 | I want to use the Cape DSL.
6 |
7 | Scenario: mirror all Rake tasks
8 | Given a full-featured Rakefile
9 | And a Capfile with:
10 | """
11 | Cape do
12 | mirror_rake_tasks
13 | end
14 | """
15 | When I run `cap -vT`
16 | Then the output should contain:
17 | """
18 | cap long # My long task -- it has a ve...
19 | """
20 | And the output should contain:
21 | """
22 | cap with_one_arg # My task with one argument.
23 | """
24 | And the output should contain:
25 | """
26 | cap my_namespace # A task that shadows a names...
27 | """
28 | And the output should contain:
29 | """
30 | cap my_namespace:in_a_namespace # My task in a namespace.
31 | """
32 | And the output should contain:
33 | """
34 | cap my_namespace:my_nested_namespace:in_a_nested_namespace # My task in a nested namespace.
35 | """
36 | And the output should contain:
37 | """
38 | cap with_two_args # My task with two arguments.
39 | """
40 | And the output should contain:
41 | """
42 | cap with_three_args # My task with three arguments.
43 | """
44 | And the output should contain:
45 | """
46 | cap hidden_task #
47 | """
48 |
49 | Scenario: mirror all Rake tasks except a Ruby-method-shadowing task
50 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
51 | And a Capfile with:
52 | """
53 | Cape do
54 | mirror_rake_tasks
55 | end
56 | """
57 | When I run `cap -vT`
58 | Then the output should contain:
59 | """
60 | cap long # My long task -- it has a ve...
61 | """
62 | And the output should contain:
63 | """
64 | cap with_one_arg # My task with one argument.
65 | """
66 | And the output should contain:
67 | """
68 | cap my_namespace # A task that shadows a names...
69 | """
70 | And the output should contain:
71 | """
72 | cap my_namespace:in_a_namespace # My task in a namespace.
73 | """
74 | And the output should contain:
75 | """
76 | cap my_namespace:my_nested_namespace:in_a_nested_namespace # My task in a nested namespace.
77 | """
78 | And the output should contain:
79 | """
80 | cap with_two_args # My task with two arguments.
81 | """
82 | And the output should contain:
83 | """
84 | cap with_three_args # My task with three arguments.
85 | """
86 | And the output should contain:
87 | """
88 | cap hidden_task #
89 | """
90 | And the output should not contain "cap load"
91 | And the output should contain:
92 | """
93 | *** WARNING: You must use Cape's renaming API in order to mirror Rake task load (defining a task named `load' would shadow an existing method with that name)
94 | """
95 |
96 | Scenario: mirror Rake task 'long' with its description
97 | Given a full-featured Rakefile
98 | And a Capfile with:
99 | """
100 | Cape do
101 | mirror_rake_tasks
102 | end
103 | """
104 | When I run `cap -e long`
105 | Then the output should contain exactly:
106 | """
107 | ------------------------------------------------------------
108 | cap long
109 | ------------------------------------------------------------
110 | My long task -- it has a very, very, very, very, very, very, very, very, very,
111 | very, very, very, very, very, very, very, very, very, very, very, very, very,
112 | very, very, very, very long description.
113 |
114 |
115 | """
116 |
117 | Scenario: mirror Rake task 'long' with its implementation
118 | Given a full-featured Rakefile
119 | And a Capfile with:
120 | """
121 | set :current_path, '/current/path'
122 |
123 | Cape do
124 | mirror_rake_tasks
125 | end
126 | """
127 | When I run `cap long`
128 | Then the output should contain:
129 | """
130 | * executing `long'
131 | """
132 | And the output should contain:
133 | """
134 | `long' is only run for servers matching {}, but no servers matched
135 | """
136 |
137 | Scenario: mirror Rake task 'long' with its description when a Ruby-method-shadowing task is defined
138 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
139 | And a Capfile with:
140 | """
141 | Cape do
142 | mirror_rake_tasks
143 | end
144 | """
145 | When I run `cap -e long`
146 | Then the output should contain exactly:
147 | """
148 | ------------------------------------------------------------
149 | cap long
150 | ------------------------------------------------------------
151 | My long task -- it has a very, very, very, very, very, very, very, very, very,
152 | very, very, very, very, very, very, very, very, very, very, very, very, very,
153 | very, very, very, very long description.
154 |
155 | *** WARNING: You must use Cape's renaming API in order to mirror Rake task load (defining a task named `load' would shadow an existing method with that name)
156 |
157 | """
158 |
159 | Scenario: mirror Rake task 'long' with its implementation when a Ruby-method-shadowing task is defined
160 | Given a full-featured Rakefile defining a Ruby-method-shadowing task
161 | And a Capfile with:
162 | """
163 | set :current_path, '/current/path'
164 |
165 | Cape do
166 | mirror_rake_tasks
167 | end
168 | """
169 | When I run `cap long`
170 | Then the output should contain:
171 | """
172 | * executing `long'
173 | """
174 | And the output should contain:
175 | """
176 | `long' is only run for servers matching {}, but no servers matched
177 | """
178 | And the output should contain:
179 | """
180 | *** WARNING: You must use Cape's renaming API in order to mirror Rake task load (defining a task named `load' would shadow an existing method with that name)
181 | """
182 |
183 | Scenario: mirror Rake task 'with_one_arg' with its description
184 | Given a full-featured Rakefile
185 | And a Capfile with:
186 | """
187 | Cape do
188 | mirror_rake_tasks
189 | end
190 | """
191 | When I run `cap -e with_one_arg`
192 | Then the output should contain exactly:
193 | """
194 | ------------------------------------------------------------
195 | cap with_one_arg
196 | ------------------------------------------------------------
197 | My task with one argument.
198 |
199 | Set environment variable THE_ARG if you want to pass a Rake task argument.
200 |
201 |
202 | """
203 |
204 | Scenario: mirror Rake task 'my_namespace' with its description
205 | Given a full-featured Rakefile
206 | And a Capfile with:
207 | """
208 | Cape do
209 | mirror_rake_tasks
210 | end
211 | """
212 | When I run `cap -e my_namespace`
213 | Then the output should contain exactly:
214 | """
215 | ------------------------------------------------------------
216 | cap my_namespace
217 | ------------------------------------------------------------
218 | A task that shadows a namespace.
219 |
220 |
221 | """
222 |
223 | Scenario: mirror Rake task 'my_namespace' with its implementation
224 | Given a full-featured Rakefile
225 | And a Capfile with:
226 | """
227 | set :current_path, '/current/path'
228 |
229 | Cape do
230 | mirror_rake_tasks
231 | end
232 | """
233 | When I run `cap my_namespace`
234 | Then the output should contain:
235 | """
236 | * executing `my_namespace'
237 | """
238 | And the output should contain:
239 | """
240 | `my_namespace' is only run for servers matching {}, but no servers matched
241 | """
242 |
243 | Scenario: mirror Rake task 'my_namespace:in_a_namespace' with its description
244 | Given a full-featured Rakefile
245 | And a Capfile with:
246 | """
247 | Cape do
248 | mirror_rake_tasks
249 | end
250 | """
251 | When I run `cap -e my_namespace:in_a_namespace`
252 | Then the output should contain exactly:
253 | """
254 | ------------------------------------------------------------
255 | cap my_namespace:in_a_namespace
256 | ------------------------------------------------------------
257 | My task in a namespace.
258 |
259 |
260 | """
261 |
262 | Scenario: mirror Rake task 'my_namespace:my_nested_namespace:in_a_nested_namespace' with its description
263 | Given a full-featured Rakefile
264 | And a Capfile with:
265 | """
266 | Cape do
267 | mirror_rake_tasks
268 | end
269 | """
270 | When I run `cap -e my_namespace:my_nested_namespace:in_a_nested_namespace`
271 | Then the output should contain exactly:
272 | """
273 | ------------------------------------------------------------
274 | cap my_namespace:my_nested_namespace:in_a_nested_namespace
275 | ------------------------------------------------------------
276 | My task in a nested namespace.
277 |
278 |
279 | """
280 |
281 | Scenario: mirror Rake task 'my_namespace:my_nested_namespace:in_a_nested_namespace' with its implementation
282 | Given a full-featured Rakefile
283 | And a Capfile with:
284 | """
285 | set :current_path, '/current/path'
286 |
287 | Cape do
288 | mirror_rake_tasks
289 | end
290 | """
291 | When I run `cap my_namespace:my_nested_namespace:in_a_nested_namespace`
292 | Then the output should contain:
293 | """
294 | * executing `my_namespace:my_nested_namespace:in_a_nested_namespace'
295 | """
296 | And the output should contain:
297 | """
298 | `my_namespace:my_nested_namespace:in_a_nested_namespace' is only run for servers matching {}, but no servers matched
299 | """
300 |
301 | Scenario: mirror Rake task 'with_two_args' with its description
302 | Given a full-featured Rakefile
303 | And a Capfile with:
304 | """
305 | Cape do
306 | mirror_rake_tasks
307 | end
308 | """
309 | When I run `cap -e with_two_args`
310 | Then the output should contain exactly:
311 | """
312 | ------------------------------------------------------------
313 | cap with_two_args
314 | ------------------------------------------------------------
315 | My task with two arguments.
316 |
317 | Set environment variables MY_ARG1 and MY_ARG2 if you want to pass Rake task
318 | arguments.
319 |
320 |
321 | """
322 |
323 | Scenario: mirror Rake task 'with_three_args' with its description
324 | Given a full-featured Rakefile
325 | And a Capfile with:
326 | """
327 | Cape do
328 | mirror_rake_tasks
329 | end
330 | """
331 | When I run `cap -e with_three_args`
332 | Then the output should contain exactly:
333 | """
334 | ------------------------------------------------------------
335 | cap with_three_args
336 | ------------------------------------------------------------
337 | My task with three arguments.
338 |
339 | Set environment variables AN_ARG1, AN_ARG2, and AN_ARG3 if you want to pass Rake
340 | task arguments.
341 |
342 |
343 | """
344 |
345 | Scenario: mirror Rake task 'with_three_args' with its implementation
346 | Given a full-featured Rakefile
347 | And a Capfile with:
348 | """
349 | set :current_path, '/current/path'
350 |
351 | Cape do
352 | mirror_rake_tasks
353 | end
354 | """
355 | When I run `cap with_three_args AN_ARG1="a value for an_arg1" AN_ARG2="a value for an_arg2" AN_ARG3="a value for an_arg3"`
356 | Then the output should contain:
357 | """
358 | * executing `with_three_args'
359 | """
360 | And the output should contain:
361 | """
362 | `with_three_args' is only run for servers matching {}, but no servers matched
363 | """
364 |
365 | Scenario: mirror Rake task 'with_three_args' with its implementation not enforcing arguments
366 | Given a full-featured Rakefile
367 | And a Capfile with:
368 | """
369 | set :current_path, '/current/path'
370 |
371 | Cape do
372 | mirror_rake_tasks
373 | end
374 | """
375 | When I run `cap with_three_args AN_ARG2="a value for an_arg2"`
376 | Then the output should contain:
377 | """
378 | * executing `with_three_args'
379 | """
380 | And the output should contain:
381 | """
382 | `with_three_args' is only run for servers matching {}, but no servers matched
383 | """
384 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | _____ __
2 | / ___/___ / /_ __ _____ __ ______
3 | / (_ // -_) __/ / // / _ | // / __/
4 | \___/ \__/\__/ \_, /\___|_,_/_/
5 | '-(+++++... /___/ ............ s++++++++.
6 | .(++++++++++++( -+++~~~~~ 'N++++++++++= B++++++++s
7 | '+++++++++++++++( DN=++++++< -NB+++++++++s Bz++++++++
8 | +++++++++++++++++. 'NNN=++++++(-BNB+++++++++'BN========-
9 | =B=+++++++++sDBBBBs<. +NNNN=+++++( \____
27 | ~BNh .+B+ / _ \/ _ \ = \ /
28 | ~Dz~. .~+zB=' \___/_//_/ / \ \__/
29 | 1'
73 |
74 | ## Compatibility
75 |
76 | Cape [runs](http://travis-ci.org/njonsson/cape) on the following Ruby versions:
77 |
78 | * MRI v1.8.7, v1.9.2, v1.9.3, v2.0.0
79 | * REE v1.8.7
80 |
81 | Cape integrates with the following RubyGems:
82 |
83 | * Capistrano v2.x
84 | * Rake v0.8.7 and later
85 |
86 | **A Rake task that lacks a description can be mirrored or enumerated only if Rake v0.9.3 or newer is installed.** Older versions of Rake did not support enumerating such tasks. You may want to make Rake v0.9.3 a dependency of your project.
87 |
88 | # Gemfile
89 |
90 | source 'http://rubygems.org'
91 |
92 | gem 'rake', '>= 0.9.3'
93 |
94 | ## Examples
95 |
96 | Assume the following Rake tasks are defined.
97 |
98 | desc 'Rakes the leaves'
99 | task :leaves do
100 | $stdout.puts "Raking the leaves"
101 | end
102 |
103 | desc 'Rakes and bags the leaves'
104 | task :bag_leaves, [:paper_or_plastic] => :leaves do |task, arguments|
105 | $stdout.puts "Putting the leaves in a #{arguments[:paper_or_plastic]} bag"
106 | end
107 |
108 | Rake lists these tasks in the expected fashion.
109 |
110 | $ rake --tasks
111 | rake bag_leaves[paper_or_plastic] # Rakes and bags the leaves
112 | rake leaves # Rakes the leaves
113 |
114 | $ rake --describe bag_leaves
115 | rake bag_leaves[paper_or_plastic]
116 | Rakes and bags the leaves
117 |
118 | ### Simply mirror all Rake tasks as Capistrano recipes
119 |
120 | Add the following to your Capistrano recipes. Note that Cape statements must be contained in a `Cape` block.
121 |
122 | # config/deploy.rb
123 |
124 | require 'cape'
125 |
126 | Cape do
127 | # Create Capistrano recipes for all Rake tasks.
128 | mirror_rake_tasks
129 | end
130 |
131 | Now all your Rake tasks appear alongside your Capistrano recipes.
132 |
133 | $ cap --tasks
134 | cap deploy # Deploys your project.
135 | ...
136 | [other built-in Capistrano recipes]
137 | ...
138 | cap bag_leaves # Rakes and bags the leaves.
139 | cap leaves # Rakes the leaves.
140 | Some tasks were not listed, either because they have no description,
141 | or because they are only used internally by other tasks. To see all
142 | tasks, type `cap -vT'.
143 |
144 | Extended help may be available for these tasks.
145 | Type `cap -e taskname' to view it.
146 |
147 | Let’s use Capistrano to view the unabbreviated description of a Rake task recipe, including instructions for how to pass arguments to it. Note that Rake task parameters are automatically converted to environment variables.
148 |
149 | $ cap --explain bag_leaves
150 | ------------------------------------------------------------
151 | cap bag_leaves
152 | ------------------------------------------------------------
153 | Bags the leaves.
154 |
155 | Set environment variable PAPER_OR_PLASTIC if you want to pass a Rake task argument.
156 |
157 | Here’s how to invoke a task/recipe with arguments. On the local computer, via Rake:
158 |
159 | $ rake bag_leaves[plastic]
160 | (in /current/working/directory)
161 | Raking the leaves
162 | Putting the leaves in a plastic bag
163 |
164 | On remote computers, via Capistrano:
165 |
166 | $ cap bag_leaves PAPER_OR_PLASTIC=plastic
167 | * executing `bag_leaves'
168 | * executing "cd /path/to/currently/deployed/version/of/your/app && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake bag_leaves[plastic]"
169 | servers: ["your.server.name"]
170 | [your.server.name] executing command
171 | ** [out :: your.server.name] (in /path/to/currently/deployed/version/of/your/app)
172 | ** [out :: your.server.name] Raking the leaves
173 | ** [out :: your.server.name] Putting the leaves in a plastic bag
174 | command finished in 1000ms
175 |
176 | ### Mirror some Rake tasks, but not others
177 |
178 | Cape lets you filter the Rake tasks to be mirrored. Note that Cape statements must be contained in a `Cape` block.
179 |
180 | # config/deploy.rb
181 |
182 | require 'cape'
183 |
184 | Cape do
185 | # Create Capistrano recipes for the Rake task 'foo' and/or for the tasks in
186 | # the 'foo' namespace.
187 | mirror_rake_tasks :foo
188 | end
189 |
190 | ### Mirror Rake tasks that require renaming, Capistrano recipe options, path switching, and/or environment variables
191 |
192 | Cape lets you customize mirrored Rake tasks to suit your needs. Note that Cape statements must be contained in a `Cape` block, and references to Capistrano variables such as `rails_env` and `release_path` must be contained in an inner block, lambda, or other callable object.
193 |
194 | # config/deploy.rb
195 |
196 | require 'cape'
197 |
198 | Cape do
199 | # Display defined Rails routes on application server remote machines only.
200 | mirror_rake_tasks :routes do |recipes|
201 | recipes.options[:roles] = :app
202 | end
203 |
204 | # Execute database migration on application server remote machines only,
205 | # and set the 'RAILS_ENV' environment variable to the value of the
206 | # Capistrano variable 'rails_env'.
207 | mirror_rake_tasks 'db:migrate' do |recipes|
208 | recipes.options[:roles] = :app
209 | recipes.env['RAILS_ENV'] = lambda { rails_env }
210 | end
211 |
212 | # Support a Rake task that must be run on application server remote
213 | # machines only, and in the remote directory 'release_path' instead of the
214 | # default, 'current_path'.
215 | before 'deploy:symlink', :spec
216 | mirror_rake_tasks :spec do |recipes|
217 | recipes.cd { release_path }
218 | recipes.options[:roles] = :app
219 | end
220 |
221 | # Avoid collisions with the existing Ruby method #test, run tests on
222 | # application server remote machines only, and set the 'RAILS_ENV'
223 | # environment variable to the value of the Capistrano variable
224 | # 'rails_env'.
225 | mirror_rake_tasks :test do |recipes|
226 | recipes.rename do |rake_task_name|
227 | "#{rake_task_name}_task"
228 | end
229 | recipes.options[:roles] = :app
230 | recipes.env['RAILS_ENV'] = lambda { rails_env }
231 | end
232 | end
233 |
234 | The above is equivalent to the following manually-defined Capistrano recipes.
235 |
236 | # config/deploy.rb
237 |
238 | # These translations to Capistrano are just for illustration.
239 |
240 | RAKE = '/usr/bin/env ' +
241 | '`' +
242 | '/usr/bin/env bundle check >/dev/null 2>&1; ' +
243 | 'case $? in ' +
244 | # Exit code 0: bundle is defined and installed
245 | # Exit code 1: bundle is defined but not installed
246 | '0|1 ) ' +
247 | 'echo bundle exec ' +
248 | ';; ' +
249 | 'esac' +
250 | '` ' +
251 | 'rake'
252 |
253 | task :routes, :roles => :app do
254 | run "cd #{current_path} && #{RAKE} routes"
255 | end
256 |
257 | namespace :db do
258 | task :migrate, :roles => :app do
259 | run "cd #{current_path} && #{RAKE} db:migrate RAILS_ENV=#{rails_env}"
260 | end
261 | end
262 |
263 | before 'deploy:symlink', :spec
264 | task :spec, :roles => :app do
265 | run "cd #{release_path} && #{RAKE} routes"
266 | end
267 |
268 | task :test_task, :roles => :app do
269 | run "cd #{current_path} && #{RAKE} test RAILS_ENV=#{rails_env}"
270 | end
271 |
272 | ### Mirror Rake tasks into a Capistrano namespace
273 |
274 | Cape plays friendly with the Capistrano DSL for organizing Rake tasks in Capistrano namespaces. Note that Cape statements must be contained in a `Cape` block.
275 |
276 | # config/deploy.rb
277 |
278 | require 'cape'
279 |
280 | namespace :rake_tasks do
281 | # Use an argument with the Cape block, if you want to or need to.
282 | Cape do |cape|
283 | cape.mirror_rake_tasks
284 | end
285 | end
286 |
287 | ### Iterate over available Rake tasks
288 |
289 | Cape lets you enumerate Rake tasks, optionally filtering them by task name or namespace. Note that Cape statements must be contained in a `Cape` block.
290 |
291 | # config/deploy.rb
292 |
293 | require 'cape'
294 |
295 | Cape do
296 | # Enumerate all Rake tasks.
297 | each_rake_task do |t|
298 | # Do something interesting with this hash:
299 | # * t[:name] -- the full name of the task
300 | # * t[:parameters] -- the names of task arguments
301 | # * t[:description] -- documentation on the task, including parameters
302 | end
303 |
304 | # Enumerate the Rake task 'foo' and/or the tasks in the 'foo' namespace.
305 | each_rake_task 'foo' do |t|
306 | # ...
307 | end
308 | end
309 |
310 | ### Configure Rake execution
311 |
312 | Cape lets you specify how Rake should be run on the local computer and on remote computers. But the default behavior is most likely just right for your needs:
313 |
314 | * It detects whether Bundler is installed on the computer
315 | * It detects whether the project uses Bundler to manage its dependencies
316 | * It runs Rake via Bundler if the above conditions are true; otherwise, it runs Rake directly
317 |
318 | Note that Cape statements must be contained in a `Cape` block.
319 |
320 | # config/deploy.rb
321 |
322 | require 'cape'
323 |
324 | # Configure Cape never to run Rake via Bundler, neither locally nor remotely.
325 | Cape.local_rake_executable = '/usr/bin/env rake'
326 | Cape.remote_rake_executable = '/usr/bin/env rake'
327 |
328 | Cape do
329 | # Create Capistrano recipes for all Rake tasks.
330 | mirror_rake_tasks
331 | end
332 |
333 | ## Contributing
334 |
335 | Report defects and feature requests on [GitHub Issues](http://github.com/njonsson/cape/issues).
336 |
337 | Your patches are welcome, and you will receive attribution here for good stuff.
338 |
339 | ## License
340 |
341 | Released under the [MIT License](http://github.com/njonsson/cape/blob/master/License.markdown).
342 |
343 | [Travis CI build status]: https://secure.travis-ci.org/njonsson/cape.png?branch=master
344 | [Gemnasium build status]: https://gemnasium.com/njonsson/cape.png
345 | [Code Climate report]: https://codeclimate.com/github/njonsson/cape.png
346 | [Inline docs]: http://inch-pages.github.io/github/njonsson/cape.png
347 | [RubyGems release]: https://badge.fury.io/rb/cape.png
348 |
--------------------------------------------------------------------------------