codger 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +17 -13
- data/lib/codger/cli.rb +31 -39
- data/lib/codger/manager.rb +63 -41
- data/lib/codger/skeleton.rb +26 -19
- data/lib/codger/version.rb +1 -1
- metadata +10 -10
data/README.md
CHANGED
@@ -9,17 +9,13 @@ Goals:
|
|
9
9
|
|
10
10
|
Put the skeleton in a git repo. Before testing it, make sure to at least `git add` the files. Then:
|
11
11
|
|
12
|
-
codger
|
13
|
-
|
14
|
-
Now, to generate code inside the current working directory, do
|
15
|
-
|
16
|
-
codger gen your-repo-name
|
12
|
+
codger gen path/to/your/repo path/to/new/project
|
17
13
|
|
18
14
|
Here's how the skeleton repo will be used:
|
19
15
|
|
20
16
|
1. If there is a script named `generate.rb` in the root directory, it will be run.
|
21
17
|
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)
|
18
|
+
3. Other files will be copied directly to the target directory. (Exceptions: README, README.md, README.markdown, .gitignore)
|
23
19
|
|
24
20
|
### Parameters
|
25
21
|
|
@@ -32,23 +28,31 @@ The first time a prompt is given, the skeleton repo's README will be printed (if
|
|
32
28
|
* In a template, you can use `<%- rename "relative_path" -%>` to override the output file name.
|
33
29
|
* 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
30
|
* 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.
|
31
|
+
* Use `cancel` inside a template to stop its interpolation.
|
35
32
|
* Use `ignore "path1", "path2"` to prevent automatic copying or interpolation of files.
|
36
33
|
|
37
34
|
## Using Skeletons
|
38
35
|
|
39
|
-
|
36
|
+
Run in a new folder:
|
40
37
|
|
41
|
-
codger
|
38
|
+
codger gen boilerplate monumental-endeavor
|
42
39
|
|
43
|
-
|
40
|
+
Or run in the current working directory:
|
44
41
|
|
45
|
-
codger
|
42
|
+
codger gen boilerplate
|
46
43
|
|
47
|
-
|
44
|
+
### Caching
|
48
45
|
|
49
|
-
codger
|
46
|
+
codger cache git://uri/to/boilerplate.git # stores a local clone in ~/.codger/cached
|
47
|
+
codger gen boilerplate # uses the local clone (but will update it first if possible)
|
48
|
+
|
49
|
+
### Diffing
|
50
|
+
|
51
|
+
You can add the `-r` (record) option:
|
52
|
+
|
53
|
+
codger gen boilerplate -r
|
50
54
|
|
51
|
-
|
55
|
+
to have the run recorded in a file named `.codger`, so that later, after the project or the skeleton have changed, you can use
|
52
56
|
|
53
57
|
codger diff
|
54
58
|
|
data/lib/codger/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
require 'git'
|
3
5
|
require 'thor'
|
@@ -6,6 +8,23 @@ require 'yaml'
|
|
6
8
|
|
7
9
|
module Codger
|
8
10
|
class CLI < Thor
|
11
|
+
desc 'cache GENERATOR', 'keep a local clone of the generator from the given location'
|
12
|
+
def cache identifier
|
13
|
+
Manager.default.cache identifier
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'cached', 'list all cached generators'
|
17
|
+
def cached
|
18
|
+
Manager.default.settings[:cached].each do |identifier, _|
|
19
|
+
puts identifier
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'uncache GENERATOR', 'remove local clone of a generator'
|
24
|
+
def uncache identifier
|
25
|
+
Manager.default.uncache identifier
|
26
|
+
end
|
27
|
+
|
9
28
|
desc 'config [NAME [VALUE]]', 'lists, shows, or alters configuration'
|
10
29
|
def config(name = nil, value = nil)
|
11
30
|
if name
|
@@ -20,41 +39,19 @@ module Codger
|
|
20
39
|
end
|
21
40
|
end
|
22
41
|
|
23
|
-
desc '
|
24
|
-
|
25
|
-
|
26
|
-
|
42
|
+
desc 'gen GENERATOR [PATH]', 'run the specified generator at the given path or the current working directory'
|
43
|
+
method_option :record, aliases: '-r', type: :boolean, desc: 'record this run in a .codger file in the directory'
|
44
|
+
def gen identifier, path='.'
|
45
|
+
path = File.expand_path path
|
46
|
+
unless File.exists? path
|
47
|
+
FileUtils.mkdir path
|
48
|
+
Git.init path
|
27
49
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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)
|
50
|
+
manager = Manager.new File.join(path, '.codger')
|
51
|
+
generator = manager.generator identifier
|
52
|
+
puts "Running #{generator.identifier}"
|
53
|
+
generator.run path
|
54
|
+
manager.record_run generator if options[:record]
|
58
55
|
end
|
59
56
|
|
60
57
|
desc 'history', 'show the actions recorded for this directory'
|
@@ -91,10 +88,5 @@ module Codger
|
|
91
88
|
end
|
92
89
|
end
|
93
90
|
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
91
|
end
|
100
92
|
end
|
data/lib/codger/manager.rb
CHANGED
@@ -35,8 +35,7 @@ module Codger
|
|
35
35
|
config: {
|
36
36
|
diff: 'diff -ur %SOURCE %DEST'
|
37
37
|
},
|
38
|
-
|
39
|
-
generators: {}
|
38
|
+
cached: {}
|
40
39
|
}.with_indifferent_access
|
41
40
|
if File.exists?(globals_path)
|
42
41
|
@global_settings.merge! YAML.load(File.read(globals_path))
|
@@ -44,43 +43,32 @@ module Codger
|
|
44
43
|
end
|
45
44
|
|
46
45
|
# Creates a Generator, currently always a Skeleton.
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
46
|
+
# id can be:
|
47
|
+
# * a filesystem path, which will be used directly
|
48
|
+
# * a git uri, which will be cloned to a temporary directory
|
49
|
+
# * a substring of the identifier of a cached skeleton
|
50
|
+
def generator(id)
|
51
|
+
if File.exists? id
|
52
|
+
clone = id
|
53
|
+
id = File.expand_path id
|
54
|
+
elsif cached = match_cached_identifier(id)
|
55
|
+
id, clone = cached
|
56
|
+
git = Git.open(clone)
|
57
|
+
# https://github.com/schacon/ruby-git/issues/32
|
58
|
+
begin
|
59
|
+
git.lib.send(:command, 'pull')
|
60
|
+
rescue StandardError => ex
|
61
|
+
puts "Warning: could not update cached clone: #{ex.inspect}"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
clone = Dir.mktmpdir
|
65
|
+
Git.clone id, clone
|
66
|
+
at_exit do
|
67
|
+
raise "I'm scared to delete #{clone}" unless clone.size > 10 # just being paranoid before rm_rf'ing
|
68
|
+
FileUtils.rm_rf clone
|
61
69
|
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
70
|
end
|
83
|
-
|
71
|
+
Skeleton.new clone, id
|
84
72
|
end
|
85
73
|
|
86
74
|
# Saves the tags, identifier, and params from the last run of the given generator instance
|
@@ -88,12 +76,46 @@ module Codger
|
|
88
76
|
def record_run(generator)
|
89
77
|
@project_settings[:runs] << {
|
90
78
|
tags: [generator.name] + generator.tags,
|
91
|
-
generator: generator.
|
79
|
+
generator: generator.identifier,
|
92
80
|
params: generator.params
|
93
81
|
}.with_indifferent_access
|
94
82
|
save_project
|
95
83
|
end
|
96
84
|
|
85
|
+
# Clone the specified repo into #cached_base.
|
86
|
+
def cache identifier
|
87
|
+
return if settings[:cached][identifier]
|
88
|
+
identifier = File.expand_path identifier if File.exist?(identifier) # hacky
|
89
|
+
FileUtils.mkdir_p cached_base
|
90
|
+
next_id = Dir.entries(cached_base).map(&:to_i).max + 1
|
91
|
+
clone = File.join cached_base, next_id.to_s
|
92
|
+
Git.clone identifier, clone
|
93
|
+
@global_settings[:cached][identifier] = clone
|
94
|
+
save_globals
|
95
|
+
end
|
96
|
+
|
97
|
+
# Remove the specified repo from #cached_base. identifier may
|
98
|
+
# be a substring of the desired repo.
|
99
|
+
def uncache identifier
|
100
|
+
if match = match_cached_identifier(identifier)
|
101
|
+
@global_settings[:cached].delete match[0]
|
102
|
+
clone = match[1]
|
103
|
+
raise "I'm scared to delete #{clone}" unless clone.size > 10 && clone.start_with?(cached_base) # again, paranoia
|
104
|
+
FileUtils.rm_rf clone
|
105
|
+
save_globals
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Find a cached repository with an identifier similar to
|
110
|
+
# the given one (specifically, that has the given one as a substring).
|
111
|
+
# Returns [identifier, clone]
|
112
|
+
def match_cached_identifier identifier
|
113
|
+
return nil if identifier.size < 4 # crude typo protection
|
114
|
+
settings[:cached].detect do |cached_id, clone|
|
115
|
+
cached_id.include?(identifier)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
97
119
|
# Save #project_settings.
|
98
120
|
def save_project
|
99
121
|
File.write @project_path, @project_settings.to_yaml
|
@@ -116,9 +138,9 @@ module Codger
|
|
116
138
|
File.join(codger_home, 'codger.yaml')
|
117
139
|
end
|
118
140
|
|
119
|
-
# Return the folder where
|
120
|
-
def
|
121
|
-
File.join
|
141
|
+
# Return the folder where clones can be saved - 'cached' in #codger_home.
|
142
|
+
def cached_base
|
143
|
+
File.join codger_home, 'cached'
|
122
144
|
end
|
123
145
|
|
124
146
|
# Return a merged map of #global_settings and #project_settings.
|
data/lib/codger/skeleton.rb
CHANGED
@@ -17,6 +17,7 @@ module Codger
|
|
17
17
|
#
|
18
18
|
# Methods for use in generate.rb and templates:
|
19
19
|
# * #src_path
|
20
|
+
# * #cancel
|
20
21
|
# * #copy
|
21
22
|
# * #interpolate
|
22
23
|
# * #ignore
|
@@ -26,24 +27,20 @@ module Codger
|
|
26
27
|
# skeletons this is based on the last segment of the origin
|
27
28
|
# URI or of the clone's path.
|
28
29
|
attr_reader :name
|
29
|
-
# Returns the
|
30
|
-
attr_reader :
|
30
|
+
# Returns the identifier that can (hopefully) be used to locate this skeleton in the future.
|
31
|
+
attr_reader :identifier
|
31
32
|
|
32
|
-
# Create an instance reading from the git repository at the specified
|
33
|
-
# path.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@
|
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/,'')
|
33
|
+
# Create an instance reading from the git repository at the specified (local)
|
34
|
+
# path.
|
35
|
+
def initialize clone, identifier
|
36
|
+
@identifier = identifier
|
37
|
+
@git = Git.open(clone)
|
38
|
+
@name = identifier.split('/').last.sub(/\.git\z/,'')
|
42
39
|
end
|
43
40
|
|
44
41
|
# Perform code generation using the process outlined in the class documentation.
|
45
42
|
def generate
|
46
|
-
@to_copy = @git.ls_files.keys - ['README', 'README.md', 'README.markdown', 'generate.rb']
|
43
|
+
@to_copy = @git.ls_files.keys - ['README', 'README.md', 'README.markdown', 'generate.rb', '.gitignore']
|
47
44
|
|
48
45
|
code_path = src_path('generate.rb')
|
49
46
|
if File.exists?(code_path)
|
@@ -104,12 +101,15 @@ module Codger
|
|
104
101
|
end
|
105
102
|
|
106
103
|
mappings.each do |src, dest|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
104
|
+
begin
|
105
|
+
@current_template_src = src
|
106
|
+
@current_template_dest = dest
|
107
|
+
template = ERB.new(File.read(src_path(src)), nil, '-')
|
108
|
+
ensure_folder @current_template_dest
|
109
|
+
result = template.result(binding)
|
110
|
+
File.write dest_path(@current_template_dest), result
|
111
|
+
rescue CancelInterpolation
|
112
|
+
end
|
113
113
|
@to_copy.delete src
|
114
114
|
end
|
115
115
|
end
|
@@ -121,6 +121,11 @@ module Codger
|
|
121
121
|
@current_template_dest = File.join(File.dirname(@current_template_src), dest)
|
122
122
|
end
|
123
123
|
|
124
|
+
# Stop interpolation of the current template.
|
125
|
+
def cancel
|
126
|
+
raise CancelInterpolation.new
|
127
|
+
end
|
128
|
+
|
124
129
|
# For each path or array of paths, disable implicit copying.
|
125
130
|
def ignore(*paths)
|
126
131
|
@to_copy -= paths.flatten
|
@@ -134,4 +139,6 @@ module Codger
|
|
134
139
|
end
|
135
140
|
end
|
136
141
|
end
|
142
|
+
|
143
|
+
class CancelInterpolation < StandardError; end
|
137
144
|
end
|
data/lib/codger/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70203594029980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70203594029980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: deep_merge
|
27
|
-
requirement: &
|
27
|
+
requirement: &70203594029380 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70203594029380
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: git
|
38
|
-
requirement: &
|
38
|
+
requirement: &70203594028620 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.2.5
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70203594028620
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: thor
|
49
|
-
requirement: &
|
49
|
+
requirement: &70203594028020 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: 0.14.6
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70203594028020
|
58
58
|
description: Manages invocation of code generators.
|
59
59
|
email:
|
60
60
|
- jacobaw@gmail.com
|