templater 0.1.6 → 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.
@@ -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]