passbook 0.2.0

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.
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "passbook-ios"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Thomas Lauro", "Lance Gleason"]
12
+ s.date = "2013-01-06"
13
+ s.description = "This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it."
14
+ s.email = ["thomas@lauro.fr", "lgleason@polyglotprogramminginc.com"]
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".travis.yml",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/passbook.rb",
28
+ "lib/passbook/pkpass.rb",
29
+ "lib/passbook/push_notification.rb",
30
+ "lib/passbook/version.rb",
31
+ "lib/rack/passbook_rack.rb",
32
+ "lib/rails/generators/passbook/config/config_generator.rb",
33
+ "lib/rails/generators/passbook/config/templates/initializer.rb",
34
+ "passbook-ios.gemspec",
35
+ "passbook.gemspec",
36
+ "spec/data/icon.png",
37
+ "spec/data/icon@2x.png",
38
+ "spec/data/logo.png",
39
+ "spec/data/logo@2x.png",
40
+ "spec/lib/passbook/pkpass_spec.rb",
41
+ "spec/lib/passbook/push_notification_spec.rb",
42
+ "spec/lib/rack/passbook_rack_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+ s.homepage = "http://github.com/frozon/passbook"
46
+ s.licenses = ["MIT"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = "1.8.24"
49
+ s.summary = "A IOS Passbook generator."
50
+
51
+ if s.respond_to? :specification_version then
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ s.add_runtime_dependency(%q<rubyzip>, [">= 0"])
56
+ s.add_runtime_dependency(%q<grocer>, [">= 0"])
57
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
58
+ s.add_development_dependency(%q<activesupport>, [">= 0"])
59
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
60
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
61
+ s.add_development_dependency(%q<rspec>, [">= 0"])
62
+ s.add_development_dependency(%q<rake>, [">= 0"])
63
+ s.add_development_dependency(%q<yard>, [">= 0"])
64
+ else
65
+ s.add_dependency(%q<rubyzip>, [">= 0"])
66
+ s.add_dependency(%q<grocer>, [">= 0"])
67
+ s.add_dependency(%q<rack-test>, [">= 0"])
68
+ s.add_dependency(%q<activesupport>, [">= 0"])
69
+ s.add_dependency(%q<jeweler>, [">= 0"])
70
+ s.add_dependency(%q<simplecov>, [">= 0"])
71
+ s.add_dependency(%q<rspec>, [">= 0"])
72
+ s.add_dependency(%q<rake>, [">= 0"])
73
+ s.add_dependency(%q<yard>, [">= 0"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<rubyzip>, [">= 0"])
77
+ s.add_dependency(%q<grocer>, [">= 0"])
78
+ s.add_dependency(%q<rack-test>, [">= 0"])
79
+ s.add_dependency(%q<activesupport>, [">= 0"])
80
+ s.add_dependency(%q<jeweler>, [">= 0"])
81
+ s.add_dependency(%q<simplecov>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, [">= 0"])
83
+ s.add_dependency(%q<rake>, [">= 0"])
84
+ s.add_dependency(%q<yard>, [">= 0"])
85
+ end
86
+ end
87
+
data/passbook.gemspec ADDED
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "passbook"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Thomas Lauro", "Lance Gleason"]
12
+ s.date = "2013-02-23"
13
+ s.description = "This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it."
14
+ s.email = ["thomas@lauro.fr", "lgleason@polyglotprogramminginc.com"]
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".travis.yml",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/passbook.rb",
28
+ "lib/passbook/pkpass.rb",
29
+ "lib/passbook/push_notification.rb",
30
+ "lib/passbook/version.rb",
31
+ "lib/rack/passbook_rack.rb",
32
+ "lib/rails/generators/passbook/config/config_generator.rb",
33
+ "lib/rails/generators/passbook/config/templates/initializer.rb",
34
+ "passbook-ios.gemspec",
35
+ "passbook.gemspec",
36
+ "spec/data/icon.png",
37
+ "spec/data/icon@2x.png",
38
+ "spec/data/logo.png",
39
+ "spec/data/logo@2x.png",
40
+ "spec/lib/passbook/pkpass_spec.rb",
41
+ "spec/lib/passbook/push_notification_spec.rb",
42
+ "spec/lib/rack/passbook_rack_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+ s.homepage = "http://github.com/frozon/passbook"
46
+ s.licenses = ["MIT"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = "1.8.24"
49
+ s.summary = "A IOS Passbook generator."
50
+
51
+ if s.respond_to? :specification_version then
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ s.add_runtime_dependency(%q<rubyzip>, [">= 0"])
56
+ s.add_runtime_dependency(%q<grocer>, [">= 0"])
57
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
58
+ s.add_development_dependency(%q<activesupport>, [">= 0"])
59
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
60
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
61
+ s.add_development_dependency(%q<rspec>, [">= 0"])
62
+ s.add_development_dependency(%q<rake>, [">= 0"])
63
+ s.add_development_dependency(%q<yard>, [">= 0"])
64
+ else
65
+ s.add_dependency(%q<rubyzip>, [">= 0"])
66
+ s.add_dependency(%q<grocer>, [">= 0"])
67
+ s.add_dependency(%q<rack-test>, [">= 0"])
68
+ s.add_dependency(%q<activesupport>, [">= 0"])
69
+ s.add_dependency(%q<jeweler>, [">= 0"])
70
+ s.add_dependency(%q<simplecov>, [">= 0"])
71
+ s.add_dependency(%q<rspec>, [">= 0"])
72
+ s.add_dependency(%q<rake>, [">= 0"])
73
+ s.add_dependency(%q<yard>, [">= 0"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<rubyzip>, [">= 0"])
77
+ s.add_dependency(%q<grocer>, [">= 0"])
78
+ s.add_dependency(%q<rack-test>, [">= 0"])
79
+ s.add_dependency(%q<activesupport>, [">= 0"])
80
+ s.add_dependency(%q<jeweler>, [">= 0"])
81
+ s.add_dependency(%q<simplecov>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, [">= 0"])
83
+ s.add_dependency(%q<rake>, [">= 0"])
84
+ s.add_dependency(%q<yard>, [">= 0"])
85
+ end
86
+ end
87
+
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Passbook do
4
+
5
+ let (:content) {{
6
+ :formatVersion => 1,
7
+ :passTypeIdentifier => "pass.passbook.test",
8
+ :serialNumber => "001",
9
+ :teamIdentifier => ENV['APPLE_TEAM_ID'],
10
+ :relevantDate => "2012-10-02",
11
+ :locations => [ #TODO
12
+ {
13
+ :longitude => 2.35403,
14
+ :latitude => 48.893855
15
+ }
16
+ ],
17
+ :organizationName => "WorldCo",
18
+ :description => "description",
19
+ :foregroundColor => "rgb(227,210,18)",
20
+ :backgroundColor => "rgb(60, 65, 76)",
21
+ :logoText => "Event",
22
+ :eventTicket => {
23
+ :primaryFields => [
24
+ {
25
+ :key => "date",
26
+ :label => "DATE",
27
+ :value => "date"
28
+ }
29
+ ],
30
+ :backFields => [
31
+ {
32
+ :key => "description",
33
+ :label => "DESCRIPTION",
34
+ :value => "description"
35
+ },
36
+ {
37
+ :key => "aboutUs",
38
+ :label => "MORE",
39
+ :value => "about us"
40
+ }
41
+ ]
42
+ }
43
+ }}
44
+
45
+ let (:pass) {Passbook::PKPass.new content.to_json}
46
+
47
+ context 'signatures' do
48
+ before do
49
+ Passbook.should_receive(:p12_password).and_return 'password'
50
+ end
51
+
52
+ context 'p12_cert_and_key' do
53
+ context 'pem p12 certs' do
54
+ before do
55
+ Passbook.should_receive(:p12_key).twice.and_return 'my_p12_key'
56
+ Passbook.should_receive(:p12_certificate).and_return 'my_p12_certificate'
57
+ File.should_receive(:read).with('my_p12_key').and_return 'my_p12_key_file'
58
+ File.should_receive(:read).with('my_p12_certificate').and_return 'my_p12_certificate_file'
59
+ OpenSSL::PKey::RSA.should_receive(:new).with('my_p12_key_file', 'password').and_return 'my_rsa_key'
60
+ OpenSSL::X509::Certificate.should_receive(:new).with('my_p12_certificate_file').and_return 'my_ssl_p12_cert'
61
+ end
62
+
63
+ subject {pass.get_p12_cert_and_key}
64
+ its([:key]) {should eq 'my_rsa_key'}
65
+ its([:cert]) {should eq 'my_ssl_p12_cert'}
66
+ end
67
+
68
+ context 'p12 files' do
69
+ let (:p12) { double('OpenSSL::PKCS12') }
70
+ let (:final_hash) {{:key => 'my_final_p12_key', :cert => 'my_final_p12_cert'}}
71
+ before do
72
+ p12.should_receive(:key).and_return final_hash[:key]
73
+ p12.should_receive(:certificate).and_return final_hash[:cert]
74
+ Passbook.should_receive(:p12_key).and_return nil
75
+ Passbook.should_receive(:p12_cert).and_return 'my_p12_cert'
76
+ File.should_receive(:read).with('my_p12_cert').and_return 'my_p12_cert_file'
77
+ OpenSSL::PKCS12.should_receive(:new).with('my_p12_cert_file', 'password').and_return p12
78
+ end
79
+
80
+ subject {pass.get_p12_cert_and_key}
81
+ its([:key]) {should eq final_hash[:key]}
82
+ its([:cert]) {should eq final_hash[:cert]}
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'outputs' do
88
+ let (:base_path) {'spec/data'}
89
+ let (:entries) {["pass.json", "manifest.json", "signature", "icon.png", "icon@2x.png", "logo.png", "logo@2x.png"]}
90
+
91
+ before :each do
92
+ pass.addFiles ["#{base_path}/icon.png","#{base_path}/icon@2x.png","#{base_path}/logo.png","#{base_path}/logo@2x.png"]
93
+ pass.should_receive(:createSignature).and_return('Signed by the Honey Badger')
94
+ @file_entries = []
95
+ Zip::ZipInputStream::open(zip_path) {|io|
96
+ while (entry = io.get_next_entry)
97
+ @file_entries << entry.name
98
+ end
99
+ }
100
+ end
101
+
102
+ context 'zip file' do
103
+ let(:zip_path) {pass.file.path}
104
+
105
+ subject {entries}
106
+ it {should eq @file_entries}
107
+ end
108
+
109
+ context 'StringIO' do
110
+ let (:temp_file) {Tempfile.new("pass.pkpass")}
111
+ let (:zip_path) {
112
+ zip_out = pass.stream
113
+ zip_out.class.should eq(Class::StringIO)
114
+ #creating file, re-reading zip to see if correctly formed
115
+ temp_file.write zip_out.string
116
+ temp_file.close
117
+ temp_file.path
118
+ }
119
+
120
+ subject {entries}
121
+ it {should eq @file_entries}
122
+
123
+ after do
124
+ temp_file.delete
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'grocer'
3
+
4
+ describe Passbook::PushNotification do
5
+
6
+ context 'send notification' do
7
+ let(:grocer_pusher) {double 'Grocer'}
8
+ let(:notification) {double 'Grocer::Notification'}
9
+ let(:notification_settings) {{:certificate => './notification_cert.pem', :gateway => 'honeybadger.apple.com'}}
10
+
11
+ before :each do
12
+ Passbook.should_receive(:notification_cert).and_return './notification_cert.pem'
13
+ Grocer::PassbookNotification.should_receive(:new).with(:device_token => 'my token').and_return notification
14
+ grocer_pusher.should_receive(:push).with(notification).and_return 55
15
+ Grocer.should_receive(:pusher).with(notification_settings).and_return grocer_pusher
16
+ Passbook.should_receive(:notification_gateway).and_return 'honeybadger.apple.com'
17
+ end
18
+
19
+ subject {Passbook::PushNotification.send_notification('my token')}
20
+ it {should eq 55}
21
+ end
22
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::PassbookRack do
4
+
5
+ let(:register_delete_path) {'/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations/pass.com.polyglotprogramminginc.testpass/27-1'}
6
+ let(:register_delete_params) {{'deviceLibraryIdentifier' => 'fe772e610be3efafb65ed77772ca311a',
7
+ 'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass',
8
+ 'serialNumber' => '27-1'}}
9
+ let(:passes_for_device_path) {'/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations/pass.com.polyglotprogramminginc.testpass'}
10
+ let(:passes_for_device_params) {{'deviceLibraryIdentifier' => 'fe772e610be3efafb65ed77772ca311a',
11
+ 'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass'}}
12
+ let(:latest_pass_path) {'/v1/passes/pass.com.polyglotprogramminginc.testpass/27-1'}
13
+ let(:latest_pass_params) {{'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass',
14
+ 'serialNumber' => '27-1'}}
15
+ let(:log_path) {'/v1/log'}
16
+ let(:push_token) {"8c56f2e787d9c089963960ace834bc2875e3f0cf7745da5b98d58bc6be05b4dc"}
17
+
18
+ context 'find method' do
19
+ let(:passbook_rack) {Rack::PassbookRack.new nil}
20
+
21
+ shared_examples_for 'a method that can handle non passbook urls' do
22
+ context 'incomplete passbook api path' do
23
+ subject {passbook_rack.find_method('/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations')}
24
+ it {should eq nil}
25
+ end
26
+
27
+ context 'no version api path' do
28
+ subject {passbook_rack.find_method('/devices/fe772e610be3efafb65ed77772ca311a/registrations')}
29
+ it {should eq nil}
30
+ end
31
+
32
+ context 'no devices api path' do
33
+ subject {passbook_rack.find_method('/v1/fe772e610be3efafb65ed77772ca311a/registrations')}
34
+ it {should eq nil}
35
+ end
36
+
37
+ context 'no registrations api path' do
38
+ subject {passbook_rack.find_method('/v1/devices/fe772e610be3efafb65ed77772ca311a')}
39
+ it {should eq nil}
40
+ end
41
+ end
42
+
43
+ context 'device register delete' do
44
+ context 'a valid path' do
45
+ subject {passbook_rack.find_method(register_delete_path)}
46
+ its([:method]) {should eq 'device_register_delete'}
47
+ its([:params]) {should eq(register_delete_params) }
48
+ end
49
+
50
+ it_behaves_like 'a method that can handle non passbook urls'
51
+
52
+ end
53
+
54
+ context 'passes for device' do
55
+ subject {passbook_rack.find_method(passes_for_device_path)}
56
+ its([:method]) {should eq 'passes_for_device'}
57
+ its([:params]) {should eq passes_for_device_params }
58
+ end
59
+
60
+ context 'latest pass' do
61
+ subject {passbook_rack.find_method(latest_pass_path)}
62
+ its([:method]) {should eq 'latest_pass'}
63
+ its([:params]) {should eq(latest_pass_params) }
64
+ end
65
+
66
+ context 'latest pass' do
67
+ subject {passbook_rack.find_method(log_path)}
68
+ its([:method]) {should eq 'log'}
69
+ end
70
+ end
71
+
72
+ context 'rack middleware' do
73
+
74
+ context 'register pass' do
75
+ before do
76
+ Passbook::PassbookNotification.should_receive(:register_pass).
77
+ with(register_delete_params.merge!('pushToken' => push_token)).and_return({:status => 201})
78
+ post register_delete_path, {"pushToken" => push_token}.to_json
79
+ end
80
+
81
+ subject {last_response}
82
+ its(:status) {should eq 201}
83
+ end
84
+
85
+ context 'passes for device' do
86
+ context 'with passes' do
87
+ let(:passes_for_device_response) {{'last_updated' => 1, 'serial_numbers' => [343, 234]}}
88
+ before do
89
+ Passbook::PassbookNotification.should_receive(:passes_for_device).
90
+ with(passes_for_device_params).and_return(passes_for_device_response)
91
+ get passes_for_device_path
92
+ end
93
+
94
+ context 'status' do
95
+ subject {last_response.status}
96
+ it {should eq 200}
97
+ end
98
+
99
+ context 'body' do
100
+ subject{JSON.parse(last_response.body)}
101
+ it {should eq passes_for_device_response}
102
+ end
103
+ end
104
+
105
+ context 'without passes' do
106
+ before do
107
+ Passbook::PassbookNotification.should_receive(:passes_for_device).
108
+ with(passes_for_device_params).and_return(nil)
109
+ get passes_for_device_path
110
+ end
111
+
112
+ context 'status' do
113
+ subject {last_response.status}
114
+ it {should eq 204}
115
+ end
116
+ end
117
+ end
118
+
119
+ context 'get latest pass' do
120
+ context 'valid pass' do
121
+ let(:raw_pass) {'some url encoded text'}
122
+
123
+ before do
124
+ Passbook::PassbookNotification.should_receive(:latest_pass).with(latest_pass_params).
125
+ and_return(raw_pass)
126
+ get latest_pass_path
127
+ end
128
+
129
+ subject {last_response}
130
+ its(:status) {should eq 200}
131
+ its(:header) {should eq({'Content-Type' => 'application/vnd.apple.pkpass',
132
+ 'Content-Disposition' => 'attachment', 'filename' => '27-1.pkpass', 'Content-Length' => '21'})}
133
+ its(:body) {should eq raw_pass}
134
+ end
135
+
136
+ context 'no pass' do
137
+ before do
138
+ Passbook::PassbookNotification.should_receive(:latest_pass).with(latest_pass_params).
139
+ and_return(nil)
140
+ get latest_pass_path
141
+ end
142
+
143
+ subject {last_response}
144
+ its(:status) {should eq 204}
145
+ end
146
+ end
147
+
148
+ context 'unregister pass' do
149
+ before do
150
+ Passbook::PassbookNotification.should_receive(:unregister_pass).
151
+ with(register_delete_params).and_return({:status => 200})
152
+ delete register_delete_path
153
+ end
154
+
155
+ subject {last_response}
156
+ its(:status) {should eq 200}
157
+ end
158
+
159
+ context 'log' do
160
+ let(:log_params) {{'logs' => ['some error']}}
161
+ before do
162
+ Passbook::PassbookNotification.should_receive(:log).
163
+ with(log_params)
164
+ post log_path, log_params.to_json
165
+ end
166
+
167
+ subject {last_response}
168
+ its(:status) {should eq 200}
169
+ end
170
+
171
+ context 'non passbook requests' do
172
+ before do
173
+ get '/foo'
174
+ end
175
+
176
+ subject {last_response}
177
+ its(:status) {should eq 200}
178
+ its(:body) {should eq 'test app'}
179
+ end
180
+ end
181
+
182
+ end
183
+
184
+ require 'rack/test'
185
+ include Rack::Test::Methods
186
+
187
+ def app
188
+ test_app = lambda do |env|
189
+ [200, {}, 'test app']
190
+ end
191
+
192
+ Rack::PassbookRack.new test_app
193
+ end
194
+
195
+ class Passbook::PassbookNotification
196
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'json'
4
+ require 'simplecov'
5
+ require 'grocer/passbook_notification'
6
+ SimpleCov.start
7
+
8
+ Dir['lib/passbook/**/*.rb'].each {|f| require File.join(File.dirname(__FILE__), '..', f.gsub(/.rb/, ''))}
9
+ Dir['lib/rack/**/*.rb'].each {|f| require File.join(File.dirname(__FILE__), '..', f.gsub(/.rb/, ''))}