templater 0.1.6 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +13 -7
- data/Rakefile +30 -0
- data/lib/templater.rb +4 -2
- data/lib/templater/actions/action.rb +43 -0
- data/lib/templater/actions/empty_directory.rb +16 -14
- data/lib/templater/actions/file.rb +16 -21
- data/lib/templater/actions/template.rb +13 -22
- data/lib/templater/cli/generator.rb +13 -9
- data/lib/templater/core_ext/string.rb +1 -1
- data/lib/templater/description.rb +61 -0
- data/lib/templater/discovery.rb +16 -9
- data/lib/templater/generator.rb +115 -137
- data/lib/templater/spec/helpers.rb +1 -1
- data/spec/actions/empty_directory_spec.rb +105 -0
- data/spec/actions/file_spec.rb +112 -0
- data/spec/actions/template_spec.rb +141 -0
- data/spec/generator/actions_spec.rb +92 -43
- data/spec/generator/arguments_spec.rb +7 -3
- data/spec/generator/empty_directories_spec.rb +12 -4
- data/spec/generator/files_spec.rb +17 -26
- data/spec/generator/templates_spec.rb +17 -28
- data/spec/spec_helper.rb +24 -1
- metadata +18 -6
- data/lib/templater/proxy.rb +0 -62
- data/spec/empty_directory_spec.rb +0 -97
- data/spec/file_spec.rb +0 -97
- data/spec/template_spec.rb +0 -137
data/lib/templater/discovery.rb
CHANGED
@@ -53,18 +53,25 @@ module Templater
|
|
53
53
|
|
54
54
|
protected
|
55
55
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
67
|
-
path = ::File.join(
|
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
|
data/lib/templater/generator.rb
CHANGED
@@ -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
|
46
|
+
# Returns an Hash that maps the type of action to a list of ActionDescriptions.
|
42
47
|
#
|
43
48
|
# ==== Returns
|
44
|
-
#
|
45
|
-
def
|
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
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
# A
|
55
|
-
def
|
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
|
-
#
|
61
|
-
|
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,
|
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
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
361
|
-
generator = manifold.generator(i
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
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
|
505
|
-
|
506
|
-
|
507
|
-
|
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
|
-
|
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
|
-
|
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,
|
546
|
-
|
547
|
-
|
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]
|
546
|
+
@arguments[n]
|
553
547
|
end
|
554
548
|
|
555
|
-
def set_option(name,
|
556
|
-
@options[name] =
|
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?
|
565
|
-
|
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
|
-
|
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
|
578
|
+
case expected.options[:as]
|
601
579
|
when :hash
|
602
580
|
if arg.is_a?(String)
|
603
581
|
pairs = args[i..-1]
|