codger 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +63 -0
- data/Rakefile +1 -0
- data/bin/codger +4 -0
- data/codger.gemspec +28 -0
- data/lib/codger/cli.rb +100 -0
- data/lib/codger/generator.rb +84 -0
- data/lib/codger/manager.rb +134 -0
- data/lib/codger/skeleton.rb +137 -0
- data/lib/codger/version.rb +3 -0
- data/lib/codger.rb +8 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jacob Williams
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# codger
|
2
|
+
|
3
|
+
Goals:
|
4
|
+
|
5
|
+
* Provide a simple way to define and run project skeletons and other code generators.
|
6
|
+
* Help integrate later changes to the generators into a project.
|
7
|
+
|
8
|
+
## Creating Skeletons
|
9
|
+
|
10
|
+
Put the skeleton in a git repo. Before testing it, make sure to at least `git add` the files. Then:
|
11
|
+
|
12
|
+
codger skeleton path/to/your-repo-name --test
|
13
|
+
|
14
|
+
Now, to generate code inside the current working directory, do
|
15
|
+
|
16
|
+
codger gen your-repo-name
|
17
|
+
|
18
|
+
Here's how the skeleton repo will be used:
|
19
|
+
|
20
|
+
1. If there is a script named `generate.rb` in the root directory, it will be run.
|
21
|
+
2. Files ending in `.erb` will be [interpolated](http://ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html) and written to the target directory.
|
22
|
+
3. Other files will be copied directly to the target directory. (Exceptions: README, README.md, README.markdown)
|
23
|
+
|
24
|
+
### Parameters
|
25
|
+
|
26
|
+
In the templates or generate.rb, use the method `param :parameter_name` to request values to configure the skeleton. The first time a particular parameter is requested, the user will be prompted for a value.
|
27
|
+
|
28
|
+
The first time a prompt is given, the skeleton repo's README will be printed (if it exists).
|
29
|
+
|
30
|
+
### Helpers
|
31
|
+
|
32
|
+
* In a template, you can use `<%- rename "relative_path" -%>` to override the output file name.
|
33
|
+
* Use `copy "path1", "path2"` to copy files "path1" and "path2" (relative the skeleton repo's root directory) into the target directory directly. Use `copy "path1" => "newpath1"` to override the destination path in the target directory.
|
34
|
+
* Use `interpolate "path1", "path2"` to interpolate ERB files "path1" and "path2" into the target directory. As with `copy` you can use a hash to override the destination paths.
|
35
|
+
* Use `ignore "path1", "path2"` to prevent automatic copying or interpolation of files.
|
36
|
+
|
37
|
+
## Using Skeletons
|
38
|
+
|
39
|
+
Register a skeleton:
|
40
|
+
|
41
|
+
codger skeleton git://example.com/boilerplate.git
|
42
|
+
|
43
|
+
Run it in a new folder:
|
44
|
+
|
45
|
+
codger create boilerplate monumental-endeavor
|
46
|
+
|
47
|
+
Or run it in the current working directory:
|
48
|
+
|
49
|
+
codger gen boilerplate
|
50
|
+
|
51
|
+
In either case the parameters used will be recorded in a file named `.codger`, so that later, after the project or the skeleton have changed, you can use
|
52
|
+
|
53
|
+
codger diff
|
54
|
+
|
55
|
+
to compare the current state of your project with the output of the current version of the skeleton. The command used for diffing can be changed:
|
56
|
+
|
57
|
+
codger config diff "diff -ur %SOURCE %DEST"
|
58
|
+
|
59
|
+
## TODO
|
60
|
+
|
61
|
+
* Better documentation; examples
|
62
|
+
* I'm hopeful that more useful diffing can be done through git integration, but I haven't worked on it much yet.
|
63
|
+
* Code generators shouldn't be restricted to whatever dependencies happen to be pulled in by codger.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/codger
ADDED
data/codger.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "codger/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "codger"
|
7
|
+
s.version = Codger::VERSION
|
8
|
+
s.authors = ["Jacob Williams"]
|
9
|
+
s.email = ["jacobaw@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Manages invocation of code generators.}
|
12
|
+
s.description = %q{Manages invocation of code generators.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "codger"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
s.add_runtime_dependency 'activesupport', '~> 3.2.1'
|
25
|
+
s.add_runtime_dependency 'deep_merge', '~> 1.0.0'
|
26
|
+
s.add_runtime_dependency 'git', '~> 1.2.5'
|
27
|
+
s.add_runtime_dependency 'thor', '~> 0.14.6'
|
28
|
+
end
|
data/lib/codger/cli.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'git'
|
3
|
+
require 'thor'
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module Codger
|
8
|
+
class CLI < Thor
|
9
|
+
desc 'config [NAME [VALUE]]', 'lists, shows, or alters configuration'
|
10
|
+
def config(name = nil, value = nil)
|
11
|
+
if name
|
12
|
+
if value
|
13
|
+
Manager.default.global_settings[:config][name] = value
|
14
|
+
Manager.default.save_globals
|
15
|
+
else
|
16
|
+
puts Manager.default.settings[:config][name]
|
17
|
+
end
|
18
|
+
else
|
19
|
+
puts YAML.dump(Manager.default.settings[:config]).lines.drop(1).join
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'available', 'lists registered code generators'
|
24
|
+
def available
|
25
|
+
Manager.default.settings[:generators].each do |name, info|
|
26
|
+
puts "#{name}\t#{info.inspect}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'skeleton LOCATION', 'register a git repository as a code generator, creating a clone from the given location'
|
31
|
+
method_option :name, desc: 'Name you wish to refer to the skeleton by.'
|
32
|
+
method_option :test, desc: 'Prevent cloning or pulling of the repository. Location should be a path on the file system.'
|
33
|
+
def skeleton(location)
|
34
|
+
info = {}
|
35
|
+
if options[:test]
|
36
|
+
location = File.expand_path(location)
|
37
|
+
info[:test] = true
|
38
|
+
end
|
39
|
+
info[:git] = location
|
40
|
+
Manager.default.register options[:name], info
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'create NAME PATH', 'run the named generator in a new folder at the given path'
|
44
|
+
def create(name, path)
|
45
|
+
FileUtils.mkdir path
|
46
|
+
Git.init File.expand_path(path)
|
47
|
+
manager = Manager.new(File.join(path, '.codger'))
|
48
|
+
generator = manager.generator(manager.settings[:generators][name])
|
49
|
+
generator.run path, project_name: path.split('/').last
|
50
|
+
manager.record_run generator
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'gen NAME', 'run the named generator'
|
54
|
+
def gen(name)
|
55
|
+
generator = Manager.default.generator(Manager.default.settings[:generators][name])
|
56
|
+
generator.run(Dir.pwd)
|
57
|
+
Manager.default.record_run(generator)
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'history', 'show the actions recorded for this directory'
|
61
|
+
def history
|
62
|
+
Manager.default.settings[:runs].each do |info|
|
63
|
+
puts "#{info[:generator]} [#{info[:tags].join(' ')}]"
|
64
|
+
puts Generator.format_params(info[:params])
|
65
|
+
puts
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'diff [TAGS...]', 'run part or all of the history in a temp directory and display a diff'
|
70
|
+
def diff(*tags)
|
71
|
+
Dir.mktmpdir do |dir|
|
72
|
+
Manager.default.settings[:runs].each do |info|
|
73
|
+
if tags.empty? or (tags & info[:tags]).any?
|
74
|
+
generator = Manager.default.generator(info[:generator])
|
75
|
+
generator.run dir, info[:params]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
system Manager.default.diff_command(dir, Dir.pwd)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
desc 'repeat [TAGS...]', 're-run part or all of the history'
|
83
|
+
def repeat(*tags)
|
84
|
+
Manager.default.settings[:runs].each do |info|
|
85
|
+
if tags.empty? or (tags & info[:tags]).any?
|
86
|
+
puts "Running #{info[:generator]} [#{info[:tags].join(' ')}]"
|
87
|
+
puts Generator.format_params(info[:params])
|
88
|
+
puts
|
89
|
+
generator = Manager.default.generator(info[:generator])
|
90
|
+
generator.run Dir.pwd, info[:params]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
desc 'unregister NAME', 'unregister a code generator and delete its clone'
|
96
|
+
def unregister(name)
|
97
|
+
Manager.default.unregister name
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Codger
|
4
|
+
# A code generator. The #run method is called to perform code generation;
|
5
|
+
# the parameters used (which may have been specified interactively during #run)
|
6
|
+
# can be determined afterwards using #params.
|
7
|
+
#
|
8
|
+
# Subclasses must implement:
|
9
|
+
# * a #generate method which will perform the code generation
|
10
|
+
# * a #help method which returns help text
|
11
|
+
#
|
12
|
+
# Methods for use by subclasses:
|
13
|
+
# * #dest_path
|
14
|
+
# * #ensure_folder
|
15
|
+
# * #param
|
16
|
+
# * #tags
|
17
|
+
class Generator
|
18
|
+
class << self
|
19
|
+
# Given a params map, print one param per line, indented.
|
20
|
+
def format_params(params)
|
21
|
+
YAML.dump(params).lines.drop(1).map do |line|
|
22
|
+
"\t#{line}"
|
23
|
+
end.join
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# The map of parameters used during the last call to #run.
|
28
|
+
attr_reader :params
|
29
|
+
# The output directory used during the last call to #run.
|
30
|
+
attr_reader :target
|
31
|
+
|
32
|
+
# Perform code generation in the given directory. Any parameters
|
33
|
+
# already known (e.g., if we're repeating a previous run) can be
|
34
|
+
# specified; any other parameters needed will be determined interactively.
|
35
|
+
def run(target, params = {})
|
36
|
+
@showed_help = false
|
37
|
+
@target = target
|
38
|
+
@params = params.with_indifferent_access
|
39
|
+
|
40
|
+
generate
|
41
|
+
end
|
42
|
+
|
43
|
+
# Given a path relative to the output directory root, returns the full path.
|
44
|
+
def dest_path(path)
|
45
|
+
File.join(target, path)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Given a path relative to the output directory root, creates a folder
|
49
|
+
# at that location if one does not yet exist.
|
50
|
+
def ensure_folder(path)
|
51
|
+
FileUtils.mkdir_p(File.join(target, File.dirname(path)))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the value from the params map for the given name. If there
|
55
|
+
# is none, asks the user for a value. #help is called the first time
|
56
|
+
# the user is asked for a value.
|
57
|
+
# The parameter will also be saved in an instance variable of the same name.
|
58
|
+
def param(name)
|
59
|
+
until params[name]
|
60
|
+
unless @showed_help
|
61
|
+
puts help
|
62
|
+
puts
|
63
|
+
@showed_help = true
|
64
|
+
end
|
65
|
+
print "Specify #{name}: "
|
66
|
+
value = STDIN.gets.chomp
|
67
|
+
params[name] = value unless value.empty?
|
68
|
+
end
|
69
|
+
instance_variable_set("@#{name}", params[name])
|
70
|
+
params[name]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets (with parameters) or returns (without parameters) tags for
|
74
|
+
# this generator. The tags will be associated to recorded runs.
|
75
|
+
# (This may be useless, we'll see.)
|
76
|
+
def tags(*tags)
|
77
|
+
if tags == []
|
78
|
+
@tags || []
|
79
|
+
else
|
80
|
+
@tags = tags.flatten
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'git'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Codger
|
6
|
+
# Responsible for:
|
7
|
+
# * Reading, writing, and to some degree interpreting configuration files.
|
8
|
+
# * Looking up code generators from their identifiers.
|
9
|
+
class Manager
|
10
|
+
class << self
|
11
|
+
# Return an instance using any settings in the .codger
|
12
|
+
# file (if one exists) of the working directory.
|
13
|
+
def default
|
14
|
+
@config ||= Manager.new(File.join(Dir.pwd, '.codger'))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# The global settings map (i.e. from ~/.codger/codger.yaml)
|
19
|
+
attr_reader :global_settings
|
20
|
+
# The project settings map (i.e. from .codger)
|
21
|
+
attr_reader :project_settings
|
22
|
+
|
23
|
+
# Create an instance with project-level settings stored at the specified path
|
24
|
+
# (does not need to exist yet, and will not be created unless necessary).
|
25
|
+
def initialize(path)
|
26
|
+
@project_path = path
|
27
|
+
@project_settings = {
|
28
|
+
runs: []
|
29
|
+
}.with_indifferent_access
|
30
|
+
if File.exists?(@project_path)
|
31
|
+
@project_settings.merge! YAML.load(File.read(@project_path))
|
32
|
+
end
|
33
|
+
|
34
|
+
@global_settings = {
|
35
|
+
config: {
|
36
|
+
diff: 'diff -ur %SOURCE %DEST'
|
37
|
+
},
|
38
|
+
clones: {},
|
39
|
+
generators: {}
|
40
|
+
}.with_indifferent_access
|
41
|
+
if File.exists?(globals_path)
|
42
|
+
@global_settings.merge! YAML.load(File.read(globals_path))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a Generator, currently always a Skeleton.
|
47
|
+
# info should contain :git, the path/URI of the repository.
|
48
|
+
# Unless it contains :test, the repository will be cloned to #clones_base
|
49
|
+
# (if it has not been already).
|
50
|
+
def generator(info)
|
51
|
+
if location = info[:git]
|
52
|
+
if info[:test]
|
53
|
+
clone = location
|
54
|
+
elsif !(clone = settings[:clones][location] and File.exists?(clone))
|
55
|
+
FileUtils.mkdir_p clones_base
|
56
|
+
next_id = Dir.entries(clones_base).map(&:to_i).max + 1
|
57
|
+
clone = File.join(clones_base, next_id.to_s)
|
58
|
+
Git.clone location, clone
|
59
|
+
@global_settings[:clones][location] = clone
|
60
|
+
save_globals
|
61
|
+
end
|
62
|
+
Skeleton.new clone, info
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Load a generator for the given attributes and register it
|
67
|
+
# in the global configuration under the given name, or its default
|
68
|
+
# name if name is nil.
|
69
|
+
def register(name, info)
|
70
|
+
gen = generator(info)
|
71
|
+
@global_settings[:generators][name || gen.name] = gen.info
|
72
|
+
save_globals
|
73
|
+
end
|
74
|
+
|
75
|
+
# Given a generator name, removes it from the config, and delete its
|
76
|
+
# local clone if one exists.
|
77
|
+
def unregister(name)
|
78
|
+
info = @global_settings[:generators].delete name # TODO graciously handle it not existing
|
79
|
+
clone = @global_settings[:clones].delete info[:git]
|
80
|
+
if clone and clone.start_with? clones_base # sanity check before rm_rf
|
81
|
+
FileUtils.rm_rf clone
|
82
|
+
end
|
83
|
+
save_globals
|
84
|
+
end
|
85
|
+
|
86
|
+
# Saves the tags, identifier, and params from the last run of the given generator instance
|
87
|
+
# in the project settings file.
|
88
|
+
def record_run(generator)
|
89
|
+
@project_settings[:runs] << {
|
90
|
+
tags: [generator.name] + generator.tags,
|
91
|
+
generator: generator.info,
|
92
|
+
params: generator.params
|
93
|
+
}.with_indifferent_access
|
94
|
+
save_project
|
95
|
+
end
|
96
|
+
|
97
|
+
# Save #project_settings.
|
98
|
+
def save_project
|
99
|
+
File.write @project_path, @project_settings.to_yaml
|
100
|
+
end
|
101
|
+
|
102
|
+
# Save #global_settings.
|
103
|
+
def save_globals
|
104
|
+
FileUtils.mkdir_p codger_home
|
105
|
+
File.write globals_path, @global_settings.to_yaml
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return the folder where global settings and other resources can be saved.
|
109
|
+
# By default ~/.codger but this can be overridden using 'codger_home' in #project_settings.
|
110
|
+
def codger_home
|
111
|
+
@project_settings[:codger_home] || File.join(Dir.home, '.codger')
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return the file where global settings should be saved - 'codger.yaml' in #codger_home.
|
115
|
+
def globals_path
|
116
|
+
File.join(codger_home, 'codger.yaml')
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return the folder where skeleton clones can be saved - 'clones' in #codger_home.
|
120
|
+
def clones_base
|
121
|
+
File.join(codger_home, 'clones')
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return a merged map of #global_settings and #project_settings.
|
125
|
+
def settings
|
126
|
+
@global_settings.deep_merge @project_settings
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return the command to use for diffing two folders.
|
130
|
+
def diff_command(source, dest)
|
131
|
+
settings[:config][:diff].gsub('%SOURCE', source).gsub('%DEST', dest)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'git'
|
4
|
+
|
5
|
+
module Codger
|
6
|
+
# A generator that produces code by using a git repository as a template.
|
7
|
+
#
|
8
|
+
# By default:
|
9
|
+
# * README, README.md, README.markdown will be ignored, except to
|
10
|
+
# be printed as documentation if user input is required for the
|
11
|
+
# value of a param
|
12
|
+
# * generate.rb (if it exists) will be executed in the context of
|
13
|
+
# the Skeleton instance
|
14
|
+
# * Files ending in .erb will be interpolated in the context of
|
15
|
+
# the Skeleton instance
|
16
|
+
# * All other files will be copied directly
|
17
|
+
#
|
18
|
+
# Methods for use in generate.rb and templates:
|
19
|
+
# * #src_path
|
20
|
+
# * #copy
|
21
|
+
# * #interpolate
|
22
|
+
# * #ignore
|
23
|
+
# * #rename
|
24
|
+
class Skeleton < Generator
|
25
|
+
# Returns a (non-unique) name for the generator. For
|
26
|
+
# skeletons this is based on the last segment of the origin
|
27
|
+
# URI or of the clone's path.
|
28
|
+
attr_reader :name
|
29
|
+
# Returns the attributes used in creating this instance.
|
30
|
+
attr_reader :info
|
31
|
+
|
32
|
+
# Create an instance reading from the git repository at the specified
|
33
|
+
# path. Options for info:
|
34
|
+
# git:: Canonical source for the repo; required.
|
35
|
+
# test:: Unless truthy, an attempt will be made to perform a 'pull' for the repository.
|
36
|
+
def initialize(repo, info)
|
37
|
+
@info = info
|
38
|
+
@git = Git.open(repo)
|
39
|
+
# https://github.com/schacon/ruby-git/issues/32
|
40
|
+
@git.lib.send(:command, 'pull') unless info[:test] # TODO needs to be OK if this fails
|
41
|
+
@name = info[:git].split('/').last.sub(/\.git\z/,'')
|
42
|
+
end
|
43
|
+
|
44
|
+
# Perform code generation using the process outlined in the class documentation.
|
45
|
+
def generate
|
46
|
+
@to_copy = @git.ls_files.keys - ['README', 'README.md', 'README.markdown', 'generate.rb']
|
47
|
+
|
48
|
+
code_path = src_path('generate.rb')
|
49
|
+
if File.exists?(code_path)
|
50
|
+
eval(File.read(code_path), binding, code_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
interpolate(@to_copy.select {|path| path =~ /\.erb\z/})
|
54
|
+
copy @to_copy
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the full path to the given file in the repo.
|
58
|
+
def src_path(path)
|
59
|
+
File.join(@git.dir.to_s, path)
|
60
|
+
end
|
61
|
+
|
62
|
+
# For each path or array of paths, copy the
|
63
|
+
# corresponding files directly from the repository to
|
64
|
+
# the target directory.
|
65
|
+
# Alternatively, a hash of paths may be given, in which
|
66
|
+
# keys specify the name in the source repository and
|
67
|
+
# values specify the desired name in the target directory.
|
68
|
+
def copy(*paths)
|
69
|
+
paths = paths.flatten
|
70
|
+
mappings = {}
|
71
|
+
paths.each do |path|
|
72
|
+
if path.is_a? Hash
|
73
|
+
mappings.merge! path
|
74
|
+
else
|
75
|
+
mappings[path] = path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
mappings.each do |src, dest|
|
80
|
+
ensure_folder dest
|
81
|
+
FileUtils.cp src_path(src), dest_path(dest)
|
82
|
+
@to_copy.delete src
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# For each path or array of paths, interpolate (in the
|
87
|
+
# context of this object) the corresponding files and
|
88
|
+
# write the output to the target directory, stripping
|
89
|
+
# .erb from the filename.
|
90
|
+
# Alternatively, a hash of paths may be given, in which
|
91
|
+
# keys specify the name in the source repository and
|
92
|
+
# values specify the desired name in the target directory.
|
93
|
+
#
|
94
|
+
# Note that calls to #rename may override the destination path.
|
95
|
+
def interpolate(*paths)
|
96
|
+
paths = paths.flatten
|
97
|
+
mappings = {}
|
98
|
+
paths.each do |path|
|
99
|
+
if path.is_a? Hash
|
100
|
+
mappings.merge! path
|
101
|
+
else
|
102
|
+
mappings[path] = path.sub(/\.erb\z/, '')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
mappings.each do |src, dest|
|
107
|
+
@current_template_src = src
|
108
|
+
@current_template_dest = dest
|
109
|
+
template = ERB.new(File.read(src_path(src)), nil, '-')
|
110
|
+
ensure_folder @current_template_dest
|
111
|
+
result = template.result(binding)
|
112
|
+
File.write dest_path(@current_template_dest), result
|
113
|
+
@to_copy.delete src
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Should only be called from within a file being interpolated.
|
118
|
+
# The output path will be changed to dest, which should be
|
119
|
+
# relative to the template's folder.
|
120
|
+
def rename(dest)
|
121
|
+
@current_template_dest = File.join(File.dirname(@current_template_src), dest)
|
122
|
+
end
|
123
|
+
|
124
|
+
# For each path or array of paths, disable implicit copying.
|
125
|
+
def ignore(*paths)
|
126
|
+
@to_copy -= paths.flatten
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the text of the README, README.md or README.markdown file, if any.
|
130
|
+
def help
|
131
|
+
path = Dir[src_path('README')].first || Dir[src_path('README.md')].first || Dir[src_path('README.markdown')].first
|
132
|
+
if path
|
133
|
+
File.read path
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/codger.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jacob Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70337635384820 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70337635384820
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: deep_merge
|
27
|
+
requirement: &70337635384160 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70337635384160
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: git
|
38
|
+
requirement: &70337635383500 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.2.5
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70337635383500
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: thor
|
49
|
+
requirement: &70337635382820 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.14.6
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70337635382820
|
58
|
+
description: Manages invocation of code generators.
|
59
|
+
email:
|
60
|
+
- jacobaw@gmail.com
|
61
|
+
executables:
|
62
|
+
- codger
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- Gemfile
|
68
|
+
- LICENSE
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- bin/codger
|
72
|
+
- codger.gemspec
|
73
|
+
- lib/codger.rb
|
74
|
+
- lib/codger/cli.rb
|
75
|
+
- lib/codger/generator.rb
|
76
|
+
- lib/codger/manager.rb
|
77
|
+
- lib/codger/skeleton.rb
|
78
|
+
- lib/codger/version.rb
|
79
|
+
homepage: ''
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project: codger
|
99
|
+
rubygems_version: 1.8.6
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Manages invocation of code generators.
|
103
|
+
test_files: []
|