falsework 0.2.8 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{lib/falsework/templates/naive/Gemfile.erb → Gemfile} +1 -1
- data/Gemfile.lock +12 -0
- data/README.rdoc +45 -21
- data/Rakefile +5 -5
- data/bin/falsework +64 -28
- data/doc/NEWS.rdoc +37 -10
- data/doc/README.rdoc +45 -21
- data/doc/TODO.org +13 -0
- data/doc/template-tutorial.rdoc +113 -0
- data/etc/falsework.yaml +1 -1
- data/lib/falsework/meta.rb +3 -2
- data/lib/falsework/mould.rb +267 -146
- data/lib/falsework/templates/c-glib/#config.yaml +18 -0
- data/lib/falsework/templates/c-glib/README +24 -0
- data/lib/falsework/templates/c-glib/doc/#doc.ascii +46 -0
- data/lib/falsework/templates/c-glib/doc/%%@project%%.1.asciidoc +46 -0
- data/lib/falsework/templates/{naive/doc/LICENSE.erb → c-glib/doc/LICENSE} +1 -1
- data/lib/falsework/templates/c-glib/doc/Makefile +17 -0
- data/lib/falsework/templates/c-glib/src/#exe.c +23 -0
- data/lib/falsework/templates/c-glib/src/#exe.h +8 -0
- data/lib/falsework/templates/c-glib/src/%%@project%%.c +23 -0
- data/lib/falsework/templates/c-glib/src/%%@project%%.h +26 -0
- data/lib/falsework/templates/c-glib/src/Makefile +28 -0
- data/lib/falsework/templates/c-glib/src/untest.c +9 -0
- data/lib/falsework/templates/c-glib/src/untest.h +14 -0
- data/lib/falsework/templates/c-glib/src/utils.c +232 -0
- data/lib/falsework/templates/c-glib/src/utils.h +45 -0
- data/lib/falsework/templates/c-glib/test/#test.c +48 -0
- data/lib/falsework/templates/c-glib/test/Makefile +78 -0
- data/lib/falsework/templates/c-glib/test/Makefile.test.mk +72 -0
- data/lib/falsework/templates/c-glib/test/mycat.c +8 -0
- data/{test/templates/.keep_me → lib/falsework/templates/c-glib/test/semis/text/empty.txt} +0 -0
- data/lib/falsework/templates/c-glib/test/test_utils.c +134 -0
- data/lib/falsework/templates/ruby-naive/#config.yaml +15 -0
- data/lib/falsework/templates/{naive/.gitignore.erb → ruby-naive/.gitignore.#erb} +0 -0
- data/lib/falsework/templates/ruby-naive/Gemfile +4 -0
- data/lib/falsework/templates/{naive/doc/README.rdoc.erb → ruby-naive/README.rdoc} +2 -2
- data/lib/falsework/templates/{naive/Rakefile.erb → ruby-naive/Rakefile} +1 -1
- data/lib/falsework/templates/{naive/bin/%%@project%%.erb → ruby-naive/bin/%%@project%%} +5 -5
- data/lib/falsework/templates/{naive/doc/#util.rdoc.erb → ruby-naive/doc/#doc.rdoc} +6 -6
- data/lib/falsework/templates/ruby-naive/doc/LICENSE +22 -0
- data/lib/falsework/templates/{naive/doc/NEWS.rdoc.erb → ruby-naive/doc/NEWS.rdoc} +0 -0
- data/lib/falsework/templates/{naive/README.rdoc.erb → ruby-naive/doc/README.rdoc} +2 -2
- data/lib/falsework/templates/{naive/etc/%%@project%%.yaml.erb → ruby-naive/etc/%%@project%%.yaml} +0 -0
- data/lib/falsework/templates/{naive/lib/%%@project%%/meta.rb.erb → ruby-naive/lib/%%@project%%/meta.rb} +3 -2
- data/lib/falsework/templates/{naive/lib/%%@project%%/trestle.rb.erb → ruby-naive/lib/%%@project%%/trestle.rb} +22 -14
- data/lib/falsework/templates/{naive/test/helper.rb.erb → ruby-naive/test/helper.rb} +0 -0
- data/lib/falsework/templates/{naive/test/helper_trestle.rb.erb → ruby-naive/test/helper_trestle.rb} +2 -2
- data/lib/falsework/templates/{naive/test/rake_git.rb.erb → ruby-naive/test/rake_git.rb} +1 -1
- data/lib/falsework/templates/{naive/test/test_%%@project%%.rb.erb → ruby-naive/test/test_%%@project%%.rb} +1 -1
- data/lib/falsework/trestle.rb +17 -9
- data/test/rake_erb_templates.rb +4 -4
- data/test/templates/config-01.yaml +2 -0
- data/test/test_cl.rb +29 -0
- data/test/test_exe.rb +61 -30
- data/test/test_mould.rb +80 -0
- metadata +86 -60
- 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:
|
2
|
+
:foobar: 'huh?'
|
data/lib/falsework/meta.rb
CHANGED
@@ -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 = '
|
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
|
data/lib/falsework/mould.rb
CHANGED
@@ -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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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
|
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(
|
76
|
-
|
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
|
84
|
-
|
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
|
-
|
92
|
-
puts "Template: #{start}" if @verbose
|
190
|
+
puts "Template: #{@dir_t}" if @verbose
|
93
191
|
symlinks = []
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
#
|
125
|
-
|
126
|
-
|
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
|
-
|
221
|
+
|
131
222
|
r
|
132
223
|
end
|
133
224
|
|
134
|
-
#
|
225
|
+
# Parse a config. Return false on error.
|
135
226
|
#
|
136
|
-
# [
|
137
|
-
# [
|
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
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
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
|
-
|
154
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
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
|
313
|
+
# Extract file @from into @to.
|
195
314
|
#
|
196
|
-
# [
|
197
|
-
|
198
|
-
|
199
|
-
t =
|
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
|
-
|
203
|
-
md5_system = Digest::MD5.hexdigest(
|
320
|
+
output = t.result(binding)
|
321
|
+
md5_system = Digest::MD5.hexdigest(output)
|
204
322
|
rescue Exception
|
205
|
-
Trestle.errx(1, "
|
323
|
+
Trestle.errx(1, "bogus template file '#{from}': #{$!}")
|
206
324
|
end
|
207
325
|
|
208
|
-
|
209
|
-
if ! File.exists?(skeleton)
|
326
|
+
if ! File.exists?(to)
|
210
327
|
# write a skeleton
|
211
328
|
begin
|
212
|
-
File.open(
|
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
|
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(
|
219
|
-
Trestle.errx(1, "#{
|
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
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
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
|
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(
|
359
|
+
def upgradable_files()
|
238
360
|
line_max = 4
|
239
361
|
r = {}
|
240
|
-
Falsework::Mould.traverse(
|
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(/#{
|
249
|
-
r[Mould.
|
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(
|
401
|
+
def upgrade()
|
280
402
|
# 0. search for 'new' files in the template
|
281
|
-
|
282
|
-
uf
|
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})\/(
|
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
|