rake 11.3.0 → 13.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/macos.yml +22 -0
  3. data/.github/workflows/ubuntu-rvm.yml +28 -0
  4. data/.github/workflows/ubuntu.yml +20 -0
  5. data/.github/workflows/windows.yml +20 -0
  6. data/CONTRIBUTING.rdoc +11 -4
  7. data/Gemfile +8 -1
  8. data/History.rdoc +171 -37
  9. data/README.rdoc +9 -11
  10. data/Rakefile +21 -13
  11. data/bin/bundle +105 -0
  12. data/bin/rake +29 -0
  13. data/bin/rdoc +29 -0
  14. data/bin/rubocop +29 -0
  15. data/doc/jamis.rb +1 -0
  16. data/doc/rake.1 +139 -124
  17. data/doc/rakefile.rdoc +2 -4
  18. data/exe/rake +1 -1
  19. data/lib/rake/application.rb +119 -79
  20. data/lib/rake/backtrace.rb +3 -2
  21. data/lib/rake/clean.rb +7 -5
  22. data/lib/rake/cloneable.rb +1 -0
  23. data/lib/rake/cpu_counter.rb +3 -2
  24. data/lib/rake/default_loader.rb +1 -0
  25. data/lib/rake/dsl_definition.rb +5 -4
  26. data/lib/rake/early_time.rb +1 -0
  27. data/lib/rake/ext/core.rb +1 -0
  28. data/lib/rake/ext/string.rb +22 -21
  29. data/lib/rake/file_creation_task.rb +4 -3
  30. data/lib/rake/file_list.rb +13 -14
  31. data/lib/rake/file_task.rb +12 -4
  32. data/lib/rake/file_utils.rb +17 -22
  33. data/lib/rake/file_utils_ext.rb +8 -18
  34. data/lib/rake/invocation_chain.rb +1 -0
  35. data/lib/rake/invocation_exception_mixin.rb +1 -0
  36. data/lib/rake/late_time.rb +2 -1
  37. data/lib/rake/linked_list.rb +1 -0
  38. data/lib/rake/loaders/makefile.rb +5 -4
  39. data/lib/rake/multi_task.rb +2 -1
  40. data/lib/rake/name_space.rb +1 -1
  41. data/lib/rake/packagetask.rb +28 -16
  42. data/lib/rake/phony.rb +2 -1
  43. data/lib/rake/private_reader.rb +1 -0
  44. data/lib/rake/promise.rb +13 -12
  45. data/lib/rake/pseudo_status.rb +1 -0
  46. data/lib/rake/rake_module.rb +30 -1
  47. data/lib/rake/rake_test_loader.rb +18 -12
  48. data/lib/rake/rule_recursion_overflow_error.rb +2 -1
  49. data/lib/rake/scope.rb +3 -2
  50. data/lib/rake/task.rb +70 -27
  51. data/lib/rake/task_argument_error.rb +1 -0
  52. data/lib/rake/task_arguments.rb +10 -4
  53. data/lib/rake/task_manager.rb +54 -39
  54. data/lib/rake/tasklib.rb +2 -1
  55. data/lib/rake/testtask.rb +28 -16
  56. data/lib/rake/thread_history_display.rb +4 -3
  57. data/lib/rake/thread_pool.rb +15 -14
  58. data/lib/rake/trace_output.rb +1 -0
  59. data/lib/rake/version.rb +3 -2
  60. data/lib/rake/win32.rb +9 -8
  61. data/lib/rake.rb +34 -33
  62. data/rake.gemspec +22 -9
  63. metadata +25 -108
  64. data/.gitignore +0 -14
  65. data/.rubocop.yml +0 -18
  66. data/.travis.yml +0 -28
  67. data/appveyor.yml +0 -22
  68. data/doc/release_notes/README.md +0 -4
  69. data/doc/release_notes/rake-0.4.14.rdoc +0 -23
  70. data/doc/release_notes/rake-0.4.15.rdoc +0 -35
  71. data/doc/release_notes/rake-0.5.0.rdoc +0 -53
  72. data/doc/release_notes/rake-0.5.3.rdoc +0 -78
  73. data/doc/release_notes/rake-0.5.4.rdoc +0 -46
  74. data/doc/release_notes/rake-0.6.0.rdoc +0 -141
  75. data/doc/release_notes/rake-0.7.0.rdoc +0 -119
  76. data/doc/release_notes/rake-0.7.1.rdoc +0 -59
  77. data/doc/release_notes/rake-0.7.2.rdoc +0 -121
  78. data/doc/release_notes/rake-0.7.3.rdoc +0 -47
  79. data/doc/release_notes/rake-0.8.0.rdoc +0 -114
  80. data/doc/release_notes/rake-0.8.2.rdoc +0 -165
  81. data/doc/release_notes/rake-0.8.3.rdoc +0 -112
  82. data/doc/release_notes/rake-0.8.4.rdoc +0 -147
  83. data/doc/release_notes/rake-0.8.5.rdoc +0 -53
  84. data/doc/release_notes/rake-0.8.6.rdoc +0 -37
  85. data/doc/release_notes/rake-0.8.7.rdoc +0 -55
  86. data/doc/release_notes/rake-0.9.0.rdoc +0 -112
  87. data/doc/release_notes/rake-0.9.1.rdoc +0 -52
  88. data/doc/release_notes/rake-0.9.2.2.rdoc +0 -55
  89. data/doc/release_notes/rake-0.9.2.rdoc +0 -49
  90. data/doc/release_notes/rake-0.9.3.rdoc +0 -102
  91. data/doc/release_notes/rake-0.9.4.rdoc +0 -60
  92. data/doc/release_notes/rake-0.9.5.rdoc +0 -55
  93. data/doc/release_notes/rake-0.9.6.rdoc +0 -64
  94. data/doc/release_notes/rake-10.0.0.rdoc +0 -178
  95. data/doc/release_notes/rake-10.0.1.rdoc +0 -58
  96. data/doc/release_notes/rake-10.0.2.rdoc +0 -53
  97. data/doc/release_notes/rake-10.0.3.rdoc +0 -191
  98. data/doc/release_notes/rake-10.1.0.rdoc +0 -61
  99. data/lib/rake/contrib/compositepublisher.rb +0 -21
  100. data/lib/rake/contrib/ftptools.rb +0 -137
  101. data/lib/rake/contrib/sshpublisher.rb +0 -60
  102. data/lib/rake/ext/pathname.rb +0 -25
