cape 1.6.2 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/Gemfile +7 -6
  2. data/Guardfile +29 -10
  3. data/History.markdown +4 -0
  4. data/README.markdown +38 -9
  5. data/Rakefile +35 -8
  6. data/features/dsl/each_rake_task/{without_arguments.feature → unqualified.feature} +16 -37
  7. data/features/dsl/each_rake_task/{with_defined_namespace_argument.feature → with_defined_namespace.feature} +2 -13
  8. data/features/dsl/each_rake_task/{with_defined_task_argument.feature → with_defined_task.feature} +2 -6
  9. data/features/dsl/each_rake_task/with_undefined_task_or_namespace.feature +24 -0
  10. data/features/dsl/mirror_rake_tasks/inside_capistrano_namespace.feature +2 -2
  11. data/features/dsl/mirror_rake_tasks/{without_arguments.feature → unqualified.feature} +15 -11
  12. data/features/dsl/mirror_rake_tasks/with_cd.feature +63 -0
  13. data/features/dsl/mirror_rake_tasks/with_cd_and_environment_variables.feature +27 -0
  14. data/features/dsl/mirror_rake_tasks/{with_defined_namespace_argument.feature → with_defined_namespace.feature} +6 -8
  15. data/features/dsl/mirror_rake_tasks/{with_defined_task_argument.feature → with_defined_task.feature} +4 -4
  16. data/features/dsl/mirror_rake_tasks/{with_defined_task_argument_and_environment_variables.feature → with_defined_task_and_cd.feature} +8 -8
  17. data/features/dsl/mirror_rake_tasks/with_defined_task_and_cd_and_environment_variables.feature +46 -0
  18. data/features/dsl/mirror_rake_tasks/with_defined_task_and_environment_variables.feature +92 -0
  19. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename.feature +47 -0
  20. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd.feature +49 -0
  21. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_cd_and_environment_variables.feature +52 -0
  22. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_environment_variables.feature +50 -0
  23. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options.feature +49 -0
  24. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd.feature +51 -0
  25. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_cd_and_environment_variables.feature +54 -0
  26. data/features/dsl/mirror_rake_tasks/with_defined_task_and_rename_and_valid_options_and_environment_variables.feature +52 -0
  27. data/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options.feature +53 -6
  28. data/features/dsl/mirror_rake_tasks/{with_defined_task_and_valid_options_arguments_and_environment_variables.feature → with_defined_task_and_valid_options_and_cd.feature} +10 -8
  29. data/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_cd_and_environment_variables.feature +48 -0
  30. data/features/dsl/mirror_rake_tasks/with_defined_task_and_valid_options_and_environment_variables.feature +98 -0
  31. data/features/dsl/mirror_rake_tasks/with_environment_variables.feature +61 -4
  32. data/features/dsl/mirror_rake_tasks/with_rename.feature +27 -0
  33. data/features/dsl/mirror_rake_tasks/with_rename_and_cd.feature +28 -0
  34. data/features/dsl/mirror_rake_tasks/with_rename_and_cd_and_environment_variables.feature +30 -0
  35. data/features/dsl/mirror_rake_tasks/with_rename_and_environment_variables.feature +29 -0
  36. data/features/dsl/mirror_rake_tasks/with_rename_and_valid_options.feature +28 -0
  37. data/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd.feature +29 -0
  38. data/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_cd_and_environment_variables.feature +31 -0
  39. data/features/dsl/mirror_rake_tasks/with_rename_and_valid_options_and_environment_variables.feature +30 -0
  40. data/features/dsl/mirror_rake_tasks/{with_undefined_argument.feature → with_undefined_task_or_namespace.feature} +3 -4
  41. data/features/dsl/mirror_rake_tasks/with_valid_options.feature +46 -0
  42. data/features/dsl/mirror_rake_tasks/{with_valid_options_argument.feature → with_valid_options_and_cd.feature} +7 -5
  43. data/features/dsl/mirror_rake_tasks/with_valid_options_and_cd_and_environment_variables.feature +28 -0
  44. data/features/dsl/mirror_rake_tasks/with_valid_options_and_environment_variables.feature +51 -0
  45. data/features/dsl/rake_executable.feature +2 -2
  46. data/features/step_definitions.rb +40 -0
  47. data/lib/cape.rb +2 -0
  48. data/lib/cape/capistrano.rb +59 -32
  49. data/lib/cape/capistrano_deprecated.rb +165 -0
  50. data/lib/cape/core_ext.rb +2 -0
  51. data/lib/cape/deprecation.rb +12 -0
  52. data/lib/cape/deprecation/base.rb +59 -0
  53. data/lib/cape/deprecation/capistrano_deprecated_define_rake_wrapper.rb +168 -0
  54. data/lib/cape/deprecation/dsl_deprecated_mirror_rake_tasks.rb +145 -0
  55. data/lib/cape/dsl.rb +49 -60
  56. data/lib/cape/dsl_deprecated.rb +157 -0
  57. data/lib/cape/hash_list.rb +2 -0
  58. data/lib/cape/recipe_definition.rb +103 -0
  59. data/lib/cape/recipe_definition_deprecated.rb +41 -0
  60. data/lib/cape/util.rb +2 -0
  61. data/lib/cape/version.rb +1 -1
  62. data/lib/cape/xterm.rb +326 -0
  63. data/spec/cape/deprecation/base_sharedspec.rb +18 -0
  64. data/spec/cape/deprecation/capistrano_deprecated_define_rake_wrapper_spec.rb +157 -0
  65. data/spec/cape/deprecation/dsl_deprecated_mirror_rake_tasks_spec.rb +153 -0
  66. data/spec/cape/dsl_deprecated_spec.rb +307 -0
  67. data/spec/cape/dsl_spec.rb +10 -43
  68. data/spec/cape/recipe_definition_spec.rb +53 -0
  69. data/spec/cape/xterm_spec.rb +72 -0
  70. metadata +97 -28
  71. data/features/dsl/each_rake_task/with_undefined_argument.feature +0 -53
  72. data/features/dsl/mirror_rake_tasks/with_valid_options_arguments_and_environment_variables.feature +0 -26
