templater 0.1.6 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -53,18 +53,25 @@ module Templater
53
53
 
54
54
  protected
55
55
 
56
- def find_latest_gems
57
- Gem::cache.inject({}) do |latest_gems, cache|
58
- name, gem = cache
59
- currently_latest = latest_gems[gem.name]
60
- latest_gems[gem.name] = gem if currently_latest.nil? or gem.version > currently_latest.version
61
- latest_gems
62
- end.values
56
+ def find_latest_gem_paths
57
+ # Minigems provides a simpler (and much faster) method for finding the
58
+ # latest gems.
59
+ if Gem.respond_to?(:latest_gem_paths)
60
+ Gem.latest_gem_paths
61
+ else
62
+ gems = Gem.cache.inject({}) do |latest_gems, cache|
63
+ name, gem = cache
64
+ currently_latest = latest_gems[gem.name]
65
+ latest_gems[gem.name] = gem if currently_latest.nil? or gem.version > currently_latest.version
66
+ latest_gems
67
+ end
68
+ gems.values.map{|g| g.full_gem_path}
69
+ end
63
70
  end
64
71
 
65
72
  def generator_files
66
- find_latest_gems.inject([]) do |files, gem|
67
- path = ::File.join(gem.full_gem_path, "Generators")
73
+ find_latest_gem_paths.inject([]) do |files, gem_path|
74
+ path = ::File.join(gem_path, "Generators")
68
75
  files << path if ::File.exists?(path) and not ::File.directory?(path)
69
76
  files
70
77
  end
@@ -1,5 +1,7 @@
1
1
  module Templater
2
2
 
3
+ ACTION_RESERVED_OPTIONS = [:before, :after].freeze
4
+
3
5
  class Generator
4
6
 
5
7
  include Templater::CaptureHelpers
@@ -7,6 +9,7 @@ module Templater
7
9
  class << self
8
10
 
9
11
  attr_accessor :manifold
12
+
10
13
 
11
14
  # Returns an array of hashes, where each hash describes a single argument.
12
15
  #
@@ -14,56 +17,57 @@ module Templater
14
17
  # Array[Hash{Symbol=>Object}]:: A list of arguments
15
18
  def arguments; @arguments ||= []; end
16
19
 
20
+ # A shorthand method for adding the first argument, see +Templater::Generator.argument+
21
+ def first_argument(*args); argument(0, *args); end
22
+
23
+ # A shorthand method for adding the second argument, see +Templater::Generator.argument+
24
+ def second_argument(*args); argument(1, *args); end
25
+
26
+ # A shorthand method for adding the third argument, see +Templater::Generator.argument+
27
+ def third_argument(*args); argument(2, *args); end
28
+
29
+ # A shorthand method for adding the fourth argument, see +Templater::Generator.argument+
30
+ def fourth_argument(*args); argument(3, *args); end
31
+
32
+
17
33
  # Returns an array of options, where each hash describes a single option.
18
34
  #
19
35
  # === Returns
20
36
  # Array[Hash{Symbol=>Object}]:: A list of options
21
37
  def options; @options ||= []; end
22
-
23
- # Returns an array of hashes, where each hash describes a single template.
24
- #
25
- # === Returns
26
- # Array[Hash{Symbol=>Object}]:: A list of template
27
- def templates; @templates ||= []; end
28
-
29
- # Returns an array of hashes, where each hash describes a single file.
30
- #
31
- # === Returns
32
- # Array[Hash{Symbol=>Object}]:: A list of files
33
- def files; @files ||= []; end
34
-
38
+
35
39
  # Returns an array of hashes, where each hash describes a single invocation.
36
40
  #
37
41
  # === Returns
38
42
  # Array[Hash{Symbol=>Object}]:: A list of invocations
39
43
  def invocations; @invocations ||= []; end
44
+
40
45
 
41
- # Returns an array of hashes, where each hash describes a single empty directory created by generator.
46
+ # Returns an Hash that maps the type of action to a list of ActionDescriptions.
42
47
  #
43
48
  # ==== Returns
