vendor 0.0.1

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 (62) hide show
  1. data/.gitignore +12 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +68 -0
  5. data/LICENCE +19 -0
  6. data/Rakefile +11 -0
  7. data/Readme.markdown +109 -0
  8. data/bin/vendor +62 -0
  9. data/lib/vendor/api.rb +49 -0
  10. data/lib/vendor/cli/auth.rb +38 -0
  11. data/lib/vendor/cli.rb +23 -0
  12. data/lib/vendor/config.rb +51 -0
  13. data/lib/vendor/extensions/array.rb +7 -0
  14. data/lib/vendor/extensions/fixnum.rb +7 -0
  15. data/lib/vendor/extensions/hash.rb +10 -0
  16. data/lib/vendor/extensions/string.rb +11 -0
  17. data/lib/vendor/plist.rb +255 -0
  18. data/lib/vendor/vendor_spec/builder.rb +83 -0
  19. data/lib/vendor/vendor_spec/dsl.rb +39 -0
  20. data/lib/vendor/vendor_spec/loader.rb +23 -0
  21. data/lib/vendor/version.rb +5 -0
  22. data/lib/vendor/xcode/object.rb +102 -0
  23. data/lib/vendor/xcode/objects/pbx_build_file.rb +9 -0
  24. data/lib/vendor/xcode/objects/pbx_container_item_proxy.rb +8 -0
  25. data/lib/vendor/xcode/objects/pbx_file_reference.rb +21 -0
  26. data/lib/vendor/xcode/objects/pbx_frameworks_build_phase.rb +9 -0
  27. data/lib/vendor/xcode/objects/pbx_group.rb +13 -0
  28. data/lib/vendor/xcode/objects/pbx_native_target.rb +11 -0
  29. data/lib/vendor/xcode/objects/pbx_project.rb +12 -0
  30. data/lib/vendor/xcode/objects/pbx_resources_build_phase.rb +7 -0
  31. data/lib/vendor/xcode/objects/pbx_shell_script_build_phase.rb +7 -0
  32. data/lib/vendor/xcode/objects/pbx_sources_build_phase.rb +9 -0
  33. data/lib/vendor/xcode/objects/pbx_target_dependency.rb +7 -0
  34. data/lib/vendor/xcode/objects/pbx_variant_group.rb +7 -0
  35. data/lib/vendor/xcode/objects/xc_build_configuration.rb +7 -0
  36. data/lib/vendor/xcode/objects/xc_configuration_list.rb +9 -0
  37. data/lib/vendor/xcode/project.rb +130 -0
  38. data/lib/vendor.rb +33 -0
  39. data/spec/spec_helper.rb +17 -0
  40. data/spec/support/api_stubs.rb +12 -0
  41. data/spec/support/resources/files/SecondViewController.h +13 -0
  42. data/spec/support/resources/files/SecondViewController.m +43 -0
  43. data/spec/support/resources/projects/ProjectWithSpecs/ProjectWithSpecs.xcodeproj/project.pbxproj +320 -0
  44. data/spec/support/resources/projects/TabBarWithUnitTests/TabBarWithUnitTests.xcodeproj/project.pbxproj +473 -0
  45. data/spec/support/resources/vendors/DKBenchmark/DKBenchmark.h +18 -0
  46. data/spec/support/resources/vendors/DKBenchmark/DKBenchmark.m +73 -0
  47. data/spec/support/resources/vendors/DKBenchmark/DKBenchmark.vendorspec +11 -0
  48. data/spec/support/resources/vendors/DKBenchmarkUnsafe/DKBenchmark.h +18 -0
  49. data/spec/support/resources/vendors/DKBenchmarkUnsafe/DKBenchmark.m +73 -0
  50. data/spec/support/resources/vendors/DKBenchmarkUnsafe/DKBenchmark.vendorspec +11 -0
  51. data/spec/support/temp_project.rb +21 -0
  52. data/spec/vendor/api_spec.rb +25 -0
  53. data/spec/vendor/cli/auth_spec.rb +54 -0
  54. data/spec/vendor/config_spec.rb +62 -0
  55. data/spec/vendor/vendor_spec/builder_spec.rb +86 -0
  56. data/spec/vendor/vendor_spec/dsl_spec.rb +67 -0
  57. data/spec/vendor/vendor_spec/loader_spec.rb +41 -0
  58. data/spec/vendor/xcode/object_spec.rb +76 -0
  59. data/spec/vendor/xcode/objects/pbx_project_spec.rb +26 -0
  60. data/spec/vendor/xcode/project_spec.rb +211 -0
  61. data/vendor.gemspec +33 -0
  62. metadata +234 -0