@@ -1,4 +1,4 @@
1
- Feature: The #mirror_rake_tasks DSL method with an undefined argument
1
+ Feature: The #mirror_rake_tasks DSL method with an undefined task or namespace
2
2
 
3
3
  In order to include Rake tasks with descriptions in my Capistrano recipes,
4
4
  As a developer using Cape,
@@ -9,9 +9,8 @@ Feature: The #mirror_rake_tasks DSL method with an undefined argument
9
9
  And a Capfile with:
10
10
  """
11
11
  Cape do
12
- mirror_rake_tasks :this_does_not_exist
12
+ mirror_rake_tasks 'period'
13
13
  end
14
14
  """
15
15
  When I run `cap -vT`
16
- Then the output should not contain "cap this_does_not_exist"
17
- And the output should not contain "cap with_period"
16
+ Then the output should not contain "period"
@@ -0,0 +1,46 @@
1
+ Feature: The #mirror_rake_tasks DSL method with valid options
2
+
3
+ In order to include Rake tasks with descriptions in my Capistrano recipes,
4
+ As a developer using Cape,
5
+ I want to use the Cape DSL.
6
+
7
+ @deprecated
8
+ Scenario: mirror a Rake task with its implementation (deprecated)
9
+ Given a full-featured Rakefile
10
+ And a Capfile with:
11
+ """
12
+ set :current_path, '/current/path'
13
+
14
+ Cape do
15
+ mirror_rake_tasks :roles => :app
16
+ end
17
+ """
18
+ When I run `cap with_period`
19
+ Then the output should contain:
20
+ """
21
+ *** DEPRECATED: `mirror_rake_tasks :roles => :app`. Use this instead: `mirror_rake_tasks { |recipes| recipes.options[:roles] = :app }`
22
+ * executing `with_period'
23
+ * 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"
24
+ `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
25
+ """
26
+
27
+ Scenario: mirror a Rake task with its implementation
28
+ Given a full-featured Rakefile
29
+ And a Capfile with:
30
+ """
31
+ set :current_path, '/current/path'
32
+
33
+ Cape do
34
+ mirror_rake_tasks do |recipes|
35
+ recipes.options[:roles] = :app
36
+ end
37
+ end
38
+ """
39
+ When I run `cap with_period`
40
+ Then the output should contain:
41
+ """
42
+ * executing `with_period'
43
+ * 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"
44
+ `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
45
+ """
46
+ And the output should not contain "DEPRECATED"
@@ -1,4 +1,4 @@
1
- Feature: The #mirror_rake_tasks DSL method with an argument of valid options
1
+ Feature: The #mirror_rake_tasks DSL method with valid options and a different directory
2
2
 
