tap-gen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History ADDED
@@ -0,0 +1,4 @@
1
+ == 0.1.0 / 2009-05-25
2
+
3
+ Extracted from Tap-0.12.0, with updates for
4
+ Tap-0.17.0
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009, Regents of the University of Colorado.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,57 @@
1
+ = {Tap Generator}[http://tap.rubyforge.org/tap-gen]
2
+
3
+ gen.er.a.tor n. a thing that generates something
4
+
5
+ Generators for Tap.
6
+
7
+ == Description
8
+
9
+ Provides generators for Tap. Generators are subclasses of Task are therefore
10
+ easy to configure, subclass, and distribute.
11
+ {Tap-Generator}[http://tap.rubyforge.org/tap-gen] is a part of the
12
+ {Tap-Suite}[http://tap.rubyforge.org/tap-suite]. Check out these links for
13
+ documentation, development, and bug tracking.
14
+
15
+ * Website[http://tap.rubyforge.org]
16
+ * Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/tickets]
17
+ * Github[http://github.com/bahuvrihi/tap/tree/master]
18
+ * {Google Group}[http://groups.google.com/group/ruby-on-tap]
19
+
20
+ == Usage
21
+
22
+ Get started:
23
+
24
+ % tap generate root sample
25
+ % cd sample
26
+ % tap generate task goodnight
27
+ % tap run -- goodnight moon
28
+
29
+ Get some help:
30
+
31
+ % tap generate --help
32
+ % tap generate task --help
33
+
34
+ Roll your own:
35
+
36
+ % tap generate generator thing
37
+
38
+ Roll it back:
39
+
40
+ % tap destroy generator thing
41
+ % tap destroy task goodnight
42
+ % cd ..
43
+ % tap destroy root sample
44
+
45
+ == Installation
46
+
47
+ Tap-Generator is available as a gem on
48
+ RubyForge[http://rubyforge.org/projects/tap]. Use:
49
+
50
+ % gem install tap-gen
51
+
52
+ == Info
53
+
54
+ Copyright (c) 2009, Regents of the University of Colorado.
55
+ Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
56
+ Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
57
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
data/cmd/destroy.rb ADDED
@@ -0,0 +1,21 @@
1
+ # usage: tap destroy GENERATOR ...
2
+ #
3
+ # Runs a generator in reverse. Each generator works a little differently; the
4
+ # best way to figure out what a generator does is to use --help. For example:
5
+ #
6
+ # % tap generate root --help
7
+ #
8
+
9
+ require 'tap/generator/exe'
10
+ require 'tap/generator/destroy'
11
+
12
+ env = Tap::Env.instance
13
+ env.extend Tap::Generator::Exe
14
+
15
+ env.run(Tap::Generator::Destroy, ARGV) do
16
+ puts Lazydoc.usage(__FILE__)
17
+ puts
18
+ puts "generators:"
19
+ puts env.manifest('generator').summarize
20
+ exit(1)
21
+ end
data/cmd/generate.rb ADDED
@@ -0,0 +1,21 @@
1
+ # usage: tap generate GENERATOR ...
2
+ #
3
+ # Runs a generator. Each generator works a little differently; the best way to
4
+ # figure out what a generator does is to use --help. For example:
5
+ #
6
+ # % tap generate root --help
7
+ #
8
+
9
+ require 'tap/generator/exe'
10
+ require 'tap/generator/generate'
11
+
12
+ env = Tap::Env.instance
13
+ env.extend Tap::Generator::Exe
14
+
15
+ env.run(Tap::Generator::Generate, ARGV) do
16
+ puts Lazydoc.usage(__FILE__)
17
+ puts
18
+ puts "generators:"
19
+ puts env.manifest('generator').summarize
20
+ exit(1)
21
+ end
@@ -0,0 +1,13 @@
1
+ module Tap
2
+ module Generator
3
+
4
+ # A special type of Lazydoc::Arguments that shifts off the standard 'm'
5
+ # argument on generator manifest methods, to properly reflect how may
6
+ # arguments the generator should receive.
7
+ class Arguments < Lazydoc::Arguments
8
+ def arguments(shift_manifest_arg=true)
9
+ shift_manifest_arg ? @arguments[1..-1] : @arguments
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,180 @@
1
+ require 'tap'
2
+ require 'tap/generator/manifest'
3
+ require 'tap/generator/arguments'
4
+
5
+ module Tap
6
+ module Generator
7
+
8
+ # :startdoc:::-
9
+ # Base provides the basic structure of a generator and custom generators
10
+ # inherit from it. Base is patterned after the {Ruby on Rails}[http://rubyonrails.org/]
11
+ # generators, but obviously takes on all the advantages of Tasks.
12
+ #
13
+ # === Usage
14
+ #
15
+ # Tap generators define a manifest method that defines what files and
16
+ # directories are created by the generator. Then, at execution time,
17
+ # a mixin with the appropriate funtion (ie Generate or Destroy) is
18
+ # overlaid to figure out how to roll those actions forward or backwards.
19
+ #
20
+ # Generators are identified using the ::generator flag rather than ::task,
21
+ # so that generators are available to the generate/destroy commands and
22
+ # not run.
23
+ #
24
+ # Typically, generators live in a directory structure like this:
25
+ #
26
+ # root
27
+ # |- lib
28
+ # | `- sample.rb
29
+ # |
30
+ # `- templates
31
+ # `- sample
32
+ # `- template_file.erb
33
+ #
34
+ # Tap generators keep templates out of lib and under templates, in a
35
+ # directory is named after the generator class. Generators themselves
36
+ # take the form:
37
+ #
38
+ # [sample.rb]
39
+ # require 'tap/generator/base'
40
+ #
41
+ # # ::generator generates a directory, and two files
42
+ # #
43
+ # # An extended description of the
44
+ # # generator goes here...
45
+ # #
46
+ # class Sample < Tap::Generator::Base
47
+ #
48
+ # config :key, 'value' # a sample config
49
+ #
50
+ # def manifest(m, *args)
51
+ # # make a directory
52
+ # m.directory('path/to/dir')
53
+ #
54
+ # # make a file
55
+ # m.file('path/to/file.txt') do |file|
56
+ # file << "some content"
57
+ # end
58
+ #
59
+ # # template a file
60
+ # m.template('path/to/result.txt', 'template_file.erb', config.to_hash)
61
+ # end
62
+ # end
63
+ #
64
+ # The arguments that a generator receives are specified by manifest (minus
65
+ # the 'm' argument which is standard) rather than process. Creating
66
+ # directories and files is straightforward, as above. Template renders the
67
+ # erb source file using attributes specified in the last argument; in the
68
+ # example template uses the generator configurations.
69
+ #
70
+ # :startdoc:::+
71
+ class Base < Tap::Task
72
+ lazy_attr :manifest, 'generator'
73
+ lazy_attr :args, :manifest
74
+ lazy_register :manifest, Arguments
75
+
76
+ config :destination_root, Dir.pwd # The destination root directory
77
+ config :pretend, false, &c.flag # Run but rollback any changes.
78
+ config :force, false, &c.flag # Overwrite files that already exist.
79
+ config :skip, false, &c.flag # Skip files that already exist.
80
+
81
+ # The generator-specific templates directory. By default:
82
+ # 'templates/path/to/name' for 'lib/path/to/name.rb'
83
+ attr_accessor :template_dir
84
+
85
+ # The IO used to pull prompt inputs (default: $stdin)
86
+ attr_accessor :prompt_in
87
+
88
+ # The IO used to prompt users for input (default: $stdout)
89
+ attr_accessor :prompt_out
90
+
91
+ def initialize(config={}, app=Tap::App.instance)
92
+ super
93
+ @prompt_in = $stdin
94
+ @prompt_out = $stdout
95
+ @template_dir = File.expand_path("templates/#{self.class.to_s.underscore}")
96
+ end
97
+
98
+ # Builds the manifest, then executes the actions of the manifest.
99
+ # Process returns the results of iterate, which normally will be
100
+ # an array of files and directories created (or destroyed) by self.
101
+ def process(*argv)
102
+ actions = []
103
+ manifest(Manifest.new(actions), *argv)
104
+
105
+ iterate(actions) do |action, args, block|
106
+ send(action, *args, &block)
107
+ end
108
+ end
109
+
110
+ # Overridden in subclasses to add actions to the input Manifest.
111
+ # Any arguments passed to process will be passed to manifest
112
+ # unchanged.
113
+ def manifest(m, *argv)
114
+ raise NotImplementedError
115
+ end
116
+
117
+ # Peforms each of the input actions in order, and collects the
118
+ # results. The process method returns these results.
119
+ def iterate(actions)
120
+ actions.collect {|action| yield(action) }
121
+ end
122
+
123
+ # Constructs a path relative to destination_root.
124
+ def path(*paths)
125
+ File.expand_path(File.join(*paths), destination_root)
126
+ end
127
+
128
+ # Peforms a directory action (ex generate or destroy). Must be
129
+ # overridden by one of the action mixins (ex Generate or Destroy).
130
+ def directory(target, options={})
131
+ raise NotImplementedError
132
+ end
133
+
134
+ # Peforms a file action (ex generate or destroy). Calls to file specify
135
+ # input for a target by providing a block; the block recieves an IO and
136
+ # pushes content to it. Must be overridden by one of the action mixins
137
+ # (ex Generate or Destroy).
138
+ def file(target, options={}) # :yields: io
139
+ raise NotImplementedError
140
+ end
141
+
142
+ # Makes (or destroys) the root and each of the targets, relative
143
+ # to root. Options are passed onto directory.
144
+ def directories(root, targets, options={})
145
+ directory(root, options)
146
+ targets.each do |target|
147
+ directory(File.join(root, target), options)
148
+ end
149
+ end
150
+
151
+ # Makes (or destroys) the target by templating the source using
152
+ # the specified attributes. Source is expanded relative to
153
+ # template_dir. Options are passed onto file.
154
+ def template(target, source, attributes={}, options={})
155
+ template_path = File.expand_path(source, template_dir)
156
+ templater = Support::Templater.new(File.read(template_path), attributes)
157
+
158
+ file(target, options) do |file|
159
+ file << templater.build
160
+ end
161
+ end
162
+
163
+ # Yields each source file under template_dir to the block, with
164
+ # a target path of the source relative to template_dir.
165
+ def template_files
166
+ Dir.glob(template_dir + "/**/*").sort.each do |source|
167
+ next unless File.file?(source)
168
+
169
+ target = Tap::Root::Utils.relative_path(template_dir, source)
170
+ yield(source, target)
171
+ end
172
+ end
173
+
174
+ # Logs the action with the relative filepath from Dir.pwd to path.
175
+ def log_relative(action, path)
176
+ log(action, Tap::Root::Utils.relative_path(Dir.pwd, path))
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,60 @@
1
+ module Tap
2
+ module Generator
3
+
4
+ # A mixin defining how to run manifest actions in reverse.
5
+ module Destroy
6
+
7
+ # Iterates over the actions in reverse, and collects the results.
8
+ def iterate(actions)
9
+ results = []
10
+ actions.reverse_each {|action| results << yield(action) }
11
+ results
12
+ end
13
+
14
+ # Removes the target directory if it exists. Missing, non-directory and
15
+ # non-empty targets are simply logged and not removed. When pretend is
16
+ # true, removal is logged but does not actually happen.
17
+ #
18
+ # No options currently affect the behavior of this method.
19
+ def directory(target, options={})
20
+ target = File.expand_path(target)
21
+
22
+ case
23
+ when !File.exists?(target)
24
+ log_relative :missing, target
25
+ when !File.directory?(target)
26
+ log_relative 'not a directory', target
27
+ when !Root::Utils.empty?(target)
28
+ log_relative 'not empty', target
29
+ else
30
+ log_relative :rm, target
31
+ FileUtils.rmdir(target) unless pretend
32
+ end
33
+
34
+ target
35
+ end
36
+
37
+ # Removes the target file if it exists. Missing and non-file and targets
38
+ # are simply logged and not removed. When pretend is true, removal is
39
+ # logged but does not actually happen.
40
+ #
41
+ # No options currently affect the behavior of this method.
42
+ def file(target, options={})
43
+ target = File.expand_path(target)
44
+
45
+ case
46
+ when File.file?(target)
47
+ log_relative :rm, target
48
+ FileUtils.rm(target) unless pretend
49
+ when File.directory?(target)
50
+ log_relative 'not a file', target
51
+ else
52
+ log_relative :missing, target
53
+ end
54
+
55
+ target
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ require 'tap/generator/base'
2
+
3
+ module Tap
4
+ module Generator
5
+
6
+ # Methods used by the generate and destroy commands.
7
+ module Exe
8
+
9
+ def run(mod, argv=ARGV)
10
+ if argv.empty? || argv == ['--help']
11
+ yield
12
+ end
13
+
14
+ name = argv.shift
15
+ env, const = eeek('generator', name)
16
+
17
+ unless const
18
+ raise "unknown generator: #{name}"
19
+ end
20
+
21
+ generator, argv = const.constantize.parse(argv)
22
+ generator.template_dir = env.class_path(:templates, generator) {|dir| File.directory?(dir) }
23
+ generator.extend(mod).process(*argv)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ autoload(:Tempfile, 'tempfile')
2
+
3
+ module Tap
4
+ module Generator
5
+
6
+ # A mixin defining how to run manifest actions.
7
+ module Generate
8
+
9
+ # Creates the target directory if it doesn't exist. When pretend is
10
+ # true, creation is logged but does not actually happen.
11
+ #
12
+ # No options currently affect the behavior of this method.
13
+ def directory(target, options={})
14
+ target = File.expand_path(target)
15
+
16
+ case
17
+ when File.exists?(target)
18
+ log_relative :exists, target
19
+ else
20
+ log_relative :create, target
21
+ FileUtils.mkdir_p(target) unless pretend
22
+ end
23
+
24
+ target
25
+ end
26
+
27
+ # Creates the target file; content may be added to the file by providing
28
+ # block. If the target file already exists, the new and existing content
29
+ # is compared and the user will be prompted for how to handle collisions.
30
+ # All activity is logged. When pretend is true, creation is logged but
31
+ # does not actually happen.
32
+ #
33
+ # No options currently affect the behavior of this method.
34
+ def file(target, options={})
35
+ source_file = Tempfile.new('generate')
36
+ yield(source_file) if block_given?
37
+ source_file.close
38
+
39
+ source = source_file.path
40
+ target = File.expand_path(target)
41
+
42
+ copy_file = true
43
+ msg = case
44
+ when !File.exists?(target)
45
+ :create
46
+ when FileUtils.cmp(source, target)
47
+ :exists
48
+ when force_file_collision?(target)
49
+ :force
50
+ else
51
+ copy_file = false
52
+ :skip
53
+ end
54
+
55
+ log_relative msg, target
56
+ if copy_file && !pretend
57
+ dir = File.dirname(target)
58
+ FileUtils.mkdir_p(dir) unless File.exists?(dir)
59
+ FileUtils.mv(source, target, :force => true)
60
+ end
61
+
62
+ target
63
+ end
64
+
65
+ protected
66
+
67
+ # Ask the user interactively whether to force collision.
68
+ def force_file_collision?(target)
69
+ return false if skip
70
+ return true if force
71
+
72
+ prompt_out.print "overwrite #{target}? [Ynaiq] "
73
+ prompt_out.flush
74
+ case prompt_in.gets.strip
75
+ when /^y(es)?$/i
76
+ true
77
+ when /^n(o)?$/i
78
+ false
79
+ when /^a(ll)?$/i
80
+ self.force = true
81
+ true
82
+ when /^i(gnore)?$/i
83
+ self.skip = true
84
+ false
85
+ when /^q(uit)?$/i
86
+ prompt_out.puts "aborting"
87
+ raise SystemExit
88
+ else force_file_collision?(target)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,19 @@
1
+ module Tap::Generator::Generators
2
+
3
+ # :startdoc::generator a new tap command
4
+ #
5
+ # Generates a new tap command under the cmd directory. The
6
+ # new command can be run from the command line using:
7
+ #
8
+ # % tap <command>
9
+ #
10
+ class Command < Tap::Generator::Base
11
+ def manifest(m, command_name)
12
+ m.directory path('cmd')
13
+
14
+ template_files do |source, target|
15
+ m.template path('cmd', "#{command_name}.rb"), source, :command_name => command_name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,118 @@
1
+ module Tap::Generator::Generators
2
+
3
+ # :startdoc::generator a config file generator
4
+ #
5
+ # Generates a new config file for a task. The configurations, defaults,
6
+ # and documentation is determined from the source file.
7
+ #
8
+ # Configurations for other types of configurable resources may also be
9
+ # generated. Specify the constant attribute identifying the resource
10
+ # using the 'resource' flag. This generates a config file for the Root
11
+ # generator:
12
+ #
13
+ # % tap generate config root --resource generator
14
+ #
15
+ class Config < Tap::Generator::Base
16
+
17
+ dump_delegates = lambda do |leader, delegate, block|
18
+ nested_delegates = delegate.default(false).delegates
19
+ indented_dump = Configurable::Utils.dump(nested_delegates, &block).gsub(/^/, " ")
20
+ "#{leader}: \n#{indented_dump}"
21
+ end
22
+
23
+ doc_format = lambda do |key, delegate|
24
+ # get the description
25
+ desc = delegate.attributes[:desc]
26
+ doc = desc.to_s
27
+ doc = desc.comment if doc.empty?
28
+
29
+ # wrap as lines
30
+ lines = Lazydoc::Utils.wrap(doc, 50).collect {|line| "# #{line}"}
31
+ lines << "" unless lines.empty?
32
+
33
+ if delegate.is_nest?
34
+ leader = "#{lines.join("\n")}#{key}"
35
+ DUMP_DELEGATES[leader, delegate, DOC_FORMAT]
36
+ else
37
+ default = delegate.default
38
+
39
+ # setup formatting
40
+ leader = default == nil ? '# ' : ''
41
+ config = YAML.dump({key => default})[5..-1]
42
+ "#{lines.join("\n")}#{leader}#{config.strip}\n\n"
43
+ end
44
+ end
45
+
46
+ nodoc_format = lambda do |key, delegate|
47
+ if delegate.is_nest?
48
+ DUMP_DELEGATES[key, delegate, NODOC_FORMAT]
49
+ else
50
+ default = delegate.default
51
+
52
+ # setup formatting
53
+ leader = default == nil ? '# ' : ''
54
+ config = YAML.dump({key => default})[5..-1]
55
+ "#{leader}#{config.strip}\n"
56
+ end
57
+ end
58
+
59
+ # Dumps a nested configuration.
60
+ DUMP_DELEGATES = dump_delegates
61
+
62
+ # Dumps configurations as YAML with documentation,
63
+ # used when the doc config is true.
64
+ DOC_FORMAT = doc_format
65
+
66
+ # Dumps configurations as YAML without documentation,
67
+ # used when the doc config is false.
68
+ NODOC_FORMAT = nodoc_format
69
+
70
+ config :doc, true, &c.switch # include documentation in the config
71
+ config :nest, false, &c.switch # generate nested config files
72
+ config :blanks, true, &c.switch # allow generation of empty config files
73
+ config :resource, 'task' # specify the resource type
74
+
75
+ # Lookup the named resource class. Lookup happens through the active Env
76
+ # instance, specifically using:
77
+ #
78
+ # Env.instance.constant_manifest(resource)[name]
79
+ #
80
+ # Raises an error if the name cannot be resolved to a resource.
81
+ def lookup(name)
82
+ env = Tap::Env.instance
83
+ env.constant_manifest(resource)[name] or raise "unknown #{resource}: #{name}"
84
+ end
85
+
86
+ def manifest(m, name, config_name=nil)
87
+ # setup
88
+ tasc = lookup(name)
89
+ config_name ||= tasc.to_s.underscore
90
+ config_file = path('config', config_name)
91
+ config_file += ".yml" if File.extname(config_file).empty?
92
+
93
+ # generate the dumps
94
+ dumps = Configurable::Utils.dump_file(
95
+ tasc.configurations,
96
+ config_file,
97
+ nest,
98
+ true,
99
+ &format_block)
100
+
101
+ # now put the dumps to the manifest
102
+ m.directory(path('config'))
103
+
104
+ dumps.each do |path, content|
105
+ next if content.empty? && !blanks
106
+ m.file(path) do |file|
107
+ file << content
108
+ end
109
+ end
110
+ end
111
+
112
+ # A hook to set a formatting block. By default format_blocks
113
+ # returns DOC_FORMAT or NODOC_FORMAT as per the doc config.
114
+ def format_block
115
+ doc ? DOC_FORMAT : NODOC_FORMAT
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,28 @@
1
+ require 'tap/generator/generators/task'
2
+
3
+ module Tap::Generator::Generators
4
+
5
+ # :startdoc::generator a generator task and test
6
+ #
7
+ # Generates a new generator.
8
+ class Generator < Tap::Generator::Generators::Task
9
+
10
+ config :test, true, &c.switch # specifies creation of a test file
11
+
12
+ def manifest(m, const_name)
13
+ super
14
+
15
+ const = Tap::Env::Constant.new(const_name.camelize)
16
+
17
+ # make the templates directory
18
+ m.directory path('templates', const.path)
19
+
20
+ # make a template file
21
+ # (note it's easier to do this as a file since erb is
22
+ # added, and would have to be escaped in a template)
23
+ m.file path('templates', const.path, 'template_file.erb') do |file|
24
+ file << "# A sample template file.\nkey: <%= key %>\n"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,96 @@
1
+ require 'tap/generator/base'
2
+ require 'tap/constants'
3
+
4
+ module Tap::Generator::Generators
5
+
6
+ # :startdoc::generator a basic tap directory structure
7
+ #
8
+ # Generates a tap root directory structure. Use the switches to turn on or
9
+ # off the creation of various files:
10
+ #
11
+ # project
12
+ # |- MIT-LICENSE
13
+ # |- README
14
+ # |- Rakefile
15
+ # |- lib
16
+ # |- project.gemspec
17
+ # |- tap.yml
18
+ # `- test
19
+ # `- tap_test_helper.rb
20
+ #
21
+ class Root < Tap::Generator::Base
22
+
23
+ nest :gemspec do
24
+ config :name, "Your Name Here" # Author name
25
+ config :email, "your.email@pubfactory.edu" # Author email
26
+ config :homepage, "" # The project hompage
27
+ config :rubyforge_project, "" # The rubyforge project name
28
+ config :summary, "" # The project summary
29
+ end
30
+
31
+ config :env, false, &c.switch # Create a full tap.yml file
32
+ config :license, true, &c.switch # Create an MIT-LICENSE
33
+ config :history, true, &c.switch # Create History file
34
+ config :rapfile, false, &c.switch # Create a Rapfile
35
+ config :rakefile, true, &c.switch # Create a Rakefile
36
+
37
+ # ::args ROOT, PROJECT_NAME=basename(ROOT)
38
+ def manifest(m, root, project_name=nil)
39
+ r = Tap::Root.new(root)
40
+ project_name = File.basename(r.root) if project_name == nil
41
+
42
+ m.directory r.root
43
+ m.directory r['lib']
44
+ m.directory r['test']
45
+
46
+ template_files do |source, target|
47
+ case
48
+ when File.directory?(source)
49
+ m.directory r[target]
50
+ next
51
+ when source =~ /gemspec$/
52
+ locals = gemspec.config.to_hash.merge(
53
+ :project_name => project_name,
54
+ :license => license,
55
+ :history => history
56
+ )
57
+ m.template r[project_name + '.gemspec'], source, locals
58
+ next
59
+ when source =~ /Rakefile$/
60
+ next unless rakefile
61
+ when source =~ /Rapfile$/
62
+ next unless rapfile
63
+ when source =~ /MIT-LICENSE$/
64
+ next unless license
65
+ end
66
+
67
+ m.template r[target], source, :project_name => project_name, :license => license
68
+ end
69
+
70
+ m.file(r['History']) if history
71
+ m.file(r['tap.yml']) do |file|
72
+ Configurable::Utils.dump(Tap::Env.configurations, file) do |key, delegate|
73
+ default = delegate.default(false)
74
+
75
+ # get the description
76
+ desc = delegate.attributes[:desc]
77
+ doc = desc.to_s
78
+ doc = desc.comment if doc.empty?
79
+
80
+ # wrap as lines
81
+ lines = Lazydoc::Utils.wrap(doc, 78).collect {|line| "# #{line}"}
82
+ lines << "" unless lines.empty?
83
+
84
+ # note: this causes order to be lost...
85
+ default = default.to_hash if delegate.is_nest?
86
+
87
+ # setup formatting
88
+ leader = key == 'root' || default == nil ? '# ' : ''
89
+ config = YAML.dump({key => default})[5..-1].strip.gsub(/\n+/, "\n#{leader}")
90
+ "#{lines.join("\n")}#{leader}#{config}\n\n"
91
+ end if env
92
+ end
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,27 @@
1
+ require 'tap/env'
2
+
3
+ module Tap::Generator::Generators
4
+
5
+ # :startdoc::generator a task and test
6
+ #
7
+ # Generates a new Tap::Task and an associated test file.
8
+ class Task < Tap::Generator::Base
9
+
10
+ config :test, true, &c.switch # specifies creation of a test file
11
+
12
+ def manifest(m, const_name)
13
+ const = Tap::Env::Constant.new(const_name.camelize)
14
+
15
+ task_path = path('lib', "#{const.path}.rb")
16
+ m.directory File.dirname(task_path)
17
+ m.template task_path, "task.erb", :const => const
18
+
19
+ if test
20
+ test_path = path('test', "#{const.path}_test.rb")
21
+ m.directory File.dirname(test_path)
22
+ m.template test_path, "test.erb", :const => const
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Tap
2
+ module Generator
3
+
4
+ # Manifest records methods called upon it using method_missing. These
5
+ # actions are replayed on a generator in order (for generate) or in
6
+ # reverse order (for destroy).
7
+ class Manifest
8
+
9
+ # Makes a new Manifest. Method calls on self are recorded to actions.
10
+ def initialize(actions)
11
+ @actions = actions
12
+ end
13
+
14
+ # Records an action.
15
+ def method_missing(action, *args, &block)
16
+ @actions << [action, args, block]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,69 @@
1
+ require 'stringio'
2
+
3
+ module Tap
4
+ module Generator
5
+
6
+ # Preview is a testing module designed so that process will return an array
7
+ # of relative paths for the created files/directories (which are easy
8
+ # to specify in a test). Preview also collects the content of created files
9
+ # to be tested as needed.
10
+ #
11
+ # class Sample < Tap::Generator::Base
12
+ # def manifest(m)
13
+ # dir = path('dir')
14
+ #
15
+ # m.directory dir
16
+ # m.file(File.join(dir, 'file.txt')) {|io| io << "content"}
17
+ # end
18
+ # end
19
+ #
20
+ # These assertions will pass:
21
+ #
22
+ # s = Sample.new.extend Preview
23
+ # assert_equal %w{
24
+ # dir
25
+ # dir/file.txt
26
+ # }, s.process
27
+ #
28
+ # assert_equal "content", s.preview['dir/file.txt']
29
+ #
30
+ # Note that relative paths are relative to destination_root.
31
+ module Preview
32
+
33
+ # A hash of (relative_path, content) pairs representing
34
+ # content built to files.
35
+ attr_accessor :preview
36
+
37
+ def self.extended(base) # :nodoc:
38
+ base.instance_variable_set(:@preview, {})
39
+ end
40
+
41
+ # Returns the path of path, relative to destination_root. If path
42
+ # is destination_root, '.' will be returned.
43
+ def relative_path(path)
44
+ path = Root::Utils.relative_path(destination_root, path, destination_root) || path
45
+ path.empty? ? "." : path
46
+ end
47
+
48
+ # Returns the relative path of the target.
49
+ def directory(target, options={})
50
+ relative_path(target)
51
+ end
52
+
53
+ # Returns the relative path of the target. If a block is given,
54
+ # the block will be called with a StringIO and the results stored
55
+ # in builds.
56
+ def file(target, options={})
57
+ target = relative_path(target)
58
+
59
+ if block_given?
60
+ io = StringIO.new
61
+ yield(io)
62
+ preview[target] = io.string
63
+ end
64
+
65
+ target
66
+ end
67
+ end
68
+ end
69
+ end
data/tap.yml ADDED
File without changes
@@ -0,0 +1,31 @@
1
+ # tap <%= command_name %> {options} ARGS...
2
+ #
3
+ # The default command simply prints the input arguments
4
+ # and application information, then exits.
5
+ #
6
+
7
+ env = Tap::Env.instance
8
+ app = Tap::App.new
9
+
10
+ #
11
+ # handle options
12
+ #
13
+
14
+ ConfigParser.new do |opts|
15
+ opts.separator ""
16
+ opts.separator "options:"
17
+
18
+ opts.on("-h", "--help", "Show this message") do
19
+ puts Lazydoc.usage(__FILE__)
20
+ puts opts
21
+ exit
22
+ end
23
+
24
+ end.parse!(ARGV)
25
+
26
+ #
27
+ # add your script code here
28
+ #
29
+
30
+ puts "Received: #{ARGV.join(', ')}"
31
+ puts app.info
@@ -0,0 +1,29 @@
1
+ require 'tap/generator/base'
2
+
3
+ <% redirect do |target| %># <%= const.const_name %>::generator <replace with manifest summary>
4
+ # <replace with command line description>
5
+
6
+ # <%= const.name %> Documentation
7
+ class <%= const.name %> < Tap::Generator::Base
8
+
9
+ config :key, 'value' # a sample config
10
+
11
+ # The generator will receive the inputs on the command line, and
12
+ # m, a Manifest object that records the actions of this method.
13
+ def manifest(m, *inputs)
14
+
15
+ # make a directory
16
+ # m.directory path
17
+
18
+ # make a file
19
+ # m.file path do |file|
20
+ # file << content
21
+ # end
22
+
23
+ # template a file in the templates directory using ERB.
24
+ # The last argument defines a hash of local variables
25
+ # for use in the template (here config is used).
26
+ m.template "<%= const.const_name.underscore %>_file.txt", "template_file.erb", config.to_hash
27
+ end
28
+
29
+ end <% module_nest(const.nesting, ' ') { target } end %>
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), '<%= '../' * const.nesting_depth %>tap_test_helper.rb')
2
+ require '<%= const.path %>'
3
+ require 'tap/generator/preview.rb'
4
+
5
+ class <%= const.name %>Test < Test::Unit::TestCase
6
+
7
+ # Preview fakes out a generator for testing
8
+ Preview = Tap::Generator::Preview
9
+
10
+ acts_as_tap_test
11
+
12
+ def test_<%= const.basename %>
13
+ g = <%= const.const_name %>.new.extend Preview
14
+
15
+ # check the files and directories
16
+ assert_equal %w{
17
+ <%= const.const_name.underscore %>_file.txt
18
+ }, g.process
19
+
20
+ # check the content as necessary
21
+ assert_equal %q{
22
+ # A sample template file.
23
+ key: value
24
+ }, "\n" + g.preview['<%= const.const_name.underscore %>_file.txt']
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) <%= Time.now.year %>, <copyright holders>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ = <%= project_name.capitalize %>
2
+
3
+ == Description
4
+
5
+ == Usage
6
+
7
+ == Installation
8
+
9
+ == Info
10
+ <% if license %>
11
+
12
+ Copyright (c) <%= Time.now.year %>, <copyright holders>
13
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
14
+ <% end %>
@@ -0,0 +1,85 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ #
7
+ # Gem specification
8
+ #
9
+
10
+ def gemspec
11
+ data = File.read('<%= project_name %>.gemspec')
12
+ spec = nil
13
+ Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
14
+ spec
15
+ end
16
+
17
+ Rake::GemPackageTask.new(gemspec) do |pkg|
18
+ pkg.need_tar = true
19
+ end
20
+
21
+ desc 'Prints the gemspec manifest.'
22
+ task :print_manifest do
23
+ # collect files from the gemspec, labeling
24
+ # with true or false corresponding to the
25
+ # file existing or not
26
+ files = gemspec.files.inject({}) do |files, file|
27
+ files[File.expand_path(file)] = [File.exists?(file), file]
28
+ files
29
+ end
30
+
31
+ # gather non-rdoc/pkg files for the project
32
+ # and add to the files list if they are not
33
+ # included already (marking by the absence
34
+ # of a label)
35
+ Dir.glob("**/*").each do |file|
36
+ next if file =~ /^(rdoc|pkg|backup)/ || File.directory?(file)
37
+
38
+ path = File.expand_path(file)
39
+ files[path] = ["", file] unless files.has_key?(path)
40
+ end
41
+
42
+ # sort and output the results
43
+ files.values.sort_by {|exists, file| file }.each do |entry|
44
+ puts "%-5s %s" % entry
45
+ end
46
+ end
47
+
48
+ #
49
+ # Documentation tasks
50
+ #
51
+
52
+ desc 'Generate documentation.'
53
+ Rake::RDocTask.new(:rdoc) do |rdoc|
54
+ spec = gemspec
55
+
56
+ rdoc.rdoc_dir = 'rdoc'
57
+ rdoc.options.concat(spec.rdoc_options)
58
+ rdoc.rdoc_files.include( spec.extra_rdoc_files )
59
+
60
+ files = spec.files.select {|file| file =~ /^lib.*\.rb$/}
61
+ rdoc.rdoc_files.include( files )
62
+
63
+ # Using CDoc to template your RDoc will result in configurations being
64
+ # listed with documentation in a subsection following attributes. Not
65
+ # necessary, but nice.
66
+ require 'cdoc'
67
+ rdoc.template = 'cdoc/cdoc_html_template'
68
+ rdoc.options << '--fmt' << 'cdoc'
69
+ end
70
+
71
+ #
72
+ # Test tasks
73
+ #
74
+
75
+ desc 'Default: Run tests.'
76
+ task :default => :test
77
+
78
+ desc 'Run tests.'
79
+ Rake::TestTask.new(:test) do |t|
80
+ t.test_files = Dir.glob( File.join('test', ENV['pattern'] || '**/*_test.rb') )
81
+ t.ruby_opts = ['-rubygems']
82
+ t.verbose = true
83
+ t.warning = true
84
+ end
85
+
@@ -0,0 +1,11 @@
1
+ require 'tap/declarations'
2
+
3
+ module <%= project_name.camelize %>
4
+ extend Rap::Declarations
5
+
6
+ # ::desc your basic goodnight moon task
7
+ # Says goodnight with a configurable message.
8
+ task(:goodnight, :obj, :message => 'goodnight') do |task, args|
9
+ puts "#{task.message} #{args.obj}"
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "<%= project_name %>"
3
+ s.version = "0.0.1"
4
+ s.author = "<%= name %>"
5
+ s.email = "<%= email %>"
6
+ s.homepage = "<%= homepage %>"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = "<%= summary %>"
9
+ s.require_path = "lib"
10
+ s.rubyforge_project = "<%= rubyforge_project %>"
11
+ s.add_dependency("tap", "= <%= Tap::VERSION %>")
12
+ s.add_development_dependency("tap-test")
13
+ s.has_rdoc = true
14
+ s.rdoc_options.concat %W{--main README -S -N --title <%= project_name.capitalize %>}
15
+
16
+ # list extra rdoc files here.
17
+ s.extra_rdoc_files = %W{
18
+ <%= history ? " History\n" : '' %>
19
+ README
20
+ <%= license ? " MIT-LICENSE\n" : '' %>
21
+ }
22
+
23
+ # list the files you want to include here. you can
24
+ # check this manifest using 'rap print_manifest'
25
+ s.files = %W{
26
+ tap.yml
27
+ }
28
+ end
@@ -0,0 +1 @@
1
+ require 'tap/test/unit'
@@ -0,0 +1,16 @@
1
+ require 'tap/task'
2
+
3
+ <% redirect do |target| %># <%= const.const_name %>::manifest <replace with manifest summary>
4
+ # <replace with command line description>
5
+
6
+ # <%= const.name %> Documentation
7
+ class <%= const.name %> < Tap::Task
8
+
9
+ # <config file documentation>
10
+ config :message, 'goodnight' # a sample config
11
+
12
+ def process(name)
13
+ log message, name
14
+ "#{message} #{name}"
15
+ end
16
+ end <% module_nest(const.nesting, ' ') { target } end %>
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), '<%= '../' * const.nesting_depth %>tap_test_helper.rb')
2
+ require '<%= const.path %>'
3
+
4
+ class <%= const.name %>Test < Test::Unit::TestCase
5
+ acts_as_tap_test
6
+
7
+ def test_<%= const.basename %>
8
+ task = <%= const.const_name %>.new :message => "goodnight"
9
+
10
+ # a simple test
11
+ assert_equal({:message => 'goodnight'}, task.config)
12
+ assert_equal "goodnight moon", task.process("moon")
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tap-gen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Simon Chiang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-25 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: tap
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.17.0
24
+ version:
25
+ description:
26
+ email: simon.a.chiang@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - History
33
+ - README
34
+ - MIT-LICENSE
35
+ files:
36
+ - cmd/destroy.rb
37
+ - cmd/generate.rb
38
+ - lib/tap/generator/arguments.rb
39
+ - lib/tap/generator/base.rb
40
+ - lib/tap/generator/destroy.rb
41
+ - lib/tap/generator/exe.rb
42
+ - lib/tap/generator/generate.rb
43
+ - lib/tap/generator/generators/command.rb
44
+ - lib/tap/generator/generators/config.rb
45
+ - lib/tap/generator/generators/generator.rb
46
+ - lib/tap/generator/generators/root.rb
47
+ - lib/tap/generator/generators/task.rb
48
+ - lib/tap/generator/manifest.rb
49
+ - lib/tap/generator/preview.rb
50
+ - tap.yml
51
+ - templates/tap/generator/generators/command/command.erb
52
+ - templates/tap/generator/generators/generator/task.erb
53
+ - templates/tap/generator/generators/generator/test.erb
54
+ - templates/tap/generator/generators/root/MIT-LICENSE
55
+ - templates/tap/generator/generators/root/README
56
+ - templates/tap/generator/generators/root/Rakefile
57
+ - templates/tap/generator/generators/root/Rapfile
58
+ - templates/tap/generator/generators/root/gemspec
59
+ - templates/tap/generator/generators/root/test/tap_test_helper.rb
60
+ - templates/tap/generator/generators/task/task.erb
61
+ - templates/tap/generator/generators/task/test.erb
62
+ - History
63
+ - README
64
+ - MIT-LICENSE
65
+ has_rdoc: true
66
+ homepage: http://tap.rubyforge.org/tap-gen
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --main
70
+ - README
71
+ - -S
72
+ - -N
73
+ - --title
74
+ - Tap-Generator
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project: tap
92
+ rubygems_version: 1.3.1
93
+ signing_key:
94
+ specification_version: 2
95
+ summary: Generators for Tap
96
+ test_files: []
97
+