templater 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -2,12 +2,24 @@
2
2
 
3
3
  Templater is a system for generating files. Templater has the ability to both copy files from A to B and also to render templates using ERB. Templater consists of four parts:
4
4
 
5
- - Actions (Files and Templates)
6
- - Generators
7
- - Manifolds
8
- - The command line interface
5
+ - Actions (File copying routines, templates generation and directories creation routines).
6
+ - Generators (set of rules).
7
+ - Manifolds (generator suites).
8
+ - The command line interface.
9
+
10
+ Hierarchy is pretty simple: manifold has one or many public and private generators. Public ones are supposed to be called
11
+ by end user. Generators have one or more action that specify what they do, where they take files, how they name resulting
12
+ files and so forth.
13
+
14
+
15
+ == Idea behind Templater
16
+
17
+ Templater keeps declarations of generator inside it's body. You vary source and destinations of each action the way you want.
18
+ There's no magical %variables% in filenames sprinkled across your generator, generator rules are simple, readable, concise
19
+ and are in one place making it easy to take existing generator as example.
20
+
21
+ This is how Templater is different from some other code/components generation frameworks.
9
22
 
10
- Each manifold has many generator, and each generator has many actions.
11
23
 
12
24
  == Example
13
25
 
@@ -18,14 +30,24 @@ This is how to create a very simple system for generating things:
18
30
  extend Templater::Manifold
19
31
 
20
32
  class BlogGenerator < Templater::Generator
21
-
33
+ # directory this generator uses as source root when searching
34
+ # for files, directories, templates
22
35
  def self.source_root
23
36
  File.join(File.dirname(__FILE__), 'templates')
24
37
  end
25
38
 
39
+ # uses blog.rbt template (note conventional trailing t)
40
+ # placing resulting file to blog.rb relatively to
41
+ # destination root
26
42
  template :blog, 'blog.rb'
43
+
44
+ # does simple copy of me.jpg placing resulting file to me.jpg relatively to
45
+ # destination root
27
46
  file :me, 'me.jpg'
28
47
 
48
+ # creates empty directory public/javascripts relatively to
49
+ # destination root
50
+ empty_directory :javascripts, File.join("public", "javascripts")
29
51
  end
30
52
 
31
53
  class WikiGenerator < Templater::Generator
@@ -38,17 +60,19 @@ This is how to create a very simple system for generating things:
38
60
  file :img, 'wiki.jpg'
39
61
 
40
62
  end
41
-
63
+
64
+ # The generators are added to the manifold, and assigned the names 'wiki' and 'blog'.
65
+ # So you can call them <script name> blog merb-blog-in-10-minutes and
66
+ # <script name> blog merb-wiki-in-10-minutes, respectively
42
67
  add :blog, BlogGenerator
43
68
  add :wiki, WikiGenerator
44
69
 
45
70
  end
46
71
 
72
+ # registers manifold with command line interface
47
73
  MyGenerators.run_cli Dir.pwd, 'my_generators', '0.1', ARGV
48
74
 
49
- The generator classes override the source_root method to specify where templates will be located. All subclasses of Templater::Generator that have any actions must do this. The +template+ and +file+ methods add actions to the generator. In the first case, a template that is rendered with ERB and then put in its destination location, in the other case a file that is copied.
50
-
51
- The generators are added to the manifold, and assigned the names 'wiki' and 'blog'.
75
+ The generator classes override the source_root method to specify where templates will be located. All subclasses of Templater::Generator that have any actions must do this. The +template+ and +file+ methods add actions to the generator. In the first case, a template that is rendered with ERB and then put in its destination location, in the other case a file that is copied. +empty_directory+ action creates empty directory under destination root.
52
76
 
53
77
  Neither manifolds or generators actually do anything by themselves, they are just abstract represenations. The last line invokes the command-line-interface, which fetches the desired generator, tells it to render its templates and checks with the user if there are any problems. The generators can easily be used without the command-line-interface, so it is easy to construct an alternative interface.
54
78
 
@@ -91,6 +115,9 @@ This will search the source root and add all files as actions.
91
115
 
92
116
  == Templates
93
117
 
118
+ Templates are processed using generator instance scope as binding, so every instance method available on your
119
+ generator is available in template body.
120
+
94
121
  There are a lot of ways of adding templates:
95
122
 
