passifier 0.0.2
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.
- data/Gemfile +15 -0
- data/LICENSE.md +22 -0
- data/README.md +125 -0
- data/Rakefile +48 -0
- data/examples/assets/background.png +0 -0
- data/examples/assets/background@2x.png +0 -0
- data/examples/assets/icon.png +0 -0
- data/examples/assets/icon@2x.png +0 -0
- data/examples/assets/logo.png +0 -0
- data/examples/assets/logo@2x.png +0 -0
- data/examples/assets/thumbnail.png +0 -0
- data/examples/assets/thumbnail@2x.png +0 -0
- data/examples/simple.rb +87 -0
- data/lib/passifier.rb +26 -0
- data/lib/passifier/archive.rb +52 -0
- data/lib/passifier/manifest.rb +37 -0
- data/lib/passifier/manifest_signature.rb +33 -0
- data/lib/passifier/pass.rb +82 -0
- data/lib/passifier/signing.rb +38 -0
- data/lib/passifier/spec.rb +31 -0
- data/lib/passifier/static_file.rb +24 -0
- data/lib/passifier/storage.rb +94 -0
- data/lib/passifier/url_source.rb +30 -0
- data/test/assets/background.png +0 -0
- data/test/assets/background@2x.png +0 -0
- data/test/assets/icon.png +0 -0
- data/test/assets/icon@2x.png +0 -0
- data/test/assets/logo.png +0 -0
- data/test/assets/logo@2x.png +0 -0
- data/test/assets/thumbnail.png +0 -0
- data/test/assets/thumbnail@2x.png +0 -0
- data/test/helper.rb +159 -0
- data/test/passifier/test_archive.rb +44 -0
- data/test/passifier/test_manifest.rb +27 -0
- data/test/passifier/test_manifest_signature.rb +13 -0
- data/test/passifier/test_pass.rb +45 -0
- data/test/passifier/test_signing.rb +47 -0
- data/test/passifier/test_spec.rb +20 -0
- data/test/passifier/test_static_file.rb +25 -0
- data/test/passifier/test_storage.rb +157 -0
- data/test/passifier/test_url_source.rb +30 -0
- data/test/test_passifier.rb +10 -0
- metadata +172 -0
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rdoc", "~> 3.12"
|
10
|
+
gem "bundler"
|
11
|
+
gem "jeweler", "~> 1.8.4"
|
12
|
+
end
|
13
|
+
|
14
|
+
gem "rake"
|
15
|
+
gem "rubyzip"
|
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) Paperless Post, http://www.paperlesspost.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Passifier
|
2
|
+
|
3
|
+
Generate Apple Passbook passes in Ruby
|
4
|
+
|
5
|
+
Passifier does most of the hard work and will easier allow you to automate generating pkpass files. You simply supply
|
6
|
+
|
7
|
+
* A Hash of metadata and layout (basically pass.json for those experienced)
|
8
|
+
* Image urls and paths
|
9
|
+
* The location of your key/certificate files
|
10
|
+
* Output path for the generated .pkpass file
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'passifier'
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Metadata and Layout
|
23
|
+
|
24
|
+
First, supply a bunch of pass information and styling. This will become the file pass.json within the pass archive. More inforomation on pass.json and creating a layout can be found at [developers.apple.com](https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Reference/PassKit_Bundle/Chapters/Introduction.html).
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
|
28
|
+
serial = "SO_SERIAL"
|
29
|
+
|
30
|
+
spec = {
|
31
|
+
"formatVersion" => 1,
|
32
|
+
"passTypeIdentifier" => "pass.example.example",
|
33
|
+
"teamIdentifier" => "ATEAMID",
|
34
|
+
"relevantDate" => "2012-07-30T14:19Z",
|
35
|
+
"organizationName" => "Example Inc.",
|
36
|
+
"serialNumber" => serial,
|
37
|
+
"description" => "this is a pass",
|
38
|
+
"labelColor" => "rgb(122, 16, 38)",
|
39
|
+
"backgroundColor" => "rgb(227, 227, 227)",
|
40
|
+
"foregroundColor" => "rgb(110,110,110)",
|
41
|
+
"generic" => {
|
42
|
+
"headerFields" => [
|
43
|
+
{
|
44
|
+
"key" => "date",
|
45
|
+
"label" => "Date",
|
46
|
+
"value" => "October 30th"
|
47
|
+
}
|
48
|
+
],
|
49
|
+
"primaryFields" => [
|
50
|
+
{
|
51
|
+
"key" => "title",
|
52
|
+
"label" => "",
|
53
|
+
"value" => "Passifier!"
|
54
|
+
}
|
55
|
+
],
|
56
|
+
"secondaryFields" => [
|
57
|
+
{
|
58
|
+
"key" => "host",
|
59
|
+
"label" => "Host",
|
60
|
+
"value" => "paperlesspost.com",
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
```
|
66
|
+
|
67
|
+
### Images
|
68
|
+
|
69
|
+
Notice that you can use either paths or urls here
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
|
73
|
+
images = {
|
74
|
+
"background.png" => "assets/background.png",
|
75
|
+
"background@2x.png" => "assets/background@2x.png",
|
76
|
+
"icon.png" => "assets/icon.png",
|
77
|
+
"icon@2x.png" => "assets/icon@2x.png",
|
78
|
+
"logo.png" => "http://i.imgur.com/WLUf6.png",
|
79
|
+
"logo@2x.png" => "http://i.imgur.com/mOpQo.png",
|
80
|
+
"thumbnail.png" => "assets/thumbnail.png",
|
81
|
+
"thumbnail@2x.png" => "assets/thumbnail@2x.png"
|
82
|
+
}
|
83
|
+
```
|
84
|
+
|
85
|
+
### Signing
|
86
|
+
|
87
|
+
Give Passifier some info about your .pem files.
|
88
|
+
|
89
|
+
(to-do: more info on obtaining certificates and creating pem files)
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
key_pem = "../test/assets/signing/key/key.pem"
|
93
|
+
pass_phrase = "somethingsomething"
|
94
|
+
cert_pem = "../test/assets/signing/certificate/certificate.pem"
|
95
|
+
```
|
96
|
+
|
97
|
+
### Generate!
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
# Create the signing
|
101
|
+
signing = Passifier::Signing.new(key_pem, pass_phrase, cert_pem)
|
102
|
+
|
103
|
+
# Finally, create the pass archive
|
104
|
+
Passifier::Pass.create_archive("readme.pkpass", serial, spec, images, signing)
|
105
|
+
```
|
106
|
+
|
107
|
+
Passifier will have created the file `readme.pkpass` for you. When opened in Passbook, that pass looks something like:
|
108
|
+
|
109
|
+

|
110
|
+
|
111
|
+
Find a similar example with some more explanation [here](http://github.com/paperlesspost/passifier/blob/master/examples/simple.rb)
|
112
|
+
|
113
|
+
## Contributing to Passifier
|
114
|
+
|
115
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
116
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
117
|
+
* Create an issue in the issue tracker
|
118
|
+
* Fork the project.
|
119
|
+
* Start a feature/bugfix branch; include the issue number in the branch name.
|
120
|
+
* Commit and push until you are happy with your contribution.
|
121
|
+
* Make sure to add tests for it. This is important so we don't break it in a future version unintentionally.
|
122
|
+
|
123
|
+
## Copyright
|
124
|
+
|
125
|
+
Copyright © 2012 Paperless Post. See LICENSE.md for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
$LOAD_PATH.unshift 'lib'
|
17
|
+
require "passifier"
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.version = Passifier::VERSION
|
20
|
+
gem.name = "passifier"
|
21
|
+
gem.homepage = "http://github.com/paperlesspost/passifier"
|
22
|
+
gem.license = "MIT"
|
23
|
+
gem.summary = %Q{Generate Apple Passbook passes in Ruby}
|
24
|
+
gem.description = %Q{Generate Apple Passbook passes in Ruby}
|
25
|
+
gem.email = "ari.russo@gmail.com"
|
26
|
+
gem.authors = ["Ari Russo"]
|
27
|
+
# dependencies defined in Gemfile
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rake/testtask'
|
32
|
+
Rake::TestTask.new(:test) do |test|
|
33
|
+
test.libs << 'lib' << 'test'
|
34
|
+
test.pattern = 'test/**/test_*.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
|
38
|
+
task :default => :test
|
39
|
+
|
40
|
+
require 'rdoc/task'
|
41
|
+
Rake::RDocTask.new do |rdoc|
|
42
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
43
|
+
|
44
|
+
rdoc.rdoc_dir = 'rdoc'
|
45
|
+
rdoc.title = "blah #{version}"
|
46
|
+
rdoc.rdoc_files.include('README*')
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
48
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.join( File.dirname( __FILE__ ), '../lib')
|
3
|
+
|
4
|
+
require "passifier"
|
5
|
+
|
6
|
+
# This is an example that will generate a simple pass file
|
7
|
+
|
8
|
+
# 1. Pass metadata and layout
|
9
|
+
# This is used to generate the pass.json file for the archive.
|
10
|
+
# See () for more information about pass.json usage
|
11
|
+
#
|
12
|
+
serial = "SO_SERIAL"
|
13
|
+
spec = {
|
14
|
+
"formatVersion" => 1,
|
15
|
+
"passTypeIdentifier" => "pass.example.example",
|
16
|
+
"teamIdentifier" => "ATEAMID",
|
17
|
+
"relevantDate" => "2012-07-30T14:19Z",
|
18
|
+
"organizationName" => "Example Inc.",
|
19
|
+
"serialNumber" => serial,
|
20
|
+
"description" => "this is a pass",
|
21
|
+
"labelColor" => "rgb(122, 16, 38)",
|
22
|
+
"backgroundColor" => "rgb(227, 227, 227)",
|
23
|
+
"foregroundColor" => "rgb(110,110,110)",
|
24
|
+
"generic" => {
|
25
|
+
"headerFields" => [
|
26
|
+
{
|
27
|
+
"key" => "date",
|
28
|
+
"label" => "Date",
|
29
|
+
"value" => "October 30th"
|
30
|
+
}
|
31
|
+
],
|
32
|
+
"primaryFields" => [
|
33
|
+
{
|
34
|
+
"key" => "title",
|
35
|
+
"label" => "",
|
36
|
+
"value" => "Passifier!"
|
37
|
+
}
|
38
|
+
],
|
39
|
+
"secondaryFields" => [
|
40
|
+
{
|
41
|
+
"key" => "host",
|
42
|
+
"label" => "Host",
|
43
|
+
"value" => "dev.paperlesspost.com",
|
44
|
+
}
|
45
|
+
]
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
#
|
50
|
+
# 2. Image assets
|
51
|
+
# notice that you can use either paths or urls here
|
52
|
+
#
|
53
|
+
images = {
|
54
|
+
"background.png" => "assets/background.png",
|
55
|
+
"background@2x.png" => "assets/background@2x.png",
|
56
|
+
"icon.png" => "assets/icon.png",
|
57
|
+
"icon@2x.png" => "assets/icon@2x.png",
|
58
|
+
"logo.png" => "http://i.imgur.com/WLUf6.png",
|
59
|
+
"logo@2x.png" => "http://i.imgur.com/mOpQo.png",
|
60
|
+
"thumbnail.png" => "assets/thumbnail.png",
|
61
|
+
"thumbnail@2x.png" => "assets/thumbnail@2x.png"
|
62
|
+
}
|
63
|
+
|
64
|
+
#
|
65
|
+
# 3. Signing settings
|
66
|
+
# Replace with your own paths/password if you plan on running this example
|
67
|
+
#
|
68
|
+
key_pem = "../test/assets/signing/key/key.pem"
|
69
|
+
pass_phrase = File.read("../test/assets/signing/pass_phrase.txt").strip.lstrip # you can just replace this with a string if you want
|
70
|
+
cert_pem = "../test/assets/signing/certificate/certificate.pem"
|
71
|
+
|
72
|
+
#
|
73
|
+
#
|
74
|
+
# Now, generate the pass!
|
75
|
+
#
|
76
|
+
#
|
77
|
+
|
78
|
+
# Create the signing
|
79
|
+
#
|
80
|
+
signing = Passifier::Signing.new(key_pem, pass_phrase, cert_pem)
|
81
|
+
|
82
|
+
# Create the pass archive
|
83
|
+
output_file = "./simple.pkpass"
|
84
|
+
Passifier::Pass.create_archive(output_file, serial, spec, images, signing)
|
85
|
+
|
86
|
+
# Finished!
|
87
|
+
puts "Finished generating the pass archive: #{output_file}"
|
data/lib/passifier.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# libs
|
4
|
+
require "forwardable"
|
5
|
+
require "json"
|
6
|
+
require "net/http"
|
7
|
+
require "zip/zip"
|
8
|
+
|
9
|
+
# modules
|
10
|
+
require "passifier/signing"
|
11
|
+
|
12
|
+
# classes
|
13
|
+
require "passifier/archive"
|
14
|
+
require "passifier/manifest"
|
15
|
+
require "passifier/manifest_signature"
|
16
|
+
require "passifier/pass"
|
17
|
+
require "passifier/spec"
|
18
|
+
require "passifier/static_file"
|
19
|
+
require "passifier/storage"
|
20
|
+
require "passifier/url_source"
|
21
|
+
|
22
|
+
module Passifier
|
23
|
+
|
24
|
+
VERSION = "0.0.2"
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Passifier
|
4
|
+
|
5
|
+
# Represents the .pkpass archive file for the pass. Despite the extension, a .pkpass file is actually
|
6
|
+
# a zip archive.
|
7
|
+
class Archive
|
8
|
+
|
9
|
+
attr_reader :assets,
|
10
|
+
:id,
|
11
|
+
:path
|
12
|
+
|
13
|
+
# @param [String] path The archive path
|
14
|
+
# @param [String] id An ID to represent the Archive
|
15
|
+
def initialize(path, id, assets)
|
16
|
+
@assets = assets
|
17
|
+
@path = path
|
18
|
+
@id = id
|
19
|
+
end
|
20
|
+
|
21
|
+
# The raw data of this archive file
|
22
|
+
# @return [String] The raw data of this archive file
|
23
|
+
def data
|
24
|
+
File.open(@path, 'rb') {|file| file.read } unless @path.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy
|
28
|
+
unless @storage.nil?
|
29
|
+
@storage.cleanup
|
30
|
+
@storage.remove_zip(@path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Write the zip archive to disk
|
35
|
+
# @param [Hash] options The options to store an Archive with.
|
36
|
+
# @option opts [String] :scratch_directory The directory to use for temp files while creating the archive.
|
37
|
+
# (not to be confused with the archive path)
|
38
|
+
def store(options = {})
|
39
|
+
scratch_dir = options[:scratch_directory] || "/tmp/passkit/#{@id}"
|
40
|
+
@storage = Storage.new(scratch_dir, @assets)
|
41
|
+
@storage.store
|
42
|
+
@storage.zip(@path)
|
43
|
+
@storage.cleanup
|
44
|
+
end
|
45
|
+
alias_method :save, :store
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Passifier
|
4
|
+
|
5
|
+
class Manifest
|
6
|
+
|
7
|
+
attr_reader :hash
|
8
|
+
alias_method :to_hash, :hash
|
9
|
+
|
10
|
+
# @param [Array<Passifier::StaticFile, Passifier::UrlSource>] images_files The image files to populate the manifest with
|
11
|
+
# @param [Passifier::Signing] signing The signing to sign the images and generate the digests with
|
12
|
+
def initialize(image_files, signing)
|
13
|
+
@image_files = image_files
|
14
|
+
populate_content(signing)
|
15
|
+
end
|
16
|
+
|
17
|
+
def filename
|
18
|
+
"manifest.json"
|
19
|
+
end
|
20
|
+
|
21
|
+
def content
|
22
|
+
to_hash.to_json
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Convert the image files into signed SHA1 digests for use in the manifest file
|
28
|
+
# @return [String] The resulting contents of the manifest file (aka Passifier::Manifest#content)
|
29
|
+
def populate_content(signing)
|
30
|
+
@hash = {}
|
31
|
+
@image_files.each { |file| @hash[file.name] = signing.sha(file.content) }
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Passifier
|
4
|
+
|
5
|
+
# Represents the signing of the manifest file aka "signature" file in the archive
|
6
|
+
class ManifestSignature
|
7
|
+
|
8
|
+
attr_reader :content, :manifest
|
9
|
+
|
10
|
+
# @param [Passifier::Manifest] manifest The Manifest to base the signature on
|
11
|
+
def initialize(manifest, signing)
|
12
|
+
@manifest = manifest
|
13
|
+
populate_content(signing)
|
14
|
+
end
|
15
|
+
|
16
|
+
def filename
|
17
|
+
"signature"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Sign the contents of the Manifest
|
23
|
+
# @param [Passifier::Signing] signing The signing to use to generate the signature file
|
24
|
+
# @return [String] The content of the manifest signature file aka "signature" in the archive
|
25
|
+
def populate_content(signing)
|
26
|
+
@content = signing.sign(manifest.content)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|