arake 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/arake.rb +104 -30
  2. data/spec/arake_spec.rb +397 -0
  3. metadata +3 -2
@@ -8,56 +8,69 @@ require 'watchr'
8
8
 
9
9
  module ARake
10
10
  class Application
11
- attr_accessor :accumulated_args
12
-
13
11
  def initialize(top_level_self)
12
+ @_rake = nil
13
+ @_watchr = nil
14
+ @_watchr_script = nil
14
15
  @top_level_self = top_level_self
15
- @accumulated_args = []
16
+ end
16
17
 
17
- hook_task_definition
18
+ def run
19
+ original_Rake_application = Rake.application
20
+ begin
21
+ Rake.application = rake
22
+
23
+ Rake.application.run
24
+ watchr.run
25
+ ensure
26
+ Rake.application = original_Rake_application
27
+ end
18
28
  end
19
29
 
20
- def hook_task_definition
21
- a = self
22
- (class << @top_level_self; self; end).class_eval do
23
- include Rake::TaskManager
30
+ # Misc.
24
31
 
25
- # FIXME: How about other rake tasks?
26
- define_method :file do |*args, &block|
27
- super *args, &block
28
- a.accumulate_args *resolve_args(args), &block
29
- end
30
- end
32
+ def rake
33
+ @_rake ||= CustomRakeAppliation.new
34
+ end
35
+
36
+ def rake_tasks
37
+ rake.tasks
31
38
  end
32
39
 
33
- def accumulate_args(*args, &block)
34
- @accumulated_args.push [*args, block]
40
+ def watchr
41
+ @_watchr ||= _create_custom_watchr
35
42
  end
36
43
 
37
- def create_custom_watchr
44
+ def _create_custom_watchr
45
+ Watchr::Controller.new(watchr_script, Watchr.handler.new)
46
+ end
47
+
48
+ def watchr_rules
49
+ watchr_script.rules
50
+ end
51
+
52
+ def watchr_script
53
+ @_watchr_script ||= _create_custom_watchr_script
54
+ end
55
+
56
+ def _create_custom_watchr_script
38
57
  a = self
39
58
  s = Watchr::Script.new
40
59
  (class << s; self; end).class_eval do
41
60
  define_method :parse! do
42
61
  @ec.instance_eval do
43
- a.accumulated_args.each do |pattern, arg_names, deps, block|
44
- deps.each do |d|
45
- watch "^#{Regexp.escape d}$", &block
62
+ a.rake_tasks.each do |t|
63
+ t.prerequisites.each do |p|
64
+ watch "^#{Regexp.escape p}$" do
65
+ a.rake.reenable_all_tasks
66
+ a.rake.invoke_root_tasks_of(t)
67
+ end
46
68
  end
47
69
  end
48
70
  end
49
71
  end
50
72
  end
51
- Watchr::Controller.new(s, Watchr.handler.new)
52
- end
53
-
54
- def load_rakefiles
55
- CustomRakeAppliation.new.run
56
- end
57
-
58
- def run
59
- load_rakefiles
60
- create_custom_watchr.run
73
+ s
61
74
  end
62
75
  end
63
76
 
@@ -70,6 +83,67 @@ module ARake
70
83
  # run whenever dependents are updated.
71
84
  end
72
85
  end
