diff --git a/README.markdown b/README.markdown index 023da4c..bfd0ae3 100644 --- a/README.markdown +++ b/README.markdown @@ -166,9 +166,9 @@ Cape lets you filter the Rake tasks to be mirrored. Note that Cape statements mu mirror_rake_tasks :foo end -### Mirror Rake tasks that require Capistrano recipe options and/or environment variables +### Mirror Rake tasks that require renaming, Capistrano recipe options, path switching, and/or environment variables -Cape lets you specify options used for defining Capistrano recipes. You can also specify remote environment variables to be set when executing Rake tasks. Note that Cape statements must be contained in a `Cape` block. +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. # config/deploy.rb @@ -176,13 +176,37 @@ Cape lets you specify options used for defining Capistrano recipes. You can also Cape do # Display defined Rails routes on application server remote machines only. - mirror_rake_tasks :routes, :roles => :app + mirror_rake_tasks :routes do |recipes| + recipes.options[:roles] = :app + end # Execute database migration on application server remote machines only, # and set the 'RAILS_ENV' environment variable to the value of the # Capistrano variable 'rails_env'. - mirror_rake_tasks 'db:migrate', :roles => :app do |env| - env['RAILS_ENV'] = rails_env + mirror_rake_tasks 'db:migrate' do |recipes| + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + + # Support a Rake task that must be run on application server remote + # machines only, and in the remote directory 'release_path' instead of the + # default, 'current_path'. + before 'deploy:symlink', :spec + mirror_rake_tasks :spec do |recipes| + recipes.cd { release_path } + recipes.options[:roles] = :app + end + + # Avoid collisions with the existing Ruby method #test, run tests on + # application server remote machines only, and set the 'RAILS_ENV' + # environment variable to the value of the Capistrano variable + # 'rails_env'. + mirror_rake_tasks :test do |recipes| + recipes.rename do |rake_task_name| + "#{rake_task_name}_task" + end + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } end end @@ -215,6 +239,15 @@ The above is equivalent to the following manually-defined Capistrano recipes. end end + before 'deploy:symlink', :spec + task :spec, :roles => :app do + run "cd #{release_path} && #{RAKE} routes" + end + + task :test_task, :roles => :app do + run "cd #{current_path} && #{RAKE} test RAILS_ENV=#{rails_env}" + end + ### Mirror Rake tasks into a Capistrano namespace 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. @@ -286,10 +319,6 @@ Note that Cape statements must be contained in a `Cape` block. gem 'rake', '>= 0.9.3' -**A Rake task whose name collides with a Ruby method cannot be mirrored.** For example, the name of [Rails](http://rubyonrails.org)’s _db:fixtures:load_ task collides with the Ruby Core Library’s [_Kernel::load_ method](http://ruby-doc.org/core/Kernel.html#method-i-load) because that method is mixed into all objects. If you try to mirror _db:fixtures:load_, Capistrano will raise an exception. There is [a questionable workaround](http://github.com/njonsson/cape/issues/7#issuecomment-5632718 "Comment on Cape issue #7 (“defining a task named ‘load’ would shadow an existing method with that name (ArgumentError)”)") for this. - -**A Rake task is always executed in the Capistrano deployment’s _current_path_.** You may need to execute a task under _release_path_ or another remote filesystem location, but this is not possible at present. [Discuss](http://github.com/njonsson/cape/issues/9 "Cape issue #9 (“Cape Always Runs the Mirrorred Rake Tasks Under $current_path”)") this. - ## Contributing Report defects and feature requests on [GitHub Issues](http://github.com/njonsson/cape/issues). diff --git a/features/dsl/mirror_rake_tasks/with_cd.feature b/features/dsl/mirror_rake_tasks/with_cd.feature new file mode 100644 index 0000000..fdb3995 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_cd.feature @@ -0,0 +1,63 @@ +Feature: The #mirror_rake_tasks DSL method with a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Rake task with its implementation, using a Capistrano variable inside a lambda + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.cd lambda { release_path } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {}, but no servers matched + """ + + Scenario: mirror a Rake task with its implementation, using a Capistrano variable inside a block + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.cd { release_path } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {}, but no servers matched + """ + + Scenario: mirror a Rake task with its implementation, using a string + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks do |recipes| + recipes.cd '/release/path' + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_cd_and_environment_variables.feature new file mode 100644 index 0000000..dd9abd9 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_cd_and_environment_variables.feature @@ -0,0 +1,27 @@ +Feature: The #mirror_rake_tasks DSL method with a different directory and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_cd.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_cd.feature new file mode 100644 index 0000000..4a09c74 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_cd.feature @@ -0,0 +1,43 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.cd { release_path } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.cd { release_path } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..b07a443 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_cd_and_environment_variables.feature @@ -0,0 +1,46 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature index 53663f8..d9af9d9 100644 --- a/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature @@ -4,7 +4,8 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and environment v As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror only the matching Rake task + @deprecated + Scenario: mirror only the matching Rake task (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -21,8 +22,32 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and environment v """ And the output should not contain "without_period" And the output should not contain "my_namespace" + And the output should contain: + """ + *** DEPRECATED: Referencing Capistrano variables from Cape without wrapping them in a block, a lambda, or another callable object + """ - Scenario: mirror the matching Rake task with its implementation + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + And the output should not contain "DEPRECATED" + + @deprecated + Scenario: mirror the matching Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -36,9 +61,32 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and environment v end """ When I run `cap with_period` + Then the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks("with_period") { |env| env["RAILS_ENV"] = "rails-env" }`. Use this instead: `mirror_rake_tasks("with_period") { |recipes| recipes.env["RAILS_ENV"] = "rails-env" }` + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {}, but no servers matched + """ + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks 'with_period' do |recipes| + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` Then the output should contain: """ * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" `with_period' is only run for servers matching {}, but no servers matched """ + And the output should not contain "DEPRECATED" diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename.feature new file mode 100644 index 0000000..388ff16 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename.feature @@ -0,0 +1,47 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task and renaming logic + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd.feature new file mode 100644 index 0000000..93ac2af --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd.feature @@ -0,0 +1,49 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..2169ca9 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd_and_environment_variables.feature @@ -0,0 +1,52 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_environment_variables.feature new file mode 100644 index 0000000..593f0db --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_environment_variables.feature @@ -0,0 +1,50 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options.feature new file mode 100644 index 0000000..4630da2 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options.feature @@ -0,0 +1,49 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, and valid options + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd.feature new file mode 100644 index 0000000..34a4fcc --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd.feature @@ -0,0 +1,51 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..599e344 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature @@ -0,0 +1,54 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_environment_variables.feature new file mode 100644 index 0000000..fe3ee3b --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_environment_variables.feature @@ -0,0 +1,52 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, renaming logic, valid options, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap do_load # A task that shadows a Ruby method. + """ + And the output should not contain "period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :load do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature index 9fedd7e..51009c5 100644 --- a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature @@ -4,7 +4,8 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and valid options As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror only the matching Rake task + @deprecated + Scenario: mirror only the matching Rake task (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -19,8 +20,32 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and valid options """ And the output should not contain "without_period" And the output should not contain "my_namespace" + And the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks :with_period, :roles => :app`. Use this instead: `mirror_rake_tasks(:with_period) { |recipes| recipes.options[:roles] = :app }` + """ - Scenario: mirror the matching Rake task with its implementation + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + And the output should not contain "DEPRECATED" + + @deprecated + Scenario: mirror the matching Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -31,9 +56,31 @@ Feature: The #mirror_rake_tasks DSL method with a defined task and valid options end """ When I run `cap with_period` + Then the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks "with_period", :roles => :app`. Use this instead: `mirror_rake_tasks("with_period") { |recipes| recipes.options[:roles] = :app }` + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks 'with_period' do |recipes| + recipes.options[:roles] = :app + end + end + """ + When I run `cap with_period` Then the output should contain: """ * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" `with_period' is only run for servers matching {:roles=>:app}, but no servers matched """ + And the output should not contain "DEPRECATED" diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd.feature new file mode 100644 index 0000000..35b21a9 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd.feature @@ -0,0 +1,45 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..8c2b924 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd_and_environment_variables.feature @@ -0,0 +1,48 @@ +Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature index a6a0673..b45b759 100644 --- a/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature +++ b/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature @@ -4,7 +4,8 @@ Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, a As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror only the matching Rake task + @deprecated + Scenario: mirror only the matching Rake task (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -21,8 +22,37 @@ Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, a """ And the output should not contain "without_period" And the output should not contain "my_namespace" + And the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks :with_period, :roles => :app`. Use this instead: `mirror_rake_tasks(:with_period) { |recipes| recipes.options[:roles] = :app }` + """ + And the output should contain: + """ + *** DEPRECATED: Referencing Capistrano variables from Cape without wrapping them in a block, a lambda, or another callable object + """ - Scenario: mirror the matching Rake task with its implementation + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks :with_period do |recipes| + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + Then the output should contain: + """ + cap with_period # Ends with period. + """ + And the output should not contain "without_period" + And the output should not contain "my_namespace" + And the output should not contain "DEPRECATED" + + @deprecated + Scenario: mirror the matching Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -36,9 +66,33 @@ Feature: The #mirror_rake_tasks DSL method with a defined task, valid options, a end """ When I run `cap with_period` + Then the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks("with_period", :roles => :app) { |env| env["RAILS_ENV"] = "rails-env" }`. Use this instead: `mirror_rake_tasks("with_period") { |recipes| recipes.options[:roles] = :app; recipes.env["RAILS_ENV"] = "rails-env" }` + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks 'with_period' do |recipes| + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` Then the output should contain: """ * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" `with_period' is only run for servers matching {:roles=>:app}, but no servers matched """ + And the output should not contain "DEPRECATED" diff --git a/features/dsl/mirror_rake_tasks/with_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_environment_variables.feature index 6a94de5..3770875 100644 --- a/features/dsl/mirror_rake_tasks/with_environment_variables.feature +++ b/features/dsl/mirror_rake_tasks/with_environment_variables.feature @@ -4,7 +4,38 @@ Feature: The #mirror_rake_tasks DSL method with environment variables As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror a Rake task with its implementation + @deprecated + Scenario: mirror only the matching Rake task (deprecated) + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks do |env| + env['RAILS_ENV'] = rails_env + end + end + """ + When I run `cap -vT` + And the output should contain: + """ + *** DEPRECATED: Referencing Capistrano variables from Cape without wrapping them in a block, a lambda, or another callable object + """ + + Scenario: mirror only the matching Rake task + Given a full-featured Rakefile + And a Capfile with: + """ + Cape do + mirror_rake_tasks do |recipes| + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap -vT` + And the output should not contain "DEPRECATED" + + @deprecated + Scenario: mirror a Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -21,9 +52,35 @@ Feature: The #mirror_rake_tasks DSL method with environment variables end """ When I run `cap with_period` + Then the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks { |env| env["RAILS_ENV"] = "rails-env"; env[nil] = "foo"; env["FOO"] = nil; env["SOME_OTHER"] = "var" }`. Use this instead: `mirror_rake_tasks { |recipes| recipes.env["RAILS_ENV"] = "rails-env"; recipes.env[nil] = "foo"; recipes.env["FOO"] = nil; recipes.env["SOME_OTHER"] = "var" }` + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\" SOME_OTHER=\"var\"" + `with_period' is only run for servers matching {}, but no servers matched + """ + + Scenario: mirror a Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.env['RAILS_ENV'] = lambda { rails_env } + recipes.env[nil] = 'foo' + recipes.env['FOO'] = nil + recipes.env['SOME_OTHER'] = 'var' + end + end + """ + When I run `cap with_period` Then the output should contain: """ * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\" SOME_OTHER=\"var\"" `with_period' is only run for servers matching {}, but no servers matched """ + And the output should not contain "DEPRECATED" diff --git a/features/dsl/mirror_rake_tasks/with_rename.feature b/features/dsl/mirror_rake_tasks/with_rename.feature new file mode 100644 index 0000000..789565d --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename.feature @@ -0,0 +1,27 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_cd.feature b/features/dsl/mirror_rake_tasks/with_rename_and_cd.feature new file mode 100644 index 0000000..913b902 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_cd.feature @@ -0,0 +1,28 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_rename_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..2a2a85e --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_cd_and_environment_variables.feature @@ -0,0 +1,30 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_rename_and_environment_variables.feature new file mode 100644 index 0000000..285e8bf --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_environment_variables.feature @@ -0,0 +1,29 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_valid_options.feature b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options.feature new file mode 100644 index 0000000..fac339d --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options.feature @@ -0,0 +1,28 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic and valid options + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd.feature b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd.feature new file mode 100644 index 0000000..69e0ae0 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd.feature @@ -0,0 +1,29 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..08508c5 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd_and_environment_variables.feature @@ -0,0 +1,31 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, a different directory, and enviroment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_environment_variables.feature new file mode 100644 index 0000000..fc43200 --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_environment_variables.feature @@ -0,0 +1,30 @@ +Feature: The #mirror_rake_tasks DSL method with renaming logic, valid options, and enviroment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Ruby-method-shadowing Rake task with its implementation + Given a full-featured Rakefile defining a Ruby-method-shadowing task + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.rename do |task_name| + "do_#{task_name}" + end + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap do_load` + Then the output should contain: + """ + * executing `do_load' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake load RAILS_ENV=\"rails-env\"" + `do_load' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_valid_options.feature b/features/dsl/mirror_rake_tasks/with_valid_options.feature index 47e5972..3d1c02d 100644 --- a/features/dsl/mirror_rake_tasks/with_valid_options.feature +++ b/features/dsl/mirror_rake_tasks/with_valid_options.feature @@ -4,7 +4,8 @@ Feature: The #mirror_rake_tasks DSL method with valid options As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror a Rake task with its implementation + @deprecated + Scenario: mirror a Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -15,9 +16,31 @@ Feature: The #mirror_rake_tasks DSL method with valid options end """ When I run `cap with_period` + Then the output should contain: + """ + *** DEPRECATED: `mirror_rake_tasks :roles => :app`. Use this instead: `mirror_rake_tasks { |recipes| recipes.options[:roles] = :app }` + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ + + Scenario: mirror a Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.options[:roles] = :app + end + end + """ + When I run `cap with_period` Then the output should contain: """ * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" `with_period' is only run for servers matching {:roles=>:app}, but no servers matched """ + And the output should not contain "DEPRECATED" diff --git a/features/dsl/mirror_rake_tasks/with_valid_options_and_cd.feature b/features/dsl/mirror_rake_tasks/with_valid_options_and_cd.feature new file mode 100644 index 0000000..d3e1a8b --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_valid_options_and_cd.feature @@ -0,0 +1,26 @@ +Feature: The #mirror_rake_tasks DSL method with valid options and a different directory + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror a Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + + Cape do + mirror_rake_tasks do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_valid_options_and_cd_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_valid_options_and_cd_and_environment_variables.feature new file mode 100644 index 0000000..9244bfb --- /dev/null +++ b/features/dsl/mirror_rake_tasks/with_valid_options_and_cd_and_environment_variables.feature @@ -0,0 +1,28 @@ +Feature: The #mirror_rake_tasks DSL method with valid options, a different directory, and environment variables + + In order to include Rake tasks with descriptions in my Capistrano recipes, + As a developer using Cape, + I want to use the Cape DSL. + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :release_path, '/release/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.options[:roles] = :app + recipes.cd { release_path } + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /release/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ diff --git a/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature b/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature index 5e5265f..2bab3ae 100644 --- a/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature +++ b/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature @@ -4,7 +4,8 @@ Feature: The #mirror_rake_tasks DSL method with valid options and environment va As a developer using Cape, I want to use the Cape DSL. - Scenario: mirror a Rake task with its implementation + @deprecated + Scenario: mirror the matching Rake task with its implementation (deprecated) Given a full-featured Rakefile And a Capfile with: """ @@ -20,7 +21,31 @@ Feature: The #mirror_rake_tasks DSL method with valid options and environment va When I run `cap with_period` Then the output should contain: """ + *** DEPRECATED: `mirror_rake_tasks(:roles => :app) { |env| env["RAILS_ENV"] = "rails-env" }`. Use this instead: `mirror_rake_tasks { |recipes| recipes.options[:roles] = :app; recipes.env["RAILS_ENV"] = "rails-env" }` * executing `with_period' * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" `with_period' is only run for servers matching {:roles=>:app}, but no servers matched """ + + Scenario: mirror the matching Rake task with its implementation + Given a full-featured Rakefile + And a Capfile with: + """ + set :current_path, '/current/path' + set :rails_env, 'rails-env' + + Cape do + mirror_rake_tasks do |recipes| + recipes.options[:roles] = :app + recipes.env['RAILS_ENV'] = lambda { rails_env } + end + end + """ + When I run `cap with_period` + Then the output should contain: + """ + * executing `with_period' + * executing "cd /current/path && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period RAILS_ENV=\"rails-env\"" + `with_period' is only run for servers matching {:roles=>:app}, but no servers matched + """ + And the output should not contain "DEPRECATED" diff --git a/features/step_definitions.rb b/features/step_definitions.rb index d619895..0ba423f 100644 --- a/features/step_definitions.rb +++ b/features/step_definitions.rb @@ -43,3 +43,43 @@ task :hidden_task end_step end + +Given 'a full-featured Rakefile defining a Ruby-method-shadowing task' do + step 'a file named "Rakefile" with:', <<-end_step + desc 'A task that shadows a Ruby method' + task :load + + desc 'Ends with period.' + task :with_period + + desc 'Ends without period' + task :without_period + + 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' + task :long + + desc 'My task with one argument' + task :with_one_arg, [:the_arg] + + desc 'A task that shadows a namespace' + task :my_namespace + + namespace :my_namespace do + desc 'My task in a namespace' + task :in_a_namespace + + namespace :my_nested_namespace do + desc 'My task in a nested namespace' + task :in_a_nested_namespace + end + end + + desc 'My task with two arguments' + task :with_two_args, [:my_arg1, :my_arg2] + + desc 'My task with three arguments' + task :with_three_args, [:an_arg1, :an_arg2, :an_arg3] + + task :hidden_task + end_step +end diff --git a/lib/cape.rb b/lib/cape.rb index dcf2b29..73706f0 100644 --- a/lib/cape.rb +++ b/lib/cape.rb @@ -6,6 +6,7 @@ module Cape extend DSL + extend DSLDeprecated end diff --git a/lib/cape/capistrano.rb b/lib/cape/capistrano.rb index b4b08dd..7930131 100644 --- a/lib/cape/capistrano.rb +++ b/lib/cape/capistrano.rb @@ -1,5 +1,5 @@ -require 'cape/hash_list' require 'cape/rake' +require 'cape/recipe_definition' require 'cape/util' module Cape @@ -17,14 +17,13 @@ def initialize(attributes={}) attributes.each do |name, value| send "#{name}=", value end - self.rake ||= Rake.new + self.rake ||= new_rake end # Defines a wrapper in Capistrano around the specified Rake _task_. # # @param [Hash] task metadata for a Rake task - # @param [Hash] named_arguments named arguments, including options to pass to - # the Capistrano +task+ method + # @param [Hash] named_arguments # # @option task [String] :name the name of the Rake task # @option task [Array of String, nil] :parameters the names of the Rake @@ -32,33 +31,28 @@ def initialize(attributes={}) # @option task [String] :description documentation for the Rake # task # - # @option named_arguments [Binding] :binding the Binding of your - # Capistrano recipes - # file + # @option named_arguments [Binding] :binding the Binding of your Capistrano + # recipes file # - # @yield [env] a block that defines environment variables for the Rake task; - # optional - # @yieldparam [Hash] env the environment variables to set before executing - # the Rake task + # @yield [recipes] a block that customizes the Capistrano recipe(s) + # generated for the Rake task(s); optional + # @yieldparam [RecipeDefinition] recipes an interface for customizing the + # Capistrano recipe(s) generated for + # the Rake task(s) # # @return [Capistrano] the object # # @raise [ArgumentError] +named_arguments[:binding]+ is missing # # @note Any parameters that the Rake task has are integrated via environment variables, since Capistrano does not support recipe parameters per se. - # - # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano ‘task’ method options def define_rake_wrapper(task, named_arguments, &block) unless (binding = named_arguments[:binding]) raise ::ArgumentError, ':binding named argument is required' end capistrano_context = binding.eval('self', __FILE__, __LINE__) - options = named_arguments.reject do |key, value| - key == :binding - end describe task, capistrano_context - implement(task, capistrano_context, options, &block) + implement(task, capistrano_context, &block) end private @@ -88,6 +82,11 @@ def build_capistrano_description(task) description.join end + def capture_recipe_definition(recipe_definition, &recipe_definition_block) + recipe_definition_block.call(recipe_definition) if recipe_definition_block + true + end + def describe(task, capistrano_context) if (description = build_capistrano_description(task)) capistrano_context.desc description @@ -95,13 +94,18 @@ def describe(task, capistrano_context) self end - def implement(task, capistrano_context, options, &env_block) - name_tokens = task[:name].split(':') - name_tokens << 'default' if task[:default] + def implement(task, capistrano_context, &recipe_definition_block) + name_tokens = tokenize_name(task) + recipe_definition = new_recipe_definition + capture_recipe_definition(recipe_definition, &recipe_definition_block) + env = nil rake = self.rake - # Define the recipe. block = lambda { |context| - context.task name_tokens.last, options do + recipe_name = name_tokens.last + if recipe_definition.rename + recipe_name = recipe_definition.rename.call(name_tokens.last) + end + context.task recipe_name, recipe_definition.options do arguments = Array(task[:parameters]).collect do |a| if (value = ENV[a.upcase]) value = value.inspect @@ -113,17 +117,26 @@ def implement(task, capistrano_context, options, &env_block) else arguments = "[#{arguments.join ','}]" end - env_hash = HashList.new - env_block.call(env_hash) if env_block - env_hash.reject! do |var_name, var_value| - var_name.nil? || var_value.nil? - end - env_strings = env_hash.collect do |var_name, var_value| - "#{var_name}=#{var_value.inspect}" + + unless env + env_strings = recipe_definition.env.collect do |var_name, var_value| + if var_name.nil? || var_value.nil? + nil + else + if var_value.is_a?(Proc) + var_value = context.instance_eval do + var_value.call + end + end + "#{var_name}=#{var_value.inspect}" + end + end.compact + env = env_strings.empty? ? nil : (' ' + env_strings.join(' ')) end - env = env_strings.empty? ? nil : (' ' + env_strings.join(' ')) - command = "cd #{context.current_path} && " + - "#{rake.remote_executable} " + + + path = recipe_definition.cd || context.current_path + path = path.call if path.respond_to?(:call) + command = "cd #{path} && #{rake.remote_executable} " + "#{task[:name]}#{arguments}#{env}" context.run command end @@ -139,6 +152,20 @@ def implement(task, capistrano_context, options, &env_block) self end + def new_rake(*arguments) + Rake.new(*arguments) + end + + def new_recipe_definition(*arguments) + RecipeDefinition.new(*arguments) + end + + def tokenize_name(task) + task[:name].split(':').tap do |result| + result << 'default' if task[:default] + end + end + end end diff --git a/lib/cape/capistrano_deprecated.rb b/lib/cape/capistrano_deprecated.rb new file mode 100644 index 0000000..a91d0aa --- /dev/null +++ b/lib/cape/capistrano_deprecated.rb @@ -0,0 +1,165 @@ +require 'cape/capistrano' +require 'cape/deprecation/capistrano_deprecated_define_rake_wrapper' +require 'cape/recipe_definition_deprecated' +require 'cape/xterm' + +module Cape + + # Implements {Capistrano} with deprecated methods. + # + # @api private + class CapistranoDeprecated < Capistrano + + # Defines a wrapper in Capistrano around the specified Rake _task_. + # + # @deprecated Use {Capistrano#define_rake_wrapper} instead. + # + # @param [Hash] task metadata for a Rake task + # @param [Hash] named_arguments named arguments, including options to pass to + # the Capistrano +task+ method + # + # @option task [String] :name the name of the Rake task + # @option task [Array of String, nil] :parameters the names of the Rake + # task's parameters, if any + # @option task [String] :description documentation for the Rake + # task + # + # @option named_arguments [Binding] :binding the Binding of your + # Capistrano recipes + # file + # + # @yield [env] a block that defines environment variables for the Rake task; + # optional + # @yieldparam [Hash] env the environment variables to set before executing + # the Rake task + # + # @return [CapistranoDeprecated] the object + # + # @raise [ArgumentError] +named_arguments[:binding]+ is missing + # + # @note Any parameters that the Rake task has are integrated via environment variables, since Capistrano does not support recipe parameters per se. + # + # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano 'task' method options + # + # @api public + def define_rake_wrapper(task, named_arguments, &block) + unless (binding = named_arguments[:binding]) + raise ::ArgumentError, ':binding named argument is required' + end + + deprecation.task = task + deprecation.named_arguments = named_arguments + + capistrano_context = binding.eval('self', __FILE__, __LINE__) + options = named_arguments.reject do |key, value| + key == :binding + end + describe task, capistrano_context + implement(task, capistrano_context, options, &block) + end + + # The object in which deprecated API usage is recorded. + # + # @return [Deprecation::Base] + def deprecation + @deprecation ||= Deprecation::CapistranoDeprecatedDefineRakeWrapper.new + end + + private + + def capture_recipe_definition(recipe_definition, &recipe_definition_block) + begin + super + rescue NoMethodError + unless @warned + deprecation.stream.puts XTerm.bold_and_foreground_red('*** DEPRECATED:') + + ' ' + + XTerm.bold('Referencing Capistrano variables ' + + 'from Cape without wrapping them in ' + + 'a block, a lambda, or another ' + + 'callable object') + @warned = true + end + return false + end + true + end + + def implement(task, capistrano_context, options, &env_block) + return super(task, capistrano_context, &env_block) if options.empty? + + name_tokens = tokenize_name(task) + recipe_definition = new_recipe_definition(deprecation) + env = nil + if capture_recipe_definition(recipe_definition, &env_block) + env_strings = recipe_definition.env.collect do |var_name, var_value| + if var_name.nil? || var_value.nil? + nil + else + var_value = var_value.call if var_value.is_a?(Proc) + "#{var_name}=#{var_value.inspect}" + end + end.compact + env = env_strings.empty? ? nil : (' ' + env_strings.join(' ')) + end + this = env ? nil : self + rake = self.rake + block = lambda { |context| + recipe_name = name_tokens.last + if recipe_definition.rename + recipe_name = recipe_definition.rename.call(name_tokens.last) + end + context.task recipe_name, options do + arguments = Array(task[:parameters]).collect do |a| + if (value = ENV[a.upcase]) + value = value.inspect + end + value + end + if arguments.empty? + arguments = nil + else + arguments = "[#{arguments.join ','}]" + end + + unless env + if this.instance_eval { capture_recipe_definition(recipe_definition, &env_block) } + env_strings = recipe_definition.env.collect do |var_name, var_value| + if var_name.nil? || var_value.nil? + nil + else + var_value = var_value.call if var_value.is_a?(Proc) + "#{var_name}=#{var_value.inspect}" + end + end.compact + env = env_strings.empty? ? nil : (' ' + env_strings.join(' ')) + this = nil + end + end + + path = recipe_definition.cd || context.current_path + path = path.call if path.respond_to?(:call) + command = "cd #{path} && #{rake.remote_executable} " + + "#{task[:name]}#{arguments}#{env}" + context.run command + end + } + # Nest the recipe inside its containing namespaces. + name_tokens[0...-1].reverse.each do |namespace_token| + inner_block = block + block = lambda { |context| + context.namespace(namespace_token, &inner_block) + } + end + block.call capistrano_context + self + end + + def new_recipe_definition(*arguments) + arguments << deprecation if arguments.empty? + RecipeDefinitionDeprecated.new(*arguments) + end + + end + +end diff --git a/lib/cape/deprecation.rb b/lib/cape/deprecation.rb new file mode 100644 index 0000000..536675a --- /dev/null +++ b/lib/cape/deprecation.rb @@ -0,0 +1,12 @@ +::Dir.glob(::File.expand_path('../deprecation/*.rb', __FILE__)) do |f| + require "cape/deprecation/#{::File.basename f, '.rb'}" +end + +module Cape + + # Contains implementations of deprecation stream printers. + # + # @api private + module Deprecation; end + +end diff --git a/lib/cape/deprecation/base.rb b/lib/cape/deprecation/base.rb new file mode 100644 index 0000000..2755e99 --- /dev/null +++ b/lib/cape/deprecation/base.rb @@ -0,0 +1,59 @@ +require 'cape/xterm' + +module Cape + + module Deprecation + + # Prints to a stream a message related to deprecated API usage. + # + # @abstract + class Base + + # Sets the value of {#stream}. + # + # @param [IO] value a new value for {#stream} + # + # @return [IO] _value_ + attr_writer :stream + + # Formats {#message_content} for display. + # + # @return [String] the full message + def formatted_message + [].tap do |fragments| + fragments << XTerm.bold_and_foreground_red('*** DEPRECATED:') + fragments << ' ' + fragments << XTerm.bold(message_content) + end.join + end + + # Prepares a message based on deprecated API usage. + # + # @raise [NotImplementedError] + # + # @abstract + def message_content + raise ::NotImplementedError + end + + # The stream to which deprecation messages are printed. Defaults to + # $stderr. + # + # @return [IO] + def stream + @stream ||= $stderr + end + + # Writes {#formatted_message} to {#stream}. + # + # @return [Base] the object + def write_formatted_message_to_stream + stream.puts formatted_message + self + end + + end + + end + +end diff --git a/lib/cape/deprecation/capistrano_deprecated_define_rake_wrapper.rb b/lib/cape/deprecation/capistrano_deprecated_define_rake_wrapper.rb new file mode 100644 index 0000000..bed78f6 --- /dev/null +++ b/lib/cape/deprecation/capistrano_deprecated_define_rake_wrapper.rb @@ -0,0 +1,168 @@ +require 'cape/deprecation/base' +require 'cape/hash_list' + +module Cape + + module Deprecation + + # Prints to a stream a message related to deprecated usage of + # {CapistranoDeprecated#define_rake_wrapper}. + class CapistranoDeprecatedDefineRakeWrapper < Base + + # The _task_ argument to {CapistranoDeprecated#define_rake_wrapper}. + # + # @return [Symbol, String] + attr_accessor :task + + # Environment variable names and values set in a call to + # {CapistranoDeprecated#define_rake_wrapper}. + # + # @return [HashList] + def env + @env ||= HashList.new + end + + # Prepares a message based on deprecated usage of + # {CapistranoDeprecated#define_rake_wrapper}. + # + # @return [String] a deprecation message + def message_content + [].tap do |fragments| + fragments << message_content_actual + fragments << '. ' + fragments << message_content_expected + end.join + end + + # The _named_arguments_ argument to + # {CapistranoDeprecated#define_rake_wrapper}. + # + # @return [HashList] + def named_arguments + @named_arguments ||= HashList.new + end + + # Sets the value of {#named_arguments}. + # + # @param [Hash] value a new value for {#named_arguments} + # + # @return [HashList] _value_ + def named_arguments=(value) + @named_arguments = HashList.new(value) + end + + private + + def binding_named_arguments + named_arguments.select do |name, value| + name == :binding + end + end + + def message_content_env_actual + [].tap do |fragments| + env_entries = env.collect do |name, value| + "env[#{name.inspect}] = #{value.inspect}" + end + unless env_entries.empty? + fragments << ' { |env| ' + fragments << env_entries.join('; ') + fragments << ' }' + end + end.join + end + + def message_content_options_and_env_expected + [].tap do |fragments| + unless named_arguments.empty? && env.empty? + fragments << ' { |recipes| ' + + options_entries = options.collect do |name, value| + "recipes.options[#{name.inspect}] = #{value.inspect}" + end + env_entries = env.collect do |name, value| + "recipes.env[#{name.inspect}] = #{value.inspect}" + end + fragments << (options_entries + env_entries).join('; ') + + fragments << ' }' + end + end.join + end + + def message_content_actual + [].tap do |fragments| + fragments << '`' + fragments << message_content_method_name + fragments << message_content_task_and_named_arguments_actual + fragments << message_content_env_actual + fragments << '`' + end.join + end + + def message_content_expected + [].tap do |fragments| + fragments << 'Use this instead: ' + fragments << '`' + fragments << message_content_method_name + fragments << message_content_task_and_named_arguments_expected + fragments << message_content_options_and_env_expected + fragments << '`' + end.join + end + + def message_content_method_name + 'define_rake_wrapper' + end + + def message_content_task_and_named_arguments_actual + [].tap do |fragments| + arguments = [] + arguments << task.inspect unless task.nil? + named_arguments_entries = named_arguments.collect do |name, value| + "#{name.inspect} => #{value.inspect}" + end + unless named_arguments_entries.empty? + arguments << named_arguments_entries.join(', ') + end + unless arguments.empty? + if env.empty? + fragments << " #{arguments.join ', '}" + else + fragments << "(#{arguments.join ', '})" + end + end + end.join + end + + def message_content_task_and_named_arguments_expected + [].tap do |fragments| + arguments = [] + arguments << task.inspect unless task.nil? + binding_named_arguments_entries = binding_named_arguments.collect do |name, value| + "#{name.inspect} => #{value.inspect}" + end + unless binding_named_arguments_entries.empty? + arguments << binding_named_arguments_entries.join(', ') + end + unless arguments.empty? + if options.empty? && env.empty? + fragments << " #{arguments.join ', '}" + else + fragments << "(#{arguments.join ', '})" + end + end + end.join + end + + def options + named_arguments.reject do |name, value| + name == :binding + end + end + + end + + end + +end diff --git a/lib/cape/deprecation/dsl_deprecated_mirror_rake_tasks.rb b/lib/cape/deprecation/dsl_deprecated_mirror_rake_tasks.rb new file mode 100644 index 0000000..9f1d356 --- /dev/null +++ b/lib/cape/deprecation/dsl_deprecated_mirror_rake_tasks.rb @@ -0,0 +1,145 @@ +require 'cape/deprecation/base' +require 'cape/hash_list' + +module Cape + + module Deprecation + + # Prints to a stream a message related to deprecated usage of + # {DSLDeprecated#mirror_rake_tasks}. + class DSLDeprecatedMirrorRakeTasks < Base + + # The _task_expression_ argument to {DSLDeprecated#mirror_rake_tasks}. + # + # @return [Symbol, String] + attr_accessor :task_expression + + # Environment variable names and values set in a call to + # {DSLDeprecated#mirror_rake_tasks}. + # + # @return [HashList] + def env + @env ||= HashList.new + end + + # Prepares a message based on deprecated usage of + # {DSLDeprecated#mirror_rake_tasks}. + # + # @return [String] a deprecation message + def message_content + [].tap do |fragments| + fragments << message_content_actual + fragments << '. ' + fragments << message_content_expected + end.join + end + + # The _options_ argument to {DSLDeprecated#mirror_rake_tasks}. + # + # @return [HashList] + def options + @options ||= HashList.new + end + + # Sets the value of {#options}. + # + # @param [Hash] value a new value for {#options} + # + # @return [HashList] _value_ + def options=(value) + @options = HashList.new(value) + end + + private + + def message_content_env_actual + [].tap do |fragments| + env_entries = env.collect do |name, value| + "env[#{name.inspect}] = #{value.inspect}" + end + unless env_entries.empty? + fragments << ' { |env| ' + fragments << env_entries.join('; ') + fragments << ' }' + end + end.join + end + + def message_content_options_and_env_expected + [].tap do |fragments| + unless options.empty? && env.empty? + fragments << ' { |recipes| ' + + options_entries = options.collect do |name, value| + "recipes.options[#{name.inspect}] = #{value.inspect}" + end + env_entries = env.collect do |name, value| + "recipes.env[#{name.inspect}] = #{value.inspect}" + end + fragments << (options_entries + env_entries).join('; ') + + fragments << ' }' + end + end.join + end + + def message_content_actual + [].tap do |fragments| + fragments << '`' + fragments << message_content_method_name + fragments << message_content_task_expression_and_options_actual + fragments << message_content_env_actual + fragments << '`' + end.join + end + + def message_content_expected + [].tap do |fragments| + fragments << 'Use this instead: ' + fragments << '`' + fragments << message_content_method_name + fragments << message_content_task_expression_expected + fragments << message_content_options_and_env_expected + fragments << '`' + end.join + end + + def message_content_method_name + 'mirror_rake_tasks' + end + + def message_content_task_expression_and_options_actual + [].tap do |fragments| + arguments = [] + arguments << task_expression.inspect unless task_expression.nil? + options_entries = options.collect do |name, value| + "#{name.inspect} => #{value.inspect}" + end + arguments << options_entries.join(', ') unless options_entries.empty? + unless arguments.empty? + if env.empty? + fragments << " #{arguments.join ', '}" + else + fragments << "(#{arguments.join ', '})" + end + end + end.join + end + + def message_content_task_expression_expected + [].tap do |fragments| + unless task_expression.nil? + if options.empty? && env.empty? + fragments << " #{task_expression.inspect}" + else + fragments << "(#{task_expression.inspect})" + end + end + end.join + end + + end + + end + +end diff --git a/lib/cape/dsl.rb b/lib/cape/dsl.rb index 9650467..e6c1574 100644 --- a/lib/cape/dsl.rb +++ b/lib/cape/dsl.rb @@ -92,53 +92,19 @@ def method_missing(method, *args, &block) # Defines Rake tasks as Capistrano recipes. # - # @overload mirror_rake_tasks(task_expression=nil) - # Defines Rake tasks as Capistrano recipes. - # - # @param [String, Symbol] task_expression the full name of a Rake task or - # namespace to filter - # - # @yield [env] a block that defines environment variables for the Rake - # task; optional - # @yieldparam [Hash] env the environment variables to set before executing - # the Rake task - # - # @return [DSL] the object - # - # @overload mirror_rake_tasks(capistrano_task_options={}) - # Defines all Rake tasks as Capistrano recipes with options. - # - # @param [Hash] capistrano_task_options options to pass to the Capistrano - # +task+ method; optional - # - # @yield [env] a block that defines environment variables for the Rake - # task; optional - # @yieldparam [Hash] env the environment variables to set before executing - # the Rake task - # - # @return [DSL] the object - # - # @overload mirror_rake_tasks(task_expression, capistrano_task_options={}) - # Defines specific Rake tasks as Capistrano recipes with options. - # - # @param [String, Symbol] task_expression the full name of a Rake - # task or namespace to - # filter - # @param [Hash] capistrano_task_options options to pass to the - # Capistrano +task+ method; - # optional + # @param [String, Symbol] task_expression the full name of a Rake task or + # namespace to filter # - # @yield [env] a block that defines environment variables for the Rake - # task; optional - # @yieldparam [Hash] env the environment variables to set before executing - # the Rake task + # @yield [recipes] a block that customizes the Capistrano recipe(s) + # generated for the Rake task(s); optional + # @yieldparam [RecipeDefinition] recipes an interface for customizing the + # Capistrano recipe(s) generated for + # the Rake task(s) # - # @return [DSL] the object + # @return [DSL] the object # # @note Any parameters that the Rake tasks have are integrated via environment variables, since Capistrano does not support recipe parameters per se. # - # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano ‘task’ method options - # # @example Mirroring all Rake tasks # # config/deploy.rb # @@ -160,7 +126,7 @@ def method_missing(method, *args, &block) # mirror_rake_tasks :foo # end # - # @example Mirroring Rake tasks that require Capistrano recipe options and/or environment variables + # @example Mirroring Rake tasks that require renaming, Capistrano recipe options, path switching, and/or environment variables # # config/deploy.rb # # require 'cape' @@ -168,13 +134,37 @@ def method_missing(method, *args, &block) # Cape do # # Display defined Rails routes on application server remote machines # # only. - # mirror_rake_tasks :routes, :roles => :app + # mirror_rake_tasks :routes do |recipes| + # recipes.options[:roles] = :app + # end # # # Execute database migration on application server remote machines # # only, and set the 'RAILS_ENV' environment variable to the value of # # the Capistrano variable 'rails_env'. - # mirror_rake_tasks 'db:migrate', :roles => :app do |env| - # env['RAILS_ENV'] = rails_env + # mirror_rake_tasks 'db:migrate' do |recipes| + # recipes.options[:roles] = :app + # recipes.env['RAILS_ENV'] = lambda { rails_env } + # end + # + # # Support a Rake task that must be run on application server remote + # # machines only, and in the remote directory 'release_path' instead of + # # the default, 'current_path'. + # before 'deploy:symlink', :spec + # mirror_rake_tasks :spec do |recipes| + # recipes.cd { release_path } + # recipes.options[:roles] = :app + # end + # + # # Avoid collisions with the existing Ruby method #test, run tests on + # # application server remote machines only, and set the 'RAILS_ENV' + # # environment variable to the value of the Capistrano variable + # # 'rails_env'. + # mirror_rake_tasks :test do |recipes| + # recipes.rename do |rake_task_name| + # "#{rake_task_name}_task" + # end + # recipes.options[:roles] = :app + # recipes.env['RAILS_ENV'] = lambda { rails_env } # end # end # @@ -188,19 +178,9 @@ def method_missing(method, *args, &block) # cape.mirror_rake_tasks # end # end - def mirror_rake_tasks(*arguments, &block) - arguments_count = arguments.length - options = arguments.last.is_a?(Hash) ? arguments.pop.dup : {} - unless arguments.length <= 1 - raise ::ArgumentError, - ("wrong number of arguments (#{arguments_count} for 0 or 1, " + - 'plus an options hash)') - end - - task_expression = arguments.first - options[:binding] = binding + def mirror_rake_tasks(task_expression=nil, &block) rake.each_task task_expression do |t| - deployment_library.define_rake_wrapper(t, options, &block) + deployment_library.define_rake_wrapper(t, :binding => binding, &block) end self end @@ -236,7 +216,7 @@ def remote_rake_executable=(value) # Returns an abstraction of the Rake installation and available tasks. def rake - @rake ||= Rake.new + @rake ||= new_rake end private @@ -245,7 +225,15 @@ def deployment_library return @deployment_library if @deployment_library raise_unless_capistrano - @deployment_library = Capistrano.new(:rake => rake) + @deployment_library = new_capistrano(:rake => rake) + end + + def new_capistrano(*arguments) + Capistrano.new(*arguments) + end + + def new_rake(*arguments) + Rake.new(*arguments) end def raise_unless_capistrano diff --git a/lib/cape/dsl_deprecated.rb b/lib/cape/dsl_deprecated.rb new file mode 100644 index 0000000..ddb0df5 --- /dev/null +++ b/lib/cape/dsl_deprecated.rb @@ -0,0 +1,157 @@ +require 'cape/capistrano_deprecated' +require 'cape/deprecation/capistrano_deprecated_define_rake_wrapper' +require 'cape/deprecation/dsl_deprecated_mirror_rake_tasks' + +module Cape + + # Implements deprecated methods of {DSL}. + # + # @api private + module DSLDeprecated + + # The stream to which deprecation messages are printed. + # + # @return [Deprecation::Base] + def deprecation + @deprecation ||= Deprecation::DSLDeprecatedMirrorRakeTasks.new + end + + # Defines Rake tasks as Capistrano recipes. + # + # @overload mirror_rake_tasks(task_expression=nil) + # Defines Rake tasks as Capistrano recipes. + # + # @param [String, Symbol] task_expression the full name of a Rake task or + # namespace to filter + # + # @yield [env] a block that defines environment variables for the Rake + # task; optional + # @yieldparam [RecipeDefinitionDeprecated] env the environment variables to + # set before executing the + # Rake task + # + # @return [DSLDeprecated] the object + # + # @overload mirror_rake_tasks(capistrano_task_options={}) + # Defines all Rake tasks as Capistrano recipes with options. + # + # @deprecated Use {RecipeDefinition#options} instead. + # + # @param [Hash] capistrano_task_options options to pass to the Capistrano + # +task+ method + # + # @yield [env] a block that defines environment variables for the Rake + # task; optional + # @yieldparam [RecipeDefinitionDeprecated] env the environment variables to + # set before executing the + # Rake task + # + # @return [DSLDeprecated] the object + # + # @overload mirror_rake_tasks(task_expression, capistrano_task_options={}) + # Defines specific Rake tasks as Capistrano recipes with options. + # + # @deprecated Use {RecipeDefinition#options} instead. + # + # @param [String, Symbol] task_expression the full name of a Rake + # task or namespace to + # filter + # @param [Hash] capistrano_task_options options to pass to the + # Capistrano +task+ method + # + # @yield [env] a block that defines environment variables for the Rake + # task; optional + # @yieldparam [RecipeDefinitionDeprecated] env the environment variables to + # set before executing the + # Rake task + # + # @return [DSLDeprecated] the object + # + # @note Any parameters that the Rake tasks have are integrated via environment variables, since Capistrano does not support recipe parameters per se. + # + # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano 'task' method options + # + # @example Mirroring all Rake tasks + # # config/deploy.rb + # + # require 'cape' + # + # Cape do + # mirror_rake_tasks + # end + # + # @example Mirroring some Rake tasks, but not others + # # config/deploy.rb + # + # require 'cape' + # + # Cape do + # # Create Capistrano recipes for the Rake task 'foo' and/or for the + # # tasks in the 'foo' namespace. + # mirror_rake_tasks :foo + # end + # + # @example Mirroring Rake tasks that require Capistrano recipe options and/or environment variables + # # config/deploy.rb + # + # require 'cape' + # + # Cape do + # # Display defined Rails routes on application server remote machines + # # only. + # mirror_rake_tasks :routes, :roles => :app + # + # # Execute database migration on application server remote machines + # # only, and set the 'RAILS_ENV' environment variable to the value of + # # the Capistrano variable 'rails_env'. + # mirror_rake_tasks 'db:migrate', :roles => :app do |env| + # env['RAILS_ENV'] = rails_env + # end + # end + # + # @example Mirroring Rake tasks into a Capistrano namespace + # # config/deploy.rb + # + # require 'cape' + # + # namespace :rake_tasks do + # Cape do |cape| + # cape.mirror_rake_tasks + # end + # end + # + # @api public + def mirror_rake_tasks(*arguments, &block) + arguments_count = arguments.length + options = arguments.last.is_a?(::Hash) ? arguments.pop.dup : {} + deprecation.task_expression = arguments.first + deprecation.options = options.dup + unless arguments.length <= 1 + raise ::ArgumentError, + ("wrong number of arguments (#{arguments_count} for 0 or 1, " + + 'plus an options hash)') + end + + task_expression = arguments.first + options[:binding] = binding + rake.each_task task_expression do |t| + deployment_library.define_rake_wrapper(t, options, &block) + end + deployment_library.deprecation.env.each do |name, value| + deprecation.env[name] = value + end + unless deprecation.options.empty? && deprecation.env.empty? + deprecation.write_formatted_message_to_stream + end + self + end + + private + + def new_capistrano(*arguments) + CapistranoDeprecated.new(*arguments) + end + + end + +end diff --git a/lib/cape/recipe_definition.rb b/lib/cape/recipe_definition.rb new file mode 100644 index 0000000..0ab5353 --- /dev/null +++ b/lib/cape/recipe_definition.rb @@ -0,0 +1,103 @@ +require 'cape/hash_list' + +module Cape + + # Determines how a Capistrano recipe will be defined. + class RecipeDefinition + + # The remote directory from which Rake tasks will be executed. + # + # @overload cd + # The remote directory from which Rake tasks will be executed. + # + # @return [String, Proc] the value or a block that returns the value + # + # @overload cd(path) + # Sets the remote directory from which Rake tasks will be executed. + # + # @param [String, Proc] path the value or a callable object that returns + # the value + # + # @return [String, Proc] _path_ + # + # @raise [ArgumentError] _path_ is a callable object that has parameters + # + # @overload cd(&block) + # Sets the remote directory from which Rake tasks will be executed. + # + # @yieldreturn [String] the value + # + # @return [String, Proc] _block_ + # + # @raise [ArgumentError] _block_ has parameters + def cd(path=nil, &block) + if (cd = (path || block)) + if cd.respond_to?(:arity) + case cd.arity + when -1 + # Lambda: good + when 0 + # Good + else + raise ::ArgumentError, "Must have 0 parameters but has #{cd.arity}" + end + end + + @cd = cd + else + @cd + end + end + + # A hash of remote environment variables. + # + # @return [HashList] the desired environment of the remote computer + def env + @env ||= HashList.new + end + + # A hash of Capistrano recipe options to pass to the Capistrano +task+ + # method. + # + # @return [HashList] the desired Capistrano recipe options + # + # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano 'task' method options + def options + @options ||= HashList.new + end + + # How Rake tasks will be named when they are mirrored as Capistrano recipes. + # + # @overload rename + # How Rake tasks will be named when they are mirrored as Capistrano + # recipes. + # + # @return [Proc] the block that generates a Capistrano recipe name from a + # Rake task name + # + # @overload rename(&block) + # Determines how Rake tasks will be named when they are mirrored as + # Capistrano recipes. + # + # @yield [task_name] + # @yieldparam task_name [String] the name of a Rake task + # @yieldreturn [String] a name for the Capistrano recipe + # + # @return [Proc] _block_ + # + # @raise [ArgumentError] _block_ does not have exactly one parameter + def rename(&block) + if block + unless (block.arity == 1) + raise ::ArgumentError, "Must have 1 parameter but has #{block.arity}" + end + + @rename = block + else + @rename + end + end + + end + +end diff --git a/lib/cape/recipe_definition_deprecated.rb b/lib/cape/recipe_definition_deprecated.rb new file mode 100644 index 0000000..fc392ba --- /dev/null +++ b/lib/cape/recipe_definition_deprecated.rb @@ -0,0 +1,41 @@ +require 'cape/recipe_definition' + +module Cape + + # Implements {RecipeDefinition} with deprecated methods. + # + # @api private + class RecipeDefinitionDeprecated < RecipeDefinition + + # The object in which deprecated API usage is recorded. + # + # @return [Deprecation::Base] + attr_reader :deprecation + + # Constructs a new RecipeDefinitionDeprecated with the specified + # _deprecation_. + # + # @param [Deprecation::Base] deprecation the value of {#deprecation} + def initialize(deprecation) + @deprecation = deprecation + end + + # Sets a remote environment variable. + # + # @param [String] name the name of a remote environment variable + # @param [String] value a value for the remote environment variable + # + # @return [RecipeDefinitionDeprecated] the object + # + # @deprecated Use {RecipeDefinition#env} instead. + # + # @api public + def []=(name, value) + env[name] = value + deprecation.env[name] = value + self + end + + end + +end diff --git a/lib/cape/xterm.rb b/lib/cape/xterm.rb new file mode 100644 index 0000000..7db3237 --- /dev/null +++ b/lib/cape/xterm.rb @@ -0,0 +1,326 @@ +module Cape + + # Formats output for an xterm console. + # + # Convenience class methods are made available dynamically that permit multiple + # formats to be applied. + # + # @example Apply bold and red-foreground formatting to a string + # Cape::XTerm.bold_and_red_foreground 'foo' + # + # @api private + module XTerm + + # The xterm formatting codes. + FORMATS = {:bold => '1', + :underlined => '4', + :blinking => '5', + :inverse => '7', + :foreground_black => '30', + :foreground_red => '31', + :foreground_green => '32', + :foreground_yellow => '33', + :foreground_blue => '34', + :foreground_magenta => '35', + :foreground_cyan => '36', + :foreground_gray => '37', + :foreground_default => '39', + :background_black => '40', + :background_red => '41', + :background_green => '42', + :background_yellow => '43', + :background_blue => '44', + :background_magenta => '45', + :background_cyan => '46', + :background_gray => '47', + :background_default => '49'} + + # @!method bold(string) + # Formats the specified _string_ in bold type. + # + # @param [String] string the string to format in bold type + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method underlined(string) + # Formats the specified _string_ in underlined type. + # + # @param [String] string the string to underline + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method blinking(string) + # Formats the specified _string_ in blinking type. + # + # @param [String] string the string to blink + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method inverse(string) + # Formats the specified _string_ in inverse colors. + # + # @param [String] string the string whose colors are to be inverted + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_black(string) + # Formats the specified _string_ in black. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_red(string) + # Formats the specified _string_ in red. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_green(string) + # Formats the specified _string_ in green. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_yellow(string) + # Formats the specified _string_ in yellow. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_blue(string) + # Formats the specified _string_ in blue. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_magenta(string) + # Formats the specified _string_ in magenta. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_cyan(string) + # Formats the specified _string_ in cyan. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_gray(string) + # Formats the specified _string_ in gray. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method foreground_default(string) + # Formats the specified _string_ in the default foreground color. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_black(string) + # Formats the specified _string_ with a black background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_red(string) + # Formats the specified _string_ with a red background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_green(string) + # Formats the specified _string_ with a green background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_yellow(string) + # Formats the specified _string_ with a yellow background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_blue(string) + # Formats the specified _string_ with a blue background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_magenta(string) + # Formats the specified _string_ with a magenta background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_cyan(string) + # Formats the specified _string_ with a cyan background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_gray(string) + # Formats the specified _string_ with a gray background. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # @!method background_default(string) + # Formats the specified _string_ in the default background color. + # + # @param [String] string the string to color + # + # @return [String] the formatted _string_ + # + # @!scope class + # + # @api private + + # Applies the specified _formats_ to _string_. + # + # @param [String] string the string to format + # @param [Array of Symbol] formats the formats to apply + # + # @return [String] the formatted _string_ + def self.format(string, *formats) + formatting_codes = formats.collect do |f| + unless FORMATS.key?(f) + raise ::ArgumentError, "Unrecognized format #{f.inspect}" + end + + FORMATS[f] + end + + return string if formatting_codes.empty? || string.nil? + + "\e[#{formatting_codes.join ';'}m#{string}\e[0m" + end + + private + + def self.method_missing(method, *arguments, &block) + unless respond_to_missing?(method, false) + return super(method, *arguments, &block) + end + + formats = method.to_s.split('_and_').collect(&:to_sym) + format(*(arguments + formats)) + end + + def self.respond_to_missing?(method, include_private) + formats = method.to_s.split('_and_').collect(&:to_sym) + formats.all? do |f| + FORMATS.key? f + end + end + + if RUBY_VERSION <= '1.8.7' + def self.respond_to?(method, include_private=false) + respond_to_missing? method, include_private + end + end + + end + +end diff --git a/spec/cape/deprecation/base_sharedspec.rb b/spec/cape/deprecation/base_sharedspec.rb new file mode 100644 index 0000000..ea6b7ed --- /dev/null +++ b/spec/cape/deprecation/base_sharedspec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' +require 'cape/deprecation/base' + +shared_examples_for "a #{Cape::Deprecation::Base.name}" do + describe '-- without specified attributes --' do + its(:stream) { should == $stderr } + end + + describe '-- with a different #stream --' do + before :each do + subject.stream = different_stream + end + + let(:different_stream) { StringIO.new } + + its(:stream) { should == different_stream } + end +end diff --git a/spec/cape/deprecation/capistrano_deprecated_define_rake_wrapper_spec.rb b/spec/cape/deprecation/capistrano_deprecated_define_rake_wrapper_spec.rb new file mode 100644 index 0000000..e3515ef --- /dev/null +++ b/spec/cape/deprecation/capistrano_deprecated_define_rake_wrapper_spec.rb @@ -0,0 +1,157 @@ +require 'spec_helper' +require 'cape/deprecation/capistrano_deprecated_define_rake_wrapper' +require 'cape/deprecation/base_sharedspec' +require 'cape/xterm' + +describe Cape::Deprecation::CapistranoDeprecatedDefineRakeWrapper do + it_should_behave_like "a #{Cape::Deprecation::Base.name}" + + let(:deprecation_preamble) { + Cape::XTerm.bold_and_foreground_red('*** DEPRECATED:') + ' ' + } + + describe '-- without specified attributes --' do + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`define_rake_wrapper`. ' + + 'Use this instead: `define_rake_wrapper`') + } + end + + describe '-- with #task' do + before :each do + subject.task = {:name => :foo} + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`define_rake_wrapper {:name=>:foo}`. ' + + 'Use this instead: ' + + '`define_rake_wrapper {:name=>:foo}`') + } + + describe 'and with #named_arguments' do + before :each do + subject.named_arguments = {:bar => :baz} + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'define_rake_wrapper {:name=>:foo}, ' + + ':bar => :baz' + + '`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper({:name=>:foo}) { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + } + + describe 'and with #env --' do + before :each do + subject.env['QUX'] = 'quux' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'define_rake_wrapper({:name=>:foo}, ' + + ':bar => :baz) { |env| ' + + 'env["QUX"] = "quux" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper({:name=>:foo}) { |recipes| ' + + 'recipes.options[:bar] = :baz; ' + + 'recipes.env["QUX"] = "quux" ' + + '}' + + '`') + } + end + end + + describe 'and with #env --' do + before :each do + subject.env['BAR'] = 'baz' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'define_rake_wrapper({:name=>:foo}) { |env| ' + + 'env["BAR"] = "baz" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper({:name=>:foo}) { |recipes| ' + + 'recipes.env["BAR"] = "baz" ' + + '}' + + '`') + } + end + end + + describe '-- with #named_arguments' do + before :each do + subject.named_arguments = {:foo => :bar} + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`define_rake_wrapper :foo => :bar`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper { |recipes| ' + + 'recipes.options[:foo] = :bar ' + + '}' + + '`') + } + + describe 'and with #env --' do + before :each do + subject.env['BAZ'] = 'qux' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'define_rake_wrapper(:foo => :bar) { |env| ' + + 'env["BAZ"] = "qux" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper { |recipes| ' + + 'recipes.options[:foo] = :bar; ' + + 'recipes.env["BAZ"] = "qux" ' + + '}' + + '`') + } + end + end + + describe '-- with #env --' do + before :each do + subject.env['FOO'] = 'bar' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'define_rake_wrapper { |env| ' + + 'env["FOO"] = "bar" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'define_rake_wrapper { |recipes| ' + + 'recipes.env["FOO"] = "bar" ' + + '}' + + '`') + } + end +end diff --git a/spec/cape/deprecation/dsl_deprecated_mirror_rake_tasks_spec.rb b/spec/cape/deprecation/dsl_deprecated_mirror_rake_tasks_spec.rb new file mode 100644 index 0000000..02c414b --- /dev/null +++ b/spec/cape/deprecation/dsl_deprecated_mirror_rake_tasks_spec.rb @@ -0,0 +1,153 @@ +require 'spec_helper' +require 'cape/deprecation/dsl_deprecated_mirror_rake_tasks' +require 'cape/deprecation/base_sharedspec' +require 'cape/xterm' + +describe Cape::Deprecation::DSLDeprecatedMirrorRakeTasks do + it_should_behave_like "a #{Cape::Deprecation::Base.name}" + + let(:deprecation_preamble) { + Cape::XTerm.bold_and_foreground_red('*** DEPRECATED:') + ' ' + } + + describe '-- without specified attributes --' do + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`mirror_rake_tasks`. ' + + 'Use this instead: `mirror_rake_tasks`') + } + end + + describe '-- with #task_expression' do + before :each do + subject.task_expression = :foo + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`mirror_rake_tasks :foo`. ' + + 'Use this instead: `mirror_rake_tasks :foo`') + } + + describe 'and with #options' do + before :each do + subject.options = {:bar => :baz} + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`mirror_rake_tasks :foo, :bar => :baz`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks(:foo) { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + } + + describe 'and with #env --' do + before :each do + subject.env['QUX'] = 'quux' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks(:foo, ' + + ':bar => :baz) { |env| ' + + 'env["QUX"] = "quux" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks(:foo) { |recipes| ' + + 'recipes.options[:bar] = :baz; ' + + 'recipes.env["QUX"] = "quux" ' + + '}' + + '`') + } + end + end + + describe 'and with #env --' do + before :each do + subject.env['BAR'] = 'baz' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks(:foo) { |env| ' + + 'env["BAR"] = "baz" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks(:foo) { |recipes| ' + + 'recipes.env["BAR"] = "baz" ' + + '}' + + '`') + } + end + end + + describe '-- with #options' do + before :each do + subject.options = {:foo => :bar} + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`mirror_rake_tasks :foo => :bar`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks { |recipes| ' + + 'recipes.options[:foo] = :bar ' + + '}' + + '`') + } + + describe 'and with #env --' do + before :each do + subject.env['BAZ'] = 'qux' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks(:foo => :bar) { |env| ' + + 'env["BAZ"] = "qux" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks { |recipes| ' + + 'recipes.options[:foo] = :bar; ' + + 'recipes.env["BAZ"] = "qux" ' + + '}' + + '`') + } + end + end + + describe '-- with #env --' do + before :each do + subject.env['FOO'] = 'bar' + end + + its(:formatted_message) { + should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks { |env| ' + + 'env["FOO"] = "bar" ' + + '}' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks { |recipes| ' + + 'recipes.env["FOO"] = "bar" ' + + '}' + + '`') + } + end +end diff --git a/spec/cape/dsl_deprecated_spec.rb b/spec/cape/dsl_deprecated_spec.rb new file mode 100644 index 0000000..e6f2254 --- /dev/null +++ b/spec/cape/dsl_deprecated_spec.rb @@ -0,0 +1,307 @@ +require 'spec_helper' +require 'cape/dsl' +require 'cape/dsl_deprecated' +require 'cape/capistrano' +require 'cape/core_ext/hash' +require 'cape/core_ext/symbol' +require 'cape/rake' +require 'cape/xterm' + +describe Cape::DSLDeprecated do + subject do + Object.new.tap do |o| + o.extend Cape::DSL + o.extend described_class + end + end + + let(:mock_capistrano) { mock(Cape::Capistrano).as_null_object } + + let(:mock_rake) { mock(Cape::Rake).as_null_object } + + let(:task_expression) { 'task:expression' } + + before :each do + Cape::Capistrano.stub!(:new).and_return mock_capistrano + Cape::Rake. stub!(:new).and_return mock_rake + subject.deprecation.stream = StringIO.new + end + + describe '-- when sent #each_rake_task --' do + def do_each_rake_task(&block) + subject.each_rake_task(task_expression, &block) + end + + it 'should delegate to Rake#each_task' do + mock_rake.should_receive(:each_task). + with(task_expression). + and_yield({:name => task_expression}) + do_each_rake_task do |t| + t.should == {:name => task_expression} + end + end + + it 'should return itself' do + do_each_rake_task.should == subject + end + end + + describe '-- when sent #mirror_rake_tasks' do + before :each do + mock_rake.stub!(:each_task).and_yield({:name => task_expression}) + subject.stub! :raise_unless_capistrano + end + + def do_mirror_rake_tasks(*arguments, &block) + subject.mirror_rake_tasks(*arguments, &block) + end + + let(:deprecation_preamble) { + Cape::XTerm.bold_and_foreground_red('*** DEPRECATED:') + ' ' + } + + describe 'with two scalar arguments --' do + specify do + lambda { + do_mirror_rake_tasks task_expression, task_expression + }.should raise_error(ArgumentError, + 'wrong number of arguments (2 for 0 or 1, plus ' + + 'an options hash)') + end + end + + shared_examples_for "a successful call (#{Cape::DSLDeprecated.name})" do |task_expression_in_use, + options_in_use| + specify 'by collecting Rake#each_task' do + mock_rake.should_receive(:each_task).with task_expression_in_use + do_mirror_rake_tasks + end + + specify 'by verifying that Capistrano is in use' do + subject.should_receive :raise_unless_capistrano + do_mirror_rake_tasks + end + + describe 'by sending Capistrano#define_rake_wrapper for Rake#each_task' do + specify 'with the expected task' do + mock_capistrano.should_receive(:define_rake_wrapper).with do |task, + named_arguments| + task == {:name => task_expression} + end + do_mirror_rake_tasks + end + + specify 'with the expected named options' do + mock_capistrano.should_receive(:define_rake_wrapper).with do |task, + named_arguments| + named_arguments.keys.sort == ([:binding] + options_in_use.keys).sort + end + do_mirror_rake_tasks + end + + specify 'with a :binding option' do + mock_capistrano.should_receive(:define_rake_wrapper).with do |task, + named_arguments| + named_arguments[:binding].is_a? Binding + end + do_mirror_rake_tasks + end + + specify 'with any provided extra options' do + mock_capistrano.should_receive(:define_rake_wrapper).with do |task, + named_arguments| + named_arguments.slice(*options_in_use.keys) == options_in_use + end + do_mirror_rake_tasks + end + end + + specify 'by returning itself' do + do_mirror_rake_tasks.should == subject + end + end + + describe 'with one scalar argument, an options hash, and a block --' do + def do_mirror_rake_tasks + super 'task:expression', :bar => :baz do |env| + env['AN_ENV_VAR'] = 'an environment variable' + end + end + + should_behave_like "a successful call (#{Cape::DSLDeprecated.name})", + 'task:expression', + :bar => :baz + + it 'should print the expected deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks "task:expression", ' + + ':bar => :baz' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks("task:expression") { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + + "\n" + end + end + + describe 'with one scalar argument and an options hash --' do + def do_mirror_rake_tasks + super 'task:expression', :bar => :baz + end + + should_behave_like "a successful call (#{Cape::DSLDeprecated.name})", + 'task:expression', + :bar => :baz + + it 'should print the expected deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks "task:expression", ' + + ':bar => :baz' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks("task:expression") { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + + "\n" + end + end + + describe 'with an options hash and a block --' do + def do_mirror_rake_tasks + super :bar => :baz do |env| + env['AN_ENV_VAR'] = 'an environment variable' + end + end + + should_behave_like "a successful call (#{Cape::DSLDeprecated.name})", + nil, + :bar => :baz + + it 'should print the expected deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks :bar => :baz' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + + "\n" + end + end + + describe 'with an options hash --' do + def do_mirror_rake_tasks + super :bar => :baz + end + + should_behave_like "a successful call (#{Cape::DSLDeprecated.name})", + nil, + :bar => :baz + + it 'should print the expected deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should == deprecation_preamble + + Cape::XTerm.bold('`' + + 'mirror_rake_tasks :bar => :baz' + + '`. ' + + 'Use this instead: ' + + '`' + + 'mirror_rake_tasks { |recipes| ' + + 'recipes.options[:bar] = :baz ' + + '}' + + '`') + + "\n" + end + end + + describe 'with one scalar argument and a block --' do + def do_mirror_rake_tasks + super 'task:expression' do |env| + env['AN_ENV_VAR'] = 'an environment variable' + end + end + + should_behave_like("a successful call (#{Cape::DSLDeprecated.name})", + 'task:expression', + {}) + + it 'should print no deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should be_empty + end + end + + describe 'with one scalar argument --' do + def do_mirror_rake_tasks + super 'task:expression' + end + + should_behave_like("a successful call (#{Cape::DSLDeprecated.name})", + 'task:expression', + {}) + + it 'should print no deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should be_empty + end + end + + describe 'without arguments --' do + def do_mirror_rake_tasks + super + end + + should_behave_like("a successful call (#{Cape::DSLDeprecated.name})", + nil, + {}) + + it 'should print no deprecation messages to stderr' do + do_mirror_rake_tasks + subject.deprecation.stream.string.should be_empty + end + end + end + + describe '-- when sent #local_rake_executable --' do + before :each do + mock_rake.stub!(:local_executable).and_return 'foo' + end + + it 'should delegate to Rake#local_executable' do + mock_rake.should_receive :local_executable + subject.local_rake_executable + end + + it 'should return the result of Rake#local_executable' do + subject.local_rake_executable.should == 'foo' + end + end + + describe '-- when sent #remote_rake_executable --' do + before :each do + mock_rake.stub!(:remote_executable).and_return 'foo' + end + + it 'should delegate to Rake#remote_executable' do + mock_rake.should_receive :remote_executable + subject.remote_rake_executable + end + + it 'should return the result of Rake#remote_executable' do + subject.remote_rake_executable.should == 'foo' + end + end +end diff --git a/spec/cape/dsl_spec.rb b/spec/cape/dsl_spec.rb index 0c82d45..90870c4 100644 --- a/spec/cape/dsl_spec.rb +++ b/spec/cape/dsl_spec.rb @@ -57,13 +57,12 @@ def do_mirror_rake_tasks(*arguments, &block) lambda { do_mirror_rake_tasks task_expression, task_expression }.should raise_error(ArgumentError, - 'wrong number of arguments (2 for 0 or 1, plus ' + - 'an options hash)') + 'wrong number of arguments (2 for 1)') end end - shared_examples_for 'a successful call' do |task_expression_in_use, - options_in_use| + shared_examples_for "a successful call (#{Cape::DSL.name})" do |task_expression_in_use, + options_in_use| specify 'by collecting Rake#each_task' do mock_rake.should_receive(:each_task).with task_expression_in_use do_mirror_rake_tasks @@ -113,42 +112,6 @@ def do_mirror_rake_tasks(*arguments, &block) end end - describe 'with one scalar argument, an options hash, and a block --' do - def do_mirror_rake_tasks - super 'task:expression', :bar => :baz do |env| - env['AN_ENV_VAR'] = 'an environment variable' - end - end - - should_behave_like 'a successful call', 'task:expression', :bar => :baz - end - - describe 'with one scalar argument and an options hash --' do - def do_mirror_rake_tasks - super 'task:expression', :bar => :baz - end - - should_behave_like 'a successful call', 'task:expression', :bar => :baz - end - - describe 'with an options hash and a block --' do - def do_mirror_rake_tasks - super :bar => :baz do |env| - env['AN_ENV_VAR'] = 'an environment variable' - end - end - - should_behave_like 'a successful call', nil, :bar => :baz - end - - describe 'with an options hash --' do - def do_mirror_rake_tasks - super :bar => :baz - end - - should_behave_like 'a successful call', nil, :bar => :baz - end - describe 'with one scalar argument and a block --' do def do_mirror_rake_tasks super 'task:expression' do |env| @@ -156,7 +119,9 @@ def do_mirror_rake_tasks end end - should_behave_like('a successful call', 'task:expression', {}) + should_behave_like("a successful call (#{Cape::DSL.name})", + 'task:expression', + {}) end describe 'with one scalar argument --' do @@ -164,7 +129,9 @@ def do_mirror_rake_tasks super 'task:expression' end - should_behave_like('a successful call', 'task:expression', {}) + should_behave_like("a successful call (#{Cape::DSL.name})", + 'task:expression', + {}) end describe 'without arguments --' do @@ -172,7 +139,7 @@ def do_mirror_rake_tasks super end - should_behave_like('a successful call', nil, {}) + should_behave_like("a successful call (#{Cape::DSL.name})", nil, {}) end end diff --git a/spec/cape/recipe_definition_spec.rb b/spec/cape/recipe_definition_spec.rb new file mode 100644 index 0000000..d6b29cd --- /dev/null +++ b/spec/cape/recipe_definition_spec.rb @@ -0,0 +1,53 @@ +require 'cape/recipe_definition' + +describe Cape::RecipeDefinition do + its(:cd) { should be_nil } + + it 'should have a mutable #cd' do + subject.cd '/foo/bar' + subject.cd.should == '/foo/bar' + + subject.cd lambda { '/foo/bar' } + subject.cd.call.should == '/foo/bar' + + subject.cd { '/foo/bar' } + subject.cd.call.should == '/foo/bar' + end + + it 'should complain about a #cd with the wrong arity' do + expect { + subject.cd do |foo, bar| + end + }.to raise_error(ArgumentError, 'Must have 0 parameters but has 2') + end + + its(:env) { should == {} } + + it 'should have a mutable #env' do + subject.env['FOO'] = 'bar' + subject.env.should == {'FOO' => 'bar'} + end + + its(:options) { should == {} } + + it 'should have mutable #options' do + subject.options[:some_option] = 'foo' + subject.options.should == {:some_option => 'foo'} + end + + its(:rename) { should be_nil } + + it 'should have a mutable #rename' do + subject.rename do |task_name| + "#{task_name}_recipe" + end + subject.rename.call(:foo).should == 'foo_recipe' + end + + it 'should complain about a #rename with the wrong arity' do + expect { + subject.rename do |foo, bar| + end + }.to raise_error(ArgumentError, 'Must have 1 parameter but has 2') + end +end diff --git a/spec/cape/xterm_spec.rb b/spec/cape/xterm_spec.rb new file mode 100644 index 0000000..a7e85c1 --- /dev/null +++ b/spec/cape/xterm_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' +require 'cape/xterm' + +describe Cape::XTerm do + describe '.format' do + it 'should not try to format a nil argument' do + described_class.format(nil).should be_nil + end + + it 'should not try to format a nil argument with a recognized format' do + described_class.format(nil, :bold).should be_nil + end + + it 'should complain about an unrecognized format' do + expect { + described_class.format nil, :this_does_not_exist + }.to raise_error(ArgumentError, 'Unrecognized format :this_does_not_exist') + expect { + described_class.format 'foo', :this_does_not_exist + }.to raise_error(ArgumentError, 'Unrecognized format :this_does_not_exist') + end + + described_class::FORMATS.each do |format, code| + it "should format a String argument with the #{format.inspect} format" do + described_class.format('foo', format).should == "\e[#{code}mfoo\e[0m" + end + end + + it "should format a String argument with the :bold and :foreground_red formats" do + described_class.format('foo', + :bold, + :foreground_red).should == "\e[1;31mfoo\e[0m" + end + end + + it 'should not respond to :this_does_not_exist' do + described_class.should_not respond_to(:this_does_not_exist) + end + + it '.this_does_not_exist should complain' do + expect { + described_class.this_does_not_exist + }.to raise_error(NoMethodError, + "undefined method `this_does_not_exist' for #{described_class.name}:Module") + end + + described_class::FORMATS.each do |format, code| + it "should respond to .#{format.inspect}" do + described_class.should respond_to(format) + end + + describe ".#{format}" do + it "should format a String argument with the #{format.inspect} format" do + described_class.send(format, + 'foo').should == described_class.format('foo', + format) + end + end + end + + it 'should respond to .bold_and_foreground_red' do + described_class.should respond_to(:bold_and_foreground_red) + end + + describe '.bold_and_foreground_red' do + it "should format a String argument with the :bold and :foreground_red formats" do + described_class.bold_and_foreground_red('foo').should == described_class.format('foo', + :bold, + :foreground_red) + end + end +end