passbookpgh 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +130 -0
  5. data/LICENSE +22 -0
  6. data/README.md +294 -0
  7. data/Rakefile +36 -0
  8. data/VERSION +1 -0
  9. data/bin/pk +22 -0
  10. data/lib/commands/build.rb +62 -0
  11. data/lib/commands/commands.rb +31 -0
  12. data/lib/commands/generate.rb +44 -0
  13. data/lib/commands/templates/boarding-pass.json +56 -0
  14. data/lib/commands/templates/coupon.json +33 -0
  15. data/lib/commands/templates/event-ticket.json +33 -0
  16. data/lib/commands/templates/generic.json +33 -0
  17. data/lib/commands/templates/store-card.json +33 -0
  18. data/lib/passbook/pkpass.rb +121 -0
  19. data/lib/passbook/push_notification.rb +19 -0
  20. data/lib/passbook/signer.rb +40 -0
  21. data/lib/passbook/version.rb +3 -0
  22. data/lib/passbook.rb +15 -0
  23. data/lib/rack/passbook_rack.rb +98 -0
  24. data/lib/rails/generators/passbook/config/config_generator.rb +16 -0
  25. data/lib/rails/generators/passbook/config/templates/initializer.rb +13 -0
  26. data/lib/utils/command_utils.rb +12 -0
  27. data/passbookpgh.gemspec +110 -0
  28. data/spec/data/icon.png +0 -0
  29. data/spec/data/icon@2x.png +0 -0
  30. data/spec/data/logo.png +0 -0
  31. data/spec/data/logo@2x.png +0 -0
  32. data/spec/lib/commands/build_spec.rb +92 -0
  33. data/spec/lib/commands/commands_spec.rb +102 -0
  34. data/spec/lib/commands/commands_spec_helper.rb +69 -0
  35. data/spec/lib/commands/generate_spec.rb +72 -0
  36. data/spec/lib/passbook/pkpass_spec.rb +108 -0
  37. data/spec/lib/passbook/push_notification_spec.rb +23 -0
  38. data/spec/lib/passbook/signer_spec.rb +84 -0
  39. data/spec/lib/rack/passbook_rack_spec.rb +233 -0
  40. data/spec/spec_helper.rb +9 -0
  41. metadata +244 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ae39964d25f048fe43178a28e80e2eed797930c1f3c0b002482607cf290ab0b2