@@ -0,0 +1,255 @@
1
+ module Vendor
2
+
3
+ module Plist
4
+
5
+ require "strscan"
6
+ require "time"
7
+ require "stringio"
8
+
9
+ #
10
+ # string - the plist string to parse
11
+ # opts - options (see +AsciiParser.new+)
12
+ #
13
+
14
+ def self.parse_ascii(string, opts = {})
15
+ AsciiParser.new(string, opts).parse
16
+ end
17
+
18
+ #
19
+ # Plist::AsciiParser
20
+ #
21
+ # Parser for the old style ASCII/NextSTEP property lists.
22
+ #
23
+ # Created by Jari Bakken on 2008-08-13.
24
+ #
25
+ class AsciiParser < StringScanner
26
+
27
+ class ParseError < StandardError; end
28
+
29
+ SPACE_REGEXP = %r{ ( /\*.*?\*/ | # block comments
30
+ //.*?$\n? | # single-line comments
31
+ \s* )+ # space
32
+ }mx
33
+
34
+ CONTROL_CHAR = {
35
+ "a" => "\a",
36
+ "b" => "\b",
37
+ "n" => "\n",
38
+ "f" => "\f",
39
+ "t" => "\t",
40
+ "r" => "\r",
41
+ "v" => "\v",
42
+ }
43
+
44
+ BOOLS = {true => "1", false => "0"}
45
+
46
+ #
47
+ # string - the plist string to parse
48
+ #
49
+ # options hash:
50
+ #
51
+ # :parse_numbers => true/false : Set this to true if you numeric values (float/ints) as the correct Ruby type
52
+ # :parse_booleans => true/false : Set this to true if you want "true" and "false" to return the boolean Ruby types
53
+ #
54
+ # Note: Apple's parsers return strings for all old-style plist types.
55
+ #
56
+
57
+ def initialize(string, opts = {})
58
+ string = case string
59
+ when StringIO
60
+ string.string
61
+ when IO
62
+ string.read
63
+ else
64
+ string
65
+ end
66
+
67
+ @parse_numbers = opts.delete(:parse_numbers)
68
+ @parse_bools = opts.delete(:parse_booleans)
69
+ @debug = $VERBOSE == true # ruby -W3
70
+
71
+ raise ArgumentError, "unknown option #{opts.inspect}" unless opts.empty?
72
+
73
+ super(string)
74
+ end
75
+
76
+ def parse
77
+ res = object
78
+
79
+ skip_space
80
+ error "junk after plist" unless eos?
81
+
82
+ res
83
+ end
84
+
85
+ private
86
+
87
+ def object
88
+ skip_space
89
+
90
+ if scan(/\{/) then dictionary
91
+ elsif scan(/\(/) then array
92
+ elsif scan(/</) then data
93
+ elsif scan(/"/) then quoted_string
94
+ else unquoted_string
95
+ end
96
+ end
97
+
98
+ def quoted_string
99
+ puts "creating quoted string #{inspect}" if @debug
100
+
101
+ result = ''
102
+
103
+ loop do
104
+ if scan(/\\/)
105
+ result << escaped
106
+ elsif scan(/"/)
107
+ break
108
+ elsif eos?
109
+ error("unterminated quoted string")
110
+ else scan(/./)
111
+ error("unterminated quoted string") unless matched
112
+ result << matched
113
+ end
114
+ end
115
+
116
+ result
117
+ end
118
+
119
+ def escaped
120
+ if scan(/"|\\|\\\//)
121
+ matched
122
+ elsif scan(/a|b|f|n|v|r|t/)
123
+ CONTROL_CHAR[matched]
124
+ elsif scan(/u[0-9a-f]{4}/i)
125
+ [ matched[1..-1].to_i(16) ].pack("U")
126
+ elsif scan(/\d{1,3}/)
127
+ [ matched.to_i(8) ].pack("C")
128
+ end
129
+ end
130
+
131
+ def unquoted_string
132
+ puts "creating unquoted string #{inspect}" if @debug
133
+
134
+ if scan(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} ((\+|-)\d{4})?/)
135
+ puts "returning time" if @debug
136
+ require 'date'
137
+ DateTime.parse(matched)
138
+ elsif scan(/-?\d+?\.\d+\b/)
139
+ puts "returning float" if @debug
140
+ @parse_numbers ? matched.to_f : matched
141
+ elsif scan(/-?\d+\b/)
142
+ puts "returning int" if @debug
143
+ @parse_numbers ? matched.to_i : matched
144
+ elsif scan(/\b(true|false)\b/)
145
+ val = matched == 'true'
146
+ if @parse_bools
147
+ val
148
+ else
149
+ @parse_numbers ? BOOLS[val].to_i : BOOLS[val]
150
+ end
151
+ elsif eos?
152
+ error("unexpected end-of-string")
153
+ else
154
+ puts "returning string" if @debug
155
+ scan(/(\w|\.|\/)+/)
156
+ end
157
+ end
158
+
159
+ def data
160
+ puts "creating data #{inspect}" if @debug
161
+
162
+ scan(/(.+?)>/)
163
+
164
+ hex = self[1].delete(" ")
165
+ [hex].pack("H*")
166
+ end
167
+
168
+ def array
169
+ puts "creating array #{inspect}" if @debug
170
+
171
+ skip_space
172
+
173
+ arr = []
174
+ until scan(/\)/)
175
+ val = object()
176
+
177
+ return nil unless val
178
+ skip_space
179
+
180
+ unless skip(/,\s*/)
181
+ skip_space
182
+ if scan(/\)/)
183
+ return arr << val
184
+ else
185
+ error "missing ',' or ')' for array"
186
+ end
187
+ end
188
+
189
+ arr << val
190
+ error "unexpected end-of-string when parsing array" if eos?
191
+ end
192
+
193
+ arr
194
+ end
195
+
196
+ def dictionary
197
+ puts "creating dict #{inspect}" if @debug
198
+
199
+ skip_space
200
+
201
+ dict = {}
202
+ until scan(/\}/)
203
+ key = object()
204
+ p :key => key if @debug
205
+
206
+ error "expected terminating '}' for dictionary" unless key
207
+
208
+ # dictionary keys must be strings, even if represented as 12345 in the plist
209
+ if key.is_a?(Integer) && key >= 0
210
+ key = key.to_s
211
+ end
212
+ error "dictionary key must be string (\"quoted\" or alphanumeric)" unless key.is_a? String
213
+
214
+ skip_space
215
+ error "missing '=' in dictionary" unless scan(/=/)
216
+ skip_space
217
+
218
+ val = object()
219
+ p :val => val if @debug
220
+
221
+ skip_space
222
+ error "missing ';' in dictionary" unless skip(/;/)
223
+ skip_space
224
+
225
+ dict[key] = val
226
+ error "unexpected end-of-string when parsing dictionary" if eos?
227
+ end
228
+
229
+ dict
230
+ end
231
+
232
+ def error(msg)
233
+ line = 1
234
+ string.split(//).each_with_index do |e, i|
235
+ line += 1 if e == "\n"
236
+ break if i == pos
237
+ end
238
+
239
+ context = (pos - 10) < 0 ? 0 : pos - 10
240
+ err = "#{msg} at line #{line}\n"
241
+ err << "#{string[context..pos+10]}".inspect << "\n"
242
+
243
+ err << "\n#{inspect}" if @debug
244
+ raise ParseError, err
245
+ end
246
+
247
+ def skip_space
248
+ puts "skipping whitespace" if @debug
249
+ skip SPACE_REGEXP
250
+ end
251
+
252
+ end
253
+ end
254
+
255
+ end
@@ -0,0 +1,83 @@
1
+ module Vendor
2
+ module VendorSpec
3
+
4
+ class Builder
5
+
6
+ require 'fileutils'
7
+ require 'tmpdir'
8
+ require 'find'
9
+ require 'zip/zipfilesystem'
10
+ require 'colorize'
11
+
12
+ attr_reader :name
13
+ attr_reader :version
14
+ attr_reader :filename
15
+ attr_reader :vendor_spec
16
+
17
+ def initialize(vendor_spec)
18
+ loader = Vendor::VendorSpec::Loader.new
19
+ loader.load vendor_spec
20
+
21
+ @folder = File.expand_path(File.join(vendor_spec, '..'))
22
+ @vendor_spec = loader.dsl.vendor_spec
23
+
24
+ @name = safe_filename(@vendor_spec[:name])
25
+ @version = safe_filename(@vendor_spec[:version])
26
+ @filename = "#{@name}-#{@version}.vendor"
27
+ end
28
+
29
+ def build
30
+ tmpdir = Dir.mktmpdir(@filename)
31
+
32
+ vendor_file = File.join(tmpdir, "vendor.json")
33
+ data_dir = File.join(tmpdir, "data")
34
+ data_files = copy_files(data_dir)
35
+
36
+ open(vendor_file, 'w+') { |f| f << @vendor_spec.to_json }
37
+
38
+ FileUtils.rm(@filename) if File.exist?(@filename)
39
+
40
+ zip_file @filename, [ vendor_file, *data_files ], tmpdir
41
+
42
+ true
43
+ end
44
+
45
+ private
46
+
47
+ def copy_files(data_dir)
48
+ data_files = []
49
+
50
+ @vendor_spec[:files].each do |file|
51
+ dir = File.dirname(file)
52
+ path = File.join(@folder, file)
53
+ copy_to_dir = File.expand_path(File.join(data_dir, dir))
54
+ copy_to_file = File.join(copy_to_dir, File.basename(file))
55
+
56
+ FileUtils.mkdir_p copy_to_dir
57
+ FileUtils.cp path, copy_to_file
58
+
59
+ data_files << copy_to_file
60
+ end
61
+
62
+ data_files
63
+ end
64
+
65
+ def zip_file(filename, files, base_dir)
66
+ Zip::ZipFile.open(filename, Zip::ZipFile::CREATE)do |zipfile|
67
+
68
+ files.each do |file|
69
+ path = file.gsub(base_dir, '').gsub(/^\//, '')
70
+ zipfile.add path, file
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+ def safe_filename(filename)
77
+ filename.gsub(/[^a-zA-Z0-9\-\_\.]/, '')
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,39 @@
1
+ module Vendor
2
+ module VendorSpec
3
+
4
+ class DSL
5
+
6
+ attr_reader :vendor_spec
7
+
8
+ def initialize
9
+ @vendor_spec = Hash.new
10
+ end
11
+
12
+ def method_missing(name, *args)
13
+ if name.to_s.match(/\=$/) || args.length > 0
14
+ without_equals = name.to_s.gsub(/=$/, '')
15
+
16
+ @vendor_spec[without_equals.to_sym] = args.length == 1 ? args.first : args
17
+ else
18
+ @vendor_spec[name.to_sym]
19
+ end
20
+ end
21
+
22
+ def validate!
23
+ [ :name, :version, :email, :files ].each do |key|
24
+ value = @vendor_spec[key]
25
+
26
+ if value.respond_to?(:empty?) ? value.empty? : !value
27
+ raise StandardError.new("Specification is missing the `#{key}` option")
28
+ end
29
+ end
30
+ end
31
+
32
+ def to_json
33
+ vendor_spec.to_json
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ module Vendor
2
+ module VendorSpec
3
+
4
+ class Loader
5
+
6
+ attr_reader :dsl
7
+
8
+ def initialize
9
+ @dsl = Vendor::VendorSpec::DSL.new
10
+ end
11
+
12
+ def vendor(&block)
13
+ @dsl.instance_eval &block
14
+ end
15
+
16
+ def load(filename)
17
+ @dsl.instance_eval(File.read(filename), filename)
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module Vendor
2
+
3
+ VERSION = "0.0.1"
4
+
5
+ end
@@ -0,0 +1,102 @@
1
+ module Vendor::XCode
2
+
3
+ class Object
4
+
5
+ attr_accessor :id
6
+ attr_reader :attributes
7
+
8
+ # I don't know what the ID's are made up of in XCode,
9
+ # so lets just generate a 24 character string.
10
+ def self.generate_id
11
+ (0...24).map { '%02X' % rand(256) }.join
12
+ end
13
+
14
+ def self.object_references
15
+ @references || []
16
+ end
17
+
18
+ def self.reference(name)
19
+ attribute = attribute_name(name)
20
+
21
+ define_method name do
22
+ value = @attributes[attribute]
23
+ if value.kind_of?(Array)
24
+ value.map { |id| @project.find_object(id) }
25
+ else
26
+ @project.find_object(value)
27
+ end
28
+ end
29
+
30
+ @references ||= []
31
+ @references << name
32
+ end
33
+
34
+ # References are stored in camel case in the project file
35
+ def self.attribute_name(name)
36
+ camelized = name.to_s.gsub(/[^\w]/, '').gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
37
+ camelized[0].chr.downcase + camelized[1..-1]
38
+ end
39
+
40
+ def initialize(options)
41
+ @id = options[:id]
42
+ @project = options[:project]
43
+ @attributes = options[:attributes]
44
+
45
+ @attributes['isa'] = self.class.name.split('::').last unless @attributes['isa']
46
+ end
47
+
48
+ def inspect
49
+ properties = { "id" => @id }.merge(@attributes)
50
+
51
+ # The class name contains the ISA (the type of the object)
52
+ properties.delete("isa")
53
+
54
+ # Always show the ID first
55
+ keys = properties.keys.map(&:to_s)
56
+ keys.delete("id")
57
+ keys.unshift("id")
58
+
59
+ "#<#{self.class.name} #{keys.map{ |key| "#{underscore(key)}: #{properties[key].inspect}" }.join(', ')}>"
60
+ end
61
+
62
+ def method_missing(v, *args)
63
+ setting = v.to_s.match(/\=$/)
64
+ name = self.class.attribute_name(v)
65
+
66
+ if @attributes.has_key?(name)
67
+ if setting
68
+ write_attribute(name, args.first)
69
+ else
70
+ read_attribute(name)
71
+ end
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ def write_attribute(name, value)
78
+ @attributes[name.to_s] = value
79
+ end
80
+
81
+ def read_attribute(name)
82
+ @attributes[name.to_s]
83
+ end
84
+
85
+ def to_ascii_plist
86
+ @attributes.to_ascii_plist
87
+ end
88
+
89
+ private
90
+
91
+ # I miss active support...
92
+ def underscore(string)
93
+ string.gsub(/::/, '/').
94
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
95
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
96
+ tr("-", "_").
97
+ downcase
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,9 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXBuildFile < Vendor::XCode::Object
4
+
5
+ reference :file_ref
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,8 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXContainerItemProxy < Vendor::XCode::Object
4
+
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,21 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXFileReference < Vendor::XCode::Object
4
+
5
+ reference :file_ref
6
+
7
+ def name
8
+ read_attribute :path
9
+ end
10
+
11
+ def self.file_type_from_extension(extension)
12
+ case extension
13
+ when ".h" then "sourcecode.c.h"
14
+ when ".m" then "sourcecode.c.objc"
15
+ else "unknown"
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,9 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXFrameworksBuildPhase < Vendor::XCode::Object
4
+
5
+ reference :files
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,13 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXGroup < Vendor::XCode::Object
4
+
5
+ reference :children
6
+
7
+ def name
8
+ @attributes['name'] || @attributes['path']
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,11 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXNativeTarget < Vendor::XCode::Object
4
+
5
+ reference :build_phases
6
+ reference :product_reference
7
+ reference :build_configuration_list
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,12 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXProject < Vendor::XCode::Object
4
+
5
+ reference :build_configuration_list
6
+ reference :product_ref_group
7
+ reference :main_group
8
+ reference :targets
9
+
10
+ end
11
+
12
+ end
@@ -0,0 +1,7 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXResourcesBuildPhase < Vendor::XCode::Object
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXShellScriptBuildPhase < Vendor::XCode::Object
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,9 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXSourcesBuildPhase < Vendor::XCode::Object
4
+
5
+ reference :files
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,7 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXTargetDependency < Vendor::XCode::Object
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class PBXVariantGroup < Vendor::XCode::Object
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class XCBuildConfiguration < Vendor::XCode::Object
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,9 @@
1
+ module Vendor::XCode::Objects
2
+
3
+ class XCConfigurationList < Vendor::XCode::Object
4
+
5
+ reference :build_configurations
6
+
7
+ end
8
+
9
+ end