arake 0.0.1 → 0.0.2

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 (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: []