3
3
  In order to include Rake tasks with descriptions in my Capistrano recipes,
4
4
  As a developer using Cape,
@@ -8,17 +8,19 @@ Feature: The #mirror_rake_tasks DSL method with an argument of valid options
8
8
  Given a full-featured Rakefile
9
9
  And a Capfile with:
10
10
  """
11
- set :current_path, '/path/to/current/deployed/application'
12
- set :rails_env, 'production'
11
+ set :release_path, '/release/path'
13
12
 
14
13
  Cape do
15
- mirror_rake_tasks :roles => :app
14
+ mirror_rake_tasks do |recipes|
15
+ recipes.options[:roles] = :app
16
+ recipes.cd { release_path }
17
+ end
16
18
  end
17
19
  """
18
20
  When I run `cap with_period`
19
21
  Then the output should contain:
20
22
  """
21
23
  * executing `with_period'
22
- * executing "cd /path/to/current/deployed/application && /usr/bin/env `/usr/bin/env bundle check >/dev/null 2>&1; case $? in 0|1 ) echo bundle exec ;; esac` rake with_period"
24
+ * 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"
23
25
  `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
24
26
  """
@@ -0,0 +1,28 @@
1
+ Feature: The #mirror_rake_tasks DSL method with valid options, a different directory, and environment variables
2
+
3
+ In order to include Rake tasks with descriptions in my Capistrano recipes,
4
+ As a developer using Cape,
5
+ I want to use the Cape DSL.
6
+
7
+ Scenario: mirror the matching Rake task with its implementation
8
+ Given a full-featured Rakefile
9
+ And a Capfile with:
10
+ """
11
+ set :release_path, '/release/path'
12
+ set :rails_env, 'rails-env'
13
+
14
+ Cape do
15
+ mirror_rake_tasks do |recipes|
16
+ recipes.options[:roles] = :app
17
+ recipes.cd { release_path }
18
+ recipes.env['RAILS_ENV'] = lambda { rails_env }
19
+ end
20
+ end
21
+ """
22
+ When I run `cap with_period`
23
+ Then the output should contain:
24
+ """
25
+ * executing `with_period'
26
+ * 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\""
27
+ `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
28
+ """
@@ -0,0 +1,51 @@
1
+ Feature: The #mirror_rake_tasks DSL method with valid options and environment variables
2
+
3
+ In order to include Rake tasks with descriptions in my Capistrano recipes,
4
+ As a developer using Cape,
5
+ I want to use the Cape DSL.
6
+
7
+ @deprecated
8
+ Scenario: mirror the matching Rake task with its implementation (deprecated)
9
+ Given a full-featured Rakefile
10
+ And a Capfile with:
11
+ """
12
+ set :current_path, '/current/path'
13
+ set :rails_env, 'rails-env'
14
+
15
+ Cape do
16
+ mirror_rake_tasks :roles => :app do |env|
17
+ env['RAILS_ENV'] = rails_env
18
+ end
19
+ end
20
+ """
21
+ When I run `cap with_period`
22
+ Then the output should contain:
23
+ """
24
+ *** 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" }`
25
+ * executing `with_period'
26
+ * 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\""
27
+ `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
28
+ """
29
+
30
+ Scenario: mirror the matching Rake task with its implementation
31
+ Given a full-featured Rakefile
32
+ And a Capfile with:
33
+ """
34
+ set :current_path, '/current/path'
35
+ set :rails_env, 'rails-env'
36
+
37
+ Cape do
38
+ mirror_rake_tasks do |recipes|
39
+ recipes.options[:roles] = :app
40
+ recipes.env['RAILS_ENV'] = lambda { rails_env }
41
+ end
42
+ end
43
+ """
44
+ When I run `cap with_period`
45
+ Then the output should contain:
46
+ """
47
+ * executing `with_period'
48
+ * 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\""
49
+ `with_period' is only run for servers matching {:roles=>:app}, but no servers matched
50
+ """
51
+ And the output should not contain "DEPRECATED"
@@ -44,7 +44,7 @@ Feature: The #local_rake_executable and #remote_rake_executable DSL attributes
44
44
  Given a full-featured Rakefile
