codger 0.0.1 → 0.0.2

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/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 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
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
- Register a skeleton:
36
+ Run in a new folder:
40
37
 
41
- codger skeleton git://example.com/boilerplate.git
38
+ codger gen boilerplate monumental-endeavor
42
39
 
43
- Run it in a new folder:
40
+ Or run in the current working directory:
44
41
 
45
- codger create boilerplate monumental-endeavor
42
+ codger gen boilerplate
46
43
 
47
- Or run it in the current working directory:
44
+ ### Caching
48
45
 
49
- codger gen boilerplate
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
- 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
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 'available', 'lists registered code generators'
24
- def available
25
- Manager.default.settings[:generators].each do |name, info|
26
- puts "#{name}\t#{info.inspect}"
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
- 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)
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
@@ -35,8 +35,7 @@ module Codger
35
35
  config: {
36
36
  diff: 'diff -ur %SOURCE %DEST'
37
37
  },
38
- clones: {},
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
- # 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
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
- save_globals
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.info,
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 skeleton clones can be saved - 'clones' in #codger_home.
120
- def clones_base
121
- File.join(codger_home, 'clones')
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.
@@ -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 attributes used in creating this instance.
30
- attr_reader :info
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. 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/,'')
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
- @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
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
@@ -1,3 +1,3 @@
1
1
  module Codger
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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.1
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-05 00:00:00.000000000 Z
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: &70337635384820 !ruby/object:Gem::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: *70337635384820
24
+ version_requirements: *70203594029980
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: deep_merge
27
- requirement: &70337635384160 !ruby/object:Gem::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: *70337635384160
35
+ version_requirements: *70203594029380
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: git
38
- requirement: &70337635383500 !ruby/object:Gem::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: *70337635383500
46
+ version_requirements: *70203594028620
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: thor
49
- requirement: &70337635382820 !ruby/object:Gem::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: *70337635382820
57
+ version_requirements: *70203594028020
58
58
  description: Manages invocation of code generators.
59
59
  email:
60
60
  - jacobaw@gmail.com