96
123
  template :one_argument, 'source_and_destination.rb'
@@ -135,17 +162,20 @@ A generator for creating a model class, such as it used by Merb or Rails, could
135
162
  def self.source_root
136
163
  File.join(super, 'model')
137
164
  end
138
-
165
+
166
+ # description end users see next to generator name
139
167
  desc <<-DESC
140
168
  This is a model generator
141
169
  DESC
142
-
170
+
171
+ # options generator takes, their metadata, like description or arguments type
143
172
  option :testing_framework, :desc => 'Specify which testing framework to use (spec, test_unit)'
144
173
  option :orm, :desc => 'Specify which Object-Relation Mapper to use (none, activerecord, datamapper, sequel)'
145
-
174
+
175
+ # you may use shortcuts for first 4 option positions
146
176
  first_argument :name, :required => true
147
177
  second_argument :attributes, :as => :hash, :default => {}
148
-
178
+
149
179
  invoke :migration do |generator|
150
180
  generator.new(destination_root, options.merge(:model => true), name, attributes)
151
181
  end
data/Rakefile CHANGED
@@ -12,6 +12,11 @@ EMAIL = "jonas.nicklas@gmail.com"
12
12
  HOMEPAGE = "http://templater.rubyforge.org/"
13
13
  SUMMARY = "File generation system"
14
14
 
15
+
16
+ #
17
+ # ==== Gemspec and installation
18
+ #
19
+
15
20
  spec = Gem::Specification.new do |s|
16
21
  s.name = NAME
17
22
  s.version = Templater::VERSION
@@ -38,9 +43,15 @@ Rake::GemPackageTask.new(spec) do |pkg|
38
43
  pkg.gem_spec = spec
39
44
  end
40
45
 
46
+ desc "removes any generated content"
47
+ task :clean do
48
+ FileUtils.rm_rf "clobber/*"
49
+ FileUtils.rm_rf "pkg/*"
50
+ end
51
+
41
52
  desc "install the plugin locally"