data/lib/rake/task.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'rake/invocation_exception_mixin'
1
+ # frozen_string_literal: true
2
+ require "rake/invocation_exception_mixin"
2
3
 
3
4
  module Rake
4
5
 
@@ -14,6 +15,10 @@ module Rake
14
15
  class Task
15
16
  # List of prerequisites for a task.
16
17
  attr_reader :prerequisites
18
+ alias prereqs prerequisites
19
+
20
+ # List of order only prerequisites for a task.
21
+ attr_reader :order_only_prerequisites
17
22
 
18
23
  # List of actions attached to a task.
19
24
  attr_reader :actions
@@ -54,7 +59,7 @@ module Rake
54
59
 
55
60
  # List of prerequisite tasks
56
61
  def prerequisite_tasks
57
- prerequisites.map { |pre| lookup_prerequisite(pre) }
62
+ (prerequisites + order_only_prerequisites).map { |pre| lookup_prerequisite(pre) }
58
63
  end
59
64
 
60
65
  def lookup_prerequisite(prerequisite_name) # :nodoc:
@@ -102,6 +107,8 @@ module Rake
102
107
  @scope = app.current_scope
103
108
  @arg_names = nil
104
109
  @locations = []
110
+ @invocation_exception = nil
111
+ @order_only_prerequisites = []
105
112
  end
106
113
 
107
114
  # Enhance a task with prerequisites or actions. Returns self.
@@ -139,13 +146,15 @@ module Rake
139
146
  # is invoked again.
140
147
  def reenable
141
148
  @already_invoked = false
149
+ @invocation_exception = nil
142
150
  end
143
151
 
144
- # Clear the existing prerequisites and actions of a rake task.
152
+ # Clear the existing prerequisites, actions, comments, and arguments of a rake task.
145
153
  def clear
146
154
  clear_prerequisites
147
155
  clear_actions
148
156
  clear_comments
157
+ clear_args
149
158
  self
150
159
  end
151
160
 
@@ -167,6 +176,12 @@ module Rake
167
176
  self
168
177
  end
169
178
 
179
+ # Clear the existing arguments on a rake task.
180
+ def clear_args
181
+ @arg_names = nil
182
+ self
183
+ end
184
+
170
185
  # Invoke the task if it is needed. Prerequisites are invoked first.
171
186
  def invoke(*args)
172
187
  task_args = TaskArguments.new(arg_names, args)
@@ -175,20 +190,39 @@ module Rake
175
190
 
176
191
  # Same as invoke, but explicitly pass a call chain to detect
177
192
  # circular dependencies.
178
- def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
179
- new_chain = InvocationChain.append(self, invocation_chain)
193
+ #
194
+ # If multiple tasks depend on this
195
+ # one in parallel, they will all fail if the first execution of
196
+ # this task fails.
197
+ def invoke_with_call_chain(task_args, invocation_chain)
198
+ new_chain = Rake::InvocationChain.append(self, invocation_chain)
180
199
  @lock.synchronize do
181
- if application.options.trace
182
- application.trace "** Invoke #{name} #{format_trace_flags}"
200
+ begin
201
+ if application.options.trace
202
+ application.trace "** Invoke #{name} #{format_trace_flags}"
203
+ end
204
+
205
+ if @already_invoked
206
+ if @invocation_exception
207
+ if application.options.trace
208
+ application.trace "** Previous invocation of #{name} failed #{format_trace_flags}"
209
+ end
210
+ raise @invocation_exception
211
+ else
212
+ return
213
+ end
214
+ end
215
+
216
+ @already_invoked = true
217
+
218
+ invoke_prerequisites(task_args, new_chain)
219
+ execute(task_args) if needed?
220
+ rescue Exception => ex
221
+ add_chain_to(ex, new_chain)
222
+ @invocation_exception = ex
223
+ raise ex
183
224
  end
184
- return if @already_invoked
185
- @already_invoked = true
186
- invoke_prerequisites(task_args, new_chain)
187
- execute(task_args) if needed?
188
225
  end
189
- rescue Exception => ex
190
- add_chain_to(ex, new_chain)
191
- raise ex
192
226
  end
193
227
  protected :invoke_with_call_chain
194
228
 
@@ -219,7 +253,8 @@ module Rake
219
253
  r.invoke_with_call_chain(prereq_args, invocation_chain)
220
254
  end
221
255
  end
222
- futures.each { |f| f.value }
256
+ # Iterate in reverse to improve performance related to thread waiting and switching
257
+ futures.reverse_each(&:value)
223
258
  end
224
259
 
225
260
  # Format the trace flags for display.
@@ -240,13 +275,10 @@ module Rake
240
275
  end
241
276
  application.trace "** Execute #{name}" if application.options.trace
242
277
  application.enhance_with_matching_rule(name) if @actions.empty?
243
- @actions.each do |act|
244
- case act.arity
245
- when 1
246
- act.call(self)
247
- else
248
- act.call(self, args)
249
- end
278
+ if opts = Hash.try_convert(args) and !opts.empty?
279
+ @actions.each { |act| act.call(self, args, **opts)}
280
+ else
281
+ @actions.each { |act| act.call(self, args)}
250
282
  end
251
283
  end
252
284
 
@@ -266,7 +298,7 @@ module Rake
266
298
  def add_description(description)
267
299
  return unless description
268
300
  comment = description.strip
269
- add_comment(comment) if comment && ! comment.empty?
301
+ add_comment(comment) if comment && !comment.empty?
270
302
  end
271
303
 
272
304
  def comment=(comment) # :nodoc:
@@ -314,13 +346,13 @@ module Rake
314
346
  # Set the names of the arguments for this task. +args+ should be
315
347
  # an array of symbols, one for each argument name.
316
348
  def set_arg_names(args)
317
- @arg_names = args.map { |a| a.to_sym }
349
+ @arg_names = args.map(&:to_sym)
318
350
  end
319
351
 
320
352
  # Return a string describing the internal state of a task. Useful for