4
+ data.tar.gz: 538200e837f2f913dcf9375f81623168e27cffac95c8daf65c40ae6a757b02ca
5
+ SHA512:
6
+ metadata.gz: f565cde9634cf1f0ae9579c90c262ed9878753b4015d1bc7cddff22c89da86d7f75a9094cd18326853b4662a448ff461760f8996f4560b016109a0dd3f6c8f7b
7
+ data.tar.gz: 5e87648f0eaf58fc323c8f90df309dc64a98419d577b226eb1016b06002147493ea673217b51f9a30556d5e6ee5544650df52fa8b4c93b9973e9b87fde27c2cc
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - ruby-head
6
+ - 2.0.0
7
+ - 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in passbookpgh.gemspec
4
+ gem 'rubyzip', '>= 1.0.0'
5
+ gem 'grocer'
6
+ gem 'commander'
7
+ gem 'terminal-table'
8
+
9
+ group :test, :development do
10
+ gem 'rack-test'
11
+ gem 'activesupport'
12
+ gem 'jeweler'
13
+ gem 'simplecov'
14
+ gem 'rspec'
15
+ gem 'rake'
16
+ gem 'yard'
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,130 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (6.1.4.4)
5
+ concurrent-ruby (~> 1.0, >= 1.0.2)
6
+ i18n (>= 1.6, < 2)
7
+ minitest (>= 5.1)
8
+ tzinfo (~> 2.0)
9
+ zeitwerk (~> 2.3)
10
+ addressable (2.4.0)
11
+ builder (3.2.4)
12
+ commander (4.6.0)
13
+ highline (~> 2.0.0)
14
+ concurrent-ruby (1.1.9)
15
+ descendants_tracker (0.0.4)
16
+ thread_safe (~> 0.3, >= 0.3.1)
17
+ diff-lcs (1.5.0)
18
+ docile (1.4.0)
19
+ faraday (0.9.2)
20
+ multipart-post (>= 1.2, < 3)
21
+ git (1.10.2)
22
+ rchardet (~> 1.8)
23
+ github_api (0.16.0)
24
+ addressable (~> 2.4.0)
25
+ descendants_tracker (~> 0.0.4)
26
+ faraday (~> 0.8, < 0.10)
27
+ hashie (>= 3.4)
28
+ mime-types (>= 1.16, < 3.0)
29
+ oauth2 (~> 1.0)
30
+ grocer (0.7.1)
31
+ hashie (5.0.0)
32
+ highline (2.0.3)
33
+ i18n (1.8.11)
34
+ concurrent-ruby (~> 1.0)
35
+ jar-dependencies (0.4.1)
36
+ jeweler (2.3.9)
37
+ builder
38
+ bundler
39
+ git (>= 1.2.5)
40
+ github_api (~> 0.16.0)
41
+ highline (>= 1.6.15)
42
+ nokogiri (>= 1.5.10)
43
+ psych
44
+ rake
45
+ rdoc
46
+ semver2
47
+ jwt (2.3.0)
48
+ mime-types (2.99.3)
49
+ mini_portile2 (2.7.1)
50
+ minitest (5.15.0)
51
+ multi_json (1.15.0)
52
+ multi_xml (0.6.0)
53
+ multipart-post (2.1.1)
54
+ nokogiri (1.13.1)
55
+ mini_portile2 (~> 2.7.0)
56
+ racc (~> 1.4)
57
+ nokogiri (1.13.1-java)
58
+ racc (~> 1.4)
59
+ oauth2 (1.4.7)
60
+ faraday (>= 0.8, < 2.0)
61
+ jwt (>= 1.0, < 3.0)
62
+ multi_json (~> 1.3)
63
+ multi_xml (~> 0.5)
64
+ rack (>= 1.2, < 3)
65
+ psych (4.0.3)
66
+ stringio
67
+ psych (4.0.3-java)
68
+ jar-dependencies (>= 0.1.7)
69
+ racc (1.6.0)
70
+ racc (1.6.0-java)
71
+ rack (2.2.3)
72
+ rack-test (1.1.0)
73
+ rack (>= 1.0, < 3)
74
+ rake (13.0.6)
75
+ rchardet (1.8.0)
76
+ rdoc (6.4.0)
77
+ psych (>= 4.0.0)
78
+ rspec (3.10.0)
79
+ rspec-core (~> 3.10.0)
80
+ rspec-expectations (~> 3.10.0)
81
+ rspec-mocks (~> 3.10.0)
82
+ rspec-core (3.10.1)
83
+ rspec-support (~> 3.10.0)
84
+ rspec-expectations (3.10.2)
85
+ diff-lcs (>= 1.2.0, < 2.0)
86
+ rspec-support (~> 3.10.0)
87
+ rspec-mocks (3.10.2)
88
+ diff-lcs (>= 1.2.0, < 2.0)
89
+ rspec-support (~> 3.10.0)
90
+ rspec-support (3.10.3)
91
+ rubyzip (2.3.2)
92
+ semver2 (3.4.2)
93
+ simplecov (0.21.2)
94
+ docile (~> 1.1)
95
+ simplecov-html (~> 0.11)
96
+ simplecov_json_formatter (~> 0.1)
97
+ simplecov-html (0.12.3)
98
+ simplecov_json_formatter (0.1.3)
99
+ stringio (3.0.1)
100
+ terminal-table (3.0.2)
101
+ unicode-display_width (>= 1.1.1, < 3)
102
+ thread_safe (0.3.6)
103
+ thread_safe (0.3.6-java)
104
+ tzinfo (2.0.4)
105
+ concurrent-ruby (~> 1.0)
106
+ unicode-display_width (2.1.0)
107
+ webrick (1.7.0)
108
+ yard (0.9.27)
109
+ webrick (~> 1.7.0)
110
+ zeitwerk (2.5.3)
111
+
112
+ PLATFORMS
113
+ java
114
+ ruby
115
+
116
+ DEPENDENCIES
117
+ activesupport
118
+ commander
119
+ grocer
120
+ jeweler
121
+ rack-test
122
+ rake
123
+ rspec
124
+ rubyzip (>= 1.0.0)
125
+ simplecov
126
+ terminal-table
127
+ yard
128
+
129
+ BUNDLED WITH
130
+ 2.2.31
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Thomas Lauro
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,294 @@
1
+ [![Build Status](https://travis-ci.org/lgleasain/passbook.png)](https://travis-ci.org/lgleasain/passbook)
2
+
3
+ # passbookpgh
4
+
5
+ The passbook gem let's you create a pkpass for passbook in iOS 14+ (this gem is forked/cloned from [frozon/passbook](https://github.com/frozon/passbook))
6
+
7
+ ## Installation
8
+
9
+ Include the passbook gem in your project.
10
+
11
+ IE: In your Gemfile
12
+ ```
13
+ gem 'passbook'
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ If you want to jump in without having to integrate this into your code you can use the command line options to get started. Start by installing the gem
19
+
20
+ ```
21
+ gem install passbook
22
+ ```
23
+
24
+ Then go to a directory that you want to generate your pass under and use the "pk generate command". (note: do not use spaces in your passname or else strange things will happen.)
25
+
26
+ ```
27
+ pk generate your_pass_name
28
+ ```
29
+
30
+ This will generate a directory called your_pass_name. Edit your pass.json file in the your_pass_directory to have a valid team identifier and passTypeIdentifier and create your certificates if you haven't yet. [See this article for information on how to do this.](http://www.raywenderlich.com/20734/beginning-passbook-part-1#more-20734)
31
+
32
+ Assuming that you have put your certificate files etc. in your working directory.
33
+
34
+ ```
35
+ pk build your_pass_name -w ./wwdc.pem -c ./your_pass_name.p12 -p ''
36
+ ```
37
+ The wwdc.pem file is the exported Apple World Wide Developer Relations Certification Authority certificate file from your key manager and the your_pass_name.p12 is the exported p12 file from your pass certificate.
38
+
39
+ If you are not building your passes on a mac or just prefer to use the pass certificate and key pem file.
40
+
41
+ ```
42
+ pk build passbook_gem_name -w ./wwdc.pem -c ./your_pass_name_certificate.pem -k your_pass_name_key.pem -p '12345'
43
+ ```
44
+
45
+ Now you can drag the file over to a simulator or send it to your iPhone via e-mail to view your pass.
46
+
47
+
48
+ ## Configuration
49
+
50
+ Create initializer
51
+ ```
52
+ rails g passbook:config
53
+ or with params
54
+ rails g passbook:config [Absolute path to the wwdc cert file] [Absolute path to your cert.p12 file] [Password for your certificate]
55
+ ```
56
+
57
+ Configure your config/initializers/passbook.rb
58
+ ```
59
+ Passbook.configure do |passbook|
60
+ passbook.wwdc_cert = Rails.root.join('wwdc_cert.pem')
61
+ passbook.p12_certificate = Rails.root.join('cert.p12')
62
+ passbook.p12_password = 'cert password'
63
+ end
64
+ ```
65
+
66
+ If you are running this on a different machine from what you used to create your WWDC keys
67
+ ```
68
+ Passbook.configure do |passbook|
69
+ passbook.wwdc_cert = Rails.root.join('wwdc_cert.pem')
70
+ passbook.p12_key = Rails.root.join('key.pem')
71
+ passbook.p12_certificate = Rails.root.join('certificate.pem')
72
+ passbook.p12_password = 'cert password'
73
+ end
74
+ ```
75
+ If you are using Sinatra you can place this in the file you are executing or in a file that you do a require on. You would also not reference Rails.root when specifying your file path.
76
+
77
+ If You are doing push notifications then you will need to add some extra configuration options, namely a push notification certificate and a notification gateway certificate. Look at the Grocer gem documentation to find information on how to create this certificate. Settings you will want to use for the notification gateway are either 'gateway.push.apple.com' for production, 'gateway.sandbox.push.apple.com' for development and 'localhost' for unit tests.
78
+ ```
79
+ Passbook.configure do |passbook|
80
+ .....other settings.....
81
+ passbook.notification_gateway = 'gateway.push.apple.com'
82
+ passbook.notification_passphrase = 'my_hard_password' (optional)
83
+ passbook.notification_cert = 'lib/assets/my_notification_cert.pem'
84
+ end
85
+ ```
86
+
87
+ If you want to also support the push notification endpoints you will also need to include the Rack::PassbookRack middleware. In rails your config will look something like this.
88
+ ```
89
+ config.middleware.use Rack::PassbookRack
90
+ ```
91
+
92
+ ## Usage
93
+
94
+ Please refer to apple iOS dev center for how to build cert and json. [This article is also helpful.](http://www.raywenderlich.com/20734/beginning-passbook-part-1#more-20734)
95
+ ```
96
+ pass = Passbook::PKPass.new 'your json data'
97
+
98
+ # Add file from disk
99
+ pass.addFile 'file_path'
100
+
101
+ # Add file from memory
102
+ file[:name] = 'file name'
103
+ file[:content] = 'whatever you want'
104
+ pass.addFile file
105
+
106
+ # Add multiple files
107
+ pass.addFiles [file_path_1, file_path_2, file_path_3]
108
+
109
+ # Add multiple files from memory
110
+ pass.addFiles [{name: 'file1', content: 'content1'}, {name: 'file2', content: 'content2'}, {name: 'file3', content: 'content3'}]
111
+
112
+ # Output a Tempfile
113
+
114
+ pkpass = pass.file
115
+ send_file pkpass.path, type: 'application/vnd.apple.pkpass', disposition: 'attachment', filename: "pass.pkpass"
116
+
117
+ # Or a stream
118
+
119
+ pkpass = pass.stream
120
+ send_data pkpass.string, type: 'application/vnd.apple.pkpass', disposition: 'attachment', filename: "pass.pkpass"
121
+
122
+ ```
123
+ If you are using Sinatra you will need to include the 'active_support' gem and will need to require 'active_support/json/encoding'. Here is an example using the streaming mechanism.
124
+
125
+ ```
126
+ require 'sinatra'
127
+ require 'passbook'
128
+ require 'active_support/json/encoding'
129
+
130
+ Passbook.configure do |passbook|
131
+ passbook.p12_password = '12345'
132
+ passbook.p12_key = 'passkey.pem'
133
+ passbook.p12_certificate = 'passcertificate.pem'
134
+ passbook.wwdc_cert = 'WWDR.pem'
135
+ end
136
+
137
+ get '/passbook' do
138
+ pass = # valid passbook json. refer to the wwdc documentation.
139
+ passbook = Passbook::PKPass.new pass
140
+ passbook.addFiles ['logo.png', 'logo@2x.png', 'icon.png', 'icon@2x.png']
141
+ response['Content-Type'] = 'application/vnd.apple.pkpass'
142
+ attachment 'mypass.pkpass'
143
+ passbook.stream.string
144
+ end
145
+ ```
146
+
147
+ We will try to make this cleaner in subsequent releases.
148
+
149
+ ### Using Different Certificates For Different Passes
150
+
151
+ Sometime you might want to be able to use different certificates for different passes. This can be done by passing in a Signer class into your PKPass initializer like so
152
+
153
+ ```
154
+ signer = Passbook::Signer.new {certificate: some_cert, password: some_password, key: some_key, wwdc_cert: some_wwdc_cert}
155
+ pk_pass = Passbook::PKPass.new your_json_data, signer
156
+
157
+ ....
158
+ ```
159
+
160
+ ### Push Notifications
161
+
162
+ If you want to support passbook push notification updates you will need to configure the appropriate bits above.
163
+
164
+ In order to support push notifications you will need to have a basic understanding of the way that push notifications work and how the data is passed back and forth. See [this](http://developer.apple.com/library/ios/#Documentation/UserExperience/Conceptual/PassKit_PG/Chapters/Creating.html) for basic information about passes and [this](http://developer.apple.com/library/ios/#Documentation/UserExperience/Conceptual/PassKit_PG/Chapters/Updating.html#//apple_ref/doc/uid/TP40012195-CH5-SW1) to understand the information that needs to be exchanged between each device and your application to support the update service.
165
+
166
+ Your pass will need to have a field called 'webServiceURL' with the base url to your site and a field called 'authenticationToken'. The json snippet should look like this. Note that your url needs to be a valid signed https endpoint for production. You can put your phone in dev mode to test updates against a insecure http endpoint (under settings => developer => passkit testing).
167
+
168
+ ```
169
+ ...
170
+ "webserviceURL" : "https://www.honeybadgers.com/",
171
+ "authenticationToken" : "yummycobras"
172
+ ...
173
+ ```
174
+
175
+ Passbook includes rack middleware to make the job of supporting the passbook endpoints easier. You will need to configure the middleware as outlined above and then implement a class called Passbook::PassbookNotification. Below is an annotated implementation.
176
+
177
+ ```
178
+ module Passbook
179
+ class PassbookNotification
180
+
181
+ # This is called whenever a new pass is saved to a users passbook or the
182
+ # notifications are re-enabled. You will want to persist these values to
183
+ # allow for updates on subsequent calls in the call chain. You can have
184
+ # multiple push tokens and serial numbers for a specific
185
+ # deviceLibraryIdentifier.
186
+
187
+ def self.register_pass(options)
188
+ the_passes_serial_number = options['serialNumber']
189
+ the_devices_device_library_identifier = options['deviceLibraryIdentifier']
190
+ the_devices_push_token = options['pushToken']
191
+ the_pass_type_identifier = options["passTypeIdentifier"]
192
+ the_authentication_token = options['authToken']
193
+
194
+ # this is if the pass registered successfully
195
+ # change the code to 200 if the pass has already been registered
196
+ # 404 if pass not found for serialNubmer and passTypeIdentifier
197
+ # 401 if authorization failed
198
+ # or another appropriate code if something went wrong.
199
+ {:status => 201}
200
+ end
201
+
202
+ # This is called when the device receives a push notification from apple.
203
+ # You will need to return the serial number of all passes associated with
204
+ # that deviceLibraryIdentifier.
205
+
206
+ def self.passes_for_device(options)
207
+ device_library_identifier = options['deviceLibraryIdentifier']
208
+ passes_updated_since = options['passesUpdatedSince']
209
+
210
+ # the 'lastUpdated' uses integers values to tell passbook if the pass is
211
+ # more recent than the current one. If you just set it is the same value
212
+ # every time the pass will update and you will get a warning in the log files.
213
+ # you can use the time in milliseconds, a counter or any other numbering scheme.
214
+ # you then also need to return an array of serial numbers.
215
+ {'lastUpdated' => '1', 'serialNumbers' => ['various', 'serial', 'numbers']}
216
+ end
217
+
218
+ # this is called when a pass is deleted or the user selects the option to disable pass updates.
219
+ def self.unregister_pass(options)
220
+ # a solid unique pair of identifiers to identify the pass are
221
+ serial_number = options['serialNumber']
222
+ device_library_identifier = options['deviceLibraryIdentifier']
223
+ the_pass_type_identifier = options["passTypeIdentifier"]
224
+ the_authentication_token = options['authToken']
225
+ # return a status 200 to indicate that the pass was successfully unregistered.
226
+ {:status => 200}
227
+ end
228
+
229
+ # this returns your updated pass
230
+ def self.latest_pass(options)
231
+ the_pass_serial_number = options['serialNumber']
232
+ # create your PkPass the way you did when your first created the pass.
233
+ # you will want to return
234
+ my_pass = PkPass.new 'your pass json'
235
+ # you will want to return the string from the stream of your PkPass object.
236
+ {:status => 200, :latest_pass => mypass.stream.string, :last_modified => '1442120893'}
237
+ end
238
+
239
+ # This is called whenever there is something from the update process that is a warning
240
+ # or error
241
+ def self.passbook_log(log)
242
+ # this is a VERY crude logging example. use the logger of your choice here.
243
+ p "#{Time.now} #{log}"
244
+ end
245
+
246
+ end
247
+ end
248
+
249
+ ```
250
+
251
+ To send a push notification for a updated pass simply call Passbook::PushNotification.send_notification with the push token for the device you are updating
252
+
253
+ ```
254
+ Passbook::PushNotification.send_notification the_device_push_token
255
+
256
+ ```
257
+
258
+ Apple will send out a notification to your phone (usually within 15 minutes or less), which will cause the phone that this push notification is associated with to make a call to your server to get pass serial numbers and to then get the updated pass. Each phone/pass combination has it's own push token whch will require a separate call for every phone that has push notifications enabled for a pass (this is an Apple thing). In the future we may look into offering background process support for this as part of this gem. For now, if you have a lot of passes to update you will need to do this yourself.
259
+
260
+ ## Tests
261
+
262
+ To launch tests :
263
+ ```
264
+ bundle exec rake spec
265
+ ```
266
+
267
+ ## Contributing
268
+
269
+ 1. Fork it
270
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
271
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
272
+ 4. Push to the branch (`git push origin my-new-feature`)
273
+ 5. Create new Pull Request
274
+
275
+ ## Changelog
276
+
277
+ ### 0.0.4
278
+ Allow passbook gem to return a ZipOutputStream (needed when garbage collector delete tempfile before beeing able to use it) [Thx to applidget]
279
+
280
+ ### 0.2.0
281
+ Adding support for push notification updates for passes.
282
+
283
+ ### 0.4.0
284
+ Adding support for using multiple signatures per gem configuration and introducing command line helpers. More in [this](http://www.polyglotprogramminginc.com/allowing-for-more-signature-flexibility-in-the-ruby-passbook-gem/) blog post.
285
+
286
+ ### 0.4.1
287
+ Update rubyzip dependency to >= 1.0.0
288
+
289
+ License
290
+ -------
291
+
292
+ passbook is released under the MIT license:
293
+
294
+ * http://www.opensource.org/licenses/MIT
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "passbook"
16
+ gem.homepage = "http://github.com/frozon/passbook"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A IOS Passbook generator.}
19
+ gem.description = %Q{This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it.}
20
+ gem.email = ['thomas@lauro.fr', 'lgleason@polyglotprogramminginc.com']
21
+ gem.authors = ['Thomas Lauro', 'Lance Gleason']
22
+ gem.executables = ['pk']
23
+ # dependencies defined in Gemfile
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rspec/core'
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec) do |spec|
30
+ spec.pattern = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ task :default => :spec
34
+
35
+ require 'yard'
36
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.4
data/bin/pk ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+ require 'terminal-table'
5
+
6
+ $:.push File.expand_path("../../lib", __FILE__)
7
+ require 'passbook'
8
+ require 'utils/command_utils'
9
+
10
+ HighLine.track_eof = false # Fix for built-in Ruby
11
+ Signal.trap("INT") {} # Suppress backtrace when exiting command
12
+
13
+ program :version, '0.1'
14
+ program :description, 'A command-line interface for generating and previewing passbook passes'
15
+
16
+ program :help, 'Author', 'Thomas Lauro <>, Lance Gleason <lgleason@polyglotprogramminginc.com>'
17
+ program :help, 'Website', 'https://github.com/frozon/passbook'
18
+ program :help_formatter, :compact
19
+
20
+ default_command :help
21
+
22
+ require 'commands/commands'
@@ -0,0 +1,62 @@
1
+ command :build do |c|
2
+ c.syntax = 'pk build [PASSNAME]'
3
+ c.summary = 'Creates a .pkpass archive'
4
+ c.description = ''
5
+
6
+ c.example 'description', 'pk archive mypass -o mypass.pkpass'
7
+ c.option '-w', '--wwdc_certificate /path/to/wwdc_cert.pem', 'Pass certificate'
8
+ c.option '-k', '--p12_key /path/to/cert.p12'
9
+ c.option '-c', '--p12_certificate /path/to/cert.p12'
10
+ c.option '-p', '--password password', 'certificate password'
11
+ c.option '-o', '--output /path/to/out.pkpass', '.pkpass output filepath'
12
+
13
+ c.action do |args, options|
14
+ determine_directory! unless @directory = args.first
15
+ validate_directory!
16
+
17
+ @filepath = options.output || "#{@directory}.pkpass"
18
+ validate_output_filepath!
19
+
20
+ @certificate = options.wwdc_certificate
21
+ validate_certificate!
22
+
23
+ @password = (options.password ? options.password : (ask("Enter certificate password:"){|q| q.echo = false}))
24
+
25
+ Passbook.configure do |passbook|
26
+ passbook.wwdc_cert = @certificate
27
+ passbook.p12_key = options.p12_key
28
+ passbook.p12_certificate = options.p12_certificate
29
+ passbook.p12_password = @password
30
+ end
31
+
32
+ assets = CommandUtils.get_assets @directory
33
+ pass_json = File.read(assets.delete(assets.detect{|file| File.basename(file) == 'pass.json'}))
34
+ pass = Passbook::PKPass.new(pass_json)
35
+ pass.addFiles assets
36
+
37
+ begin
38
+ pass_stream = pass.stream
39
+ pass_string = pass_stream.string
40
+
41
+ File.open(@filepath, 'w') do |f|
42
+ f.write pass_string
43
+ end
44
+ rescue OpenSSL::PKCS12::PKCS12Error => error
45
+ say_error "Error: #{error.message}"
46
+ say_warning "You may be getting this error because the certificate password is either incorrect or missing"
47
+ abort
48
+ rescue => error
49
+ say_error "Error: #{error.message}" and abort
50
+ end
51
+ end
52
+ end
53
+
54
+ alias_command :archive, :build
55
+ alias_command :b, :build
56
+
57
+ private
58
+
59
+ def validate_output_filepath!
60
+ say_error "Filepath required" and abort if @filepath.nil? or @filepath.empty?
61
+ say_error "#{@filepath} already exists" and abort if File.exist?(@filepath)
62
+ end
@@ -0,0 +1,31 @@
1
+ $:.push File.expand_path('../', __FILE__)
2
+
3
+ require 'commands/build'
4
+ require 'commands/generate'
5
+ #require 'commands/serve'
6
+ # this was added for testability because I couldn't figure out something better.
7
+
8
+ private
9
+
10
+ def determine_directory!
11
+ files = Dir['*/pass.json']
12
+ @directory ||= case files.length
13
+ when 0 then nil
14
+ when 1 then File.dirname(files.first)
15
+ else
16
+ @directory = choose "Select a directory:", *files.collect{|f| File.dirname(f)}
17
+ end
18
+ end
19
+
20
+ def validate_directory!
21
+ say_error "Missing argument" and abort if @directory.nil?
22
+ say_error "Directory #{@directory} does not exist" and abort unless File.directory?(@directory)
23
+ say_error "Directory #{@directory} is not a valid pass" and abort unless File.exist?(File.join(@directory, "pass.json"))
24
+ end
25
+
26
+ def validate_certificate!
27
+ say_error "Missing or invalid certificate file" and abort if @certificate.nil? or not File.exist?(@certificate)
28
+ end
29
+
30
+
31
+
@@ -0,0 +1,44 @@
1
+ require 'fileutils'
2
+
3
+ command :generate do |c|
4
+ c.syntax = 'pk generate PASSNAME'
5
+ c.summary = 'Generates a template pass directory'
6
+ c.description = ''
7
+
8
+ c.example 'description', 'pk generate mypass'
9
+ c.option '-T', '--type [boardingPass|coupon|eventTicket|storeCard|generic]', 'Type of pass'
10
+
11
+ c.action do |args, options|
12
+ @directory = args.first
13
+ @directory ||= ask "Enter a passbook name: "
14
+ say_error "Missing pass name" and abort if @directory.nil? or @directory.empty?
15
+ say_error "Directory #{@directory} already exists" and abort if File.directory?(@directory)
16
+ say_error "File exists at #{@directory}" and abort if File.exist?(@directory)
17
+
18
+ @type = options.type
19
+ determine_type! unless @type
20
+ validate_type!
21
+
22
+ FileUtils.mkdir_p @directory
23
+ FileUtils.cp File.join(CommandUtils.get_current_directory, '..', 'commands/templates', "#{@type}.json"), File.join(@directory, 'pass.json')
24
+ ['icon.png', 'icon@2x.png'].each do |file|
25
+ FileUtils.touch File.join(@directory, file)
26
+ end
27
+
28
+ say_ok "Pass generated in #{@directory}"
29
+ end
30
+ end
31
+
32
+ alias_command :new, :generate
33
+ alias_command :g, :generate
34
+
35
+ private
36
+
37
+ def determine_type!
38
+ @type ||= choose "Select a pass type", *Passbook::PKPass::TYPES
39
+ end
40
+
41
+ def validate_type!
42
+ say_error %{Invalid type: "#{@type}", expected one of: [#{Passbook::PKPass::TYPES.join(', ')}]} and abort unless Passbook::PKPass::TYPES.include?(@type)
43
+ end
44
+