passbook-ios 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -3
- data/Gemfile +2 -0
- data/Gemfile.lock +6 -0
- data/README.md +119 -4
- data/VERSION +1 -1
- data/lib/passbook.rb +3 -1
- data/lib/passbook/push_notification.rb +10 -0
- data/lib/rack/passbook_rack.rb +94 -0
- data/passbook-ios.gemspec +12 -2
- data/spec/lib/passbook/push_notification_spec.rb +22 -0
- data/spec/lib/rack/passbook_rack_spec.rb +196 -0
- data/spec/spec_helper.rb +2 -0
- metadata +39 -3
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -6,6 +6,7 @@ GEM
|
|
6
6
|
multi_json (~> 1.0)
|
7
7
|
diff-lcs (1.1.3)
|
8
8
|
git (1.2.5)
|
9
|
+
grocer (0.3.0)
|
9
10
|
i18n (0.6.1)
|
10
11
|
jeweler (1.8.4)
|
11
12
|
bundler (~> 1.0)
|
@@ -15,6 +16,9 @@ GEM
|
|
15
16
|
json (1.7.5)
|
16
17
|
json (1.7.5-java)
|
17
18
|
multi_json (1.3.6)
|
19
|
+
rack (1.4.1)
|
20
|
+
rack-test (0.6.2)
|
21
|
+
rack (>= 1.0)
|
18
22
|
rake (0.9.2.2)
|
19
23
|
rdoc (3.12)
|
20
24
|
json (~> 1.4)
|
@@ -39,7 +43,9 @@ PLATFORMS
|
|
39
43
|
|
40
44
|
DEPENDENCIES
|
41
45
|
activesupport
|
46
|
+
grocer
|
42
47
|
jeweler
|
48
|
+
rack-test
|
43
49
|
rake
|
44
50
|
rspec
|
45
51
|
rubyzip
|
data/README.md
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/lgleasain/passbook.png)](https://travis-ci.org/lgleasain/passbook)
|
2
2
|
|
3
|
-
#
|
3
|
+
# passbook-ios
|
4
4
|
|
5
|
-
|
5
|
+
The passbook-ios gem let's you create a pkpass for passbook in iOS 6+
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
|
9
|
+
Include the passbook-ios gem in your project.
|
10
|
+
|
11
|
+
IE: In your Gemfile
|
12
|
+
```
|
13
|
+
gem 'passbook-ios'
|
14
|
+
```
|
10
15
|
|
11
16
|
## Configuration
|
12
17
|
|
@@ -37,6 +42,20 @@ If you are running this on a different machine from what you used to create your
|
|
37
42
|
```
|
38
43
|
If you are using Sinatra you can place this in the file you are executing or in a file that you do a require on. You would also not reference Rails.root when specifying your file path.
|
39
44
|
|
45
|
+
If You are doing push notifications then you will need to add some extra configuration options, namely a push notification certificate and a notification gateway certificate. Look at the Grocer gem documentation to find information on how to create this certificate. Settings you will want ot use for the notification gateway are either 'gateway.push.apple.com' for production, 'gateway.sandbox.push.apple.com' for developmetn and 'localhost' for unit tests.
|
46
|
+
```
|
47
|
+
Passbook.configure do |passbook|
|
48
|
+
.....other settings.....
|
49
|
+
passbook.notification_gateway = 'gateway.push.apple.com'
|
50
|
+
passbook.notification_cert = 'lib/assets/my_notification_cert.pem'
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
If you want to also support the push notification endpoints you will also need to include the Rack::PassbookRack middleware. In rails your config will look something like this.
|
55
|
+
```
|
56
|
+
config.middleware.use Rack::PassbookRack
|
57
|
+
```
|
58
|
+
|
40
59
|
## Usage
|
41
60
|
|
42
61
|
Please refer to apple iOS dev center for how to build cert and json. [This article is also helpful.](http://www.raywenderlich.com/20734/beginning-passbook-part-1#more-20734)
|
@@ -92,7 +111,100 @@ get '/passbook' do
|
|
92
111
|
end
|
93
112
|
```
|
94
113
|
|
95
|
-
We will try to make this cleaner in
|
114
|
+
We will try to make this cleaner in subsequent releases.
|
115
|
+
|
116
|
+
### Passbook
|
117
|
+
|
118
|
+
If you want to support passbook push notification updates you will need to congigure the appropriate bits above.
|
119
|
+
|
120
|
+
In order to support push notifications you will need to have a basic understanding of the way that push notifications work and how the data is passed back and forth. See [this](http://developer.apple.com/library/ios/#Documentation/UserExperience/Conceptual/PassKit_PG/Chapters/Creating.html) for basic information about passes and [this](http://developer.apple.com/library/ios/#Documentation/UserExperience/Conceptual/PassKit_PG/Chapters/Updating.html#//apple_ref/doc/uid/TP40012195-CH5-SW1) to understand the information that needs to be exchanged between each device and your application to support the update service.
|
121
|
+
|
122
|
+
Your pass will need to have a field called 'webServiceURL' with the base url to your site and a field called 'authenticationToken'. The json snippet should look like this. Note that your url needs to be a valid signed https endpoint for production. You can put your phone in dev mode to test updates against a insecure http endpoint (under settings => developer => passkit testing).
|
123
|
+
|
124
|
+
```
|
125
|
+
...
|
126
|
+
"webserviceURL" : "https://www.honeybadgers.com/",
|
127
|
+
"authenticationToken" : "yummycobras"
|
128
|
+
...
|
129
|
+
```
|
130
|
+
|
131
|
+
Passbook-ios 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
|
+
|
133
|
+
```
|
134
|
+
module Passbook
|
135
|
+
class PassbookNotification
|
136
|
+
|
137
|
+
# This is called whenever a new pass is saved to a users passbook or the
|
138
|
+
# notifications are re-enabled. You will want to persist these values to
|
139
|
+
# allow for updates on subsequent calls in the call chain. You can have
|
140
|
+
# multiple push tokens and serial numbers for a specific
|
141
|
+
# deviceLibraryIdentifier.
|
142
|
+
|
143
|
+
def self.register_pass(options)
|
144
|
+
the_passes_serial_number = options['serialNumber']
|
145
|
+
the_devices_device_library_identifier = options['deviceLibraryIdentifier']
|
146
|
+
the_devices_push_token = options['pushToken']
|
147
|
+
|
148
|
+
# this is if the pass registered successfully
|
149
|
+
# change the code to 200 if the pass has already been registered
|
150
|
+
# or another appropriate code if something went wrong.
|
151
|
+
{:status => 201}
|
152
|
+
end
|
153
|
+
|
154
|
+
# This is called when the device receives a push notification from apple.
|
155
|
+
# You will need to return the serial number of all passes associated with
|
156
|
+
# that deviceLibraryIdentifier.
|
157
|
+
|
158
|
+
def self.passes_for_device(options)
|
159
|
+
device_library_identifier = options['deviceLibraryIdentifier']
|
160
|
+
|
161
|
+
# the 'lastUpdated' uses integers values to tell passbook if the pass is
|
162
|
+
# more recent than the current one. If you just set it is the same value
|
163
|
+
# every time the pass will update and you will get a warning in the log files.
|
164
|
+
# you can use the time in milliseconds, a counter or any other numbering scheme.
|
165
|
+
# you then also need to return an array of serial numbers.
|
166
|
+
{'lastUpdated' => '1', 'serialNumbers' => ['various', 'serial', 'numbers']}
|
167
|
+
end
|
168
|
+
|
169
|
+
# this is called when a pass is deleted or the user selects the option to disable pass updates.
|
170
|
+
def self.unregister_pass(options)
|
171
|
+
# a solid unique pair of identifiers to identify the pass are
|
172
|
+
serial_number = options['serialNumber']
|
173
|
+
device_library_identifier = options['deviceLibraryIdentifier']
|
174
|
+
# return a status 200 to indicate that the pass was successfully unregistered.
|
175
|
+
{:status => 200}
|
176
|
+
end
|
177
|
+
|
178
|
+
# this returns your updated pass
|
179
|
+
def self.latest_pass(options)
|
180
|
+
the_pass_serial_number = options['serialNumber']
|
181
|
+
# create your PkPass the way you did when your first created the pass.
|
182
|
+
# you will want to return
|
183
|
+
my_pass = PkPass.new 'your pass json'
|
184
|
+
# you will want to return the string from the stream of your PkPass object.
|
185
|
+
mypass.stream.string
|
186
|
+
end
|
187
|
+
|
188
|
+
# This is called whenever there is something from the update process that is a warning
|
189
|
+
# or error
|
190
|
+
def self.log(log)
|
191
|
+
# this is a VERY crude logging example. use the logger of your choice here.
|
192
|
+
p "#{Time.now} #{log}"
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
```
|
199
|
+
|
200
|
+
To send a push notification for a updated pass simply call Passbook::PassbookPushNotification.send_notifications_for_promotion with the push token for the pass you are updating
|
201
|
+
|
202
|
+
```
|
203
|
+
Passbook::PassbookPushNotification.send_notifications_for_promotion the_pass_push_token
|
204
|
+
|
205
|
+
```
|
206
|
+
|
207
|
+
Apple will send out a notification to your phone (usually within 15 minutes or less), which will cause the phone that this push notification is associated with to make a call to your server to get pass serial numbers and to then get the updated pass. Each phone/pass combination has it's own push token whch will require a separate call for every phone that has push notifications enabled for a pass (this is an Apple thing). In the future we may look into offering background process support for this as part of this gem. For now, if you have a lot of passes to update you will need to do this yourself.
|
96
208
|
|
97
209
|
## Tests
|
98
210
|
|
@@ -114,6 +226,9 @@ We will try to make this cleaner in the next release.
|
|
114
226
|
### 0.0.4
|
115
227
|
Allow passbook gem to return a ZipOutputStream (needed when garbage collector delete tempfile before beeing able to use it) [Thx to applidget]
|
116
228
|
|
229
|
+
### 0.2.0
|
230
|
+
Adding support for push notification updates for passes.
|
231
|
+
|
117
232
|
License
|
118
233
|
-------
|
119
234
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/passbook.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require "passbook/version"
|
2
2
|
require "passbook/pkpass"
|
3
3
|
require 'active_support/core_ext/module/attribute_accessors'
|
4
|
+
require 'passbook/push_notification'
|
5
|
+
require 'grocer/passbook_notification'
|
4
6
|
|
5
7
|
module Passbook
|
6
|
-
mattr_accessor :p12_cert, :p12_password, :wwdc_cert, :p12_certificate, :p12_key
|
8
|
+
mattr_accessor :p12_cert, :p12_password, :wwdc_cert, :p12_certificate, :p12_key, :notification_cert, :notification_gateway
|
7
9
|
|
8
10
|
def self.configure
|
9
11
|
yield self
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Passbook
|
2
|
+
class PushNotification
|
3
|
+
def self.send_notification(device_token)
|
4
|
+
pusher = Grocer.pusher({:certificate => Passbook.notification_cert, :gateway => Passbook.notification_gateway})
|
5
|
+
notification = Grocer::PassbookNotification.new(:device_token => device_token)
|
6
|
+
|
7
|
+
pusher.push notification
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Rack
|
2
|
+
class PassbookRack
|
3
|
+
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
method_and_params = find_method env['PATH_INFO']
|
10
|
+
if method_and_params
|
11
|
+
case method_and_params[:method]
|
12
|
+
when 'device_register_delete'
|
13
|
+
if env['REQUEST_METHOD'] == 'POST'
|
14
|
+
[Passbook::PassbookNotification.
|
15
|
+
register_pass(method_and_params[:params].merge! JSON.parse(env['rack.input'].read 1000))[:status],
|
16
|
+
{}, ['']]
|
17
|
+
elsif env['REQUEST_METHOD'] == 'DELETE'
|
18
|
+
[Passbook::PassbookNotification.unregister_pass(method_and_params[:params])[:status], {}, {}]
|
19
|
+
end
|
20
|
+
when 'passes_for_device'
|
21
|
+
response = Passbook::PassbookNotification.passes_for_device(method_and_params[:params])
|
22
|
+
[response ? 200 : 204, {}, [response.to_json]]
|
23
|
+
when 'latest_pass'
|
24
|
+
response = Passbook::PassbookNotification.latest_pass(method_and_params[:params])
|
25
|
+
if response
|
26
|
+
[200, {'Content-Type' => 'application/vnd.apple.pkpass',
|
27
|
+
'Content-Disposition' => 'attachment',
|
28
|
+
'filename' => "#{method_and_params[:params]['serialNumber']}.pkpass"}, [response]]
|
29
|
+
else
|
30
|
+
[204, {}, {}]
|
31
|
+
end
|
32
|
+
when 'log'
|
33
|
+
Passbook::PassbookNotification.log JSON.parse(env['rack.input'].read 10000)
|
34
|
+
[200, {}, {}]
|
35
|
+
else
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@app.call env
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def append_parameter_separator url
|
43
|
+
end
|
44
|
+
|
45
|
+
def each(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def find_method(path)
|
50
|
+
parsed_path = path.split '/'
|
51
|
+
url_beginning = parsed_path.index 'v1'
|
52
|
+
if url_beginning
|
53
|
+
args_length = parsed_path.size - url_beginning
|
54
|
+
|
55
|
+
if (parsed_path[url_beginning + 1 ] == 'devices') and
|
56
|
+
(parsed_path[url_beginning + 3 ] == 'registrations')
|
57
|
+
if args_length == 6
|
58
|
+
return method_and_params_hash 'device_register_delete', path
|
59
|
+
elsif args_length == 5
|
60
|
+
return method_and_params_hash 'passes_for_device', path
|
61
|
+
end
|
62
|
+
elsif parsed_path[url_beginning + 1] == 'passes' and args_length == 4
|
63
|
+
return method_and_params_hash 'latest_pass', path
|
64
|
+
elsif parsed_path[url_beginning + 1] == 'log' and args_length == 2
|
65
|
+
return {:method => 'log'}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def method_and_params_hash(method, path)
|
75
|
+
parsed_path = path.split '/'
|
76
|
+
url_beginning = parsed_path.index 'v1'
|
77
|
+
if method == 'latest_pass'
|
78
|
+
{:method => 'latest_pass',
|
79
|
+
:params => {'passTypeIdentifier' => parsed_path[url_beginning + 2],
|
80
|
+
'serialNumber' => parsed_path[url_beginning + 3]}}
|
81
|
+
else
|
82
|
+
return_hash = {:method => method, :params =>
|
83
|
+
{'deviceLibraryIdentifier' => parsed_path[url_beginning + 2],
|
84
|
+
'passTypeIdentifier' => parsed_path[url_beginning + 4]}}
|
85
|
+
return_hash[:params]['serialNumber'] = parsed_path[url_beginning + 5] if
|
86
|
+
method == 'device_register_delete'
|
87
|
+
return_hash
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
data/passbook-ios.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "passbook-ios"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thomas Lauro", "Lance Gleason"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2013-01-06"
|
13
13
|
s.description = "This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it."
|
14
14
|
s.email = ["thomas@lauro.fr", "lgleason@polyglotprogramminginc.com"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,7 +26,9 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"lib/passbook.rb",
|
28
28
|
"lib/passbook/pkpass.rb",
|
29
|
+
"lib/passbook/push_notification.rb",
|
29
30
|
"lib/passbook/version.rb",
|
31
|
+
"lib/rack/passbook_rack.rb",
|
30
32
|
"lib/rails/generators/passbook/config/config_generator.rb",
|
31
33
|
"lib/rails/generators/passbook/config/templates/initializer.rb",
|
32
34
|
"passbook-ios.gemspec",
|
@@ -36,6 +38,8 @@ Gem::Specification.new do |s|
|
|
36
38
|
"spec/data/logo.png",
|
37
39
|
"spec/data/logo@2x.png",
|
38
40
|
"spec/lib/passbook/pkpass_spec.rb",
|
41
|
+
"spec/lib/passbook/push_notification_spec.rb",
|
42
|
+
"spec/lib/rack/passbook_rack_spec.rb",
|
39
43
|
"spec/spec_helper.rb"
|
40
44
|
]
|
41
45
|
s.homepage = "http://github.com/frozon/passbook"
|
@@ -49,6 +53,8 @@ Gem::Specification.new do |s|
|
|
49
53
|
|
50
54
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
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"])
|
52
58
|
s.add_development_dependency(%q<activesupport>, [">= 0"])
|
53
59
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
54
60
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
@@ -57,6 +63,8 @@ Gem::Specification.new do |s|
|
|
57
63
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
58
64
|
else
|
59
65
|
s.add_dependency(%q<rubyzip>, [">= 0"])
|
66
|
+
s.add_dependency(%q<grocer>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
60
68
|
s.add_dependency(%q<activesupport>, [">= 0"])
|
61
69
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
62
70
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
@@ -66,6 +74,8 @@ Gem::Specification.new do |s|
|
|
66
74
|
end
|
67
75
|
else
|
68
76
|
s.add_dependency(%q<rubyzip>, [">= 0"])
|
77
|
+
s.add_dependency(%q<grocer>, [">= 0"])
|
78
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
69
79
|
s.add_dependency(%q<activesupport>, [">= 0"])
|
70
80
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
71
81
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,8 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
require 'json'
|
4
4
|
require 'simplecov'
|
5
|
+
require 'grocer/passbook_notification'
|
5
6
|
SimpleCov.start
|
6
7
|
|
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/, ''))}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passbook-ios
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-01-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubyzip
|
@@ -28,6 +28,38 @@ dependencies:
|
|
28
28
|
- - ! '>='
|
29
29
|
- !ruby/object:Gem::Version
|
30
30
|
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: grocer
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-test
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
31
63
|
- !ruby/object:Gem::Dependency
|
32
64
|
name: activesupport
|
33
65
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,7 +176,9 @@ files:
|
|
144
176
|
- VERSION
|
145
177
|
- lib/passbook.rb
|
146
178
|
- lib/passbook/pkpass.rb
|
179
|
+
- lib/passbook/push_notification.rb
|
147
180
|
- lib/passbook/version.rb
|
181
|
+
- lib/rack/passbook_rack.rb
|
148
182
|
- lib/rails/generators/passbook/config/config_generator.rb
|
149
183
|
- lib/rails/generators/passbook/config/templates/initializer.rb
|
150
184
|
- passbook-ios.gemspec
|
@@ -154,6 +188,8 @@ files:
|
|
154
188
|
- spec/data/logo.png
|
155
189
|
- spec/data/logo@2x.png
|
156
190
|
- spec/lib/passbook/pkpass_spec.rb
|
191
|
+
- spec/lib/passbook/push_notification_spec.rb
|
192
|
+
- spec/lib/rack/passbook_rack_spec.rb
|
157
193
|
- spec/spec_helper.rb
|
158
194
|
homepage: http://github.com/frozon/passbook
|
159
195
|
licenses:
|
@@ -170,7 +206,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
170
206
|
version: '0'
|
171
207
|
segments:
|
172
208
|
- 0
|
173
|
-
hash: -
|
209
|
+
hash: -2588789106916312448
|
174
210
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
211
|
none: false
|
176
212
|
requirements:
|