321
353
  # debugging.
322
354
  def investigation
323
- result = "------------------------------\n"
355
+ result = "------------------------------\n".dup
324
356
  result << "Investigating #{name}\n"
325
357
  result << "class: #{self.class}\n"
326
358
  result << "task needed: #{needed?}\n"
@@ -331,12 +363,24 @@ module Rake
331
363
  prereqs.each do |p|
332
364
  result << "--#{p.name} (#{p.timestamp})\n"
333
365
  end
334
- latest_prereq = prerequisite_tasks.map { |pre| pre.timestamp }.max
366
+ latest_prereq = prerequisite_tasks.map(&:timestamp).max
335
367
  result << "latest-prerequisite time: #{latest_prereq}\n"
336
368
  result << "................................\n\n"
337
369
  return result
338
370
  end
339
371
 
372
+ # Format dependencies parameter to pass to task.
373
+ def self.format_deps(deps)
374
+ deps = [deps] unless deps.respond_to?(:to_ary)
375
+ deps.map { |d| Rake.from_pathname(d).to_s }
376
+ end
377
+
378
+ # Add order only dependencies.
379
+ def |(deps)
380
+ @order_only_prerequisites |= Task.format_deps(deps) - @prerequisites
381
+ self
382
+ end
383
+
340
384
  # ----------------------------------------------------------------
341
385
  # Rake Module Methods
342
386
  #
@@ -382,7 +426,6 @@ module Rake
382
426
  # this kind of task. Generic tasks will accept the scope as
383
427
  # part of the name.
384
428
  def scope_name(scope, task_name)
385
- # (scope + [task_name]).join(':')
386
429
  scope.path_with_task_name(task_name)
387
430
  end
388
431
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Rake
2
3
 
3
4
  # Error indicating an ill-formed task declaration.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Rake
2
3
 
3
4
  ##
@@ -17,7 +18,7 @@ module Rake
17
18
  @hash = {}
18
19
  @values = values
19
20
  names.each_with_index { |name, i|
20
- next if values[i].nil? || values[i] == ''
21
+ next if values[i].nil? || values[i] == ""
21
22
  @hash[name.to_sym] = values[i]
22
23
  }
23
24
  end
@@ -68,21 +69,26 @@ module Rake
68
69
 
69
70
  # Returns a Hash of arguments and their values
70
71
  def to_hash
71
- @hash
72
+ @hash.dup
72
73
  end
73
74
 
74
75
  def to_s # :nodoc:
75
- @hash.inspect
76
+ inspect
76
77
  end
77
78
 
78
79
  def inspect # :nodoc:
79
- to_s
80
+ inspection = @hash.map do |k,v|
81
+ "#{k.to_s}: #{v.to_s}"
82
+ end.join(", ")
83
+
84
+ "#<#{self.class} #{inspection}>"
80
85
  end
81
86
 
82
87
  # Returns true if +key+ is one of the arguments
83
88
  def has_key?(key)
84
89
  @hash.has_key?(key)
85
90
  end
91
+ alias key? has_key?
86
92
 
87
93
  def fetch(*args, &block)
88
94
  @hash.fetch(*args, &block)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Rake
2
3
 
3
4
  # The TaskManager module is a mixin for managing tasks.
@@ -5,19 +6,6 @@ module Rake
5
6
  # Track the last comment made in the Rakefile.
6
7
  attr_accessor :last_description
7
8
 
8
- # Remove Rake 12
9
- def last_comment # :nodoc:
10
- warn "[DEPRECATION] `last_comment` is deprecated. Please use `last_description` instead."
11
- @last_description
12
- end
13
-
14
- # Remove Rake 12
15
- def last_comment=(comment) # :nodoc:
16
- warn "[DEPRECATION] `last_comment=` is deprecated. Please use `last_description=` instead."
17
- @last_description = comment
18
- @last_description # ignore warning
19
- end
20
-
21
9
  def initialize # :nodoc:
22
10
  super
23
11
  @tasks = Hash.new
@@ -27,31 +15,31 @@ module Rake
27
15
  end
28
16
 
29
17
  def create_rule(*args, &block) # :nodoc:
