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