Skip to content

Commit b550157

Browse files
committed
New dsl method: lazy(&block)
Make possible to lazy define tasks in a namespace. The tasks are loaded only on lookup. Lazy definitions can be executed on purpose when using the option --load-lazy-definitions so that `rake --tasks --load-lazy-definitions` list all tasks including the one defined in a lazy way. Example of lazy definitions: ``` namespace 'a' do lazy { task 't' } end ``` or ``` namespace 'a' do lazy { load 'namespace_a.rake' } end ```
1 parent 9b08384 commit b550157

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

lib/rake/application.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def load_rakefile
108108
# Run the top level tasks of a Rake application.
109109
def top_level
110110
run_with_threads do
111+
execute_all_lazy_definitions if options.loadlazydefinitions
111112
if options.show_tasks
112113
display_tasks_and_comments
113114
elsif options.show_prereqs
@@ -499,6 +500,10 @@ def standard_rake_options # :nodoc:
499500
"-N", "Do not search parent directories for the Rakefile.",
500501
lambda { |value| options.nosearch = true }
501502
],
503+
["--load-lazy-definitions", "--loadlazydefinitions",
504+
"-L", "Include all lazy definitions when listing tasks with --tasks",
505+
lambda { |value| options.loadlazydefinitions = true}
506+
],
502507
["--prereqs", "-P",
503508
"Display the tasks and dependencies, then exit.",
504509
lambda { |value| options.show_prereqs = true }
@@ -807,6 +812,7 @@ def set_default_options # :nodoc:
807812
options.job_stats = false
808813
options.load_system = false
809814
options.nosearch = false
815+
options.loadlazydefinitions = false
810816
options.rakelib = %w[rakelib]
811817
options.show_all_tasks = false
812818
options.show_prereqs = false

lib/rake/dsl_definition.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ def import(*fns) # :doc:
185185
Rake.application.add_import(fn)
186186
end
187187
end
188+
189+
# Associate a block to the current scope and
190+
# execute it only when a lookup for a task is performed in the same scope.
191+
def lazy(&block)
192+
Rake.application.register_lazy_definition(&block)
193+
end
188194
end
189195
extend FileUtilsExt
190196
end

lib/rake/task_manager.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize # :nodoc:
1212
@rules = Array.new
1313
@scope = Scope.make
1414
@last_description = nil
15+
@lazy_definitions = {}
1516
end
1617

1718
def create_rule(*args, &block) # :nodoc:
@@ -208,6 +209,7 @@ def lookup(task_name, initial_scope=nil)
208209
def lookup_in_scope(name, scope)
209210
loop do
210211
tn = scope.path_with_task_name(name)
212+
execute_lazy_definitions(tn.rpartition(':')[0])
211213
task = @tasks[tn]
212214
return task if task
213215
break if scope.empty?
@@ -235,6 +237,47 @@ def in_namespace(name)
235237
@scope = @scope.tail
236238
end
237239

240+
# Execute all definitions: usefull for rake -T for instance
241+
def execute_all_lazy_definitions
242+
@lazy_definitions.each do |scope_path, _definitions|
243+
execute_lazy_definitions(scope_path)
244+
end
245+
end
246+
247+
# Execute all definitions linked to specified +scope_path+
248+
# and its parent scopes.
249+
def execute_lazy_definitions(scope_path)
250+
scope_path_elements = scope_path.split(':')
251+
sub_scope_elements = []
252+
scope_path_elements.each do |e|
253+
sub_scope_elements << e
254+
sub_scope_path = sub_scope_elements.join(':')
255+
definitions = @lazy_definitions[sub_scope_path]
256+
next unless definitions
257+
definitions.each do |definition|
258+
definition.call
259+
end
260+
definitions.clear
261+
end
262+
end
263+
264+
# Evaluate the block in specified +scope+.
265+
def in_scope(scope)
266+
cur_scope = @scope
267+
@scope = scope
268+
yield
269+
ensure
270+
@scope = cur_scope
271+
end
272+
273+
# Register a block which will be called only when necessary during the lookup
274+
# of tasks
275+
def register_lazy_definition(&block)
276+
cur_scope = @scope
277+
@lazy_definitions[cur_scope.path] ||= []
278+
@lazy_definitions[cur_scope.path] << ->() { in_scope(cur_scope, &block) }
279+
end
280+
238281
private
239282

240283
# Add a location to the locations field of the given task.

test/test_rake_dsl.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,37 @@ def test_no_commands_constant
3838
assert ! defined?(Commands), "should not define Commands"
3939
end
4040

41+
def test_lazy
42+
call_count_t1 = 0
43+
call_count_t3 = 0
44+
namespace "a" do
45+
lazy do
46+
task "t1"
47+
call_count_t1 += 1
48+
end
49+
end
50+
namespace "b" do
51+
task "t2"
52+
namespace "c" do
53+
lazy do
54+
namespace "d" do
55+
lazy do
56+
task "t3"
57+
call_count_t3 += 1
58+
end
59+
end
60+
end
61+
end
62+
end
63+
refute_nil Rake::Task["b:t2"]
64+
assert_equal 0, call_count_t1
65+
assert_equal 0, call_count_t3
66+
refute_nil Rake::Task["a:t1"]
67+
assert_equal 1, call_count_t1
68+
assert_equal 0, call_count_t3
69+
refute_nil Rake::Task["b:c:d:t3"]
70+
assert_equal 1, call_count_t1
71+
assert_equal 1, call_count_t3
72+
end
73+
4174
end

test/test_rake_task_manager.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,51 @@ def test_correctly_scoped_prerequisites_are_invoked
186186
assert_equal ["next z"], values
187187
end
188188

189+
def test_lazy_definition
190+
t1, t2, t3 = nil, nil, nil
191+
lazy_definition_call_count = 0
192+
@tm.in_namespace("a1") do
193+
@tm.register_lazy_definition do
194+
t1 = @tm.define_task(Rake::Task, :t1)
195+
lazy_definition_call_count += 1
196+
@tm.in_namespace("a2") do
197+
t2 = @tm.define_task(Rake::Task, :t2)
198+
end
199+
end
200+
end
201+
@tm.in_namespace("b") do
202+
t3 = @tm.define_task(Rake::Task, :t3)
203+
end
204+
# task t3 is not lazy. It can be found
205+
assert_equal t3, @tm[:t3, Rake::Scope.make("b")]
206+
# lazy definition is not called until we look for task in namespace a
207+
assert_equal lazy_definition_call_count, 0
208+
209+
# task t2 can be found
210+
found_task_t2 = @tm[:t2, Rake::Scope.make("a1:a2")]
211+
assert_equal t2, found_task_t2
212+
# lazy definition is expected to be called
213+
assert_equal lazy_definition_call_count, 1
214+
215+
# task t1 can also be found
216+
found_task_t1 = @tm[:t1, Rake::Scope.make("a1")]
217+
assert_equal t1, found_task_t1
218+
# lazy definition is called at most once
219+
assert_equal lazy_definition_call_count, 1
220+
end
221+
222+
def test_execute_all_lazy_definitions
223+
lazy_definition_call_count = 0
224+
@tm.in_namespace("a") do
225+
@tm.register_lazy_definition do
226+
lazy_definition_call_count += 1
227+
end
228+
end
229+
assert_equal lazy_definition_call_count, 0
230+
@tm.execute_all_lazy_definitions
231+
assert_equal lazy_definition_call_count, 1
232+
@tm.execute_all_lazy_definitions
233+
assert_equal lazy_definition_call_count, 1
234+
end
235+
189236
end

0 commit comments

Comments
 (0)