45
45
  And a Capfile with:
46
46
  """
47
- set :current_path, '/path/to/current/deployed/application'
47
+ set :current_path, '/current/path'
48
48
 
49
49
  Cape.remote_rake_executable = 'echo "This comes from overridden Rake" #'
50
50
 
@@ -66,5 +66,5 @@ Feature: The #local_rake_executable and #remote_rake_executable DSL attributes
66
66
  And the output should contain:
67
67
  """
68
68
  * executing `with_period'
69
- * executing "cd /path/to/current/deployed/application && echo \"This comes from overridden Rake\" # with_period"
69
+ * executing "cd /current/path && echo \"This comes from overridden Rake\" # with_period"
70
70
  """
@@ -43,3 +43,43 @@ Given 'a full-featured Rakefile' do
43
43
  task :hidden_task
44
44
  end_step
45
45
  end
46
+
47
+ Given 'a full-featured Rakefile defining a Ruby-method-shadowing task' do
48
+ step 'a file named "Rakefile" with:', <<-end_step
49
+ desc 'A task that shadows a Ruby method'
50
+ task :load
51
+
52
+ desc 'Ends with period.'
53
+ task :with_period
54
+
55
+ desc 'Ends without period'
56
+ task :without_period
57
+
58
+ 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'
59
+ task :long
60
+
61
+ desc 'My task with one argument'
62
+ task :with_one_arg, [:the_arg]
63
+
64
+ desc 'A task that shadows a namespace'
65
+ task :my_namespace
66
+
67
+ namespace :my_namespace do
68
+ desc 'My task in a namespace'
69
+ task :in_a_namespace
70
+
71
+ namespace :my_nested_namespace do
72
+ desc 'My task in a nested namespace'
73
+ task :in_a_nested_namespace
74
+ end
75
+ end
76
+
77
+ desc 'My task with two arguments'
78
+ task :with_two_args, [:my_arg1, :my_arg2]
79
+
80
+ desc 'My task with three arguments'
81
+ task :with_three_args, [:an_arg1, :an_arg2, :an_arg3]
82
+
83
+ task :hidden_task
84
+ end_step
85
+ end
@@ -6,12 +6,14 @@ end
6
6
  module Cape
7
7
 
8
8
  extend DSL
9
+ extend DSLDeprecated
9
10
 
10
11
  end
11
12
 
12
13
  # The method used to group Cape statements.
13
14
  #
14
15
  # @param [Proc] block Cape and Capistrano statements
16
+ #
15
17
  # @return [Cape] the Cape module
16
18
  #
17
19
  # @yield [cape] a block containing Cape statements
@@ -1,5 +1,5 @@
1
- require 'cape/hash_list'
2
1
  require 'cape/rake'
2
+ require 'cape/recipe_definition'
3
3
  require 'cape/util'
4
4
 
5
5
  module Cape
@@ -17,14 +17,13 @@ module Cape
17
17
  attributes.each do |name, value|
18
18
  send "#{name}=", value
19
19
  end
20
- self.rake ||= Rake.new
20
+ self.rake ||= new_rake
21
21
  end
22
22
 
23
23
  # Defines a wrapper in Capistrano around the specified Rake _task_.
24
24
  #
25
25
  # @param [Hash] task metadata for a Rake task
26
- # @param [Hash] named_arguments named arguments, including options to pass to
27
- # the Capistrano +task+ method
26
+ # @param [Hash] named_arguments
28
27
  #
29
28
  # @option task [String] :name the name of the Rake task
30
29
  # @option task [Array of String, nil] :parameters the names of the Rake
@@ -32,33 +31,28 @@ module Cape
32
31
  # @option task [String] :description documentation for the Rake
33
32
  # task
34
33
  #
35
- # @option named_arguments [Binding] :binding the Binding of your
36
- # Capistrano recipes
37
- # file
34
+ # @option named_arguments [Binding] :binding the Binding of your Capistrano
35
+ # recipes file
38
36
  #
39
- # @yield [env] a block that defines environment variables for the Rake task;
40
- # optional
41
- # @yieldparam [Hash] env the environment variables to set before executing
42
- # the Rake task
37
+ # @yield [recipes] a block that customizes the Capistrano recipe(s)
38
+ # generated for the Rake task(s); optional
39
+ # @yieldparam [RecipeDefinition] recipes an interface for customizing the
40
+ # Capistrano recipe(s) generated for
41
+ # the Rake task(s)
43
42
  #
44
43
  # @return [Capistrano] the object
45
44
  #
46
45
  # @raise [ArgumentError] +named_arguments[:binding]+ is missing
47
46
  #
48
47
  # @note Any parameters that the Rake task has are integrated via environment variables, since Capistrano does not support recipe parameters per se.
49
- #
50
- # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano ‘task’ method options
51
48
  def define_rake_wrapper(task, named_arguments, &block)
52
49
  unless (binding = named_arguments[:binding])
53
50
  raise ::ArgumentError, ':binding named argument is required'
54
51
  end
55
52
 
56
53
  capistrano_context = binding.eval('self', __FILE__, __LINE__)
57
- options = named_arguments.reject do |key, value|
58
- key == :binding
59
- end
60
54
  describe task, capistrano_context
61
- implement(task, capistrano_context, options, &block)
55
+ implement(task, capistrano_context, &block)
62
56
  end
63
57
 
64
58
  private
@@ -88,6 +82,11 @@ Set environment #{noun} #{parameters_list} if you want to pass #{noun_phrase}.
88
82
  description.join
89
83
  end
90
84
 
85
+ def capture_recipe_definition(recipe_definition, &recipe_definition_block)
86
+ recipe_definition_block.call(recipe_definition) if recipe_definition_block
87
+ true
88
+ end
89
+
91
90
  def describe(task, capistrano_context)
92
91
  if (description = build_capistrano_description(task))
93
92
  capistrano_context.desc description
@@ -95,13 +94,18 @@ Set environment #{noun} #{parameters_list} if you want to pass #{noun_phrase}.
95
94
  self
96
95
  end
97
96
 
98
- def implement(task, capistrano_context, options, &env_block)
99
- name_tokens = task[:name].split(':')
100
- name_tokens << 'default' if task[:default]
97
+ def implement(task, capistrano_context, &recipe_definition_block)
98
+ name_tokens = tokenize_name(task)
99
+ recipe_definition = new_recipe_definition
100
+ capture_recipe_definition(recipe_definition, &recipe_definition_block)
101
+ env = nil
101
102
  rake = self.rake
102
- # Define the recipe.
103
103
  block = lambda { |context|
104
- context.task name_tokens.last, options do
104
+ recipe_name = name_tokens.last
105
+ if recipe_definition.rename
106
+ recipe_name = recipe_definition.rename.call(name_tokens.last)
107
+ end
108
+ context.task recipe_name, recipe_definition.options do
105
109
  arguments = Array(task[:parameters]).collect do |a|
106
110
  if (value = ENV[a.upcase])
107
111
  value = value.inspect
@@ -113,17 +117,26 @@ Set environment #{noun} #{parameters_list} if you want to pass #{noun_phrase}.
113
117
  else
114
118
  arguments = "[#{arguments.join ','}]"
115
119
  end
116
- env_hash = HashList.new
117
- env_block.call(env_hash) if env_block
118
- env_hash.reject! do |var_name, var_value|
119
- var_name.nil? || var_value.nil?
120
- end
121
- env_strings = env_hash.collect do |var_name, var_value|
122
- "#{var_name}=#{var_value.inspect}"
120
+
121
+ unless env
122
+ env_strings = recipe_definition.env.collect do |var_name, var_value|
123
+ if var_name.nil? || var_value.nil?
124
+ nil
125
+ else
126
+ if var_value.is_a?(Proc)
127
+ var_value = context.instance_eval do
128
+ var_value.call
129
+ end
130
+ end
131
+ "#{var_name}=#{var_value.inspect}"
132
+ end
133
+ end.compact
134
+ env = env_strings.empty? ? nil : (' ' + env_strings.join(' '))
123
135
  end
124
- env = env_strings.empty? ? nil : (' ' + env_strings.join(' '))
125
- command = "cd #{context.current_path} && " +
126
- "#{rake.remote_executable} " +
136
+
137
+ path = recipe_definition.cd || context.current_path
138
+ path = path.call if path.respond_to?(:call)
139
+ command = "cd #{path} && #{rake.remote_executable} " +
127
140
  "#{task[:name]}#{arguments}#{env}"
128
141
  context.run command
129
142
  end
@@ -139,6 +152,20 @@ Set environment #{noun} #{parameters_list} if you want to pass #{noun_phrase}.
139
152
  self
140
153
  end
141
154
 
155
+ def new_rake(*arguments)
156
+ Rake.new(*arguments)
157
+ end
158
+
159
+ def new_recipe_definition(*arguments)
160
+ RecipeDefinition.new(*arguments)
161
+ end
162
+
163
+ def tokenize_name(task)
164
+ task[:name].split(':').tap do |result|
165
+ result << 'default' if task[:default]
166
+ end
167
+ end
168
+
142
169
  end
143
170
 
144
171
  end
@@ -0,0 +1,165 @@
1
+ require 'cape/capistrano'
2
+ require 'cape/deprecation/capistrano_deprecated_define_rake_wrapper'
3
+ require 'cape/recipe_definition_deprecated'
4
+ require 'cape/xterm'
5
+
6
+ module Cape
7
+
8
+ # Implements {Capistrano} with deprecated methods.
9
+ #
10
+ # @api private
11
+ class CapistranoDeprecated < Capistrano
12
+
13
+ # Defines a wrapper in Capistrano around the specified Rake _task_.
14
+ #
15
+ # @deprecated Use {Capistrano#define_rake_wrapper} instead.
16
+ #
17
+ # @param [Hash] task metadata for a Rake task
18
+ # @param [Hash] named_arguments named arguments, including options to pass to
19
+ # the Capistrano +task+ method
20
+ #
21
+ # @option task [String] :name the name of the Rake task
22
+ # @option task [Array of String, nil] :parameters the names of the Rake
23
+ # task's parameters, if any
24
+ # @option task [String] :description documentation for the Rake
25
+ # task
26
+ #
27
+ # @option named_arguments [Binding] :binding the Binding of your
28
+ # Capistrano recipes
29
+ # file
30
+ #
31
+ # @yield [env] a block that defines environment variables for the Rake task;
32
+ # optional
33
+ # @yieldparam [Hash] env the environment variables to set before executing
34
+ # the Rake task
35
+ #
36
+ # @return [CapistranoDeprecated] the object
37
+ #
38
+ # @raise [ArgumentError] +named_arguments[:binding]+ is missing
39
+ #
40
+ # @note Any parameters that the Rake task has are integrated via environment variables, since Capistrano does not support recipe parameters per se.
41
+ #
42
+ # @see http://github.com/capistrano/capistrano/blob/master/lib/capistrano/configuration/actions/invocation.rb#L99-L144 Valid Capistrano 'task' method options
43
+ #
44
+ # @api public
45
+ def define_rake_wrapper(task, named_arguments, &block)
46
+ unless (binding = named_arguments[:binding])
47
+ raise ::ArgumentError, ':binding named argument is required'
48
+ end
49
+
50
+ deprecation.task = task
51
+ deprecation.named_arguments = named_arguments
52
+
53
+ capistrano_context = binding.eval('self', __FILE__, __LINE__)
54
+ options = named_arguments.reject do |key, value|
55
+ key == :binding
56
+ end
57
+ describe task, capistrano_context
58
+ implement(task, capistrano_context, options, &block)
59
+ end
60
+
61
+ # The object in which deprecated API usage is recorded.
62
+ #
63
+ # @return [Deprecation::Base]
64
+ def deprecation
65
+ @deprecation ||= Deprecation::CapistranoDeprecatedDefineRakeWrapper.new
66
+ end
67
+
68
+ private
69
+
70
+ def capture_recipe_definition(recipe_definition, &recipe_definition_block)
71
+ begin
72
+ super
73
+ rescue NoMethodError
74
+ unless @warned
75
+ deprecation.stream.puts XTerm.bold_and_foreground_red('*** DEPRECATED:') +
76
+ ' ' +
77
+ XTerm.bold('Referencing Capistrano variables ' +
78
+ 'from Cape without wrapping them in ' +
79
+ 'a block, a lambda, or another ' +
80
+ 'callable object')
81
+ @warned = true
82
+ end
83
+ return false
84
+ end
85
+ true
86
+ end
87
+
88
+ def implement(task, capistrano_context, options, &env_block)
89
+ return super(task, capistrano_context, &env_block) if options.empty?
90
+
91
+ name_tokens = tokenize_name(task)
92
+ recipe_definition = new_recipe_definition(deprecation)
93
+ env = nil
94
+ if capture_recipe_definition(recipe_definition, &env_block)
95
+ env_strings = recipe_definition.env.collect do |var_name, var_value|
96
+ if var_name.nil? || var_value.nil?
97
+ nil
98
+ else
99
+ var_value = var_value.call if var_value.is_a?(Proc)
100
+ "#{var_name}=#{var_value.inspect}"
101
+ end
102
+ end.compact
103
+ env = env_strings.empty? ? nil : (' ' + env_strings.join(' '))
104
+ end
105
+ this = env ? nil : self
106
+ rake = self.rake
107
+ block = lambda { |context|
108
+ recipe_name = name_tokens.last
109
+ if recipe_definition.rename
110
+ recipe_name = recipe_definition.rename.call(name_tokens.last)
111
+ end
112
+ context.task recipe_name, options do
113
+ arguments = Array(task[:parameters]).collect do |a|
114
+ if (value = ENV[a.upcase])
115
+ value = value.inspect
116
+ end
117
+ value
118
+ end
119
+ if arguments.empty?
120
+ arguments = nil
121
+ else
122
+ arguments = "[#{arguments.join ','}]"
123
+ end
124
+
125
+ unless env
126
+ if this.instance_eval { capture_recipe_definition(recipe_definition, &env_block) }
127
+ env_strings = recipe_definition.env.collect do |var_name, var_value|
128
+ if var_name.nil? || var_value.nil?
129
+ nil
130
+ else
131
+ var_value = var_value.call if var_value.is_a?(Proc)
132
+ "#{var_name}=#{var_value.inspect}"
133
+ end
134
+ end.compact
135
+ env = env_strings.empty? ? nil : (' ' + env_strings.join(' '))
136
+ this = nil
137
+ end
138
+ end
139
+
140
+ path = recipe_definition.cd || context.current_path
141
+ path = path.call if path.respond_to?(:call)
142
+ command = "cd #{path} && #{rake.remote_executable} " +
143
+ "#{task[:name]}#{arguments}#{env}"
144
+ context.run command
145
+ end
146
+ }
147
+ # Nest the recipe inside its containing namespaces.
148
+ name_tokens[0...-1].reverse.each do |namespace_token|
149
+ inner_block = block
150
+ block = lambda { |context|
151
+ context.namespace(namespace_token, &inner_block)
152
+ }
153
+ end
154
+ block.call capistrano_context
155
+ self
156
+ end
157
+
158
+ def new_recipe_definition(*arguments)
159
+ arguments << deprecation if arguments.empty?
160
+ RecipeDefinitionDeprecated.new(*arguments)
161
+ end
162
+
163
+ end
164
+
165
+ end