86
+
87
+ def reenable_all_tasks
88
+ tasks.each do |t|
89
+ t.reenable
90
+ end
91
+ end
92
+
93
+ def invoke_root_tasks_of(task)
94
+ root_tasks_of(task).each do |t|
95
+ t.invoke
96
+ end
97
+ end
98
+
99
+ def root_tasks_of(task)
100
+ Misc.root_tasks_of task, tasks
101
+ end
102
+ end
103
+
104
+ module Misc
105
+ def self.root_tasks_of(task, tasks)
106
+ tree_from_task(task, pt_table_from_tasks(tasks)).leaves
107
+ end
108
+
109
+ def self.pt_table_from_tasks(tasks) # prerequisite-to-target table
110
+ h = Hash.new {|h, name| h[name] = []}
111
+ tasks.each do |target_task|
112
+ target_task.prerequisites.each do |prerequisite|
113
+ h[prerequisite.to_s].push target_task
114
+ end
115
+ end
116
+ h
117
+ end
118
+
119
+ def self.tree_from_task(task, table = Hash.new {|h, key| h[key] = []})
120
+ t = Tree.new
121
+ t.value = task
122
+ t.subtrees = table[task.to_s].map {|x| tree_from_task x, table}
123
+ t
124
+ end
125
+
126
+ class Tree
127
+ attr_accessor :value
128
+ attr_accessor :subtrees
129
+
130
+ def initialize(value = nil, subtrees = [])
131
+ @value = value
132
+ @subtrees = subtrees
133
+ end
134
+
135
+ def leaf?
136
+ subtrees.empty?
137
+ end
138
+
139
+ def leaves
140
+ if leaf?
141
+ [value]
142
+ else
143
+ subtrees.map{|s| s.leaves}.inject :+
144
+ end
145
+ end
146
+ end
73
147
  end
74
148
  end
