falsework 0.2.8 → 1.3.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.
Files changed (58) hide show
  1. data/{lib/falsework/templates/naive/Gemfile.erb → Gemfile} +1 -1
  2. data/Gemfile.lock +12 -0
  3. data/README.rdoc +45 -21
  4. data/Rakefile +5 -5
  5. data/bin/falsework +64 -28
  6. data/doc/NEWS.rdoc +37 -10
  7. data/doc/README.rdoc +45 -21
  8. data/doc/TODO.org +13 -0
  9. data/doc/template-tutorial.rdoc +113 -0
  10. data/etc/falsework.yaml +1 -1
  11. data/lib/falsework/meta.rb +3 -2
  12. data/lib/falsework/mould.rb +267 -146
  13. data/lib/falsework/templates/c-glib/#config.yaml +18 -0
  14. data/lib/falsework/templates/c-glib/README +24 -0
  15. data/lib/falsework/templates/c-glib/doc/#doc.ascii +46 -0
  16. data/lib/falsework/templates/c-glib/doc/%%@project%%.1.asciidoc +46 -0
  17. data/lib/falsework/templates/{naive/doc/LICENSE.erb → c-glib/doc/LICENSE} +1 -1
  18. data/lib/falsework/templates/c-glib/doc/Makefile +17 -0
  19. data/lib/falsework/templates/c-glib/src/#exe.c +23 -0
  20. data/lib/falsework/templates/c-glib/src/#exe.h +8 -0
  21. data/lib/falsework/templates/c-glib/src/%%@project%%.c +23 -0
  22. data/lib/falsework/templates/c-glib/src/%%@project%%.h +26 -0
  23. data/lib/falsework/templates/c-glib/src/Makefile +28 -0
  24. data/lib/falsework/templates/c-glib/src/untest.c +9 -0
  25. data/lib/falsework/templates/c-glib/src/untest.h +14 -0
  26. data/lib/falsework/templates/c-glib/src/utils.c +232 -0
  27. data/lib/falsework/templates/c-glib/src/utils.h +45 -0
  28. data/lib/falsework/templates/c-glib/test/#test.c +48 -0
  29. data/lib/falsework/templates/c-glib/test/Makefile +78 -0
  30. data/lib/falsework/templates/c-glib/test/Makefile.test.mk +72 -0
  31. data/lib/falsework/templates/c-glib/test/mycat.c +8 -0
  32. data/{test/templates/.keep_me → lib/falsework/templates/c-glib/test/semis/text/empty.txt} +0 -0
  33. data/lib/falsework/templates/c-glib/test/test_utils.c +134 -0
  34. data/lib/falsework/templates/ruby-naive/#config.yaml +15 -0
  35. data/lib/falsework/templates/{naive/.gitignore.erb → ruby-naive/.gitignore.#erb} +0 -0
  36. data/lib/falsework/templates/ruby-naive/Gemfile +4 -0
  37. data/lib/falsework/templates/{naive/doc/README.rdoc.erb → ruby-naive/README.rdoc} +2 -2
  38. data/lib/falsework/templates/{naive/Rakefile.erb → ruby-naive/Rakefile} +1 -1
  39. data/lib/falsework/templates/{naive/bin/%%@project%%.erb → ruby-naive/bin/%%@project%%} +5 -5
  40. data/lib/falsework/templates/{naive/doc/#util.rdoc.erb → ruby-naive/doc/#doc.rdoc} +6 -6
  41. data/lib/falsework/templates/ruby-naive/doc/LICENSE +22 -0
  42. data/lib/falsework/templates/{naive/doc/NEWS.rdoc.erb → ruby-naive/doc/NEWS.rdoc} +0 -0
  43. data/lib/falsework/templates/{naive/README.rdoc.erb → ruby-naive/doc/README.rdoc} +2 -2
  44. data/lib/falsework/templates/{naive/etc/%%@project%%.yaml.erb → ruby-naive/etc/%%@project%%.yaml} +0 -0
  45. data/lib/falsework/templates/{naive/lib/%%@project%%/meta.rb.erb → ruby-naive/lib/%%@project%%/meta.rb} +3 -2
  46. data/lib/falsework/templates/{naive/lib/%%@project%%/trestle.rb.erb → ruby-naive/lib/%%@project%%/trestle.rb} +22 -14
  47. data/lib/falsework/templates/{naive/test/helper.rb.erb → ruby-naive/test/helper.rb} +0 -0
  48. data/lib/falsework/templates/{naive/test/helper_trestle.rb.erb → ruby-naive/test/helper_trestle.rb} +2 -2
  49. data/lib/falsework/templates/{naive/test/rake_git.rb.erb → ruby-naive/test/rake_git.rb} +1 -1
  50. data/lib/falsework/templates/{naive/test/test_%%@project%%.rb.erb → ruby-naive/test/test_%%@project%%.rb} +1 -1
  51. data/lib/falsework/trestle.rb +17 -9
  52. data/test/rake_erb_templates.rb +4 -4
  53. data/test/templates/config-01.yaml +2 -0
  54. data/test/test_cl.rb +29 -0
  55. data/test/test_exe.rb +61 -30
  56. data/test/test_mould.rb +80 -0
  57. metadata +86 -60
  58. data/doc/TODO.rdoc +0 -7
data/doc/TODO.org ADDED
@@ -0,0 +1,13 @@
1
+ * TODO In the distant future [3/6]
2
+
3
+ - [-] more templates [1/4]
4
+ - [X] c glib
5
+ - [ ] java android
6
+ - [ ] ruby sinatra
7
+ - [ ] ruby cli but a lighter one
8
+ - [X] write a small tutorial 'how to write a template'
9
+ - [ ] release to rubygems.org (it must run test, pull to github, tag,
10
+ create gem & upload it)
11
+ - [ ] describe c-glib template
12
+ - [X] normalize 'name' of the project before actually making a template
13
+ - [X] target_uuid variable for exe/doc/test templates
@@ -0,0 +1,113 @@
1
+ = How To Write a Template
2
+
3
+
4
+ == Naming Scheme
5
+
6
+ A recommended scheme is <tt>language-name</tt>, for example,
7
+ <tt>ruby-naive</tt>, <tt>java-android</tt>, <tt>c-gtk</tt>. Try to use
8
+ the name <= 30 characters in length (this is just a advice, not a
9
+ restriction).
10
+
11
+
12
+ == Location
13
+
14
+ Your personal templates must be in <tt>~/.falsework/templates</tt>
15
+ directory. To view all currently available templates, run <tt>falsework
16
+ list</tt>.
17
+
18
+
19
+ == Hierarchy
20
+
21
+ The file hierarchy in your template directory represents the hierarchy
22
+ of a project generated from it. Any file you place in the directory of a
23
+ particular template goes to the future project. (See an exception
24
+ below.) Typically, the name of the file stays the same too.
25
+
26
+ If you don't want some file to appear in the resulting project, prefix
27
+ the file wit a '#' character, for example, <tt>#mytest.c</tt>.
28
+
29
+ There are some files falsework ignores, for example
30
+ <tt>.gitignore</tt>. To include such files, add a <tt>.#erb</tt>
31
+ extension to its name.
32
+
33
+ There is also a special <tt>#config.yaml</tt> file in the root directory
34
+ of the template. It is an instruction for the template how to behave on
35
+ inject falsework commands.
36
+
37
+
38
+ == How Template Files are Processed
39
+
40
+ Every file (except <tt>#config.yaml</tt>) is considered a Ruby erb
41
+ template. Naturally you want to have some dynamic places in your
42
+ template that are different from project to project, for example, its
43
+ name.
44
+
45
+ === The list of useful variables
46
+
47
+ @classy:: A name of the project including spaces, for example,
48
+ 'Foo Bar Pro'
49
+
50
+ @project:: A lowercase derivative from @classy that is suited
51
+ for executable name and Github, for example,
52
+ 'foo_bar_pro'.
53
+
54
+ @camelcase:: Can be used as a module name, for example,
55
+ 'FooBarPro'.
56
+
57
+ uuid:: A string like 'D93E3B05_DAFA_C1F6_8EEA_DBBA1E8DA432'.
58
+ It's unique for every file.
59
+
60
+
61
+ @user:: Github user name.
62
+
63
+ @email:: User email.
64
+
65
+ @gecos:: A full user name.
66
+
67
+ === Variables available only for inject falsework commands
68
+
69
+ target:: Equivalent of @project.
70
+ target_camelcase:: Equivalent of @camelcase.
71
+ target_classy:: Equivalent of @classy.
72
+
73
+
74
+ == Inject Configuration
75
+
76
+ When user types
77
+
78
+ % falsework -t c-glib test foobar
79
+
80
+ falsework looks info <tt>#config.yaml</tt> file in the <tt>c-glib</tt>
81
+ template directory, searches for 'test' key and iterates on its value to
82
+ read some file in the template directory, white that file somewhere and
83
+ set it permission afterwards.
84
+
85
+ The default configuration is:
86
+
87
+ ---
88
+ :exe:
89
+ - :src: null
90
+ :dest: bin/%s'
91
+ :mode_int: 0744
92
+
93
+ :doc:
94
+ - :src: null
95
+ :dest: 'doc/%s.rdoc'
96
+ :mode_int: null
97
+
98
+ :test:
99
+ - :src: null
100
+ :dest: 'test/test_%s.rb'
101
+ :mode_int: null
102
+
103
+ The value of each top level key is an array, so you can inject several
104
+ files at once.
105
+
106
+ src:: A relative path to a file (that usually prefixed with
107
+ '#' & hidden from a generator). When +src+ is +null+, the
108
+ key is ignored and nothing is injected.
109
+
110
+ dest:: Sub-key can have <tt>%s</tt> in it which will be replaced with
111
+ target value ('foobar' in the example above).
112
+
113
+ mode_int:: Permission bits.
data/etc/falsework.yaml CHANGED
@@ -1,2 +1,2 @@
1
1
  ---
2
- :foobar: foobar
2
+ :foobar: 'huh?'
@@ -1,7 +1,8 @@
1
+ # :include: ../../README.rdoc
1
2
  module Falsework
2
- module Meta
3
+ module Meta # :nodoc:
3
4
  NAME = 'falsework'
4
- VERSION = '0.2.8'
5
+ VERSION = '1.3.0'
5
6
  AUTHOR = 'Alexander Gromnitsky'
6
7
  EMAIL = 'alexander.gromnitsky@gmail.com'
7
8
  HOMEPAGE = 'http://github.com/gromnitsky/' + NAME
@@ -1,47 +1,95 @@
1
1
  require 'git'
2
2
  require 'erb'
3
3
  require 'digest/md5'
4
+ require 'securerandom'
4
5
 
5
6
  require_relative 'trestle'
6
7
 
7
- # Class Mould heavily uses 'naive' template. Theoretically it can manage
8
- # any template as long as it has files mentioned in #add.
9
- #
10
- # The directory with template may have files beginning with _#_ char
11
- # which will be ignored in #project_seed (a method that creates a shiny
12
- # new project form a template).
13
- #
14
- # If you need to run through erb not only the contents of a file in a
15
- # template but it name itself, then use the following convention:
16
- #
17
- # %%VARIABLE%%
18
- #
19
- # which is equivalent of erb's: <%= VARIABLE %>. See naive template
20
- # directory for examples.
21
- #
22
- # In the template files you may use any Mould instance variables. The
23
- # most usefull are:
24
- #
25
- # [@project] A project name.
26
- # [@user] Github user name.
27
- # [@email] User email.
28
- # [@gecos] A full user name.
29
8
  module Falsework
9
+ # The directory with template may have files beginning with # char
10
+ # which will be ignored in #project_seed (a method that creates a
11
+ # shiny new project form a template).
12
+ #
13
+ # If you need to run through erb not only the contents of a file in a
14
+ # template but it name itself, then use the following convention:
15
+ #
16
+ # %%VARIABLE%%
17
+ #
18
+ # which is equivalent of erb's: <%= VARIABLE %>. See 'ruby-naive'
19
+ # template directory for examples.
20
+ #
21
+ # In the template files you may use any Mould instance variables. The
22
+ # most usefull are:
23
+ #
24
+ # [@classy] An original project name, for example, 'Foobar Pro'
25
+ #
26
+ # [@project] A project name in lowercase, suitable for a name of an
27
+ # executable, for example, 'Foobar Pro' would be
28
+ # 'foobar_pro'.
29
+ #
30
+ # [@camelcase] A 'normalized' project name, for use in source code,
31
+ # for example, 'foobar pro' would be 'FoobarPro'.
32
+ #
33
+ # [@user] Github user name.
34
+ # [@email] User email.
35
+ # [@gecos] A full user name.
30
36
  class Mould
37
+ # Where @user, @email & @gecos comes from.
31
38
  GITCONFIG = '~/.gitconfig'
32
- TEMPLATE_DIRS = [Trestle.gem_libdir + '/templates',
33
- File.expand_path('~/.' + Meta::NAME + '/templates')]
34
- TEMPLATE_DEFAULT = 'naive'
39
+ # The possible dirs for templates. The first one is system-wide.
40
+ @@template_dirs = [Trestle.gem_libdir + '/templates',
41
+ File.expand_path('~/.' + Meta::NAME + '/templates')]
42
+ # The template used if user didn't select one.
43
+ TEMPLATE_DEFAULT = 'ruby-naive'
44
+ # A file name with configurations for the inject commands.
45
+ TEMPLATE_CONFIG = '#config.yaml'
46
+ # A list of files to ignore in any template.
35
47
  IGNORE_FILES = ['.gitignore']
36
48
 
37
- attr_accessor :verbose, :batch
38
-
39
- def initialize(project, user = nil, email = nil, gecos = nil)
49
+ # A verbose level for -v CLO.
50
+ attr_accessor :verbose
51
+ # -b CLO.
52
+ attr_accessor :batch
53
+ # A directory of a new generated project.
54
+ attr_reader :project
55
+
56
+ # [project] A name of the future project; may include all crap with spaces.
57
+ # [template] A name of the template for the project.
58
+ # [user] Github username; if nil we are extracting it from the ~/.gitconfig.
59
+ # [email] Github email
60
+ # [gecos] A full author name from ~/.gitconfig.
61
+ def initialize(project, template, user = nil, email = nil, gecos = nil)
62
+ @project = Mould.name_project project
63
+ raise "invalid project name '#{project}'" if !Mould.name_valid? @project
64
+ @camelcase = Mould.name_camelcase project
65
+ @classy = Mould.name_classy project
66
+
40
67
  @verbose = false
41
68
  @batch = false
69
+ @template = template
70
+ @dir_t = Mould.templates[@template || TEMPLATE_DEFAULT] || Trestle.errx(1, "no such template: #{template}")
71
+
72
+ # default config
73
+ @conf = {
74
+ exe: [{
75
+ src: nil,
76
+ dest: 'bin/%s',
77
+ mode_int: 0744
78
+ }],
79
+ doc: [{
80
+ src: nil,
81
+ dest: 'doc/%s.rdoc',
82
+ mode_int: nil
83
+ }],
84
+ test: [{
85
+ src: nil,
86
+ dir: 'test/test_%s.rb',
87
+ mode_int: nil
88
+ }]
89
+ }
90
+ Mould.config_parse(@dir_t + '/' + TEMPLATE_CONFIG, [], @conf)
42
91
 
43
92
  gc = Git.global_config rescue gc = {}
44
- @project = project
45
93
  @user = user || gc['github.user']
46
94
  @email = email || ENV['GIT_AUTHOR_EMAIL'] || ENV['GIT_COMMITTER_EMAIL'] || gc['user.email']
47
95
  @gecos = gecos || ENV['GIT_AUTHOR_NAME'] || ENV['GIT_COMMITTER_NAME'] || gc['user.name']
@@ -53,11 +101,72 @@ module Falsework
53
101
  }
54
102
  end
55
103
 
104
+ # Modifies an internal list of available template directories
105
+ def self.template_dirs_add(dirs)
106
+ return unless defined? dirs.each
107
+
108
+ dirs.each {|idx|
109
+ if ! File.directory?(idx)
110
+ Trestle.warnx "invalid additional template directory: #{idx}"
111
+ else
112
+ @@template_dirs << idx
113
+ end
114
+ }
115
+ end
116
+
117
+ # Hyper-fast generator of something like uuid suitable for code
118
+ # identifiers. Return a string.
119
+ def self.uuidgen_fake
120
+ loop {
121
+ r = ('%s_%s_%s_%s_%s' % [
122
+ SecureRandom.hex(4),
123
+ SecureRandom.hex(2),
124
+ SecureRandom.hex(2),
125
+ SecureRandom.hex(2),
126
+ SecureRandom.hex(6),
127
+ ]).upcase
128
+ return r if r[0] !~ /\d/
129
+ }
130
+ end
131
+
132
+ # Return false if @t is invalid.
133
+ def self.name_valid?(t)
134
+ return false if !t || t[0] =~ /\d/
135
+ t =~ /^[a-zA-Z0-9_]+$/ ? true : false
136
+ end
137
+
138
+ # Return cleaned version of an original project name, for example,
139
+ # 'Foobar Pro'
140
+ def self.name_classy(t)
141
+ t ? t.gsub(/\s+/, ' ').strip : ''
142
+ end
143
+
144
+ # Return a project name in lowercase, suitable for a name of an
145
+ # executable; for example, 'Foobar Pro' would be 'foobar_pro'.
146
+ def self.name_project(raw)
147
+ raw || (return '')
148
+
149
+ r = raw.gsub(/[^a-zA-Z0-9_]+/, '_').downcase
150
+ r.sub!(/^_/, '');
151
+ r.sub!(/_$/, '');
152
+
153
+ r
154
+ end
155
+
156
+ # Return a 'normalized' project name, for use in source code; for
157
+ # example, 'foobar pro' would be 'FoobarPro'.
158
+ def self.name_camelcase(raw)
159
+ raw || (return '')
160
+ raw.strip.split(/[^a-zA-Z0-9]+/).map{|idx|
161
+ idx[0].upcase + idx[1..-1]
162
+ }.join
163
+ end
164
+
56
165
  # Return a hash {name => dir} with current possible template names
57
166
  # and corresponding directories.
58
167
  def self.templates
59
168
  r = {}
60
- TEMPLATE_DIRS.each {|i|
169
+ @@template_dirs.each {|i|
61
170
  Dir.glob(i + '/*').each {|j|
62
171
  r[File.basename(j)] = j if File.directory?(j)
63
172
  }
@@ -65,111 +174,121 @@ module Falsework
65
174
  r
66
175
  end
67
176
 
68
- # Generate a new project in @project directory from _template_.
69
- #
70
- # [template] If it's nil TEMPLATE_DEFAULT will be used.
71
- # [filter] A regexp for matching particular files in the
72
- # template directory.
177
+ # Generate a new project in @project directory from @template.
73
178
  #
74
179
  # Return false if nothing was extracted.
75
- def project_seed(template, filter)
76
- sl = ->(is_dir, *args) {
77
- is_dir ? Mould.erb_fname(*args) : Mould.erb_fname(*args).sub(/\.erb$/, '')
78
- }
180
+ def project_seed()
181
+ uuid = Mould.uuidgen_fake # useful variable for the template
79
182
 
80
183
  # check for existing project
81
184
  Trestle.errx(1, "directory '#{@project}' is not empty") if Dir.glob(@project + '/*').size > 0
82
185
 
83
- Dir.mkdir(@project) unless File.directory?(@project)
84
- prjdir = File.expand_path(@project)
85
- puts "Project path: #{prjdir}" if @verbose
86
-
87
- origdir = Dir.pwd;
88
- Dir.chdir @project
186
+ Dir.mkdir @project unless File.directory?(@project)
187
+ puts "Project path: #{File.expand_path(@project)}" if @verbose
89
188
 
90
189
  r = false
91
- start = Mould.templates[template || TEMPLATE_DEFAULT] || Trestle.errx(1, "no such template: #{template}")
92
- puts "Template: #{start}" if @verbose
190
+ puts "Template: #{@dir_t}" if @verbose
93
191
  symlinks = []
94
- Mould.traverse(start) {|i|
95
- file = i.sub(/^#{start}\//, '')
96
- next if filter ? file =~ filter : false
97
- next if IGNORE_FILES.index {|ign| file.match(/#{ign}$/) }
192
+ Dir.chdir(@project) {
193
+ Mould.traverse(@dir_t) {|idx|
194
+ file = idx.sub(/^#{@dir_t}\//, '')
195
+ next if IGNORE_FILES.index {|i| file.match(/#{i}$/) }
98
196
 
99
- if File.symlink?(i)
100
- # we'll process them later on
101
- is_dir = File.directory?(start + '/' + File.readlink(i))
102
- symlinks << [sl.call(is_dir, File.readlink(i), binding),
103
- sl.call(is_dir, file, binding)]
104
- elsif File.directory?(i)
105
- puts("D: #{file}") if @verbose
106
- file = Mould.erb_fname(file, binding)
107
- # FileUtils.mkdir_p(prjdir + '/' + file)
108
- Dir.mkdir(prjdir + '/' + file)
109
- Dir.chdir(prjdir + '/' + file)
110
- else
111
- puts("N: #{file}") if @verbose
112
- to = File.basename(Mould.erb_fname(file, binding), '.erb')
113
- Mould.extract(start + '/' + file, binding, to)
114
- # make files in bin/ executable
115
- File.chmod(0744, to) if file =~ /bin\//
116
- end
117
- r = true
118
- }
197
+ if File.symlink?(idx)
198
+ # we'll process them later on
199
+ is_dir = File.directory?(@dir_t + '/' + File.readlink(idx))
200
+ symlinks << [Mould.get_filename(File.readlink(idx), binding),
201
+ Mould.get_filename(file, binding)]
202
+ elsif File.directory?(idx)
203
+ puts "D: #{file}" if @verbose
204
+ Dir.mkdir Mould.get_filename(file, binding)
205
+ else
206
+ puts "N: #{file}" if @verbose
207
+ to = Mould.get_filename(file, binding)
208
+ Mould.extract(idx, binding, to)
209
+ end
210
+ r = true
211
+ }
119
212
 
120
- # create saved symlinks
121
- Dir.chdir prjdir
122
- symlinks.each {|i|
123
- # src = i[0].sub(/#{File.extname(i[0])}$/, '')
124
- # dest = i[1].sub(/#{File.extname(i[1])}$/, '')
125
- src = i[0]
126
- dest = i[1]
127
- puts "L: #{dest} => #{src}" if @verbose
128
- File.symlink(src, dest)
213
+ # create saved symlinks
214
+ symlinks.each {|idx|
215
+ src = idx[0]
216
+ dest = idx[1]
217
+ puts "L: #{dest} => #{src}" if @verbose
218
+ File.symlink(src, dest)
219
+ }
129
220
  }
130
- Dir.chdir origdir
221
+
131
222
  r
132
223
  end
133
224
 
134
- # Add an executable or a test from the _template_.
225
+ # Parse a config. Return false on error.
135
226
  #
136
- # [mode] Is either 'exe' or 'test'.
137
- # [what] A test/exe file to create.
227
+ # [file] A file to parse.
228
+ # [rvars] A list of variable names which must be in the config.
229
+ # [hash] a hash to merge results with
230
+ def self.config_parse(file, rvars, hash)
231
+ r = true
232
+
233
+ if File.readable?(file)
234
+ begin
235
+ myconf = YAML.load_file(file)
236
+ rescue
237
+ Trestle.warnx "cannot parse #{file}: #{$!}"
238
+ return false
239
+ end
240
+ rvars.each { |i|
241
+ Trestle.warnx "missing or nil '#{i}' in #{file}" if ! myconf.key?(i.to_sym) || ! myconf[i.to_sym]
242
+ r = false
243
+ }
244
+
245
+ hash.merge!(myconf) if r && hash
246
+ else
247
+ r = false
248
+ end
249
+
250
+ r
251
+ end
252
+
253
+ # Add an executable or a test from the template.
254
+ #
255
+ # [mode] Is either 'exe', 'doc' or 'test'.
256
+ # [target] A test/doc/exe file to create.
257
+ #
258
+ # Return a list of a created files.
138
259
  #
139
- # Return a name of a created file.
140
- def add(template, mode, what)
141
- start = Mould.templates[template || TEMPLATE_DEFAULT] || Trestle.errx(1, "no such template: #{template}")
142
- r = []
260
+ # Useful variables in the template:
261
+ #
262
+ # [target]
263
+ # [target_camelcase]
264
+ # [target_classy]
265
+ # [uuid]
266
+ def add(mode, target)
267
+ target_orig = target
268
+ target = Mould.name_project target_orig
269
+ raise "invalid target name '#{target_orig}'" if !Mould.name_valid? target
270
+ target_camelcase = Mould.name_camelcase target_orig
271
+ target_classy = Mould.name_classy target_orig
272
+ uuid = Mould.uuidgen_fake
273
+
274
+ created = []
143
275
 
144
- case mode
145
- when 'exe'
146
- # script
147
- f = {}
148
- f[:from] = start + '/' + 'bin/%%@project%%.erb'
149
- f[:exe] = true
150
- f[:to] = "bin/#{what}"
151
- r << f
276
+ return [] unless @conf[mode.to_sym][0][:src]
152
277
 
153
- # doc (reading an 'ignored' file from the template)
154
- f = {}
155
- f[:from] = start + '/' + 'doc/#util.rdoc.erb'
156
- f[:exe] = false
157
- f[:to] = "doc/#{what}.rdoc"
158
- r << f
159
- when 'test'
160
- f = {}
161
- f[:from] = start + '/' + 'test/test_%%@project%%.rb.erb'
162
- f[:exe] = false
163
- f[:to] = "#{mode}/test_#{what}.rb"
164
- r << f
165
- else
166
- fail "invalid mode #{mode}"
167
- end
278
+ @conf[mode.to_sym].each {|idx|
279
+ to = idx[:dest] % target
168
280
 
169
- r.each {|i|
170
- Mould.extract(i[:from], binding, i[:to])
171
- File.chmod(0744, i[:to]) if i[:exe]
281
+ begin
282
+ Mould.extract(@dir_t + '/' + idx[:src], binding, to)
283
+ File.chmod(idx[:mode_int], to) if idx[:mode_int]
284
+ rescue
285
+ Trestle.warnx "failed to create '#{to}' (check your #config.yaml): #{$!}"
286
+ else
287
+ created << to
288
+ end
172
289
  }
290
+
291
+ created
173
292
  end
174
293
 
175
294
  # Walk through a directory tree, executing a block for each file or
@@ -191,53 +310,56 @@ module Falsework
191
310
  }
192
311
  end
193
312
 
194
- # Extract into the current directory 1 file from _path_.
313
+ # Extract file @from into @to.
195
314
  #
196
- # [bin] A binding for eval.
197
- # [to] If != nil write to a particular, not guessed file name.
198
- def self.extract(path, bin, to = nil)
199
- t = ERB.new(File.read(path))
200
- t.filename = path # to report errors relative to this file
315
+ # [binding] A binding for eval.
316
+ def self.extract(from, binding, to)
317
+ t = ERB.new(File.read(from))
318
+ t.filename = from # to report errors relative to this file
201
319
  begin
202
- # pp t.result
203
- md5_system = Digest::MD5.hexdigest(t.result(bin))
320
+ output = t.result(binding)
321
+ md5_system = Digest::MD5.hexdigest(output)
204
322
  rescue Exception
205
- Trestle.errx(1, "cannot read the template file: #{$!}")
323
+ Trestle.errx(1, "bogus template file '#{from}': #{$!}")
206
324
  end
207
325
 
208
- skeleton = to || File.basename(path, '.erb')
209
- if ! File.exists?(skeleton)
326
+ if ! File.exists?(to)
210
327
  # write a skeleton
211
328
  begin
212
- File.open(skeleton, 'w+') { |fp| fp.puts t.result(bin) }
329
+ File.open(to, 'w+') { |fp| fp.puts output }
330
+ # transfer the exec bit to the generated result
331
+ File.chmod(0744, to) if File.stat(from).executable?
213
332
  rescue
214
- Trestle.errx(1, "cannot write the skeleton: #{$!}")
333
+ Trestle.errx(1, "cannot generate: #{$!}")
215
334
  end
216
335
  elsif
217
336
  # warn a careless user
218
- if md5_system != Digest::MD5.file(skeleton).hexdigest
219
- Trestle.errx(1, "#{skeleton} already exists")
337
+ if md5_system != Digest::MD5.file(to).hexdigest
338
+ Trestle.errx(1, "'#{to}' already exists")
220
339
  end
221
340
  end
222
341
  end
223
342
 
224
- def self.erb_fname(t, bin)
225
- re = /%%([^.]+)?%%/
226
- return ERB.new(t.gsub(re, '<%= \1 %>')).result(bin) if t =~ re
227
- return t
343
+ # Resolve @t from possible %%VARIABLE%% scheme.
344
+ def self.get_filename(t, binding)
345
+ t || (return '')
346
+
347
+ re = /%%([^%]+)%%/
348
+ t = ERB.new(t.gsub(re, '<%= \+ %>')).result(binding) if t =~ re
349
+ t.sub /\.#erb$/, ''
228
350
  end
229
351
 
230
352
 
231
- # Search for all files in the _template_ for the line
353
+ # Search for all files in the template directory the line
232
354
  #
233
355
  # /^..? :erb:/
234
356
  #
235
357
  # in first n lines. If the line is found, the file is considered a
236
358
  # candidate for an upgrade. Return a hash {target:template}
237
- def upgradable_files(template)
359
+ def upgradable_files()
238
360
  line_max = 4
239
361
  r = {}
240
- Falsework::Mould.traverse(template) {|i|
362
+ Falsework::Mould.traverse(@dir_t) {|i|
241
363
  next if File.directory?(i)
242
364
  next if File.symlink?(i) # hm...
243
365
 
@@ -245,8 +367,8 @@ module Falsework
245
367
  n = 0
246
368
  while n < line_max && line = fp.gets
247
369
  if line =~ /^..? :erb:/
248
- t = i.sub(/#{template}\//, '')
249
- r[Mould.erb_fname(t, binding).sub(/\.erb$/, '')] = i
370
+ t = i.sub(/#{@dir_t}\//, '')
371
+ r[Mould.get_filename(t, binding)] = i
250
372
  break
251
373
  end
252
374
  n += 1
@@ -276,11 +398,10 @@ module Falsework
276
398
  #
277
399
  # Neithe we do check for a content of upgradable files nor try to
278
400
  # merge old with new. (Why?)
279
- def upgrade(template)
401
+ def upgrade()
280
402
  # 0. search for 'new' files in the template
281
- t = Mould.templates[template || TEMPLATE_DEFAULT] || Trestle.errx(1, "no such template: #{template}")
282
- uf = upgradable_files(t)
283
- fail "template #{template} cannot offer to you files for an upgrade" if uf.size == 0
403
+ uf = upgradable_files
404
+ fail "template #{@template} cannot offer you files for the upgrade" if uf.size == 0
284
405
  # pp uf
285
406
 
286
407
  # 1. analyse 'old' files
@@ -293,10 +414,10 @@ module Falsework
293
414
  File.open(k) {|fp|
294
415
  is_versioned = false
295
416
  while line = fp.gets
296
- if line =~ /^# Don't remove this: falsework\/(#{Gem::Version::VERSION_PATTERN})\/(\w+)\/.+/
417
+ if line =~ /^# Don't remove this: falsework\/(#{Gem::Version::VERSION_PATTERN})\/(.+)\/.+/
297
418
  is_versioned = true
298
- if $3 != (template || TEMPLATE_DEFAULT)
299
- fail "file #{k} is from #{$3} template"
419
+ if $3 != (@template || TEMPLATE_DEFAULT)
420
+ fail "file #{k} is from '#{$3}' template"
300
421
  end
301
422
  if Gem::Version.new(Meta::VERSION) >= Gem::Version.new($1)
302
423
  # puts "#{k}: #{$1}"
@@ -312,7 +433,7 @@ module Falsework
312
433
  }
313
434
  end
314
435
  }
315
- fail "template #{template || TEMPLATE_DEFAULT} cannot find files for an upgrade" if u.size == 0
436
+ fail "template #{@template || TEMPLATE_DEFAULT} cannot find files for an upgrade" if u.size == 0
316
437
 
317
438
  # 2. ask user for a commitment
318
439
  if ! @batch