grenouille 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5fb6d6bb1e943d840ef1d4b37d6701d5d2c07071
4
+ data.tar.gz: 0514a9e6533a300b3a602fe81fb5e56d56e59256
5
+ SHA512:
6
+ metadata.gz: aae2f47fb581590773f33df6341641a2cbec7f960f4cca7e6e7b7985356173f286c47a0af55588848b3eebe4c164cffa7924c9ee2726a5930999d9e5f380e880
7
+ data.tar.gz: fec7b7cc4946fe40e95fa3a95235ec44876600fe8b249922ce368b3a60bf6a8b77d5b63d3a0f97280eca687671b2c41ea61d533758670d9bcdec1b3b9ee64e26
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Lint/Void:
2
+ Enabled: false
3
+
4
+ Metrics/AbcSize:
5
+ Max: 30
6
+
7
+ Metrics/LineLength:
8
+ Max: 142
9
+
10
+ Metrics/MethodLength:
11
+ Max: 18
12
+
13
+ Style/Documentation:
14
+ Enabled: false
data/.swift-version ADDED
@@ -0,0 +1 @@
1
+ 1.2
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'bacon'
7
+ gem 'mocha-on-bacon'
8
+ gem 'mocha', '~> 0.11.4'
9
+ gem 'prettybacon', git: 'git@github.com:irrationalfab/PrettyBacon.git', branch: 'master'
10
+ gem 'coveralls', require: false
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Boris Bügling
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Grenouille
2
+
3
+ [![Build Status](https://img.shields.io/circleci/project/neonichu/grenouille.svg?style=flat)](https://circleci.com/gh/neonichu/grenouille)
4
+ [![Coverage Status](https://coveralls.io/repos/neonichu/grenouille/badge.svg)](https://coveralls.io/r/neonichu/grenouille)
5
+ [![Gem Version](http://img.shields.io/gem/v/grenouille.svg?style=flat)](http://badge.fury.io/rb/grenouille)
6
+ [![Code Climate](http://img.shields.io/codeclimate/github/neonichu/grenouille.svg?style=flat)](https://codeclimate.com/github/neonichu/grenouille)
7
+
8
+ ![](perfume.gif)
9
+
10
+ Grenouille uses Apple's `swift-update` tool to determine the version of Swift
11
+ used in a set of files automatically.
12
+
13
+ ## Usage
14
+
15
+ You can use it on the commandline:
16
+
17
+ ```bash
18
+ $ grenouille Stargate.swift
19
+ 1.2
20
+ ```
21
+
22
+ or programmatically:
23
+
24
+ ```ruby
25
+ su = Grenouille::SwiftUpdate.new
26
+ su.determine_version('Stargate.swift')
27
+ # 1.2
28
+ ```
29
+
30
+ You can also get more detailled output:
31
+
32
+ ```ruby
33
+ su.update_to_latest_swift('spec/fixtures/Blueprint.swift')
34
+ # {:report=>[{"file"=>"spec/fixtures/Blueprint.swift", "offset"=>740, "remove"=>2, "text"=>"as!"}, {"file"=>"spec/fixtures/Blueprint.swift", "offset"=>6758, "remove"=>2, "text"=>"as!"}, {"file"=>"spec/fixtures/Blueprint.swift", "offset"=>10613, "remove"=>2, "text"=>"as!"}], :output=>""}
35
+ ```
36
+
37
+ The value for the `:report` key represents the result of `swift-update`, a
38
+ list of changes to perform to transform the current code to the version of
39
+ Swift supported by the used Xcode. The value for the `:output` key is the
40
+ aggregate of stdout and stderr of `swift-update`, e.g. compilation errors.
41
+
42
+ ## Installation
43
+
44
+ Add this line to your application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'grenouille'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ $ bundle
53
+
54
+ Or install it yourself as:
55
+
56
+ $ gem install grenouille
57
+
58
+ Note: The `swift-update` tool is a part of Xcode since 6.3, so it
59
+ needs at least that version to function.
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it ( https://github.com/neonichu/grenouille/fork )
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ def specs(dir)
4
+ FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ')
5
+ end
6
+
7
+ desc 'Runs all the specs'
8
+ task :spec do
9
+ sh "bundle exec bacon #{specs('**')}"
10
+ end
11
+
12
+ task default: :specs
data/bin/grenouille ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if $PROGRAM_NAME == __FILE__
4
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__)
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
8
+ end
9
+
10
+ require 'grenouille'
11
+
12
+ su = Grenouille::SwiftUpdate.new
13
+
14
+ if su.current_version < Gem::Version.new('6.3')
15
+ puts 'Please install Xcode 6.3 or newer.'
16
+ exit(1)
17
+ end
18
+
19
+ begin
20
+ puts su.determine_version(ARGV)
21
+ rescue StandardError => msg
22
+ puts "Error: #{msg}"
23
+ exit(1)
24
+ end
data/circle.yml ADDED
@@ -0,0 +1,10 @@
1
+ machine:
2
+ environment:
3
+ LANG: en_US.UTF-8
4
+ XCODE_PROJECT: yolo.xcodeproj
5
+ XCODE_SCHEME: shrugs
6
+ xcode:
7
+ version: "6.3.1"
8
+ test:
9
+ override:
10
+ - rake spec
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grenouille/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'grenouille'
8
+ spec.version = Grenouille::VERSION
9
+ spec.authors = ['Boris Bügling']
10
+ spec.email = ['boris@buegling.com']
11
+ spec.summary = 'Automatically detect Swift versions from source code.'
12
+ spec.homepage = 'https://github.com/neonichu/grenouille'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.7'
21
+ spec.add_development_dependency 'rake', '~> 10.0'
22
+ end
data/lib/grenouille.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'grenouille/swift_update'
2
+ require 'grenouille/version'
3
+ require 'grenouille/xcode'
@@ -0,0 +1,56 @@
1
+ require 'grenouille/xcode'
2
+ require 'pathname'
3
+ require 'tempfile'
4
+ require 'yaml'
5
+
6
+ # check for xcrun: error: unable to find utility "swift-update", not a developer tool or in PATH
7
+
8
+ module Grenouille
9
+ class SwiftUpdate < Xcode
10
+ def determine_version(file_glob)
11
+ required_changes = update_to_latest_swift(file_glob)[:report]
12
+ required_changes.count == 0 ? current_swift_version : previous_swift_version
13
+ end
14
+
15
+ def update_to_latest_swift(file_glob)
16
+ fail 'Non-Swift files in input' if Dir.glob(file_glob).reject { |f| Pathname.new(f).extname == '.swift' }.count > 0
17
+ files = Dir.glob(file_glob).join(' ')
18
+ fail 'No files in input' if files == ''
19
+
20
+ platform = guess_platform(file_glob)
21
+ sdk_path = xcrun("-sdk #{platform} -show-sdk-path").chomp
22
+ sdk_version = xcrun("-sdk #{platform} -show-sdk-version").chomp
23
+ target = platform == :iphoneos ? "-target arm64-apple-ios#{sdk_version}" : ''
24
+
25
+ output = xcrun("-sdk #{platform} swift-update 2>&1")
26
+ fail 'Unable to find "swift-update", install Xcode 6.3 or later' if output =~ /unable to find utility/
27
+
28
+ temp_file = Tempfile.new('swift-update')
29
+ begin
30
+ output = xcrun("-sdk #{platform} swift-update -sdk #{sdk_path} #{files} #{target} -o #{temp_file.path} 2>&1")
31
+ report = YAML.load(File.read(temp_file))
32
+ { report: report, output: output }
33
+ ensure
34
+ temp_file.close
35
+ temp_file.unlink
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def guess_platform(file_glob)
42
+ Dir.glob(file_glob).each do |file|
43
+ content = File.read(file)
44
+ return :macosx if content =~ /^import AppKit$/
45
+ return :macosx if content =~ /NSTask/
46
+ end
47
+
48
+ :iphoneos
49
+ end
50
+
51
+ def previous_swift_version
52
+ current = current_swift_version.segments
53
+ Gem::Version.new("#{current[0]}.#{current[1] - 1}")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module Grenouille
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygems/version'
2
+
3
+ module Grenouille
4
+ class Xcode
5
+ def initialize(developer_dir = `xcode-select -p`.chomp)
6
+ @developer_dir = developer_dir
7
+ end
8
+
9
+ def current_version
10
+ get_version('xcodebuild -version')
11
+ end
12
+
13
+ def current_swift_version
14
+ get_version('swift -version', 3) || get_version('swift -version', 2)
15
+ end
16
+
17
+ def xcrun(command)
18
+ `DEVELOPER_DIR=#{@developer_dir} xcrun #{command}`
19
+ end
20
+
21
+ private
22
+
23
+ def get_version(command, index = 1)
24
+ version_string = xcrun(command).split(' ')
25
+
26
+ if version_string.count >= index + 1 && Gem::Version.correct?(version_string[index])
27
+ return Gem::Version.new(version_string[index])
28
+ end
29
+
30
+ nil
31
+ end
32
+ end
33
+ end
data/perfume.gif ADDED
Binary file
@@ -0,0 +1,365 @@
1
+ //
2
+ // Blueprint.swift
3
+ // Representor
4
+ //
5
+ // Created by Kyle Fuller on 06/01/2015.
6
+ // Copyright (c) 2015 Apiary. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ // MARK: Models
12
+
13
+ /// A structure representing an API Blueprint AST
14
+ public struct Blueprint {
15
+ /// Name of the API
16
+ public let name:String
17
+
18
+ /// Top-level description of the API in Markdown (.raw) or HTML (.html)
19
+ public let description:String?
20
+
21
+ /// The collection of resource groups
22
+ public let resourceGroups:[ResourceGroup]
23
+
24
+ public init(name:String, description:String?, resourceGroups:[ResourceGroup]) {
25
+ self.name = name
26
+ self.description = description
27
+ self.resourceGroups = resourceGroups
28
+ }
29
+
30
+ public init(ast:[String:AnyObject]) {
31
+ name = ast["name"] as String
32
+ description = ast["description"] as? String
33
+ resourceGroups = parseBlueprintResourceGroups(ast)
34
+ }
35
+
36
+ public init?(named:String, bundle:NSBundle? = nil) {
37
+ func loadFile(named:String, bundle:NSBundle) -> [String:AnyObject]? {
38
+ if let url = bundle.URLForResource(named, withExtension: nil) {
39
+ if let data = NSData(contentsOfURL: url) {
40
+ let object: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: nil)
41
+ return object as? [String:AnyObject]
42
+ }
43
+ }
44
+
45
+ return nil
46
+ }
47
+
48
+ let ast = loadFile(named, bundle ?? NSBundle.mainBundle())
49
+ if let ast = ast {
50
+ self.init(ast: ast)
51
+ } else {
52
+ return nil
53
+ }
54
+ }
55
+ }
56
+
57
+ /// Logical group of resources.
58
+ public struct ResourceGroup {
59
+ /// Name of the Resource Group
60
+ public let name:String
61
+
62
+ /// Description of the Resource Group (.raw or .html)
63
+ public let description:String?
64
+
65
+ /// Array of the respective resources belonging to the Resource Group
66
+ public let resources:[Resource]
67
+
68
+ public init(name:String, description:String?, resources:[Resource]) {
69
+ self.name = name
70
+ self.description = description
71
+ self.resources = resources
72
+ }
73
+ }
74
+
75
+ /// Description of one resource, or a cluster of resources defined by its URI template
76
+ public struct Resource {
77
+ /// Name of the Resource
78
+ public let name:String
79
+
80
+ /// Description of the Resource (.raw or .html)
81
+ public let description:String?
82
+
83
+ /// URI Template as defined in RFC6570
84
+ // TODO, make this a URITemplate object
85
+ public let uriTemplate:String
86
+
87
+ /// Array of URI parameters
88
+ public let parameters:[Parameter]
89
+
90
+ /// Array of actions available on the resource each defining at least one complete HTTP transaction
91
+ public let actions:[Action]
92
+
93
+ public init(name:String, description:String?, uriTemplate:String, parameters:[Parameter], actions:[Action]) {
94
+ self.name = name
95
+ self.description = description
96
+ self.uriTemplate = uriTemplate
97
+ self.actions = actions
98
+ self.parameters = parameters
99
+ }
100
+ }
101
+
102
+ /// Description of one URI template parameter
103
+ public struct Parameter {
104
+ /// Name of the parameter
105
+ public let name:String
106
+
107
+ /// Description of the Parameter (.raw or .html)
108
+ public let description:String?
109
+
110
+ /// An arbitrary type of the parameter (a string)
111
+ public let type:String?
112
+
113
+ /// Boolean flag denoting whether the parameter is required (true) or not (false)
114
+ public let required:Bool
115
+
116
+ /// A default value of the parameter (a value assumed when the parameter is not specified)
117
+ public let defaultValue:String?
118
+
119
+ /// An example value of the parameter
120
+ public let example:String?
121
+
122
+ /// An array enumerating possible parameter values
123
+ public let values:[String]?
124
+
125
+ public init(name:String, description:String?, type:String?, required:Bool, defaultValue:String?, example:String?, values:[String]?) {
126
+ self.name = name
127
+ self.description = description
128
+ self.type = type
129
+ self.required = required
130
+ self.defaultValue = defaultValue
131
+ self.example = example
132
+ self.values = values
133
+ }
134
+ }
135
+
136
+ // An HTTP transaction (a request-response transaction). Actions are specified by an HTTP request method within a resource
137
+ public struct Action {
138
+ /// Name of the Action
139
+ public let name:String
140
+
141
+ /// Description of the Action (.raw or .html)
142
+ public let description:String?
143
+
144
+ /// HTTP request method defining the action
145
+ public let method:String
146
+
147
+ /// Array of URI parameters
148
+ public let parameters:[Parameter]
149
+
150
+ /// URI Template for the action, if it differs from the resource's URI
151
+ public let uriTemplate:String?
152
+
153
+ /// Link relation identifier of the action
154
+ public let relation:String?
155
+
156
+ /// HTTP transaction examples for the relevant HTTP request method
157
+ public let examples:[TransactionExample]
158
+
159
+ public init(name:String, description:String?, method:String, parameters:[Parameter], uriTemplate:String? = nil, relation:String? = nil, examples:[TransactionExample]? = nil) {
160
+ self.name = name
161
+ self.description = description
162
+ self.method = method
163
+ self.parameters = parameters
164
+ self.uriTemplate = uriTemplate
165
+ self.relation = relation
166
+ self.examples = examples ?? []
167
+ }
168
+ }
169
+
170
+ /// An HTTP transaction example with expected HTTP message request and response payload
171
+ public struct TransactionExample {
172
+ /// Name of the Transaction Example
173
+ public let name:String
174
+
175
+ /// Description of the Transaction Example (.raw or .html)
176
+ public let description:String?
177
+
178
+ /// Example transaction request payloads
179
+ public let requests:[Payload]
180
+
181
+ /// Example transaction response payloads
182
+ public let responses:[Payload]
183
+
184
+ public init(name:String, description:String? = nil, requests:[Payload]? = nil, responses:[Payload]? = nil) {
185
+ self.name = name
186
+ self.description = description
187
+ self.requests = requests ?? []
188
+ self.responses = responses ?? []
189
+ }
190
+ }
191
+
192
+
193
+ /// An API Blueprint payload.
194
+ public struct Payload {
195
+ public typealias Header = (name:String, value:String)
196
+
197
+ /// Name of the payload
198
+ public let name:String
199
+
200
+ /// Description of the Payload (.raw or .html)
201
+ public let description:String?
202
+
203
+ /// HTTP headers that are expected to be transferred with HTTP message represented by this payload
204
+ public let headers:[Header]
205
+
206
+ /// An entity body to be transferred with HTTP message represented by this payload
207
+ public let body:NSData?
208
+
209
+ public init(name:String, description:String? = nil, headers:[Header]? = nil, body:NSData? = nil) {
210
+ self.name = name
211
+ self.description = description
212
+ self.headers = headers ?? []
213
+ self.body = body
214
+ }
215
+ }
216
+
217
+
218
+ // MARK: AST Parsing
219
+
220
+ func compactMap<C : CollectionType, T>(source: C, transform: (C.Generator.Element) -> T?) -> [T] {
221
+ var collection = [T]()
222
+
223
+ for element in source {
224
+ if let item = transform(element) {
225
+ collection.append(item)
226
+ }
227
+ }
228
+
229
+ return collection
230
+ }
231
+
232
+ func parseParameter(source:[[String:AnyObject]]?) -> [Parameter] {
233
+ if let source = source {
234
+ return source.map { item in
235
+ let name = item["name"] as String
236
+ let description = item["description"] as? String
237
+ let type = item["type"] as? String
238
+ let required = item["required"] as? Bool
239
+ let defaultValue = item["default"] as? String
240
+ let example = item["example"] as? String
241
+ let values = item["values"] as? [String]
242
+ return Parameter(name: name, description: description, type: type, required: required ?? true, defaultValue: defaultValue, example: example, values: values)
243
+ }
244
+ }
245
+
246
+ return []
247
+ }
248
+
249
+ func parseActions(source:[[String:AnyObject]]?) -> [Action] {
250
+ if let source = source {
251
+ return compactMap(source) { item in
252
+ let name = item["name"] as? String
253
+ let description = item["description"] as? String
254
+ let method = item["method"] as? String
255
+ let parameters = parseParameter(item["parameters"] as? [[String:AnyObject]])
256
+ let attributes = item["attributes"] as? [String:String]
257
+ let uriTemplate = attributes?["uriTemplate"]
258
+ let relation = attributes?["relation"]
259
+ let examples = parseExamples(item["examples"] as? [[String:AnyObject]])
260
+
261
+ if let name = name {
262
+ if let method = method {
263
+ return Action(name: name, description: description, method: method, parameters: parameters, uriTemplate:uriTemplate, relation:relation, examples:examples)
264
+ }
265
+ }
266
+
267
+ return nil
268
+ }
269
+ }
270
+
271
+ return []
272
+ }
273
+
274
+ func parseExamples(source:[[String:AnyObject]]?) -> [TransactionExample] {
275
+ if let source = source {
276
+ return compactMap(source) { item in
277
+ let name = item["name"] as? String
278
+ let description = item["description"] as? String
279
+ let requests = parsePayloads(item["requests"] as? [[String:AnyObject]])
280
+ let responses = parsePayloads(item["responses"] as? [[String:AnyObject]])
281
+
282
+ if let name = name {
283
+ return TransactionExample(name: name, description: description, requests: requests, responses: responses)
284
+ }
285
+
286
+ return nil
287
+ }
288
+ }
289
+
290
+ return []
291
+ }
292
+
293
+ func parsePayloads(source:[[String:AnyObject]]?) -> [Payload] {
294
+ if let source = source {
295
+ return compactMap(source) { item in
296
+ let name = item["name"] as? String
297
+ let description = item["description"] as? String
298
+ let headers = parseHeaders(item["headers"] as? [[String:String]])
299
+ let bodyString = item["body"] as? String
300
+ let body = bodyString?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
301
+
302
+ if let name = name {
303
+ return Payload(name: name, description: description, headers: headers, body: body)
304
+ }
305
+
306
+ return nil
307
+ }
308
+ }
309
+
310
+ return []
311
+ }
312
+
313
+ func parseHeaders(source:[[String:String]]?) -> [Payload.Header] {
314
+ if let source = source {
315
+ return compactMap(source) { item in
316
+ if let name = item["name"] {
317
+ if let value = item["value"] {
318
+ return (name, value)
319
+ }
320
+ }
321
+
322
+ return nil
323
+ }
324
+ }
325
+
326
+ return []
327
+ }
328
+
329
+ func parseResources(source:[[String:AnyObject]]?) -> [Resource] {
330
+ if let source = source {
331
+ return compactMap(source) { item in
332
+ let name = item["name"] as? String
333
+ let description = item["description"] as? String
334
+ let uriTemplate = item["uriTemplate"] as? String
335
+ let actions = parseActions(item["actions"] as? [[String:AnyObject]])
336
+ let parameters = parseParameter(item["parameters"] as? [[String:AnyObject]])
337
+
338
+ if let name = name {
339
+ if let uriTemplate = uriTemplate {
340
+ return Resource(name: name, description: description, uriTemplate: uriTemplate, parameters: parameters, actions: actions)
341
+ }
342
+ }
343
+
344
+ return nil
345
+ }
346
+ }
347
+
348
+ return []
349
+ }
350
+
351
+ private func parseBlueprintResourceGroups(blueprint:Dictionary<String, AnyObject>) -> [ResourceGroup] {
352
+ if let resourceGroups = blueprint["resourceGroups"] as? [[String:AnyObject]] {
353
+ return compactMap(resourceGroups) { dictionary in
354
+ if let name = dictionary["name"] as String? {
355
+ let resources = parseResources(dictionary["resources"] as? [[String:AnyObject]])
356
+ let description = dictionary["description"] as String?
357
+ return ResourceGroup(name: name, description: description, resources: resources)
358
+ }
359
+
360
+ return nil
361
+ }
362
+ }
363
+
364
+ return []
365
+ }
@@ -0,0 +1,179 @@
1
+ import Foundation
2
+
3
+ /// The result of a task execution
4
+ public typealias ChoreResult = (result: Int32, stdout: String, stderr: String)
5
+
6
+ private func string_trim(string: NSString!) -> String {
7
+ return string.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) ?? ""
8
+ }
9
+
10
+ private func chore_task(command: String, _ arguments: [String] = [String](), stdin: String = "") -> ChoreResult {
11
+ let task = NSTask()
12
+
13
+ task.launchPath = command
14
+ task.arguments = arguments
15
+
16
+ if !(task.launchPath as NSString).absolutePath {
17
+ task.launchPath = (chore_task("/usr/bin/which", [task.launchPath])).stdout
18
+ }
19
+
20
+ var isDirectory: ObjCBool = false
21
+
22
+ if !NSFileManager.defaultManager().fileExistsAtPath(task.launchPath, isDirectory: &isDirectory) {
23
+ return (255, "", String(format: "%@: launch path not accessible", task.launchPath))
24
+ }
25
+
26
+ if (isDirectory) {
27
+ return (255, "", String(format: "%@: launch path is a directory", task.launchPath))
28
+ }
29
+
30
+ if !NSFileManager.defaultManager().isExecutableFileAtPath(task.launchPath) {
31
+ return (255, "", String(format: "%@: launch path not executable", task.launchPath))
32
+ }
33
+
34
+ if count(stdin) > 0 {
35
+ let stdinPipe = NSPipe()
36
+ task.standardInput = stdinPipe
37
+ let stdinHandle = stdinPipe.fileHandleForWriting
38
+
39
+ if let data = stdin.dataUsingEncoding(NSUTF8StringEncoding) {
40
+ stdinHandle.writeData(data)
41
+ stdinHandle.closeFile()
42
+ }
43
+ }
44
+
45
+ let stderrPipe = NSPipe()
46
+ task.standardError = stderrPipe
47
+ let stderrHandle = stderrPipe.fileHandleForReading
48
+
49
+ let stdoutPipe = NSPipe()
50
+ task.standardOutput = stdoutPipe
51
+ let stdoutHandle = stdoutPipe.fileHandleForReading
52
+
53
+ task.launch()
54
+ task.waitUntilExit()
55
+
56
+ let stderr = string_trim(NSString(data: stderrHandle.readDataToEndOfFile(), encoding: NSUTF8StringEncoding)) ?? ""
57
+ let stdout = string_trim(NSString(data: stdoutHandle.readDataToEndOfFile(), encoding: NSUTF8StringEncoding)) ?? ""
58
+
59
+ return (task.terminationStatus, stdout, stderr)
60
+ }
61
+
62
+ prefix operator > {}
63
+
64
+ /**
65
+ Executes a command.
66
+
67
+ :param: command The command to execute.
68
+ :returns: A tuple containing the exit code, stdout and stderr output.
69
+ */
70
+ public prefix func > (command: String) -> ChoreResult {
71
+ return chore_task(command)
72
+ }
73
+
74
+ /**
75
+ Executes a command with arguments.
76
+
77
+ :param: command The command to execute and its arguments.
78
+ :returns: A tuple containing the exit code, stdout and stderr output.
79
+ */
80
+ public prefix func > (command: [String]) -> ChoreResult {
81
+ switch command.count {
82
+ case 0:
83
+ return (0, "", "")
84
+ case 1:
85
+ return chore_task(command[0])
86
+ default:
87
+ break
88
+ }
89
+
90
+ return chore_task(command[0], Array(command[1..<command.count]))
91
+ }
92
+
93
+ infix operator | {}
94
+
95
+ /**
96
+ Executes a command with standard input from another command.
97
+
98
+ :param: left The result of a previous command.
99
+ :param: right The command to execute.
100
+ :returns: A tuple containing the exit code, stdout and stderr output.
101
+ */
102
+ public func | (left: ChoreResult, right: String) -> ChoreResult {
103
+ return left|[right]
104
+ }
105
+
106
+ /**
107
+ Executes a command with standard input from another command.
108
+
109
+ :param: left The result of a previous command.
110
+ :param: right The command to execute and its arguments.
111
+ :returns: A tuple containing the exit code, stdout and stderr output.
112
+ */
113
+ public func | (left: ChoreResult, right: [String]) -> ChoreResult {
114
+ if left.result != 0 {
115
+ return left
116
+ }
117
+
118
+ let arguments = right.count >= 2 ? Array(right[1..<right.count]) : [String]()
119
+ return chore_task(right[0], arguments, stdin: left.stdout)
120
+ }
121
+
122
+ /**
123
+ Executes a closure with input from a previous command.
124
+
125
+ :param: left The result of a previous command.
126
+ :param: right The closure to execute.
127
+ :returns: A tuple containing the exit code, stdout and stderr output.
128
+ */
129
+ public func | (left: ChoreResult, right: ((String) -> String)) -> ChoreResult {
130
+ if left.result != 0 {
131
+ return left
132
+ }
133
+
134
+ return (0, right(left.stdout), "")
135
+ }
136
+
137
+ /**
138
+ Executes a command with input from a closure.
139
+
140
+ :param: left The closure to execute.
141
+ :param: right The command to execute.
142
+ :returns: A tuple containing the exit code, stdout and stderr output.
143
+ */
144
+ public func | (left: (() -> String), right: String) -> ChoreResult {
145
+ return (0, left(), "")|right
146
+ }
147
+
148
+ /**
149
+ Executes a command with input from a closure.
150
+
151
+ :param: left The closure to execute.
152
+ :param: right The command to execute and its arguments.
153
+ :returns: A tuple containing the exit code, stdout and stderr output.
154
+ */
155
+ public func | (left: (() -> String), right: [String]) -> ChoreResult {
156
+ return (0, left(), "")|right
157
+ }
158
+
159
+ /**
160
+ Executes a command with input from a string.
161
+
162
+ :param: left The string to use a stdin.
163
+ :param: right The command to execute.
164
+ :returns: A tuple containing the exit code, stdout and stderr output.
165
+ */
166
+ public func | (left: String, right: String) -> ChoreResult {
167
+ return (0, left, "")|right
168
+ }
169
+
170
+ /**
171
+ Executes a command with input from a string.
172
+
173
+ :param: left The string to use a stdin.
174
+ :param: right The command to execute and its arguments.
175
+ :returns: A tuple containing the exit code, stdout and stderr output.
176
+ */
177
+ public func | (left: String, right: [String]) -> ChoreResult {
178
+ return (0, left, "")|right
179
+ }
@@ -0,0 +1,8 @@
1
+ import Foundation
2
+ import AppKit
3
+
4
+ private func chore_task() {
5
+ let task = NSTask()
6
+ let array = [1,2,3]
7
+ println(countElements(array))
8
+ }
@@ -0,0 +1,149 @@
1
+ //
2
+ // Stargate.swift
3
+ // Stargate
4
+ //
5
+ // Created by Boris Bügling on 28/04/15.
6
+ // Copyright (c) 2015 Contentful GmbH. All rights reserved.
7
+ //
8
+
9
+ import MultipeerConnectivity
10
+ import PeerKit
11
+
12
+ public typealias DebugHandler = (message: String) -> Void
13
+
14
+ public class Base {
15
+ private var applicationGroupIdentifier: String = ""
16
+ var pingIdentifier = "274EAEF1-A178-47FE-81F4-96E87C242456"
17
+ var pingPayload = "ping"
18
+ var sanitizedIdentifier: String {
19
+ return applicationGroupIdentifier.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.allZeros, range: nil).substringToIndex(advance(applicationGroupIdentifier.startIndex, 15))
20
+ }
21
+
22
+ public init(applicationGroupIdentifier: String) {
23
+ self.applicationGroupIdentifier = applicationGroupIdentifier
24
+ }
25
+
26
+ public func listenForMessage(#identifier: String, _ listener: ((AnyObject!) -> Void)) {
27
+ fatalError("listenForMessage() needs to be overidden in subclasses.")
28
+ }
29
+
30
+ public func passMessage(message: NSCoding, identifier: String) {
31
+ fatalError("passMessageObject() needs to be overidden in subclasses.")
32
+ }
33
+
34
+ public func sendMultipeerMessage(message: AnyObject, identifier: String) {
35
+ let allPeers = PeerKit.session?.connectedPeers as? [MCPeerID]
36
+ PeerKit.sendEvent(identifier, object: message, toPeers: allPeers)
37
+ }
38
+
39
+ public func stopListeningForMessage(#identifier: String) {
40
+ fatalError("stopListeningForMessage() needs to be overidden in subclasses.")
41
+ }
42
+ }
43
+
44
+ #if os(iOS)
45
+ import MMWormhole
46
+ import WatchKit
47
+ import UIKit
48
+
49
+ /// Stargate endpoint to be used on the phone
50
+ public class Abydos : Base {
51
+ private var callback: DebugHandler?
52
+ var wormhole: MMWormhole!
53
+
54
+ public override init(applicationGroupIdentifier: String) {
55
+ super.init(applicationGroupIdentifier: applicationGroupIdentifier)
56
+
57
+ PeerKit.transceive(sanitizedIdentifier)
58
+ sendMultipeerMessage(pingPayload, identifier: pingIdentifier)
59
+
60
+ wormhole = MMWormhole(applicationGroupIdentifier: applicationGroupIdentifier, optionalDirectory: "stargate")
61
+ wormhole.passMessageObject(pingPayload, identifier: pingIdentifier)
62
+ }
63
+
64
+ public func debug(callback: DebugHandler) {
65
+ PeerKit.onConnect = { (me, you) -> Void in callback(message: "connect: \(me) <=> \(you)") }
66
+ self.callback = callback
67
+ }
68
+
69
+ public func tunnel() {
70
+ PeerKit.transceive(sanitizedIdentifier)
71
+ PeerKit.onEvent = { (peerID, event, object) -> Void in
72
+ if let object = object as? NSCoding {
73
+ if let callback = self.callback {
74
+ callback(message: "Received message from Mac: \(object) for \(event)")
75
+ }
76
+
77
+ self.wormhole.passMessageObject(object, identifier: event)
78
+ }
79
+ }
80
+
81
+ //UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler() {}
82
+ }
83
+
84
+ public func tunnelReplies(#identifier: String) {
85
+ wormhole.listenForMessageWithIdentifier(identifier) { (message) -> Void in
86
+ if let message: AnyObject = message {
87
+ if let callback = self.callback {
88
+ callback(message: "Received message from watch: \(message) for \(identifier)")
89
+ }
90
+
91
+ self.sendMultipeerMessage(message, identifier: identifier)
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ /// Stargate endpoint to be used on the ᴡᴀᴛᴄʜ
98
+ public class Atlantis : Base {
99
+ var wormhole: MMWormhole!
100
+
101
+ public override init(applicationGroupIdentifier: String) {
102
+ super.init(applicationGroupIdentifier: applicationGroupIdentifier)
103
+
104
+ wormhole = MMWormhole(applicationGroupIdentifier: applicationGroupIdentifier, optionalDirectory: "stargate")
105
+ passMessage(pingPayload, identifier: pingIdentifier)
106
+ }
107
+
108
+ public override func listenForMessage(#identifier: String, _ listener: ((AnyObject!) -> Void)) {
109
+ wormhole.listenForMessageWithIdentifier(identifier, listener: listener)
110
+
111
+ WKInterfaceController.openParentApplication([NSObject : AnyObject](), reply: nil)
112
+ }
113
+
114
+ public override func passMessage(message: NSCoding, identifier: String) {
115
+ wormhole.passMessageObject(message, identifier: identifier)
116
+
117
+ WKInterfaceController.openParentApplication([NSObject : AnyObject](), reply: nil)
118
+ }
119
+
120
+ public override func stopListeningForMessage(#identifier: String) {
121
+ wormhole.stopListeningForMessageWithIdentifier(identifier)
122
+ }
123
+ }
124
+
125
+ #endif
126
+
127
+ /// Stargate endpoint to be used on the Mac
128
+ public class Earth : Base {
129
+ public override init(applicationGroupIdentifier: String) {
130
+ super.init(applicationGroupIdentifier: applicationGroupIdentifier)
131
+
132
+ PeerKit.transceive(sanitizedIdentifier)
133
+ passMessage(pingPayload, identifier: pingIdentifier)
134
+ }
135
+
136
+ public override func listenForMessage(#identifier: String, _ listener: ((AnyObject!) -> Void)) {
137
+ PeerKit.eventBlocks[identifier] = { (peerID, object) -> Void in
138
+ listener(object)
139
+ }
140
+ }
141
+
142
+ public override func passMessage(message: NSCoding, identifier: String) {
143
+ sendMultipeerMessage(message, identifier: identifier)
144
+ }
145
+
146
+ public override func stopListeningForMessage(#identifier: String) {
147
+ PeerKit.stopTransceiving()
148
+ }
149
+ }
@@ -0,0 +1,14 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'pathname'
5
+ ROOT = Pathname.new(File.expand_path('../../', __FILE__))
6
+ $LOAD_PATH.unshift((ROOT + 'lib').to_s)
7
+ $LOAD_PATH.unshift((ROOT + 'spec').to_s)
8
+
9
+ require 'bundler/setup'
10
+ require 'bacon'
11
+ require 'mocha-on-bacon'
12
+ require 'pretty_bacon'
13
+
14
+ require 'grenouille'
@@ -0,0 +1,61 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ module Grenouille
4
+ describe SwiftUpdate do
5
+ it 'reports Swift 1.2 code correctly' do
6
+ detected_version = SwiftUpdate.new.determine_version('spec/fixtures/Stargate.swift')
7
+
8
+ detected_version.should == Gem::Version.new('1.2')
9
+ end
10
+
11
+ it 'reports Swift 1.1 code correctly' do
12
+ detected_version = SwiftUpdate.new.determine_version('spec/fixtures/Blueprint.swift')
13
+
14
+ detected_version.should == Gem::Version.new('1.1')
15
+ end
16
+
17
+ it 'can handle Swift 1.2 on iOS' do
18
+ result = SwiftUpdate.new.update_to_latest_swift('spec/fixtures/Stargate.swift')
19
+
20
+ result[:report].should == []
21
+ result[:output].start_with?('spec/fixtures/Stargate.swift:10:8: error: no such module \'PeerKit\'').should == true
22
+ end
23
+
24
+ it 'can handle Swift 1.1 on iOS' do
25
+ result = SwiftUpdate.new.update_to_latest_swift('spec/fixtures/Blueprint.swift')
26
+
27
+ result[:report].count.should == 3
28
+ result[:output].should == ''
29
+ end
30
+
31
+ it 'can handle Swift 1.2 on OS X' do
32
+ result = SwiftUpdate.new.update_to_latest_swift('spec/fixtures/ChoreTask.swift')
33
+
34
+ result[:report].should == []
35
+ result[:output].should == ''
36
+ end
37
+
38
+ it 'can handle Swift 1.1 on OS X' do
39
+ result = SwiftUpdate.new.update_to_latest_swift('spec/fixtures/Mac-1.1.swift')
40
+
41
+ result[:report].count.should == 1
42
+ result[:output].should == ''
43
+ end
44
+
45
+ it 'supports file globbing' do
46
+ detected_version = SwiftUpdate.new.determine_version('spec/fixtures/*.swift')
47
+
48
+ detected_version.should == Gem::Version.new('1.1')
49
+ end
50
+
51
+ it 'accepts a list of files' do
52
+ detected_version = SwiftUpdate.new.determine_version(['spec/fixtures/ChoresTask.swift', 'spec/fixtures/Mac-1.1.swift'])
53
+
54
+ detected_version.should == Gem::Version.new('1.1')
55
+ end
56
+
57
+ it 'throws when non Swift files are provided as arguments' do
58
+ should.raise(StandardError) { SwiftUpdate.new.determine_version('spec/*.rb') }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ module Grenouille
4
+ describe Xcode do
5
+ it 'can retrieve the version number of the currently selected Swift' do
6
+ Xcode.any_instance.stubs(:`).returns("Apple Swift version 1.3 (swiftlang-602.0.49.6 clang-602.0.49)\nTarget: x86_64-apple-darwin14.4.0")
7
+
8
+ Xcode.new.current_swift_version.should == Gem::Version.new('1.3')
9
+ end
10
+
11
+ it 'can retrieve the version of Swift 1.1' do
12
+ Xcode.any_instance.stubs(:`).returns('Swift version 1.1 (swift-600.0.57.4)')
13
+
14
+ Xcode.new.current_swift_version.should == Gem::Version.new('1.1')
15
+ end
16
+
17
+ it 'can retrieve the version number of the currently selected Xcode' do
18
+ Xcode.any_instance.stubs(:`).returns("Xcode 3.2.1\nBuild version 6D1002")
19
+
20
+ Xcode.new.current_version.should == Gem::Version.new('3.2.1')
21
+ end
22
+
23
+ it 'is resilient against xcodebuild not producing any output' do
24
+ Xcode.any_instance.stubs(:`).returns('')
25
+
26
+ Xcode.new.current_version.should.be.nil
27
+ end
28
+
29
+ it 'is resilient against xcodebuild not producing the desired output' do
30
+ Xcode.any_instance.stubs(:`).returns(File.read(__FILE__))
31
+
32
+ Xcode.new.current_version.should.be.nil
33
+ end
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grenouille
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Boris Bügling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - boris@buegling.com
44
+ executables:
45
+ - grenouille
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - .rubocop.yml
51
+ - .swift-version
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - bin/grenouille
57
+ - circle.yml
58
+ - grenouille.gemspec
59
+ - lib/grenouille.rb
60
+ - lib/grenouille/swift_update.rb
61
+ - lib/grenouille/version.rb
62
+ - lib/grenouille/xcode.rb
63
+ - perfume.gif
64
+ - spec/fixtures/Blueprint.swift
65
+ - spec/fixtures/ChoreTask.swift
66
+ - spec/fixtures/Mac-1.1.swift
67
+ - spec/fixtures/Stargate.swift
68
+ - spec/spec_helper.rb
69
+ - spec/swift_update_spec.rb
70
+ - spec/xcode_spec.rb
71
+ homepage: https://github.com/neonichu/grenouille
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.0.14
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Automatically detect Swift versions from source code.
95
+ test_files:
96
+ - spec/fixtures/Blueprint.swift
97
+ - spec/fixtures/ChoreTask.swift
98
+ - spec/fixtures/Mac-1.1.swift
99
+ - spec/fixtures/Stargate.swift
100
+ - spec/spec_helper.rb
101
+ - spec/swift_update_spec.rb
102
+ - spec/xcode_spec.rb
103
+ has_rdoc: