du-passifier 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/LICENSE.md +22 -0
- data/README.md +133 -0
- data/Rakefile +57 -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 +86 -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 +169 -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,133 @@
|
|
1
|
+
# Passifier
|
2
|
+
|
3
|
+
Generate Apple Passbook passes in Ruby
|
4
|
+
|
5
|
+
Passifier does most of the hard work and will more easily allow you to automate generating pkpass files. You simply supply
|
6
|
+
|
7
|
+
* A Hash of metadata and layout (the contents of pass.json for those experienced)
|
8
|
+
* Image URLs and paths
|
9
|
+
* The location of your key and certificate .pem files
|
10
|
+
* Output path where you'd like 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 information 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_number = "SO_SERIAL"
|
29
|
+
|
30
|
+
spec = {
|
31
|
+
"formatVersion": 1,
|
32
|
+
"passTypeIdentifier": "pass.example.example",
|
33
|
+
"teamIdentifier": "METS",
|
34
|
+
"relevantDate": "2012-07-30T14:19Z",
|
35
|
+
"organizationName": "Example Inc.",
|
36
|
+
"serialNumber": serial_number,
|
37
|
+
"description": "The example pass from README.md",
|
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
|
+
Specify a Hash of images. Notice that you can use either paths or urls here.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
|
73
|
+
assets = {
|
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 = "path/to/a/key.pem"
|
93
|
+
pass_phrase = "somethingsomething"
|
94
|
+
cert_pem = "path/to/a/certificate.pem"
|
95
|
+
|
96
|
+
# Create a Signing object
|
97
|
+
signing = Passifier::Signing.new(key_pem, pass_phrase, cert_pem)
|
98
|
+
```
|
99
|
+
|
100
|
+
### Generate!
|
101
|
+
|
102
|
+
Now it's time to create your pass.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Passifier::Pass.generate("readme.pkpass", serial_number, spec, assets, signing)
|
106
|
+
```
|
107
|
+
|
108
|
+
Passifier will have created the file `readme.pkpass` for you. When opened in Passbook, that pass looks something like:
|
109
|
+
|
110
|
+
![image](http://i.imgur.com/fooaB.jpg)
|
111
|
+
|
112
|
+
## Further Reading
|
113
|
+
|
114
|
+
* Find a similar example with some more explanation [here](http://github.com/paperlesspost/passifier/blob/master/examples/simple.rb)
|
115
|
+
* Read a blog post about Passifier [here]()
|
116
|
+
|
117
|
+
## Documentation
|
118
|
+
|
119
|
+
* [rubydoc](http://rubydoc.info/github/paperlesspost/passifier)
|
120
|
+
|
121
|
+
## Contributing to Passifier
|
122
|
+
|
123
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
124
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
125
|
+
* Create an issue in the issue tracker
|
126
|
+
* Fork the project.
|
127
|
+
* Start a feature/bugfix branch; include the issue number in the branch name.
|
128
|
+
* Commit and push until you are happy with your contribution.
|
129
|
+
* Make sure to add tests for it. This is important so we don't break it in a future version unintentionally.
|
130
|
+
|
131
|
+
## Copyright
|
132
|
+
|
133
|
+
Copyright © 2012 Paperless Post. See LICENSE.md for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
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
|
+
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
begin
|
18
|
+
Jeweler::Tasks.new do |gem|
|
19
|
+
$LOAD_PATH.unshift 'lib'
|
20
|
+
require "passifier"
|
21
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
22
|
+
gem.version = Passifier::VERSION
|
23
|
+
gem.name = "passifier"
|
24
|
+
gem.homepage = "http://github.com/paperlesspost/passifier"
|
25
|
+
gem.license = "MIT"
|
26
|
+
gem.summary = %Q{Generate Apple Passbook passes in Ruby}
|
27
|
+
gem.description = %Q{Generate Apple Passbook passes in Ruby}
|
28
|
+
gem.email = "ari.russo@gmail.com"
|
29
|
+
gem.authors = ["Ari Russo"]
|
30
|
+
# dependencies defined in Gemfile
|
31
|
+
end
|
32
|
+
|
33
|
+
Jeweler::RubygemsDotOrgTasks.new
|
34
|
+
Jeweler::GemcutterTasks.new
|
35
|
+
rescue LoadError
|
36
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
require 'rake/testtask'
|
41
|
+
Rake::TestTask.new(:test) do |test|
|
42
|
+
test.libs << 'lib' << 'test'
|
43
|
+
test.pattern = 'test/**/test_*.rb'
|
44
|
+
test.verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
require 'rdoc/task'
|
50
|
+
Rake::RDocTask.new do |rdoc|
|
51
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "blah #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
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,86 @@
|
|
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
|
+
# Create a signing object
|
73
|
+
signing = Passifier::Signing.new(key_pem, pass_phrase, cert_pem)
|
74
|
+
|
75
|
+
#
|
76
|
+
#
|
77
|
+
# Now, generate the pass!
|
78
|
+
#
|
79
|
+
#
|
80
|
+
|
81
|
+
# Create the pass archive
|
82
|
+
output_file = "./simple.pkpass"
|
83
|
+
Passifier::Pass.generate(output_file, serial, spec, images, signing)
|
84
|
+
|
85
|
+
# Finished!
|
86
|
+
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.4"
|
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>] asset_files The asset 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(asset_files, signing)
|
13
|
+
@asset_files = asset_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
|
+
@asset_files.each { |file| @hash[file.name] = signing.sha(file.content) }
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|