44
- # Array[Hash{Symbol=>Object}]:: A list of empty directories created by generator.
45
- def empty_directories; @empty_directories ||= []; end
49
+ # Hash{Symbol=>Array[Templater::ActionDescription]}:: A Hash of actions
50
+ def actions; @actions ||= {} end
46
51
 
47
-
48
-
49
-
52
+ # Returns an array of ActionDescriptions, where each describes a single template.
53
+ #
54
+ # === Returns
55
+ # Array[Templater::ActionDescription]:: A list of template descriptions.
56
+ def templates; actions[:templates] ||= []; end
50
57
 
51
- # A shorthand method for adding the first argument, see +Templater::Generator.argument+
52
- def first_argument(*args); argument(0, *args); end
53
-
54
- # A shorthand method for adding the second argument, see +Templater::Generator.argument+
55
- def second_argument(*args); argument(1, *args); end
56
-
57
- # A shorthand method for adding the third argument, see +Templater::Generator.argument+
58
- def third_argument(*args); argument(2, *args); end
58
+ # Returns an array of ActionDescriptions, where each describes a single file.
59
+ #
60
+ # === Returns
61
+ # Array[Templater::ActionDescription]:: A list of file descriptions.
62
+ def files; actions[:files] ||= []; end
59
63
 
60
- # A shorthand method for adding the fourth argument, see +Templater::Generator.argument+
61
- def fourth_argument(*args); argument(3, *args); end
62
-
63
-
64
+ # Returns an array of ActionDescriptions, where each describes a single empty directory created by generator.
65
+ #
66
+ # ==== Returns
67
+ # Array[Templater::ActionDescription]:: A list of empty directory descriptions.
68
+ def empty_directories; actions[:empty_directories] ||= []; end
64
69
 
65
70
 
66
-
67
71
  # If the argument is omitted, simply returns the description for this generator, otherwise
68
72
  # sets the description to the passed string.
69
73
  #
@@ -96,11 +100,7 @@ module Templater
96
100
  # :required<Boolean>:: If set to true, the generator will throw an error if it initialized without this argument
97
101
  # :desc<Symbol>:: Provide a description for this argument
98
102
  def argument(n, name, options={}, &block)
99
- self.arguments[n] = {
100
- :name => name.to_sym,
101
- :options => options,
102
- :block => block
103
- }
103
+ self.arguments[n] = ArgumentDescription.new(name.to_sym, options, &block)
104
104
  class_eval <<-CLASS
105
105
  def #{name}
