klipp 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|