passbook 0.2.1 → 0.3.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.
- 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
|
+
}
|