106
106
  get_argument(#{n})
@@ -124,10 +124,7 @@ module Templater
124
124
  # :as<Symbol>:: If set to :boolean provides a hint to the interface using this generator.
125
125
  # :desc<Symbol>:: Provide a description for this option
126
126
  def option(name, options={})
127
- self.options << {
128
- :name => name.to_sym,
129
- :options => options
130
- }
127
+ self.options << Description.new(name.to_sym, options)
131
128
  class_eval <<-CLASS
132
129
  def #{name}
133
130
  get_option(:#{name})
@@ -178,11 +175,7 @@ module Templater
178
175
  # invoke :other_generator, :amimal => :bear
179
176
  # end
180
177
  def invoke(name, options={}, &block)
181
- self.invocations << {
182
- :name => name.to_sym,
183
- :options => options,
184
- :block => block
185
- }
178
+ self.invocations << InvocationDescription.new(name.to_sym, options, &block)
186
179
  end
187
180
 
188
181
  # Adds a template to this generator. Templates are named and can later be retrieved by that name.
@@ -196,8 +189,8 @@ module Templater
196
189
  # Source and destination can be set in a block, which makes it possible to call instance methods to
197
190
  # determine the correct source and/or desination.
198
191
  #
199
- # A hash of options can be passed, all of these options are matched against the options passed to the
200
- # generator.
192
+ # A hash of options can be passed, these options are matched against the options passed to the
193
+ # generator. Some special options, for callbacks for example, are also supported, see below.
201
194
  #
202
195
  # === Parameters
203
196
  # name<Symbol>:: The name of this template
@@ -206,6 +199,10 @@ module Templater
206
199
  # options<Hash>:: Options for this template
207
200
  # &block<Proc>:: A block to execute when the generator is instantiated
208
201
  #
202
+ # === Options
203
+ # :before<Symbol>:: Name of a method to execute before this template is invoked
204
+ # :after<Symbol>:: Name of a method to execute after this template is invoked
205
+ #
209
206
  # ==== Examples
210
207
  #
211
208
  # class MyGenerator < Templater::Generator
@@ -225,14 +222,11 @@ module Templater
225
222
  source, destination = args
226
223
  source, destination = source + 't', source if args.size == 1
227
224
 
228
- self.templates << {
229
- :name => name.to_sym,
230
- :options => options,
231
- :source => source,
232
- :destination => destination,
233
- :block => block,
234
- :render => true
235
- }
225
+ templates << ActionDescription.new(name, options) do |generator|
226
+ template = Actions::Template.new(generator, name, source, destination, options)
227
+ generator.instance_exec(template, &block) if block
228
+ template
229
+ end
236
230
  end
237
231
 
238
232
  # Adds a template that is not rendered using ERB, but copied directly. Unlike Templater::Generator.template
@@ -244,19 +238,20 @@ module Templater
244
238
  # destination<String>:: The destination where the result will be put.
245
239
  # options<Hash>:: Options for this template
246
240
  # &block<Proc>:: A block to execute when the generator is instantiated
241
+ #
242
+ # === Options
243
+ # :before<Symbol>:: Name of a method to execute before this template is invoked
244
+ # :after<Symbol>:: Name of a method to execute after this template is invoked
247
245
  def file(name, *args, &block)
248
246
  options = args.last.is_a?(Hash) ? args.pop : {}
249
247
  source, destination = args
250
248
  source, destination = source, source if args.size == 1
251
249
 
252
- self.files << {
253
- :name => name.to_sym,
254
- :options => options,
255
- :source => source,
256
- :destination => destination,
257
- :block => block,
258
- :render => false
259
- }
250
+ files << ActionDescription.new(name, options) do |generator|
251
+ file = Actions::File.new(generator, name, source, destination, options)
252
+ generator.instance_exec(file, &block) if block
253
+ file
254
+ end
260
255
  end
261
256
 
262
257
  # Adds an empty directory that will be created when the generator is run.
@@ -266,16 +261,19 @@ module Templater
266
261
  # destination<String>:: The destination where the empty directory will be created
267
262
  # options<Hash>:: Options for this empty directory
268
263
  # &block<Proc>:: A block to execute when the generator is instantiated
264
+ #
265
+ # === Options
266
+ # :before<Symbol>:: Name of a method to execute before this template is invoked
267
+ # :after<Symbol>:: Name of a method to execute after this template is invoked
269
268
  def empty_directory(name, *args, &block)
270
269
  options = args.last.is_a?(Hash) ? args.pop : {}
271
270
  destination = args.first
272
271
 
273
- self.empty_directories << {
274
- :name => name.to_sym,
275
- :destination => destination,
276
- :options => options,
277
- :block => block
278
- }
272
+ empty_directories << ActionDescription.new(name, options) do |generator|
273
+ directory = Actions::EmptyDirectory.new(generator, name, destination, options)
274
+ generator.instance_exec(directory, &block) if block
275
+ directory
276
+ end
279
277
  end
280
278
 
281
279
  # An easy way to add many templates to a generator, each item in the list is added as a
@@ -356,15 +354,14 @@ module Templater
356
354
  # === Returns
357
355
  # Array[Templater::Generator]:: an array of generator classes.
358
356
  def generators
357
+ generators = [self]
359
358
  if manifold
360
- generators = invocations.map do |i|
361
- generator = manifold.generator(i[:name])
359
+ generators += invocations.map do |i|
360
+ generator = manifold.generator(i.name)
362
361
  generator ? generator.generators : nil
363
362
  end
364
- generators.unshift(self).flatten.compact
365
- else
366
- [self]
367
363
  end
364
+ generators.flatten.compact
368
365
  end
369
366
 
370
367
  # This should return the directory where source templates are located. This method must be overridden in
@@ -401,10 +398,15 @@ module Templater
401
398
 
402
399
  # Initialize options to their default values.
403
400
  self.class.options.each do |option|
404
- @options[option[:name]] ||= option[:options][:default]
401
+ @options[option.name] ||= option.options[:default]
405
402
  end
406
403
 
407
404
  extract_arguments(*args)
405
+
406
+ # Initialize arguments to their default values.
407
+ self.class.arguments.each_with_index do |argument, i|
408
+ @arguments[i] ||= argument.options[:default]
409
+ end
408
410
 
409
411
  valid_arguments?
410
412
  end
@@ -418,7 +420,7 @@ module Templater
418
420
  # === Returns
419
421
  # Templater::Actions::Template:: The found template.
420
422
  def template(name)
421
- self.templates.find { |t| t.name == name }
423
+ templates.find { |t| t.name == name }
422
424
  end
423
425
 
424
426
  # Finds and returns the file of the given name. If that file's options don't match the generator
@@ -430,7 +432,7 @@ module Templater
430
432
  # === Returns
431
433
  # Templater::Actions::File:: The found file.
432
434
  def file(name)
433
- self.files.find { |f| f.name == name }
435
+ files.find { |f| f.name == name }
434
436
  end
435
437
 
436
438
  # Finds and returns all empty directories whose options match the generator options.
@@ -438,7 +440,7 @@ module Templater
438
440
  # === Returns
439
441
  # [Templater::Actions::EmptyDirectory]:: The found empty directories that generator creates.
440
442
  def empty_directory(name)
441
- self.empty_directories.find { |d| d.name == name }
443
+ empty_directories.find { |d| d.name == name }
442
444
  end
443
445
 
444
446
  # Finds and returns all templates whose options match the generator options.
@@ -446,10 +448,7 @@ module Templater
446
448
  # === Returns
447
449
  # [Templater::Actions::Template]:: The found templates.
448
450
  def templates
449
- self.class.templates.inject([]) do |templates, template|
450
- templates << Templater::Proxy.new(self, template).to_template if match_options?(template[:options])
451
- templates
452
- end
451
+ actions(:templates)
453
452
  end
454
453
 
455
454
  # Finds and returns all files whose options match the generator options.
@@ -457,10 +456,7 @@ module Templater
457
456
  # === Returns
458
457
  # [Templater::Actions::File]:: The found files.
459
458
  def files
460
- self.class.files.inject([]) do |files, file|
461
- files << Templater::Proxy.new(self, file).to_file if match_options?(file[:options])
462
- files
463
- end
459
+ actions(:files)
464
460
  end
465
461
 
466
462
  # Finds and returns all empty directories generator creates.
@@ -468,10 +464,7 @@ module Templater
468
464
  # === Returns
469
465
  # [Templater::Actions::File]:: The found files.
470
466
  def empty_directories
471
- self.class.empty_directories.inject([]) do |empty_directories, action|
472
- empty_directories << Templater::Proxy.new(self, action).to_empty_directory if match_options?(action[:options])
473
- empty_directories
474
- end
467
+ actions(:empty_directories)
475
468
  end
476
469
 
477
470
  # Finds and returns all templates whose options match the generator options.
@@ -481,18 +474,20 @@ module Templater
481
474
  def invocations
482
475
  return [] unless self.class.manifold
483
476
 
484
- self.class.invocations.inject([]) do |invocations, invocation|
485
- generator = self.class.manifold.generator(invocation[:name])
486
-
487
- if generator and match_options?(invocation[:options])
488
-
489
- if invocation[:block]
490
- invocations << instance_exec(generator, &invocation[:block])
491
- else
492
- invocations << generator.new(destination_root, options, *@arguments)
493
- end
494
- end
495
- invocations
477
+ self.class.invocations.map do |invocation|
478
+ invocation.get(self) if match_options?(invocation.options)
479
+ end.compact
480
+ end
481
+
482
+ # Finds and returns all templates and files for this generators whose options match its options.
483
+ #
484
+ # === Returns
485
+ # [Templater::Actions::*]:: The found templates and files.
486
+ def actions(type=nil)
487
+ actions = type ? self.class.actions[type] : self.class.actions.values.flatten
488
+ actions.inject([]) do |actions, description|
489
+ actions << description.compile(self) if match_options?(description.options)
490
+ actions
496
491
  end
497
492
  end
498
493
 
@@ -501,15 +496,15 @@ module Templater
501
496
  #
502
497
  # === Returns
503
498
  # [Templater::Actions::File, Templater::Actions::Template]:: The found templates and files.
504
- def actions
505
- actions = templates + files + empty_directories
506
- actions += invocations.map { |i| i.actions }
507
- actions.flatten
499
+ def all_actions(type=nil)
500
+ all_actions = actions(type)
501
+ all_actions += invocations.map { |i| i.all_actions(type) }
502
+ all_actions.flatten
508
503
  end
509
504
 
510
505
  # Invokes the templates for this generator
511
506
  def invoke!
512
- actions.each { |t| t.invoke! }
507
+ all_actions.each { |t| t.invoke! }
513
508
  end
514
509
 
515
510
  # Renders all actions in this generator. Use this to verify that rendering templates raises no errors.
@@ -517,7 +512,7 @@ module Templater
517
512
  # === Returns
518
513
  # [String]:: The results of the rendered actions
519
514
  def render!
520
- actions.map { |t| t.render }
515
+ all_actions.map { |t| t.render }
521
516
  end
522
517
 
523
518
  # Returns this generator's source root
@@ -542,18 +537,17 @@ module Templater
542
537
 
543
538
  protected
544
539
 
545
- def set_argument(n, arg)
546
- argument = self.class.arguments[n]
547
- valid_argument?(arg, argument[:options], &argument[:block])
548
- @arguments[n] = arg
540
+ def set_argument(n, value)
541
+ self.class.arguments[n].valid?(value)
542
+ @arguments[n] = value
549
543
  end
550
544
 
551
545
  def get_argument(n)
552
- @arguments[n] || self.class.arguments[n][:options][:default]
546
+ @arguments[n]
553
547
  end
554
548
 
555
- def set_option(name, arg)
556
- @options[name] = arg
549
+ def set_option(name, value)
550
+ @options[name] = value
557
551
  end
558
552
 
559
553
  def get_option(name)
@@ -561,30 +555,14 @@ module Templater
561
555
  end
562
556
 
563
557
  def match_options?(options)
564
- options.all? { |key, value| self.send(key) == value }
565
- end
566
-
567
- def valid_argument?(arg, options, &block)
568
- if arg.nil? and options[:required]
569
- raise Templater::TooFewArgumentsError
570
- elsif not arg.nil?
571
- if options[:as] == :hash and not arg.is_a?(Hash)
572
- raise Templater::MalformattedArgumentError, "Expected the argument to be a Hash, but was '#{arg.inspect}'"
573
- elsif options[:as] == :array and not arg.is_a?(Array)
574
- raise Templater::MalformattedArgumentError, "Expected the argument to be an Array, but was '#{arg.inspect}'"
575
- end
576
-
577
- invalid = catch :invalid do
578
- yield if block_given?
579
- throw :invalid, :not_invalid
580
- end
581
- raise Templater::ArgumentError, invalid unless invalid == :not_invalid
558
+ options.all? do |key, value|
559
+ key.to_sym.in?(Templater::ACTION_RESERVED_OPTIONS) or self.send(key) == value
582
560
  end
583
561
  end
584
-
562
+
585
563
  def valid_arguments?
586
564
  self.class.arguments.each_with_index do |arg, i|
587
- valid_argument?(@arguments[i], arg[:options], &arg[:block])
565
+ arg.valid?(@arguments[i])
588
566
  end
589
567
  end
590
568
 
@@ -597,7 +575,7 @@ module Templater
597
575
 
598
576
  # When one of the arguments has :as set to :hash or :list, the remaining arguments should be consumed
599
577
  # and converted to a Hash or an Array respectively
600
- case expected[:options][:as]
578
+ case expected.options[:as]
601
579
  when :hash
602
580
  if arg.is_a?(String)
603
581
  pairs = args[i..-1]