30
- pattern, args, deps = resolve_args(args)
31
- pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
32
- @rules << [pattern, args, deps, block]
18
+ pattern, args, deps, order_only = resolve_args(args)
19
+ pattern = Regexp.new(Regexp.quote(pattern) + "$") if String === pattern
20
+ @rules << [pattern, args, deps, order_only, block]
33
21
  end
34
22
 
35
23
  def define_task(task_class, *args, &block) # :nodoc:
36
- task_name, arg_names, deps = resolve_args(args)
24
+ task_name, arg_names, deps, order_only = resolve_args(args)
37
25
 
38
26
  original_scope = @scope
39
27
  if String === task_name and
40
- not task_class.ancestors.include? Rake::FileTask then
28
+ not task_class.ancestors.include? Rake::FileTask
41
29
  task_name, *definition_scope = *(task_name.split(":").reverse)
42
30
  @scope = Scope.make(*(definition_scope + @scope.to_a))
43
31
  end
44
32
 
45
33
  task_name = task_class.scope_name(@scope, task_name)
46
- deps = [deps] unless deps.respond_to?(:to_ary)
47
- deps = deps.map { |d| Rake.from_pathname(d).to_s }
48
34
  task = intern(task_class, task_name)
49
35
  task.set_arg_names(arg_names) unless arg_names.empty?
50
36
  if Rake::TaskManager.record_task_metadata
51
37
  add_location(task)
52
38
  task.add_description(get_description(task))
53
39
  end
54
- task.enhance(deps, &block)
40
+ task.enhance(Task.format_deps(deps), &block)
41
+ task | order_only unless order_only.nil?
42
+ task
55
43
  ensure
56
44
  @scope = original_scope
57
45
  end
@@ -68,7 +56,26 @@ module Rake
68
56
  self.lookup(task_name, scopes) or
69
57
  enhance_with_matching_rule(task_name) or
70
58
  synthesize_file_task(task_name) or
71
- fail "Don't know how to build task '#{task_name}' (see --tasks)"
59
+ fail generate_message_for_undefined_task(task_name)
60
+ end
61
+
62
+ def generate_message_for_undefined_task(task_name)
63
+ message = "Don't know how to build task '#{task_name}' "\
64
+ "(See the list of available tasks with `#{Rake.application.name} --tasks`)"
65
+ message + generate_did_you_mean_suggestions(task_name)
66
+ end
67
+
68
+ def generate_did_you_mean_suggestions(task_name)
69
+ return "" unless defined?(::DidYouMean::SpellChecker)
70
+
71
+ suggestions = ::DidYouMean::SpellChecker.new(dictionary: @tasks.keys).correct(task_name.to_s)
72
+ if ::DidYouMean.respond_to?(:formatter)# did_you_mean v1.2.0 or later
73
+ ::DidYouMean.formatter.message_for(suggestions)
74
+ elsif defined?(::DidYouMean::Formatter) # before did_you_mean v1.2.0
75
+ ::DidYouMean::Formatter.new(suggestions).to_s
76
+ else
77
+ ""
78
+ end
72
79
  end
73
80
 
74
81
  def synthesize_file_task(task_name) # :nodoc:
@@ -76,8 +83,8 @@ module Rake
76
83
  define_task(Rake::FileTask, task_name)
77
84
  end
78
85
 
79
- # Resolve the arguments for a task/rule. Returns a triplet of
80
- # [task_name, arg_name_list, prerequisites].
86
+ # Resolve the arguments for a task/rule. Returns a tuple of
87
+ # [task_name, arg_name_list, prerequisites, order_only_prerequisites].
81
88
  def resolve_args(args)
82
89
  if args.last.is_a?(Hash)
83
90
  deps = args.pop
@@ -102,7 +109,7 @@ module Rake
102
109
  else
103
110
  arg_names = args
104
111
  end
105
- [task_name, arg_names, []]
112
+ [task_name, arg_names, [], nil]
106
113
  end
107
114
  private :resolve_args_without_dependencies
108
115
 
@@ -111,11 +118,17 @@ module Rake
111
118
  #
112
119
  # The patterns recognized by this argument resolving function are:
113
120
  #
121
+ # task :t, order_only: [:e]
114
122
  # task :t => [:d]
