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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +5 -0
  4. data/README.md +13 -26
  5. data/bin/klipp +1 -1
  6. data/klipp.gemspec +11 -6
  7. data/lib/klipp/configuration.rb +0 -4
  8. data/lib/klipp/creator.rb +78 -0
  9. data/lib/klipp/parameter_list.rb +3 -3
  10. data/lib/klipp/version.rb +1 -1
  11. data/lib/klipp.rb +148 -92
  12. data/lib/template/spec.rb +231 -0
  13. data/lib/template/token.rb +81 -0
  14. data/lib/template.rb +59 -0
  15. data/spec/fixtures/ambiguous-repo/Ambiguous/Ambiguous.klippspec +5 -0
  16. data/spec/fixtures/projects/Klippfile +26 -0
  17. data/spec/fixtures/projects/Klippfile-after-prepare +27 -0
  18. data/spec/fixtures/projects/Klippfile-ambiguous +1 -0
  19. data/spec/fixtures/projects/Klippfile-bad-ruby +3 -0
  20. data/spec/fixtures/projects/Klippfile-minimal +1 -0
  21. data/spec/fixtures/projects/Klippfile-unambiguous +1 -0
  22. data/spec/fixtures/template-repository/Ambiguous/Ambiguous.klippspec +5 -0
  23. data/spec/fixtures/template-repository/Another-Template/Another-Template.klippspec +20 -0
  24. data/spec/fixtures/template-repository/BadExample/BadExample.klippspec +35 -0
  25. data/spec/fixtures/template-repository/Empty/Empty.klippspec +20 -0
  26. data/spec/fixtures/template-repository/Example/.gitignore +10 -0
  27. data/spec/fixtures/template-repository/Example/Example.klippspec +40 -0
  28. data/spec/fixtures/template-repository/Example/Podfile +10 -0
  29. data/spec/fixtures/template-repository/Example/XXBLANKXX.hidden +10 -0
  30. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Config/Base.xcconfig +8 -0
  31. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default-568h@2x.png +0 -0
  32. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default.png +0 -0
  33. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Images/Default@2x.png +0 -0
  34. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.h +13 -0
  35. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXAppDelegate.m +30 -0
  36. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXRootViewController.h +11 -0
  37. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/Source/XXCLASS_PREFIXXXRootViewController.m +30 -0
  38. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/XXPROJECT_TITLEXX-Info.plist +38 -0
  39. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/XXPROJECT_TITLEXX-Prefix.pch +14 -0
  40. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/en.lproj/Localizable.strings +1 -0
  41. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/main.m +17 -0
  42. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX/nl.lproj/Localizable.strings +1 -0
  43. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX.xcodeproj/project.pbxproj +466 -0
  44. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXX.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  45. data/spec/fixtures/template-repository/Example/XXPROJECT_IDXXTests/XXPROJECT_TITLEXXTests.m +22 -0
  46. data/spec/fixtures/template-repository/Interactive/Interactive.klippspec +15 -0
  47. data/spec/fixtures/template-repository/Interactive/XXSUBJECT_UNDER_TESTXXTests.m +15 -0
  48. data/spec/klipp/configuration_spec.rb +8 -0
  49. data/spec/klipp/creator_spec.rb +120 -0
  50. data/spec/klipp_spec.rb +80 -85
  51. data/spec/spec_helper.rb +8 -2
  52. data/spec/template/spec_spec.rb +225 -0
  53. data/spec/template/token_spec.rb +100 -0
  54. data/spec/template_spec.rb +82 -0
  55. metadata +118 -43
  56. data/lib/klipp/buffered_output.rb +0 -17
  57. data/lib/klipp/project.rb +0 -46
  58. data/lib/klipp/template.rb +0 -50
  59. data/lib/klipp/token.rb +0 -35
  60. data/spec/fixtures/klipps/Example.klippfile +0 -4
  61. data/spec/fixtures/klipps/ExcessiveExample.klippfile +0 -5
  62. data/spec/fixtures/klipps/Generated.klippfile +0 -11
  63. data/spec/fixtures/klipps/LackingExample.klippfile +0 -3
  64. data/spec/fixtures/klipps/MalformedExample.klippfile +0 -4
  65. data/spec/fixtures/klipps/single-token.yml +0 -5
  66. data/spec/fixtures/templates/Example/RegularFileWithContents.txt +0 -3
  67. data/spec/fixtures/templates/Example/XXCLASS_PREFIXXXPrefixedFile.txt +0 -3
  68. data/spec/fixtures/templates/Example/XXPROJECT_IDXX/BinaryFile.png +0 -0
  69. data/spec/fixtures/templates/Example/XXPROJECT_IDXX/XXCLASS_PREFIXXXPrefixedFileInDirectory.txt +0 -3
  70. data/spec/fixtures/templates/Example.yml +0 -29
  71. data/spec/klipp/project_spec.rb +0 -46
  72. data/spec/klipp/template_spec.rb +0 -80
  73. 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,5 @@
1
+ spec 'Ambiguous' do |s|
2
+
3
+ # Ambiguous in B
4
+
5
+ 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,3 @@
1
+ create 'template-repository/FCUtrechtFancal' do
2
+ lalala
3
+ end
@@ -0,0 +1 @@
1
+ create 'template-repository/Example'
@@ -0,0 +1 @@
1
+ create 'Ambiguous'
@@ -0,0 +1,5 @@
1
+ spec 'Ambiguous' do |s|
2
+
3
+ # Ambiguous in A
4
+
5
+ end
@@ -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,10 @@
1
+ # OS X noise
2
+ .DS_Store
3
+
4
+ # Xcode noise
5
+ xcuserdata
6
+ .xccheckout
7
+ /Build
8
+
9
+ # AppCode noise
10
+ .idea
@@ -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
@@ -0,0 +1,10 @@
1
+ platform :ios, "6.0"
2
+
3
+ target "XXPROJECT_TITLEXX" do
4
+ pod 'AFNetworking'
5
+ end
6
+
7
+ target "XXPROJECT_TITLEXXTests" do
8
+ pod 'OCMockito'
9
+ end
10
+
@@ -0,0 +1,10 @@
1
+ # OS X noise
2
+ .DS_Store
3
+
4
+ # Xcode noise
5
+ xcuserdata
6
+ .xccheckout
7
+ /Build
8
+
9
+ # AppCode noise
10
+ .idea
@@ -0,0 +1,8 @@
1
+ //
2
+ // XXPROJECT_TITLEXX
3
+ //
4
+ // Copyright (c) XXYEARXX XXORGANIZATION_NAMEXX. All rights reserved.
5
+ //
6
+
7
+ BUNDLE_ID=XXBUNDLE_IDXX
8
+ BUNDLE_SUFFIX=.develop
@@ -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
@@ -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
@@ -0,0 +1,11 @@
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_PREFIXXXRootViewController : UIViewController
10
+
11
+ @end