75
149
 
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'arake'
4
+ require 'stringio'
5
+ require 'tmpdir'
6
+
7
+ # $ mount
8
+ # /dev/disk0s2 on / (hfs, local, journaled)
9
+ # devfs on /dev (devfs, local)
10
+ # fdesc on /dev (fdesc, union)
11
+ # map -hosts on /net (autofs, automounted)
12
+ # map auto_home on /home (autofs, automounted)
13
+ #
14
+ # $ ruby -e 'p (Dir.glob "/a", File::FNM_CASEFOLD)'
15
+ # -e:1:in `glob': invalid byte sequence in US-ASCII (ArgumentError)
16
+ # from -e:1:in `<main>'
17
+ #
18
+ # $ ruby --version
19
+ # ruby 1.9.2p180 (2011-02-18 revision 30909) [i386-darwin9.8.0]
20
+ File::FNM_CASEFOLD = 0
21
+
22
+ def with_argv(*args, &block)
23
+ original_ARGV = ARGV.dup
24
+
25
+ ARGV.clear
26
+ ARGV.push *args
27
+
28
+ block.call
29
+
30
+ ARGV.clear
31
+ ARGV.push *original_ARGV
32
+ end
33
+
34
+ def with_rakefile(rakefile_content, &block)
35
+ Dir.mktmpdir do |d|
36
+ rakefile = "#{d}/Rakefile"
37
+ File.open rakefile, 'w' do |io|
38
+ io.write rakefile_content
39
+ end
40
+ with_argv *['-f', rakefile] do
41
+ block.call
42
+ end
43
+ end
44
+ end
45
+
46
+ def with_rake_application(rake_application, &block)
47
+ original_Rake_application = Rake.application
48
+ begin
49
+ Rake.application = rake_application
50
+ block.call
51
+ ensure
52
+ Rake.application = original_Rake_application
53
+ end
54
+ end
55
+
56
+ def with_rake(rake_application, rakefile_content, &block)
57
+ with_rakefile rakefile_content do
58
+ with_rake_application rake_application do
59
+ block.call
60
+ end
61
+ end
62
+ end
63
+
64
+ def redirect(stdout = $stdout, &block)
65
+ _stdout = $stdout
66
+ begin
67
+ $stdout = stdout
68
+ block.call
69
+ ensure
70
+ $stdout = _stdout
71
+ end
72
+ end
73
+
74
+ top_level_self = self
75
+
76
+
77
+
78
+
79
+ describe ARake::CustomRakeAppliation do
80
+ it 'should read rakefiles but should not run any specified targets' do
81
+ rakefile_content = <<-"END"
82
+ p 'outer#{self.object_id}'
83
+ task :default do
84
+ p 'inner#{self.object_id}'
85
+ end
86
+ END
87
+ with_rake ARake::CustomRakeAppliation.new, rakefile_content do
88
+ s = String.new
89
+ redirect (StringIO.new s) do
90
+ Rake.application.run
91
+ end
92
+
93
+ (s.index "outer#{self.object_id}").should_not be_nil
94
+ (s.index "inner#{self.object_id}").should be_nil
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+
101
+
102
+ describe ARake::Application do
103
+ def re(s)
104
+ "^#{Regexp.escape s}$"
105
+ end
106
+
107
+ it 'should not affect the default Rake.application' do
108
+ oa = Rake.application
109
+ a = ARake::Application.new top_level_self
110
+ with_rake_application a.rake do
111
+ oa.tasks.should be_empty
112
+ a.rake_tasks.should be_empty
113
+
114
+ block = Proc.new {}
115
+ top_level_self.instance_eval do
116
+ task 'bar1'
117
+ task 'bar2'
118
+ task 'baz1'
119
+ task 'baz2'
120
+ task 'foo1' => ['bar1', 'baz1'], &block
121
+ task 'foo2' => ['bar2', 'baz2'], &block
122
+ end
123
+
124
+ oa.tasks.should be_empty
125
+ a.rake_tasks.should_not be_empty
126
+ end
127
+ end
128
+
129
+ it 'should be able to refer tasks' do
130
+ a = ARake::Application.new top_level_self
131
+ with_rake_application a.rake do
132
+ a.rake_tasks.should be_empty
133
+
134
+ block = Proc.new {}
135
+ top_level_self.instance_eval do
136
+ task 'bar1'
137
+ task 'bar2'
138
+ task 'baz1'
139
+ task 'baz2'
140
+ task 'foo1' => ['bar1', 'baz1'], &block
141
+ task 'foo2' => ['bar2', 'baz2'], &block
142
+ end
143
+
144
+ a.rake_tasks.should_not be_empty
145
+ a.rake_tasks[-2].to_s.should eql 'foo1'
146
+ a.rake_tasks[-2].prerequisites.should eql ['bar1', 'baz1']
147
+ a.rake_tasks[-2].actions.should eql [block]
148
+ a.rake_tasks[-1].to_s.should eql 'foo2'
149
+ a.rake_tasks[-1].prerequisites.should eql ['bar2', 'baz2']
150
+ a.rake_tasks[-1].actions.should eql [block]
151
+ end
152
+ end
153
+
154
+ it 'should define a watch rule for each dependent' do
155
+ a = ARake::Application.new top_level_self
156
+ with_rake_application a.rake do
157
+ a.rake_tasks.should be_empty
158
+ a.watchr_rules.should be_empty
159
+
160
+ block = Proc.new {}
161
+ top_level_self.instance_eval do
162
+ task 'bar1'
163
+ task 'bar2'
164
+ task 'baz1'
165
+ task 'baz2'
166
+ task 'foo1' => ['bar1', 'baz1'], &block
167
+ task 'foo2' => ['bar2', 'baz2'], &block
168
+ end
169
+ a.watchr_script.parse!
170
+
171
+ a.rake_tasks.should_not be_empty
172
+ a.watchr_rules.should_not be_empty
173
+ a.watchr_rules[0].pattern.should eql re('bar1')
174
+ a.watchr_rules[1].pattern.should eql re('baz1')
175
+ a.watchr_rules[2].pattern.should eql re('bar2')
176
+ a.watchr_rules[3].pattern.should eql re('baz2')
177
+ end
178
+ end
179
+
180
+ it 'should invoke a rake task with proper parameters' do
181
+ a = ARake::Application.new top_level_self
182
+ with_rake_application a.rake do
183
+ passed_task = nil
184
+ block = Proc.new {|task| passed_task = task}
185
+ top_level_self.instance_eval do
186
+ task 'bar'
187
+ task 'foo' => 'bar', &block
188
+ end
189
+ a.watchr_script.parse!
190
+ r = a.watchr_rules[0]
191
+
192
+ r.pattern.should eql re('bar')
193
+ passed_task.should be_nil
194
+
195
+ r.action.call
196
+ t = a.rake_tasks[1]
197
+
198
+ t.to_s.should eql 'foo'
199
+ passed_task.should equal t
200
+ end
201
+ end
202
+
203
+ it 'should execute rake tasks even if they are already executed before' do
204
+ a = ARake::Application.new top_level_self
205
+ with_rake_application a.rake do
206
+ call_count = 0
207
+ block = Proc.new {call_count += 1}
208
+ top_level_self.instance_eval do
209
+ task :dependent
210
+ task :target => :dependent, &block
211
+ end
212
+ a.watchr_script.parse!
213
+ r = a.watchr_rules[0]
214
+
215
+ r.pattern.should eql re(:dependent)
216
+ call_count.should eql 0
217
+
218
+ r.action.call
219
+
220
+ call_count.should eql 1
221
+
222
+ r.action.call
223
+
224
+ call_count.should eql 2
225
+ end
226
+ end
227
+
228
+ it 'should execute chained tasks properly' do
229
+ a = ARake::Application.new top_level_self
230
+ with_rake_application a.rake do
231
+ calling_history = []
232
+ block = Proc.new {|t| calling_history.push t.to_s}
233
+ top_level_self.instance_eval do
234
+ # t1a t1b t1c
235
+ # |\ | |
236
+ # | \ | |
237
+ # | \| |
238
+ # t2a t2b t2c
239
+ # | | /|
240
+ # | | / |
241
+ # | |/ |
242
+ # t3a t3b t3c
243
+ task :t1a => [:t2a, :t2b], &block
244
+ task :t1b => [:t2b], &block
245
+ task :t1c => [:t2c], &block
246
+ task :t2a => [:t3a], &block
247
+ task :t2b => [:t3b], &block
248
+ task :t2c => [:t3b, :t3c], &block
249
+ task :t3a => [], &block
250
+ task :t3b => [], &block
251
+ task :t3c => [], &block
252
+ end
253
+ a.watchr_script.parse!
254
+ rules_table = Hash.new {|h, key| h[key] = []}
255
+ a.watchr_rules.each do |r|
256
+ rules_table[r.pattern].push r
257
+ end
258
+ def rules_table.trigger(task_name)
259
+ self[task_name].each do |r|
260
+ r.action.call
261
+ end
262
+ end
263
+
264
+ rules_table.keys.sort.should eql [
265
+ re(:t2a),
266
+ re(:t2b),
267
+ re(:t2c),
268
+ re(:t3a),
269
+ re(:t3b),
270
+ re(:t3c),
271
+ ]
272
+ calling_history.should be_empty
273
+
274
+ calling_history.clear
275
+ rules_table.trigger re(:t3c)
276
+
277
+ calling_history.sort.should eql %w[t1c t2c t3b t3c]
278
+
279
+ calling_history.clear
280
+ rules_table.trigger re(:t2c)
281
+
282
+ calling_history.sort.should eql %w[t1c t2c t3b t3c]
283
+
284
+ calling_history.clear
285
+ rules_table.trigger re(:t3b)
286
+
287
+ calling_history.sort.should eql %w[
288
+ t1a t1b t1c t2a t2b t2c t3a t3b t3b t3c
289
+ ]
290
+
291
+ calling_history.clear
292
+ rules_table.trigger re(:t2b)
293
+
294
+ calling_history.sort.should eql %w[t1a t1b t2a t2b t2b t3a t3b t3b]
295
+
296
+ calling_history.clear
297
+ rules_table.trigger re(:t3a)
298
+
299
+ calling_history.sort.should eql %w[t1a t2a t2b t3a t3b]
300
+
301
+ calling_history.clear
302
+ rules_table.trigger re(:t2a)
303
+
304
+ calling_history.sort.should eql %w[t1a t2a t2b t3a t3b]
305
+ end
306
+ end
307
+ end
308
+
309
+
310
+
311
+
312
+ describe ARake::Misc do
313
+ Tree = ARake::Misc::Tree
314
+
315
+ it 'should create an empty instance' do
316
+ t = Tree.new
317
+ t.value.should be_nil
318
+ t.subtrees.should be_empty
319
+
320
+ t.leaf?.should be_true
321
+ t.leaves.should eql [nil]
322
+ end
323
+
324
+ it 'should return values of leaves' do
325
+ t = Tree.new(
326
+ 1,
327
+ [
328
+ Tree.new(2),
329
+ Tree.new(
330
+ 3,
331
+ [Tree.new(4)]
332
+ ),
333
+ Tree.new(5),
334
+ ]
335
+ )
336
+
337
+ t.leaves.should eql [2, 4, 5]
338
+ end
339
+
340
+ it 'should return a tree of tasks based on their dependencies' do
341
+ def task(name, *prerequisites)
342
+ t = Rake::Task.new name, Rake::Application.new
343
+ t.enhance prerequisites
344
+ t
345
+ end
346
+
347
+ # t1a t1b t1c
348
+ # \ /| |
349
+ # X | |
350
+ # / \| |
351
+ # t2a t2b t2c
352
+ # | | /|
353
+ # | | / |
354
+ # | |/ |
355
+ # t3a t3b t3c
356
+ # |
357
+ # |
358
+ # |
359
+ # t4b
360
+ t4b = task 't4b'
361
+ t3c = task 't3c'
362
+ t3b = task 't3b', t4b
363
+ t3a = task 't3a'
364
+ t2c = task 't2c', t3b, t3c
365
+ t2b = task 't2b', t3b
366
+ t2a = task 't2a', t3a
367
+ t1c = task 't1c', t2c
368
+ t1b = task 't1b', t2a, t2b
369
+ t1a = task 't1a', t2b
370
+
371
+ tasks = [
372
+ t1a,
373
+ t1b,
374
+ t1c,
375
+ t2a,
376
+ t2b,
377
+ t2c,
378
+ t3a,
379
+ t3b,
380
+ t3c,
381
+ t4b,
382
+ ]
383
+
384
+ ARake::Misc.root_tasks_of(t1a, tasks).should eql [t1a]
385
+ ARake::Misc.root_tasks_of(t1b, tasks).should eql [t1b]
386
+ ARake::Misc.root_tasks_of(t1c, tasks).should eql [t1c]
387
+ ARake::Misc.root_tasks_of(t2a, tasks).should eql [t1b]
388
+ ARake::Misc.root_tasks_of(t2b, tasks).should eql [t1a, t1b]
389
+ ARake::Misc.root_tasks_of(t2c, tasks).should eql [t1c]
390
+ ARake::Misc.root_tasks_of(t3a, tasks).should eql [t1b]
391
+ ARake::Misc.root_tasks_of(t3b, tasks).should eql [t1a, t1b, t1c]
392
+ ARake::Misc.root_tasks_of(t3c, tasks).should eql [t1c]
393
+ ARake::Misc.root_tasks_of(t4b, tasks).should eql [t1a, t1b, t1c]
394
+ end
395
+ end
396
+
397
+ __END__
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: arake
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Kana Natsuno
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-04 00:00:00 +09:00
13
+ date: 2011-05-06 00:00:00 +09:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -59,6 +59,7 @@ files:
59
59
  - arake.gemspec
60
60
  - bin/arake
61
61
  - lib/arake.rb
62
+ - spec/arake_spec.rb
62
63
  has_rdoc: true
63
64
  homepage: http://github.com/kana/ruby-arake
64
65
  licenses: []