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