passbook 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +40 -28
- data/README.md +9 -2
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/pk +22 -0
- data/lib/commands/build.rb +62 -0
- data/lib/commands/commands.rb +31 -0
- data/lib/commands/generate.rb +44 -0
- data/lib/commands/templates/boarding-pass.json +56 -0
- data/lib/commands/templates/coupon.json +33 -0
- data/lib/commands/templates/event-ticket.json +33 -0
- data/lib/commands/templates/generic.json +33 -0
- data/lib/commands/templates/store-card.json +33 -0
- data/lib/passbook/pkpass.rb +7 -4
- data/lib/rack/passbook_rack.rb +14 -10
- data/lib/utils/command_utils.rb +12 -0
- data/passbook.gemspec +29 -7
- data/spec/lib/commands/build_spec.rb +92 -0
- data/spec/lib/commands/commands_spec.rb +102 -0
- data/spec/lib/commands/commands_spec_helper.rb +69 -0
- data/spec/lib/commands/generate_spec.rb +72 -0
- data/spec/lib/passbook/pkpass_spec.rb +22 -3
- data/spec/lib/rack/passbook_rack_spec.rb +41 -4
- metadata +50 -27
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2U1MTYxOTRhMDk5YmJmYWRhMDcxNWEyZTU0MjAzMmIyZGU5ZGQ4NA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Yzk2Mzk1NzA5OTEzYTNkZTI2ZWM3YTZiYWZkMjBiNjE5ZDhiMzJkZA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OWVjYzI2ODJlNTcxNzdiZDcxMDBkOGJmMTdjMzJiNTBhMTJkOTBjZWExZTdm
|
10
|
+
ODdhMzkzNGYyOGRhM2VkNGJlNDg3YzBhNTY5NDhlMmM2YjI3ZGNkMWJmYWEy
|
11
|
+
OGJhN2E2ZjU4ZTU1YTNlOWUyMzcxYTQ1MDRiNmFhNzk0NjgyNmI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ODY0NDc4NWNhZWNhMjFiN2M0NjQwNjA3ODM0ZDlmNzRkYTIzZjdiYmRiNWY5
|
14
|
+
NjAyNDE1NGZhMzcxMmQ2OWJiZDk4OGIxNmRjMDdhMzZiMGNlNzk2ZjEzNjM2
|
15
|
+
YmE3YjM2OWQxYzM3MDA2MzFjNWJmYzE5YTIwNGY0NjFiYzQ0YmU=
|
data/Gemfile
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in passbook.gemspec
|
4
|
-
gem 'rubyzip'
|
4
|
+
gem 'rubyzip', '~> 1.0.0'
|
5
5
|
gem 'grocer'
|
6
|
+
gem 'commander'
|
7
|
+
gem 'terminal-table'
|
6
8
|
|
7
9
|
group :test, :development do
|
8
10
|
gem 'rack-test'
|
9
11
|
gem 'activesupport'
|
10
|
-
gem 'jeweler'
|
12
|
+
gem 'jeweler', :git => 'git://github.com/foxnewsnetwork/jeweler.git', :branch => 'ruby-2.0.0-ifying'
|
11
13
|
gem 'simplecov'
|
12
14
|
gem 'rspec'
|
13
15
|
gem 'rake'
|
data/Gemfile.lock
CHANGED
@@ -1,41 +1,51 @@
|
|
1
|
-
|
2
|
-
remote:
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/foxnewsnetwork/jeweler.git
|
3
|
+
revision: f05c62e168cfc29bd82cebe06df8fd11e1ef09ee
|
4
|
+
branch: ruby-2.0.0-ifying
|
3
5
|
specs:
|
4
|
-
activesupport (3.2.8)
|
5
|
-
i18n (~> 0.6)
|
6
|
-
multi_json (~> 1.0)
|
7
|
-
diff-lcs (1.1.3)
|
8
|
-
git (1.2.5)
|
9
|
-
grocer (0.3.0)
|
10
|
-
i18n (0.6.1)
|
11
6
|
jeweler (1.8.4)
|
12
|
-
bundler (~> 1.0)
|
7
|
+
bundler (~> 1.3.0.pre)
|
13
8
|
git (>= 1.2.5)
|
14
9
|
rake
|
15
10
|
rdoc
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activesupport (3.2.14)
|
16
|
+
i18n (~> 0.6, >= 0.6.4)
|
17
|
+
multi_json (~> 1.0)
|
18
|
+
commander (4.1.5)
|
19
|
+
highline (~> 1.6.11)
|
20
|
+
diff-lcs (1.2.4)
|
21
|
+
git (1.2.6)
|
22
|
+
grocer (0.4.1)
|
23
|
+
highline (1.6.19)
|
24
|
+
i18n (0.6.5)
|
25
|
+
json (1.8.0)
|
26
|
+
json (1.8.0-java)
|
27
|
+
multi_json (1.8.0)
|
28
|
+
rack (1.5.2)
|
20
29
|
rack-test (0.6.2)
|
21
30
|
rack (>= 1.0)
|
22
|
-
rake (
|
23
|
-
rdoc (
|
31
|
+
rake (10.1.0)
|
32
|
+
rdoc (4.0.1)
|
24
33
|
json (~> 1.4)
|
25
|
-
rspec (2.
|
26
|
-
rspec-core (~> 2.
|
27
|
-
rspec-expectations (~> 2.
|
28
|
-
rspec-mocks (~> 2.
|
29
|
-
rspec-core (2.
|
30
|
-
rspec-expectations (2.
|
31
|
-
diff-lcs (
|
32
|
-
rspec-mocks (2.
|
33
|
-
rubyzip (0.
|
34
|
+
rspec (2.14.1)
|
35
|
+
rspec-core (~> 2.14.0)
|
36
|
+
rspec-expectations (~> 2.14.0)
|
37
|
+
rspec-mocks (~> 2.14.0)
|
38
|
+
rspec-core (2.14.5)
|
39
|
+
rspec-expectations (2.14.3)
|
40
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
41
|
+
rspec-mocks (2.14.3)
|
42
|
+
rubyzip (1.0.0)
|
34
43
|
simplecov (0.7.1)
|
35
44
|
multi_json (~> 1.0)
|
36
45
|
simplecov-html (~> 0.7.1)
|
37
46
|
simplecov-html (0.7.1)
|
38
|
-
|
47
|
+
terminal-table (1.4.5)
|
48
|
+
yard (0.8.7.2)
|
39
49
|
|
40
50
|
PLATFORMS
|
41
51
|
java
|
@@ -43,11 +53,13 @@ PLATFORMS
|
|
43
53
|
|
44
54
|
DEPENDENCIES
|
45
55
|
activesupport
|
56
|
+
commander
|
46
57
|
grocer
|
47
|
-
jeweler
|
58
|
+
jeweler!
|
48
59
|
rack-test
|
49
60
|
rake
|
50
61
|
rspec
|
51
|
-
rubyzip
|
62
|
+
rubyzip (~> 1.0.0)
|
52
63
|
simplecov
|
64
|
+
terminal-table
|
53
65
|
yard
|
data/README.md
CHANGED
@@ -128,7 +128,7 @@ Your pass will need to have a field called 'webServiceURL' with the base url to
|
|
128
128
|
...
|
129
129
|
```
|
130
130
|
|
131
|
-
Passbook
|
131
|
+
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.
|
132
132
|
|
133
133
|
```
|
134
134
|
module Passbook
|
@@ -144,9 +144,13 @@ module Passbook
|
|
144
144
|
the_passes_serial_number = options['serialNumber']
|
145
145
|
the_devices_device_library_identifier = options['deviceLibraryIdentifier']
|
146
146
|
the_devices_push_token = options['pushToken']
|
147
|
+
the_pass_type_identifier = options["passTypeIdentifier"]
|
148
|
+
the_authentication_token = options['authToken']
|
147
149
|
|
148
150
|
# this is if the pass registered successfully
|
149
151
|
# change the code to 200 if the pass has already been registered
|
152
|
+
# 404 if pass not found for serialNubmer and passTypeIdentifier
|
153
|
+
# 401 if authorization failed
|
150
154
|
# or another appropriate code if something went wrong.
|
151
155
|
{:status => 201}
|
152
156
|
end
|
@@ -157,6 +161,7 @@ module Passbook
|
|
157
161
|
|
158
162
|
def self.passes_for_device(options)
|
159
163
|
device_library_identifier = options['deviceLibraryIdentifier']
|
164
|
+
passes_updated_since = options['passesUpdatedSince']
|
160
165
|
|
161
166
|
# the 'lastUpdated' uses integers values to tell passbook if the pass is
|
162
167
|
# more recent than the current one. If you just set it is the same value
|
@@ -171,6 +176,8 @@ module Passbook
|
|
171
176
|
# a solid unique pair of identifiers to identify the pass are
|
172
177
|
serial_number = options['serialNumber']
|
173
178
|
device_library_identifier = options['deviceLibraryIdentifier']
|
179
|
+
the_pass_type_identifier = options["passTypeIdentifier"]
|
180
|
+
the_authentication_token = options['authToken']
|
174
181
|
# return a status 200 to indicate that the pass was successfully unregistered.
|
175
182
|
{:status => 200}
|
176
183
|
end
|
@@ -187,7 +194,7 @@ module Passbook
|
|
187
194
|
|
188
195
|
# This is called whenever there is something from the update process that is a warning
|
189
196
|
# or error
|
190
|
-
def self.
|
197
|
+
def self.passbook_log(log)
|
191
198
|
# this is a VERY crude logging example. use the logger of your choice here.
|
192
199
|
p "#{Time.now} #{log}"
|
193
200
|
end
|
data/Rakefile
CHANGED
@@ -19,6 +19,7 @@ Jeweler::Tasks.new do |gem|
|
|
19
19
|
gem.description = %Q{This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it.}
|
20
20
|
gem.email = ['thomas@lauro.fr', 'lgleason@polyglotprogramminginc.com']
|
21
21
|
gem.authors = ['Thomas Lauro', 'Lance Gleason']
|
22
|
+
gem.executables = ['pk']
|
22
23
|
# dependencies defined in Gemfile
|
23
24
|
end
|
24
25
|
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
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
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
{
|
2
|
+
"formatVersion" : 1,
|
3
|
+
"passTypeIdentifier" : "pass.com.example.boarding-pass",
|
4
|
+
"description" : "Example Boarding Pass",
|
5
|
+
"teamIdentifier": "Example",
|
6
|
+
"organizationName": "Example",
|
7
|
+
"serialNumber" : "123456",
|
8
|
+
"foregroundColor": "#866B23",
|
9
|
+
"backgroundColor": "#FFD248",
|
10
|
+
"boardingPass" : {
|
11
|
+
"primaryFields" : [
|
12
|
+
{
|
13
|
+
"key" : "origin",
|
14
|
+
"label" : "Atlanta",
|
15
|
+
"value" : "ATL"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"key" : "destination",
|
19
|
+
"label" : "Johannesburg",
|
20
|
+
"value" : "JNB"
|
21
|
+
}
|
22
|
+
],
|
23
|
+
"secondaryFields" : [
|
24
|
+
{
|
25
|
+
"key" : "boarding-gate",
|
26
|
+
"label" : "Gate",
|
27
|
+
"value" : "F12"
|
28
|
+
}
|
29
|
+
],
|
30
|
+
"auxiliaryFields" : [
|
31
|
+
{
|
32
|
+
"key" : "seat",
|
33
|
+
"label" : "Seat",
|
34
|
+
"value" : "7A"
|
35
|
+
},
|
36
|
+
{
|
37
|
+
"key" : "passenger-name",
|
38
|
+
"label" : "Passenger",
|
39
|
+
"value" : "Honey Badger"
|
40
|
+
}
|
41
|
+
],
|
42
|
+
"transitType" : "PKTransitTypeAir",
|
43
|
+
"barcode" : {
|
44
|
+
"message" : "DL123",
|
45
|
+
"format" : "PKBarcodeFormatQR",
|
46
|
+
"messageEncoding" : "iso-8859-1"
|
47
|
+
},
|
48
|
+
"backFields" : [
|
49
|
+
{
|
50
|
+
"key" : "terms",
|
51
|
+
"label" : "Terms and Conditions",
|
52
|
+
"value" : "Valid for date of travel only"
|
53
|
+
}
|
54
|
+
]
|
55
|
+
}
|
56
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"formatVersion" : 1,
|
3
|
+
"passTypeIdentifier" : "pass.com.example.coupon",
|
4
|
+
"description" : "Example Coupon",
|
5
|
+
"teamIdentifier": "Example",
|
6
|
+
"organizationName": "Example",
|
7
|
+
"serialNumber" : "123456",
|
8
|
+
"foregroundColor": "#FFFFFF",
|
9
|
+
"backgroundColor": "#C799FF",
|
10
|
+
"generic" : {
|
11
|
+
"primaryFields" : [
|
12
|
+
|
13
|
+
],
|
14
|
+
"secondaryFields" : [
|
15
|
+
|
16
|
+
],
|
17
|
+
"auxiliaryFields" : [
|
18
|
+
|
19
|
+
],
|
20
|
+
"barcode" : {
|
21
|
+
"message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
|
22
|
+
"format" : "PKBarcodeFormatPDF417",
|
23
|
+
"messageEncoding" : "iso-8859-1"
|
24
|
+
},
|
25
|
+
"backFields" : [
|
26
|
+
{
|
27
|
+
"key" : "terms",
|
28
|
+
"label" : "Terms and Conditions",
|
29
|
+
"value" : "T's and C's Apply"
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
}
|