tap-gen 0.1.0

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.
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
+