123
+ # task :t => [:d], order_only: [:e]
115
124
  # task :t, [a] => [:d]
125
+ # task :t, [a] => [:d], order_only: [:e]
116
126
  #
117
127
  def resolve_args_with_dependencies(args, hash) # :nodoc:
118
- fail "Task Argument Error" if hash.size != 1
128
+ fail "Task Argument Error" if
129
+ hash.size != 1 &&
130
+ (hash.size != 2 || !hash.key?(:order_only))
131
+ order_only = hash.delete(:order_only)
119
132
  key, value = hash.map { |k, v| [k, v] }.first
120
133
  if args.empty?
121
134
  task_name = key
@@ -123,11 +136,11 @@ module Rake
123
136
  deps = value || []
124
137
  else
125
138
  task_name = args.shift
126
- arg_names = key
127
- deps = value
139
+ arg_names = key || args.shift|| []
140
+ deps = value || []
128
141
  end
129
142
  deps = [deps] unless deps.respond_to?(:to_ary)
130
- [task_name, arg_names, deps]
143
+ [task_name, arg_names, deps, order_only]
131
144
  end
132
145
  private :resolve_args_with_dependencies
133
146
 
@@ -138,9 +151,10 @@ module Rake
138
151
  def enhance_with_matching_rule(task_name, level=0)
139
152
  fail Rake::RuleRecursionOverflowError,
140
153
  "Rule Recursion Too Deep" if level >= 16
141
- @rules.each do |pattern, args, extensions, block|
142
- if pattern.match(task_name)
143
- task = attempt_rule(task_name, args, extensions, block, level)
154
+ @rules.each do |pattern, args, extensions, order_only, block|
155
+ if pattern && pattern.match(task_name)
156
+ task = attempt_rule(task_name, pattern, args, extensions, block, level)
157
+ task | order_only unless order_only.nil?
144
158
  return task if task
145
159
  end
146
160
  end
@@ -180,10 +194,10 @@ module Rake
180
194
  task_name = task_name.to_s
181
195
  if task_name =~ /^rake:/
182
196
  scopes = Scope.make
183
- task_name = task_name.sub(/^rake:/, '')
197
+ task_name = task_name.sub(/^rake:/, "")
184
198
  elsif task_name =~ /^(\^+)/
185
199
  scopes = initial_scope.trim($1.size)
186
- task_name = task_name.sub(/^(\^+)/, '')
200
+ task_name = task_name.sub(/^(\^+)/, "")
187
201
  else
188
202
  scopes = initial_scope
189
203
  end
@@ -254,8 +268,8 @@ module Rake
254
268
  end
255
269
 
256
270
  # Attempt to create a rule given the list of prerequisites.
257
- def attempt_rule(task_name, args, extensions, block, level)
258
- sources = make_sources(task_name, extensions)
271
+ def attempt_rule(task_name, task_pattern, args, extensions, block, level)
272
+ sources = make_sources(task_name, task_pattern, extensions)
259
273
  prereqs = sources.map { |source|
260
274
  trace_rule level, "Attempting Rule #{task_name} => #{source}"
261
275
  if File.exist?(source) || Rake::Task.task_defined?(source)
@@ -269,14 +283,14 @@ module Rake
269
283
  return nil
270
284
  end
271
285
  }
272
- task = FileTask.define_task(task_name, {args => prereqs}, &block)
286
+ task = FileTask.define_task(task_name, { args => prereqs }, &block)
273
287
  task.sources = prereqs
274
288
  task
275
289
  end
276
290
 
277
291
  # Make a list of sources from the list of file name extensions /
278
292
  # translation procs.
279
- def make_sources(task_name, extensions)
293
+ def make_sources(task_name, task_pattern, extensions)
280
294
  result = extensions.map { |ext|
281
295
  case ext
282
296
  when /%/
@@ -284,7 +298,8 @@ module Rake
284
298
  when %r{/}
285
299
  ext
286
300
  when /^\./
287
- task_name.ext(ext)
301
+ source = task_name.sub(task_pattern, ext)
302
+ source == ext ? task_name.ext(ext) : source
288
303
  when String
289
304
  ext
290
305
  when Proc, Method