klipp 0.0.1 → 0.2.0

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