klipp 0.0.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/README.md +13 -26
- data/bin/klipp +1 -1
- data/klipp.gemspec +11 -6
- data/lib/klipp/configuration.rb +0 -4
- data/lib/klipp/creator.rb +78 -0
- data/lib/klipp/parameter_list.rb +3 -3
- data/lib/klipp/version.rb +1 -1
- data/lib/klipp.rb +148 -92
- data/lib/template/spec.rb +231 -0
- data/lib/template/token.rb +81 -0
- data/lib/template.rb +59 -0
- data/spec/fixtures/ambiguous-repo/Ambiguous/Ambiguous.klippspec +5 -0
- data/spec/fixtures/projects/Klippfile +26 -0
- data/spec/fixtures/projects/Klippfile-after-prepare +27 -0
- data/spec/fixtures/projects/Klippfile-ambiguous +1 -0
- data/spec/fixtures/projects/Klippfile-bad-ruby +3 -0
- data/spec/fixtures/projects/Klippfile-minimal +1 -0
- data/spec/fixtures/projects/Klippfile-unambiguous +1 -0
- data/spec/fixtures/template-repository/Ambiguous/Ambiguous.klippspec +5 -0
- data/spec/fixtures/template-repository/Another-Template/Another-Template.klippspec +20 -0
- data/spec/fixtures/template-repository/BadExample/BadExample.klippspec +35 -0
- data/spec/fixtures/template-repository/Empty/Empty.klippspec +20 -0
- data/spec/fixtures/template-repository/Example/.gitignore +10 -0
- data/spec/fixtures/template-repository/Example/Example.klippspec +40 -0
- data/spec/fixtures/template-repository/Example/Podfile +10 -0
- data/spec/fixtures/template-repository/Example/XXBLANKXX.hidden +10 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Config/Base.xcconfig +8 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default-568h@2x.png +0 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default.png +0 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default@2x.png +0 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.h +13 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.m +30 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXRootViewController.h +11 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXRootViewController.m +30 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/XXPROJECT_TITLEXX-Info.plist +38 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/XXPROJECT_TITLEXX-Prefix.pch +14 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/en.lproj/Localizable.strings +1 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/main.m +17 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/nl.lproj/Localizable.strings +1 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX.xcodeproj/project.pbxproj +466 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/spec/fixtures/template-repository/Example/XXPROJECT_IDXXTests/XXPROJECT_TITLEXXTests.m +22 -0
- data/spec/fixtures/template-repository/Interactive/Interactive.klippspec +15 -0
- data/spec/fixtures/template-repository/Interactive/XXSUBJECT_UNDER_TESTXXTests.m +15 -0
- data/spec/klipp/configuration_spec.rb +8 -0
- data/spec/klipp/creator_spec.rb +120 -0
- data/spec/klipp_spec.rb +80 -85
- data/spec/spec_helper.rb +8 -2
- data/spec/template/spec_spec.rb +225 -0
- data/spec/template/token_spec.rb +100 -0
- data/spec/template_spec.rb +82 -0
- metadata +118 -43
- data/lib/klipp/buffered_output.rb +0 -17
- data/lib/klipp/project.rb +0 -46
- data/lib/klipp/template.rb +0 -50
- data/lib/klipp/token.rb +0 -35
- data/spec/fixtures/klipps/Example.klippfile +0 -4
- data/spec/fixtures/klipps/ExcessiveExample.klippfile +0 -5
- data/spec/fixtures/klipps/Generated.klippfile +0 -11
- data/spec/fixtures/klipps/LackingExample.klippfile +0 -3
- data/spec/fixtures/klipps/MalformedExample.klippfile +0 -4
- data/spec/fixtures/klipps/single-token.yml +0 -5
- data/spec/fixtures/templates/Example/RegularFileWithContents.txt +0 -3
- data/spec/fixtures/templates/Example/XXCLASS_PREFIXXXPrefixedFile.txt +0 -3
- data/spec/fixtures/templates/Example/XXPROJECT_IDXX/BinaryFile.png +0 -0
- data/spec/fixtures/templates/Example/XXPROJECT_IDXX/XXCLASS_PREFIXXXPrefixedFileInDirectory.txt +0 -3
- data/spec/fixtures/templates/Example.yml +0 -29
- data/spec/klipp/project_spec.rb +0 -46
- data/spec/klipp/template_spec.rb +0 -80
- data/spec/klipp/token_spec.rb +0 -86
@@ -0,0 +1,231 @@
|
|
1
|
+
module Template
|
2
|
+
|
3
|
+
class Spec
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
attr_accessor :identifier, :block_actions_under_git
|
7
|
+
attr_reader :post_actions
|
8
|
+
|
9
|
+
def self.identifier_is_ambiguous(identifier)
|
10
|
+
specs_matching_identifier(identifier).count == 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.specs_matching_identifier(identifier)
|
14
|
+
chunks = identifier.split(File::SEPARATOR)
|
15
|
+
name = chunks.pop
|
16
|
+
repo = chunks.count > 0 ? File.join(chunks.pop, '**') : '**'
|
17
|
+
Dir.glob(File.join(Klipp::Configuration.root_dir, repo, "#{name}.klippspec"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.spec_path_for_identifier(identifier)
|
21
|
+
specs = specs_matching_identifier identifier
|
22
|
+
raise "Unknown template: #{identifier}. Use `klipp template list` to see your options" unless specs && specs.count > 0
|
23
|
+
raise "Found multiple templates named #{identifier}, use a full template identifier to pick one. Run `klipp template list` to see your options" if specs && specs.count > 1
|
24
|
+
specs.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.hash_for_spec_path(spec_path)
|
28
|
+
relative_spec = spec_path.gsub(Klipp::Configuration.root_dir, '')
|
29
|
+
{
|
30
|
+
name: File.basename(spec_path, '.klippspec'),
|
31
|
+
repo: relative_spec.split(File::SEPARATOR).map { |x| x=="" ? File::SEPARATOR : x }[1..-1].first
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.hash_to_identifier(template_hash)
|
36
|
+
"#{template_hash[:repo]}/#{template_hash[:name]}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.expand_identifier(template_identifier)
|
40
|
+
path = spec_path_for_identifier template_identifier
|
41
|
+
hash = hash_for_spec_path path
|
42
|
+
hash_to_identifier hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.from_file(path)
|
46
|
+
string = IO.read path
|
47
|
+
spec = Template::Spec.new
|
48
|
+
spec.from_string(string, path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@tokens = Hash[]
|
53
|
+
|
54
|
+
self[:BLANK] = Template::Token.new('', true)
|
55
|
+
self[:DATE] = Template::Token.new(DateTime.now.strftime('%F'), true)
|
56
|
+
self[:YEAR] = Template::Token.new(DateTime.now.strftime('%Y'), true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def post_action=(action)
|
60
|
+
post_actions << action
|
61
|
+
end
|
62
|
+
|
63
|
+
def post_actions
|
64
|
+
@post_actions ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
def post_actions=(post_actions)
|
68
|
+
@post_actions = post_actions.is_a?(Array) ? post_actions : [post_actions.to_s]
|
69
|
+
end
|
70
|
+
|
71
|
+
def pre_action=(action)
|
72
|
+
pre_actions << action
|
73
|
+
end
|
74
|
+
|
75
|
+
def pre_actions
|
76
|
+
@pre_actions ||= []
|
77
|
+
end
|
78
|
+
|
79
|
+
def pre_actions=(pre_actions)
|
80
|
+
@pre_actions = pre_actions.is_a?(Array) ? pre_actions : [pre_actions.to_s]
|
81
|
+
end
|
82
|
+
|
83
|
+
def from_string(string, path)
|
84
|
+
begin
|
85
|
+
eval(string, nil, path)
|
86
|
+
rescue Exception => e
|
87
|
+
raise "Error evaluating spec: #{File.basename(path)}: #{e.message}\n #{e.backtrace.join("\n ")}"
|
88
|
+
end
|
89
|
+
validate_spec
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method is called from the klippspec
|
93
|
+
def spec identifier, &config
|
94
|
+
self.identifier = identifier
|
95
|
+
begin
|
96
|
+
config.yield(self) if (block_given?)
|
97
|
+
rescue Exception => e
|
98
|
+
raise "Invalid klippspec configuration: #{e.message}\n #{e.backtrace.join("\n ")}"
|
99
|
+
end
|
100
|
+
validate_spec
|
101
|
+
end
|
102
|
+
|
103
|
+
def token identifier, &config
|
104
|
+
token = Template::Token.new
|
105
|
+
raise 'Incomplete token configuration' unless block_given?
|
106
|
+
config.yield(token)
|
107
|
+
self[identifier] = token
|
108
|
+
end
|
109
|
+
|
110
|
+
def [](name)
|
111
|
+
@tokens[name]
|
112
|
+
end
|
113
|
+
|
114
|
+
def []=(name, token)
|
115
|
+
raise "Redeclaring tokens not allowed: #{name}" if @tokens[name]
|
116
|
+
@tokens[name] = token
|
117
|
+
end
|
118
|
+
|
119
|
+
def validate_spec
|
120
|
+
msg = 'Template configuration invalid: '
|
121
|
+
invalidate msg+'missing name' unless @identifier && @identifier.length > 0
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def set_token_values(tokens, verbose=false)
|
126
|
+
msg = 'Token configuration error: '
|
127
|
+
puts() if verbose
|
128
|
+
tokens.each do |name, value|
|
129
|
+
token = self[name]
|
130
|
+
invalidate msg+"unknown token :#{name}" unless token
|
131
|
+
begin
|
132
|
+
Formatador.display_line("#{name}: [bold]#{value}[/]") if verbose
|
133
|
+
token.value = value
|
134
|
+
rescue Exception => e
|
135
|
+
invalidate msg+"token :#{name}. #{e.message}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
puts() if verbose
|
139
|
+
@tokens.each do |name, token|
|
140
|
+
invalidate msg+"missing value for token :#{name}" if token.value == nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def invalidate(message)
|
145
|
+
raise message
|
146
|
+
end
|
147
|
+
|
148
|
+
def each
|
149
|
+
@tokens.each { |name, token| yield(name, token) }
|
150
|
+
end
|
151
|
+
|
152
|
+
def klippfile
|
153
|
+
kf = "create '#{self.class.expand_identifier(self.identifier)}' do |tokens|\n\n"
|
154
|
+
@tokens.each do |name, token|
|
155
|
+
unless token.hidden
|
156
|
+
kf += " # #{token.comment}\n" if token.comment
|
157
|
+
kf += " # #{token.validation_hint}\n" if token.validation_hint
|
158
|
+
kf += " tokens[:#{name}] = #{token.type == :bool ? 'false' : "\"\""}\n"
|
159
|
+
kf += "\n"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
kf += "end"
|
163
|
+
end
|
164
|
+
|
165
|
+
def klippspec
|
166
|
+
ks = "spec '#{identifier}' do |s|\n"
|
167
|
+
ks += " s.block_actions_under_git = true\n"
|
168
|
+
ks += " # s.pre_actions = ['echo \"Hello klipp!\"']\n"
|
169
|
+
ks += " # s.post_actions = ['pod install']\n"
|
170
|
+
ks += "\n"
|
171
|
+
ks += " s.token :REPLACEABLE do |t|\n"
|
172
|
+
ks += " t.comment = \"Replaceable value (to insert in any filename or string containing 'XXREPLACEABLEXX')\"\n"
|
173
|
+
ks += " t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/\n"
|
174
|
+
ks += " t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'\n"
|
175
|
+
ks += " end\n"
|
176
|
+
ks += "\n"
|
177
|
+
ks += " s.token :TOGGLE do |t|\n"
|
178
|
+
ks += " t.comment = \"Toggle value (to insert in any filename or string containing 'XXTOGGLEXX')\"\n"
|
179
|
+
ks += " t.type = :bool\n"
|
180
|
+
ks += " # t.bool_strings = ['NO','YES']\n"
|
181
|
+
ks += " end\n"
|
182
|
+
ks += "\n"
|
183
|
+
ks += " # ...\n"
|
184
|
+
ks += "\n"
|
185
|
+
ks += "end"
|
186
|
+
end
|
187
|
+
|
188
|
+
def target_file(source_dir, source_file, target_dir)
|
189
|
+
stripped_path = source_file.gsub(source_dir, '')
|
190
|
+
customizable_path = replace_tokens(stripped_path)
|
191
|
+
File.join(target_dir, customizable_path)
|
192
|
+
end
|
193
|
+
|
194
|
+
def transfer_file(source_file, target_file, overwrite)
|
195
|
+
FileUtils.mkdir_p File.dirname(target_file)
|
196
|
+
|
197
|
+
if File.directory? source_file
|
198
|
+
FileUtils.mkdir_p target_file
|
199
|
+
elsif !File.exists?(target_file) || overwrite
|
200
|
+
if File.binary? source_file
|
201
|
+
FileUtils.cp(source_file, target_file)
|
202
|
+
else
|
203
|
+
begin
|
204
|
+
IO.write target_file, replace_tokens(File.read(source_file))
|
205
|
+
rescue
|
206
|
+
FileUtils.cp(source_file, target_file)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
else
|
210
|
+
raise "#{target_file} already exists, not overwriting. Use -f to force overwriting."
|
211
|
+
end
|
212
|
+
|
213
|
+
target_file
|
214
|
+
end
|
215
|
+
|
216
|
+
def replace_tokens(string_with_tokens, delimiter='XX')
|
217
|
+
unless string_with_tokens.valid_encoding?
|
218
|
+
raise "Invalid string encoding #{string_with_tokens.encoding}"
|
219
|
+
end
|
220
|
+
|
221
|
+
replaced = string_with_tokens
|
222
|
+
@tokens.each do |name, token|
|
223
|
+
needle = delimiter+name.to_s+delimiter
|
224
|
+
replacement = token.to_s
|
225
|
+
replaced.gsub!(needle, replacement)
|
226
|
+
end
|
227
|
+
replaced
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class String
|
2
|
+
def matches_true?
|
3
|
+
Regexp.new('y|yes|true', Regexp::IGNORECASE).match(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
def matches_false?
|
7
|
+
Regexp.new('n|no|false', Regexp::IGNORECASE).match(self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Template
|
12
|
+
|
13
|
+
class Token
|
14
|
+
attr_accessor :hidden, :value, :type, :bool_strings
|
15
|
+
attr_accessor :comment, :validation, :validation_hint
|
16
|
+
|
17
|
+
def initialize(value = nil, hidden = false)
|
18
|
+
self.value = value if value
|
19
|
+
@hidden = hidden
|
20
|
+
end
|
21
|
+
|
22
|
+
def type=(type)
|
23
|
+
allowed_types = [:string, :bool]
|
24
|
+
raise "Invalid type '#{type}'. Allowed types are: #{allowed_types.join(', ')}" unless allowed_types.include?(type)
|
25
|
+
@type = type
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
@type || :string
|
30
|
+
end
|
31
|
+
|
32
|
+
def value=(value)
|
33
|
+
if (self.type == :bool) && value.is_a?(String)
|
34
|
+
value = if value.matches_true?
|
35
|
+
true
|
36
|
+
elsif value.matches_false?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise "Invalid value '#{value.to_s}'. #{validation_hint}" unless validate?(value)
|
41
|
+
@value = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def bool_strings
|
45
|
+
%w(NO YES)
|
46
|
+
end
|
47
|
+
|
48
|
+
def validation
|
49
|
+
self.type == :bool ? Regexp.new('([yn])|yes|no', Regexp::IGNORECASE) : @validation
|
50
|
+
end
|
51
|
+
|
52
|
+
def validation_hint
|
53
|
+
case
|
54
|
+
when self.type == :string then
|
55
|
+
@validation_hint || (validation ? "Match /#{validation.to_s}/ (no custom validation_hint given)." : "Text required")
|
56
|
+
when self.type == :bool then
|
57
|
+
"Boolean value, either 'true' or 'false'"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate?(value)
|
62
|
+
case
|
63
|
+
when self.type == :string then
|
64
|
+
value.is_a?(String) && (validation.nil? || validation.match(value))
|
65
|
+
when self.type == :bool then
|
66
|
+
value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
case
|
72
|
+
when self.type == :bool then
|
73
|
+
bool_strings[value ? 1 : 0]
|
74
|
+
else
|
75
|
+
value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/lib/template.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'template/spec'
|
2
|
+
require 'template/token'
|
3
|
+
|
4
|
+
module Template
|
5
|
+
|
6
|
+
def self.route(*argv)
|
7
|
+
params = Klipp::ParameterList.new(argv)
|
8
|
+
command = params.shift_argument
|
9
|
+
commands = {
|
10
|
+
list: lambda { cli_list },
|
11
|
+
spec: lambda { cli_spec(params) }
|
12
|
+
}
|
13
|
+
case command
|
14
|
+
when nil
|
15
|
+
raise Klipp::Hint.new "Add a command to `klipp template [#{commands.keys.join('|')}]`"
|
16
|
+
else
|
17
|
+
if commands[command.to_sym]
|
18
|
+
commands[command.to_sym].call
|
19
|
+
else
|
20
|
+
raise "Unknown command `klipp template #{command}`"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.cli_list
|
26
|
+
l = list
|
27
|
+
l.each do |template|
|
28
|
+
Formatador.display_line "* #{template[:repo]}/[green]#{template[:name].ljust(16)}[/]"
|
29
|
+
end
|
30
|
+
|
31
|
+
Formatador.display_line "You can use just the name in commands `#{l.first[:name]}`, as long as it's unambiguous."
|
32
|
+
Formatador.display_line "Otherwise include the repository, e.g. `#{l.first[:repo]+'/'+l.first[:name]}`"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.list
|
36
|
+
specs = Dir.glob(File.join(Klipp::Configuration.root_dir, '**', '*.klippspec'))
|
37
|
+
specs.map do |spec|
|
38
|
+
Template::Spec.hash_for_spec_path spec
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.cli_spec(params)
|
43
|
+
params = Klipp::ParameterList.new(params)
|
44
|
+
identifier = params.shift_argument
|
45
|
+
raise Klipp::Hint.new("Add a new template name, like `klipp template spec AwesomeTemplate`") unless identifier && identifier.length > 0
|
46
|
+
raise "Invalid template name `#{identifier}`. Stick to simple characters and spaces." unless identifier.match(/^[ A-Za-z0-9_-]+$/)
|
47
|
+
|
48
|
+
spec = Template::Spec.new
|
49
|
+
spec.identifier = identifier.strip
|
50
|
+
file = File.join(Dir.pwd, "#{spec.identifier}.klippspec")
|
51
|
+
force = params.splice_option('-f')
|
52
|
+
|
53
|
+
file_existed = File.exists?(file)
|
54
|
+
allow_write = force || !file_existed
|
55
|
+
|
56
|
+
File.write(file, spec.klippspec) if allow_write
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
create 'template-repository/Example' do |tokens|
|
2
|
+
|
3
|
+
# Project title (e.g. 'Amazing Application')
|
4
|
+
# At least three characters long, start with a capital character, may contain spaces
|
5
|
+
tokens[:PROJECT_TITLE] = "Amazing Application"
|
6
|
+
|
7
|
+
# Project id (e.g. 'AmazingApp')
|
8
|
+
# At least three characters long, no spaces, start with a capital character
|
9
|
+
tokens[:PROJECT_ID] = "AmazingApp"
|
10
|
+
|
11
|
+
# Bundle id (e.g. 'com.acme.amazingapp')
|
12
|
+
# Reverse domain notation, lower case, no spaces
|
13
|
+
tokens[:BUNDLE_ID] = "com.acme.amazingapp"
|
14
|
+
|
15
|
+
# Organization name (e.g. 'ACME')
|
16
|
+
# At least two characters long, may contain spaces
|
17
|
+
tokens[:ORGANIZATION_NAME] = "ACME"
|
18
|
+
|
19
|
+
# Class prefix (e.g. 'AMZ')
|
20
|
+
# All caps, at least three, starting with an alphabetical character
|
21
|
+
tokens[:CLASS_PREFIX] = "AMZ"
|
22
|
+
|
23
|
+
tokens[:SECRET_TOGGLE] = true
|
24
|
+
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
create 'template-repository/Example' do |tokens|
|
2
|
+
|
3
|
+
# Project title (e.g. 'Amazing Application')
|
4
|
+
# At least three characters long, start with a capital character, may contain spaces
|
5
|
+
tokens[:PROJECT_TITLE] = ""
|
6
|
+
|
7
|
+
# Project id (e.g. 'AmazingApp')
|
8
|
+
# At least three characters long, no spaces, start with a capital character
|
9
|
+
tokens[:PROJECT_ID] = ""
|
10
|
+
|
11
|
+
# Bundle id (e.g. 'com.acme.amazingapp')
|
12
|
+
# Reverse domain notation, lower case, no spaces
|
13
|
+
tokens[:BUNDLE_ID] = ""
|
14
|
+
|
15
|
+
# Organization name (e.g. 'ACME')
|
16
|
+
# At least two characters long, may contain spaces
|
17
|
+
tokens[:ORGANIZATION_NAME] = ""
|
18
|
+
|
19
|
+
# Class prefix (e.g. 'AMZ')
|
20
|
+
# All caps, at least three, starting with an alphabetical character
|
21
|
+
tokens[:CLASS_PREFIX] = ""
|
22
|
+
|
23
|
+
# Secret toggle (to enable/disable that amazing feature)
|
24
|
+
# Boolean value, either 'true' or 'false'
|
25
|
+
tokens[:SECRET_TOGGLE] = false
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
create 'template-repository/Ambiguous'
|
@@ -0,0 +1 @@
|
|
1
|
+
create 'template-repository/Example'
|
@@ -0,0 +1 @@
|
|
1
|
+
create 'Ambiguous'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
spec 'Another-Template' do |s|
|
2
|
+
s.block_actions_under_git = true
|
3
|
+
# s.pre_actions = ['echo "Hello klipp!"']
|
4
|
+
# s.post_actions = ['pod install']
|
5
|
+
|
6
|
+
s.token :REPLACEABLE do |t|
|
7
|
+
t.comment = "Replaceable value (to insert in any filename or string containing 'XXREPLACEABLEXX')"
|
8
|
+
t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/
|
9
|
+
t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'
|
10
|
+
end
|
11
|
+
|
12
|
+
s.token :TOGGLE do |t|
|
13
|
+
t.comment = "Toggle value (to insert in any filename or string containing 'XXTOGGLEXX')"
|
14
|
+
t.type = :bool
|
15
|
+
# t.bool_strings = ['NO','YES']
|
16
|
+
end
|
17
|
+
|
18
|
+
# ...
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Template::Spec.new do |s|
|
2
|
+
s.name = 'BadExample'
|
3
|
+
s.lalala = 'pod install'
|
4
|
+
|
5
|
+
s.token :PROJECT_TITLE do |t|
|
6
|
+
t.comment = "Project title (e.g. 'Amazing Application')"
|
7
|
+
t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/
|
8
|
+
t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'
|
9
|
+
end
|
10
|
+
|
11
|
+
s.token :PROJECT_ID do |t|
|
12
|
+
t.comment = "Project id (e.g. 'AmazingApp')"
|
13
|
+
t.validation = /^[A-Z][A-Za-z0-9]{2,}$/
|
14
|
+
t.validation_hint = 'At least three characters long, no spaces, start with a capital character'
|
15
|
+
end
|
16
|
+
|
17
|
+
s.token :BUNDLE_ID do |t|
|
18
|
+
t.comment = "Bundle id (e.g. 'com.acme.amazingapp')"
|
19
|
+
t.validation = /^[a-z0-9.]{3,}$/
|
20
|
+
t.validation_hint = 'Reverse domain notation, lower case, no spaces'
|
21
|
+
end
|
22
|
+
|
23
|
+
s.token :ORGANIZATION_NAME do |t|
|
24
|
+
t.comment = "Organization name (e.g. 'ACME')"
|
25
|
+
t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/
|
26
|
+
t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'
|
27
|
+
end
|
28
|
+
|
29
|
+
s.token :CLASS_PREFIX do |t|
|
30
|
+
t.comment = "Class prefix (e.g. 'AMZ')"
|
31
|
+
t.validation = /^[A-Z][A-Z0-9]{2,}$/
|
32
|
+
t.validation_hint = 'All caps, at least three, starting with an alphabetical character'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
spec 'Empty' do |s|
|
2
|
+
s.block_actions_under_git = true
|
3
|
+
# s.pre_actions = ['echo "Hello klipp!"']
|
4
|
+
# s.post_actions = ['pod install']
|
5
|
+
|
6
|
+
s.token :REPLACEABLE do |t|
|
7
|
+
t.comment = "Replaceable value (to insert in any filename or string containing 'XXREPLACEABLEXX')"
|
8
|
+
t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/
|
9
|
+
t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'
|
10
|
+
end
|
11
|
+
|
12
|
+
s.token :TOGGLE do |t|
|
13
|
+
t.comment = "Toggle value (to insert in any filename or string containing 'XXTOGGLEXX')"
|
14
|
+
t.type = :bool
|
15
|
+
# t.bool_strings = ['NO','YES']
|
16
|
+
end
|
17
|
+
|
18
|
+
# ...
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
spec 'Example' do |s|
|
2
|
+
s.block_actions_under_git = true
|
3
|
+
s.pre_actions = ['git init', 'git add .', 'git commit -m "Initial commit."']
|
4
|
+
s.post_actions = ['git add .', 'git commit -m "Klipp project"', 'pod install', 'git add .', 'git commit -m "Installed cocoapods"']
|
5
|
+
|
6
|
+
s.token :PROJECT_TITLE do |t|
|
7
|
+
t.comment = "Project title (e.g. 'Amazing Application')"
|
8
|
+
t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/
|
9
|
+
t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'
|
10
|
+
end
|
11
|
+
|
12
|
+
s.token :PROJECT_ID do |t|
|
13
|
+
t.comment = "Project id (e.g. 'AmazingApp')"
|
14
|
+
t.validation = /^[A-Z][A-Za-z0-9]{2,}$/
|
15
|
+
t.validation_hint = 'At least three characters long, no spaces, start with a capital character'
|
16
|
+
end
|
17
|
+
|
18
|
+
s.token :BUNDLE_ID do |t|
|
19
|
+
t.comment = "Bundle id (e.g. 'com.acme.amazingapp')"
|
20
|
+
t.validation = /^[a-z0-9.-]{3,}$/
|
21
|
+
t.validation_hint = 'Reverse domain notation, lower case, no spaces'
|
22
|
+
end
|
23
|
+
|
24
|
+
s.token :ORGANIZATION_NAME do |t|
|
25
|
+
t.comment = "Organization name (e.g. 'ACME')"
|
26
|
+
t.validation = /^[\p{L}0-9 -]{2,}$/
|
27
|
+
t.validation_hint = 'At least two characters long, may contain spaces'
|
28
|
+
end
|
29
|
+
|
30
|
+
s.token :CLASS_PREFIX do |t|
|
31
|
+
t.comment = "Class prefix (e.g. 'AMZ')"
|
32
|
+
t.validation = /^[A-Z][A-Z0-9]{2,}$/
|
33
|
+
t.validation_hint = 'All caps, at least three, starting with an alphabetical character'
|
34
|
+
end
|
35
|
+
|
36
|
+
s.token :SECRET_TOGGLE do |t|
|
37
|
+
t.comment = 'Secret toggle (to enable/disable that amazing feature)'
|
38
|
+
t.type = :bool
|
39
|
+
end
|
40
|
+
end
|
Binary file
|
Binary file
|
data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.h
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
//
|
2
|
+
// XXPROJECT_TITLEXX
|
3
|
+
//
|
4
|
+
// Copyright (c) XXYEARXX XXORGANIZATION_NAMEXX. All rights reserved.
|
5
|
+
//
|
6
|
+
|
7
|
+
#import <UIKit/UIKit.h>
|
8
|
+
|
9
|
+
@interface XXCLASS_PREFIXXXAppDelegate : UIResponder <UIApplicationDelegate>
|
10
|
+
|
11
|
+
@property (strong, nonatomic) UIWindow *window;
|
12
|
+
|
13
|
+
@end
|
data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.m
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
//
|
2
|
+
// XXPROJECT_TITLEXX
|
3
|
+
//
|
4
|
+
// Copyright (c) XXYEARXX XXORGANIZATION_NAMEXX. All rights reserved.
|
5
|
+
//
|
6
|
+
|
7
|
+
#import "XXCLASS_PREFIXXXAppDelegate.h"
|
8
|
+
#import "XXCLASS_PREFIXXXRootViewController.h"
|
9
|
+
|
10
|
+
@interface XXCLASS_PREFIXXXAppDelegate ()
|
11
|
+
|
12
|
+
@property (nonatomic, strong) XXCLASS_PREFIXXXRootViewController *rootViewController;
|
13
|
+
|
14
|
+
@end
|
15
|
+
|
16
|
+
@implementation XXCLASS_PREFIXXXAppDelegate
|
17
|
+
|
18
|
+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
19
|
+
{
|
20
|
+
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
21
|
+
|
22
|
+
self.rootViewController = [[XXCLASS_PREFIXXXRootViewController alloc] init];
|
23
|
+
self.window.rootViewController = self.rootViewController;
|
24
|
+
|
25
|
+
self.window.backgroundColor = [UIColor whiteColor];
|
26
|
+
[self.window makeKeyAndVisible];
|
27
|
+
return YES;
|
28
|
+
}
|
29
|
+
|
30
|
+
@end
|