42
- task :install => [:package] do
43
- sh %{sudo gem install pkg/#{NAME}-#{VERSION} --no-update-sources}
53
+ task :install => [:clean, :package] do
54
+ sh %{sudo gem install pkg/#{NAME}-#{Templater::VERSION} --no-update-sources}
44
55
  end
45
56
 
46
57
  desc "create a gemspec file"
@@ -54,11 +65,15 @@ namespace :jruby do
54
65
 
55
66
  desc "Run :package and install the resulting .gem with jruby"
56
67
  task :install => :package do
57
- sh %{#{SUDO} jruby -S gem install pkg/#{NAME}-#{Merb::VERSION}.gem --no-rdoc --no-ri}
68
+ sh %{#{SUDO} jruby -S gem install pkg/#{NAME}-#{Templater::VERSION}.gem --no-rdoc --no-ri}
58
69
  end
59
70
 
60
71
  end
61
72
 
73
+ #
74
+ # ==== RDoc
75
+ #
76
+
62
77
  desc 'Generate documentation for Templater.'
63
78
  Rake::RDocTask.new(:doc) do |rdoc|
64
79
  rdoc.rdoc_dir = 'doc'
@@ -67,4 +82,23 @@ Rake::RDocTask.new(:doc) do |rdoc|
67
82
  rdoc.rdoc_files.include('README')
68
83
  rdoc.rdoc_files.include('LICENSE')
69
84
  rdoc.rdoc_files.include('lib/**/*.rb')
70
- end
85
+ end
86
+
87
+ #
88
+ # ==== RCov
89
+ #
90
+
91
+ desc "Run coverage suite"
92
+ task :rcov do
93
+ require 'fileutils'
94
+ FileUtils.rm_rf("coverage") if File.directory?("coverage")
95
+ FileUtils.mkdir("coverage")
96
+ path = File.expand_path(Dir.pwd)
97
+ files = Dir["spec/**/*_spec.rb"]
98
+ files.each do |spec|
99
+ puts "Getting coverage for #{File.expand_path(spec)}"
100
+ command = %{rcov #{File.expand_path(spec)} --aggregate #{path}/coverage/data.data}
101
+ command += " --no-html" unless spec == files.last
102
+ `#{command} 2>&1`
103
+ end
104
+ end
@@ -10,6 +10,7 @@ require 'facets'
10
10
  require path + 'capture_helpers'
11
11
  require path + 'template'
12
12
  require path + 'file'
13
+ require path + 'empty_directory'
13
14
  require path + 'generator'
14
15
  require path + 'proxy'
15
16
  require path + 'manifold'
@@ -39,6 +40,6 @@ module Templater
39
40
  class MalformattedArgumentError < ArgumentError #:nodoc:
40
41
  end
41
42
 
42
- VERSION = '0.1.1'
43
+ VERSION = '0.1.2'
43
44
 
44
- end
45
+ end
@@ -57,8 +57,12 @@ module Templater
57
57
  # Try to instantiate a generator, if the arguments to it were incorrect: show a help message
58
58
  begin
59
59
  @generator = @generator_class.new(@destination_root, @options, *arguments)
60
- rescue Templater::ArgumentError
61
- self.help
60
+ rescue Templater::ArgumentError => e
61
+ if @options[:debug]
62
+ raise e
63
+ else
64
+ self.help
65
+ end
62
66
  end
63
67
 
64
68
  if @options[:pretend]
@@ -71,6 +71,10 @@ module Templater
71
71
  opts.on("--version", "Show the version") do
72
72
  options[:version] = true
73
73
  end
74
+
75
+ opts.on("--debug", "Do not catch errors") do
76
+ options[:debug] = true
77
+ end
74
78
 
75
79
  end
76
80
 
@@ -0,0 +1,53 @@
1
+ module Templater
2
+ class EmptyDirectory
3
+
4
+ attr_reader :name, :destination
5
+
6
+ def initialize(name, destination)
7
+ @name, @destination = name, destination
8
+ end
9
+
10
+ # Returns the destination path relative to Dir.pwd. This is useful for prettier output in interfaces
11
+ # where the destination root is Dir.pwd.
12
+ #
13
+ # === Returns
14
+ # String:: The destination relative to Dir.pwd
15
+ def relative_destination
16
+ @destination.sub(::Dir.pwd + ::File::SEPARATOR, '')
17
+ end
18
+
19
+ # Returns the contents of the source file as a String
20
+ #
21
+ # === Returns
22
+ # String:: The source file.
23
+ def render
24
+ ::File.read(source)
25
+ end
26
+
27
+ # Checks if the destination file already exists.
28
+ #
29
+ # === Returns
30
+ # Boolean:: true if the file exists, false otherwise.
31
+ def exists?
32
+ ::File.exists?(destination)
33
+ end
34
+
35
+ # For empty directory this is in fact alias for exists? method.
36
+ #
37
+ # === Returns
38
+ # Boolean:: true if it is identical, false otherwise.
39
+ def identical?
40
+ exists?
41
+ end
42
+
43
+ # Renders the template and copies it to the destination.
44
+ def invoke!
45
+ ::FileUtils.mkdir_p(destination)
46
+ end
47
+
48
+ # removes the destination file
49
+ def revoke!
50
+ ::FileUtils.rm_rf(::File.expand_path(destination))
51
+ end
52
+ end # EmptyDirectory
53
+ end # Templater
@@ -21,7 +21,7 @@ module Templater
21
21
  # === Returns
22
22
  # String:: The destination relative to Dir.pwd
23
23
  def relative_destination
24
- @destination.sub(::Dir.pwd + '/', '')
24
+ @destination.sub(::Dir.pwd + ::File::SEPARATOR, '')
25
25
  end
26
26
 
27
27
  # Returns the contents of the source file as a String
@@ -60,4 +60,4 @@ module Templater
60
60
  end
61
61
 
62
62
  end
63
- end
63
+ end
@@ -38,6 +38,16 @@ module Templater
38
38
  # Array[Hash{Symbol=>Object}]:: A list of invocations
39
39
  def invocations; @invocations ||= []; end
40
40
 
41
+ # Returns an array of hashes, where each hash describes a single empty directory created by generator.
42
+ #
43
+ # ==== Returns
44
+ # Array[Hash{Symbol=>Object}]:: A list of empty directories created by generator.
45
+ def empty_directories; @empty_directories ||= []; end
46
+
47
+
48
+
49
+
50
+
41
51
  # A shorthand method for adding the first argument, see +Templater::Generator.argument+
42
52
  def first_argument(*args); argument(0, *args); end
43
53
 
@@ -50,6 +60,10 @@ module Templater
50
60
  # A shorthand method for adding the fourth argument, see +Templater::Generator.argument+
51
61
  def fourth_argument(*args); argument(3, *args); end
52
62
 
63
+
64
+
65
+
66
+
53
67
  # If the argument is omitted, simply returns the description for this generator, otherwise
54
68
  # sets the description to the passed string.
55
69
  #
@@ -244,6 +258,15 @@ module Templater
244
258
  :render => false
245
259
  }
246
260
  end
261
+
262
+ def empty_directory(name, path = nil)
263
+ path = name.to_s unless path
264
+
265
+ self.empty_directories << {
266
+ :name => name,
267
+ :destination => path
268
+ }
269
+ end
247
270
 
248
271
  # An easy way to add many templates to a generator, each item in the list is added as a
249
272
  # template. The provided list can be either an array of Strings or a Here-Doc with templates
@@ -304,11 +327,12 @@ module Templater
304
327
  # template_destination<Array[String]>:: A list of extensions. If a file has one of these
305
328
  # extensions, it is considered a template and will be rendered with ERB.
306
329
  # options<Hash{Symbol=>Object}>:: A list of options.
307
- def glob!(dir = nil, template_extensions = %w(rb css js erb html yml), options={})
330
+ def glob!(dir = nil, template_extensions = %w(rb css js erb html yml Rakefile TODO LICENSE README), options={})
308
331
  ::Dir[::File.join(source_root, dir.to_s, '**/*')].each do |action|
309
332
  unless ::File.directory?(action)
310
333
  action = action.sub("#{source_root}/", '')
311
- if template_extensions.include?(::File.extname(action)[1..-1])
334
+
335
+ if template_extensions.include?(::File.extname(action)[1..-1]) or template_extensions.include?(::File.basename(action))
312
336
  template(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
313
337
  else
314
338
  file(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
@@ -342,7 +366,12 @@ module Templater
342
366
  raise Templater::SourceNotSpecifiedError, "Subclasses of Templater::Generator must override the source_root method, to specify where source templates are located."
343
367
  end
344
368
 
345
- end
369
+ end # end of eigenclass
370
+
371
+
372
+ #
373
+ # ==== Instance methods
374
+ #
346
375
 
347
376
  attr_accessor :destination_root, :arguments, :options
348
377
 
@@ -394,6 +423,14 @@ module Templater
394
423
  def file(name)
395
424
  self.files.find { |f| f.name == name }
396
425
  end
426
+
427
+ # Finds and returns all empty directories whose options match the generator options.
428
+ #
429
+ # === Returns
430
+ # [Templater::EmptyDirectory]:: The found empty directories that generator creates.
431
+ def empty_directory(name)
432
+ self.empty_directories.find { |d| d.name == name }
433
+ end
397
434
 
398
435
  # Finds and returns all templates whose options match the generator options.
399
436
  #
@@ -418,6 +455,16 @@ module Templater
418
455
  end
419
456
  files.compact
420
457
  end
458
+
459
+ # Finds and returns all empty directories generator creates.
460
+ #
461
+ # === Returns
462
+ # [Templater::File]:: The found files.
463
+ def empty_directories
464
+ self.class.empty_directories.map do |t|
465
+ Templater::Proxy.new(self, t[:name], nil, t[:destination], &t[:block]).to_empty_directory
466
+ end.compact
467
+ end
421
468
 
422
469
  # Finds and returns all templates whose options match the generator options.
423
470
  #
@@ -445,7 +492,7 @@ module Templater
445
492
  # === Returns
446
493
  # [Templater::File, Templater::Template]:: The found templates and files.
447
494
  def actions
448
- actions = templates + files
495
+ actions = templates + files + empty_directories
449
496
  actions += invocations.map { |i| i.actions }
450
497
  actions.flatten
451
498
  end
@@ -557,4 +604,4 @@ module Templater
557
604
  end
558
605
  end
559
606
 
560
- end
607
+ end
@@ -24,6 +24,11 @@ module Templater
24
24
  instance_eval(&@block) if @block
25
25
  Templater::File.new(@name, get_source, get_destination)
26
26
  end
27
+
28
+ def to_empty_directory
29
+ instance_eval(&@block) if @block
30
+ Templater::EmptyDirectory.new(@name, get_destination)
31
+ end
27
32
 
28
33
  def method_missing(method, *args, &block)
29
34
  if @generator
@@ -52,4 +57,4 @@ module Templater
52
57
 
53
58
  end
54
59
 
55
- end
60
+ end
@@ -25,7 +25,7 @@ module Templater
25
25
  # === Returns
26
26
  # String:: The destination relative to Dir.pwd
27
27
  def relative_destination
28
- @destination.sub(::Dir.pwd + '/', '')
28
+ @destination.sub(::Dir.pwd + ::File::SEPARATOR, '')
29
29
  end
30
30
 
31
31
  # Renders the template using ERB and returns the result as a String.
@@ -64,4 +64,4 @@ module Templater
64
64
  end
65
65
 
66
66
  end
67
- end
67
+ end
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Templater::EmptyDirectory, '.new' do
4
+ it "sets name and destination" do
5
+ Templater::EmptyDirectory.new(:monkey, '/path/to/destination').
6
+ name.should == :monkey
7
+ end
8
+
9
+ it 'sets destination' do
10
+ Templater::EmptyDirectory.new(:monkey, '/path/to/destination').
11
+ destination.should == '/path/to/destination'
12
+ end
13
+ end
14
+
15
+
16
+
17
+
18
+ describe Templater::EmptyDirectory, '#relative_destination' do
19
+ it "returns destination relative to the pwd" do
20
+ Dir.stub!(:pwd).and_return('/path/to')
21
+ file = Templater::EmptyDirectory.new(:monkey, '/path/to/destination/with/some/more/subdirs')
22
+ file.relative_destination.should == 'destination/with/some/more/subdirs'
23
+ end
24
+ end
25
+
26
+
27
+
28
+
29
+ describe Templater::EmptyDirectory, '#render' do
30
+ it 'does nothing for empty directories?'
31
+ end
32
+
33
+
34
+
35
+
36
+ describe Templater::EmptyDirectory, '#exists?' do
37
+
38
+ it "should exist if the destination file exists" do
39
+ file = Templater::EmptyDirectory.new(:monkey, result_path('erb.rbs'))
40
+ file.should be_exists
41
+ end
42
+
43
+ it "should not exist if the destination file does not exist" do
44
+ file = Templater::EmptyDirectory.new(:monkey, result_path('some_weird_file.rbs'))
45
+ file.should_not be_exists
46
+ end
47
+ end
48
+
49
+
50
+
51
+ describe Templater::EmptyDirectory, '#identical' do
52
+ it "should not be identical if the destination file doesn't exist" do
53
+ file = Templater::EmptyDirectory.new(:monkey, result_path('some_weird/path/that_does/not_exist'))
54
+ file.should_not be_identical
55
+ end
56
+
57
+ it "should not be identical if the destination file is not identical to the source file" do
58
+ file = Templater::EmptyDirectory.new(:monkey, result_path('simple_erb.rbs'))
59
+ file.should be_exists
60
+ file.should be_identical
61
+ end
62
+
63
+ it "should be identical if the destination file is identical to the source file" do
64
+ file= Templater::EmptyDirectory.new(:monkey, result_path('file.rbs'))
65
+ file.should be_exists
66
+ file.should be_identical
67
+ end
68
+ end
69
+
70
+
71
+
72
+ describe Templater::EmptyDirectory, '#invoke!' do
73
+ it "should copy the source file to the destination" do
74
+ file = Templater::EmptyDirectory.new(:monkey, result_path('path/to/subdir/test2.rbs'))
75
+
76
+ file.invoke!
77
+
78
+ File.exists?(result_path('path/to/subdir/test2.rbs')).should be_true
79
+
80
+ # cleanup
81
+ FileUtils.rm_rf(result_path('path'))
82
+ end
83
+ end
84
+
85
+
86
+
87
+ describe Templater::EmptyDirectory, '#revoke!' do
88
+ it "removes the destination directory" do
89
+ file = Templater::EmptyDirectory.new(:monkey, result_path('path/to/empty/subdir/'))
90
+
91
+ file.invoke!
92
+ File.exists?(result_path('path/to/empty/subdir/')).should be_true
93
+
94
+ file.revoke!
95
+ File.exists?(result_path('path/to/empty/subdir/')).should be_false
96
+ end
97
+ end