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.
@@ -2,7 +2,4 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 1.9.2
5
- - rbx-18mode
6
5
  - ruby-head
7
- - 1.8.7
8
- - ree
data/Gemfile CHANGED
@@ -2,8 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in passbook.gemspec
4
4
  gem 'rubyzip'
5
+ gem 'grocer'
5
6
 
6
7
  group :test, :development do
8
+ gem 'rack-test'
7
9
  gem 'activesupport'
8
10
  gem 'jeweler'
9
11
  gem 'simplecov'
@@ -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
- # Passbook
3
+ # passbook-ios
4
4
 
5
- Passbook gem let's you create pkpass for passbook iOS 6
5
+ The passbook-ios gem let's you create a pkpass for passbook in iOS 6+
6
6
 
7
7
  ## Installation
8
8
 
9
- TODO : push the gem to rubygems.org
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 the next release.
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.2
1
+ 0.2.0
@@ -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
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "passbook-ios"
8
- s.version = "0.1.2"
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 = "2012-11-18"
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
@@ -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.1.2
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: 2012-11-18 00:00:00.000000000 Z
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: -4209167394935444035
209
+ hash: -2588789106916312448
174
210
  required_rubygems_version: !ruby/object:Gem::Requirement
175
211
  none: false
176
212
  requirements: