houston 1.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +9 -3
- data/README.md +4 -0
- data/houston.gemspec +3 -2
- data/lib/houston/client.rb +3 -1
- data/lib/houston/notification.rb +31 -7
- data/lib/houston/version.rb +1 -1
- data/spec/notification_spec.rb +199 -0
- data/spec/spec_helper.rb +10 -0
- metadata +28 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19ea781099fb74df316010704e9335580db3418c
|
4
|
+
data.tar.gz: bb5452a82ab13181e6fee98915a37799ae1dbe0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24c557aa4a8ac29e937cc0f8c4ecccb2bd1218c3f204e3cf273d7c942646e0aa7cd7c01ddc5228ba8d6693ee46a0c65d1a732f2a8c50364ac7d452b8ca274390
|
7
|
+
data.tar.gz: 552d74c3ff780d5168fe3a3fda11b31e3da9292c3a13607cc8741fa088e76b4d1c52677bbbe7e40ebcd6c029ce1b7d0a2eed7341fb1525c089303fe95d467717
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
houston (
|
4
|
+
houston (2.0.0)
|
5
5
|
commander (~> 4.1)
|
6
6
|
json
|
7
7
|
|
@@ -12,13 +12,19 @@ GEM
|
|
12
12
|
highline (~> 1.6.11)
|
13
13
|
highline (1.6.19)
|
14
14
|
json (1.8.1)
|
15
|
+
multi_json (1.8.0)
|
15
16
|
rake (0.9.6)
|
16
17
|
rspec (0.9.4)
|
18
|
+
simplecov (0.7.1)
|
19
|
+
multi_json (~> 1.0)
|
20
|
+
simplecov-html (~> 0.7.1)
|
21
|
+
simplecov-html (0.7.1)
|
17
22
|
|
18
23
|
PLATFORMS
|
19
24
|
ruby
|
20
25
|
|
21
26
|
DEPENDENCIES
|
22
27
|
houston!
|
23
|
-
rake
|
24
|
-
rspec
|
28
|
+
rake
|
29
|
+
rspec
|
30
|
+
simplecov
|
data/README.md
CHANGED
@@ -60,6 +60,10 @@ connection.write(notification.message)
|
|
60
60
|
connection.close
|
61
61
|
```
|
62
62
|
|
63
|
+
## Versioning
|
64
|
+
|
65
|
+
Houston 2.0 supports the new [enhanced notification format](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4). Support for the legacy notification format is available in 1.x releases.
|
66
|
+
|
63
67
|
## Command Line Tool
|
64
68
|
|
65
69
|
Houston also comes with the `apn` binary, which provides a convenient way to test notifications from the command line.
|
data/houston.gemspec
CHANGED
@@ -16,8 +16,9 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_dependency "commander", "~> 4.1"
|
17
17
|
s.add_dependency "json"
|
18
18
|
|
19
|
-
s.add_development_dependency "rspec"
|
20
|
-
s.add_development_dependency "rake"
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
s.add_development_dependency "rake"
|
21
|
+
s.add_development_dependency "simplecov"
|
21
22
|
|
22
23
|
s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
|
23
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/houston/client.rb
CHANGED
@@ -71,7 +71,9 @@ module Houston
|
|
71
71
|
end
|
72
72
|
|
73
73
|
if error
|
74
|
-
command, status, index = error.unpack("
|
74
|
+
command, status, index = error.unpack("ccN")
|
75
|
+
# status == 10 means shutdown, and the given id is the last one successfully sent
|
76
|
+
index += 1 if status == 10
|
75
77
|
notifications.slice!(0..index)
|
76
78
|
notifications.each(&:mark_as_unsent!)
|
77
79
|
push(*notifications)
|
data/lib/houston/notification.rb
CHANGED
@@ -4,7 +4,7 @@ module Houston
|
|
4
4
|
class Notification
|
5
5
|
MAXIMUM_PAYLOAD_SIZE = 256
|
6
6
|
|
7
|
-
attr_accessor :token, :alert, :badge, :sound, :content_available, :custom_data, :id, :expiry
|
7
|
+
attr_accessor :token, :alert, :badge, :sound, :content_available, :custom_data, :id, :expiry, :priority
|
8
8
|
attr_reader :sent_at
|
9
9
|
|
10
10
|
alias :device :token
|
@@ -17,6 +17,7 @@ module Houston
|
|
17
17
|
@sound = options.delete(:sound)
|
18
18
|
@expiry = options.delete(:expiry)
|
19
19
|
@id = options.delete(:id)
|
20
|
+
@priority = options.delete(:priority)
|
20
21
|
@content_available = options.delete(:content_available)
|
21
22
|
|
22
23
|
@custom_data = options
|
@@ -34,12 +35,12 @@ module Houston
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def message
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
[
|
38
|
+
data = [device_token_item,
|
39
|
+
payload_item,
|
40
|
+
identifier_item,
|
41
|
+
expiration_item,
|
42
|
+
priority_item].compact.join
|
43
|
+
[2, data.bytes.count, data].pack('cNa*')
|
43
44
|
end
|
44
45
|
|
45
46
|
def mark_as_sent!
|
@@ -57,5 +58,28 @@ module Houston
|
|
57
58
|
def valid?
|
58
59
|
payload.to_json.bytesize <= MAXIMUM_PAYLOAD_SIZE
|
59
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def device_token_item
|
65
|
+
[1, 32, @token.gsub(/[<\s>]/, '')].pack('cnH*')
|
66
|
+
end
|
67
|
+
|
68
|
+
def payload_item
|
69
|
+
json = payload.to_json
|
70
|
+
[2, json.bytes.count, json].pack('cna*')
|
71
|
+
end
|
72
|
+
|
73
|
+
def identifier_item
|
74
|
+
[3, 4, @id].pack('cnN') unless @id.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
def expiration_item
|
78
|
+
[4, 4, @expiry].pack('cnN') unless @expiry.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
def priority_item
|
82
|
+
[5, 1, @priority].pack('cnc') unless @priority.nil?
|
83
|
+
end
|
60
84
|
end
|
61
85
|
end
|
data/lib/houston/version.rb
CHANGED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Houston::Notification do
|
4
|
+
let(:notification_options) {
|
5
|
+
{
|
6
|
+
token: '<ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b5969>',
|
7
|
+
alert: 'Houston, we have a problem.',
|
8
|
+
badge: 2701,
|
9
|
+
sound: 'sosumi.aiff',
|
10
|
+
expiry: 1234567890,
|
11
|
+
id: 42,
|
12
|
+
priority: 10,
|
13
|
+
content_available: true,
|
14
|
+
# custom data
|
15
|
+
key1: 1,
|
16
|
+
key2: 'abc'
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
subject { Houston::Notification.new(notification_options) }
|
21
|
+
|
22
|
+
its(:token) { should == '<ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b5969>' }
|
23
|
+
its(:alert) { should == 'Houston, we have a problem.' }
|
24
|
+
its(:badge) { should == 2701 }
|
25
|
+
its(:sound) { should == 'sosumi.aiff' }
|
26
|
+
its(:expiry) { should == 1234567890 }
|
27
|
+
its(:id) { should == 42 }
|
28
|
+
its(:priority) { should == 10 }
|
29
|
+
its(:content_available) { should be_true }
|
30
|
+
its(:custom_data) { should == { key1: 1, key2: 'abc' } }
|
31
|
+
|
32
|
+
context 'using :device instead of :token' do
|
33
|
+
subject do
|
34
|
+
notification_options[:device] = notification_options[:token]
|
35
|
+
notification_options.delete(:token)
|
36
|
+
Houston::Notification.new(notification_options)
|
37
|
+
end
|
38
|
+
|
39
|
+
its(:device) { '<ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b5969>' }
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#payload' do
|
43
|
+
it 'should create a compliant dictionary' do
|
44
|
+
subject.payload.should == {
|
45
|
+
'aps' => {
|
46
|
+
'alert' => 'Houston, we have a problem.',
|
47
|
+
'badge' => 2701,
|
48
|
+
'sound' => 'sosumi.aiff',
|
49
|
+
'content-available' => 1
|
50
|
+
},
|
51
|
+
:key1 => 1,
|
52
|
+
:key2 => 'abc'
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should create a dictionary of only custom data and empty aps' do
|
57
|
+
Houston::Notification.new(key1: 123, key2: 'xyz').payload.should == {
|
58
|
+
'aps' => {},
|
59
|
+
:key1 => 123,
|
60
|
+
:key2 => 'xyz'
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should create a dictionary only with alerts' do
|
65
|
+
Houston::Notification.new(alert: 'Hello, World!').payload.should == {
|
66
|
+
'aps' => { 'alert' => 'Hello, World!' }
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should create a dictionary only with badges' do
|
71
|
+
Houston::Notification.new(badge: '123').payload.should == {
|
72
|
+
'aps' => { 'badge' => 123 }
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should create a dictionary only with sound' do
|
77
|
+
Houston::Notification.new(sound: 'ring.aiff').payload.should == {
|
78
|
+
'aps' => { 'sound' => 'ring.aiff' }
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should create a dictionary only with content-available' do
|
83
|
+
Houston::Notification.new(content_available: true).payload.should == {
|
84
|
+
'aps' => { 'content-available' => 1 }
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should allow custom data inside aps key' do
|
89
|
+
notification_options = { :badge => 567, 'aps' => { 'loc-key' => 'my-key' } }
|
90
|
+
Houston::Notification.new(notification_options).payload.should == {
|
91
|
+
'aps' => { 'loc-key' => 'my-key', 'badge' => 567 }
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#sent?' do
|
97
|
+
it 'should be false initially' do
|
98
|
+
subject.sent?.should be_false
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be true after marking as sent' do
|
102
|
+
subject.mark_as_sent!
|
103
|
+
subject.sent?.should be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should be false after marking as unsent' do
|
107
|
+
subject.mark_as_sent!
|
108
|
+
subject.mark_as_unsent!
|
109
|
+
subject.sent?.should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#message' do
|
114
|
+
it 'should create a message with command 2' do
|
115
|
+
command, _1, _2 = subject.message.unpack('cNa*')
|
116
|
+
command.should == 2
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should create a message with correct frame length' do
|
120
|
+
_1, length, _2 = subject.message.unpack('cNa*')
|
121
|
+
length.should == 182
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_items(items_stream)
|
125
|
+
items = []
|
126
|
+
until items_stream.empty?
|
127
|
+
item_id, item_length, items_stream = items_stream.unpack('cna*')
|
128
|
+
item_data, items_stream = items_stream.unpack("a#{item_length}a*")
|
129
|
+
items << [item_id, item_length, item_data]
|
130
|
+
end
|
131
|
+
items
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should include five items' do
|
135
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
136
|
+
parse_items(items_stream).should have(5).items
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should include an item #1 with the token as hexadecimal' do
|
140
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
141
|
+
items = parse_items(items_stream)
|
142
|
+
items.should include([1, 32, ['ce8be6272e43e85516033e24b4c289220eeda4879c477160b2545e95b68b5969'].pack('H*')])
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should include an item #2 with the payload as JSON' do
|
146
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
147
|
+
items = parse_items(items_stream)
|
148
|
+
items.should include([2, 126, '{"key1":1,"key2":"abc","aps":{"alert":"Houston, we have a problem.","badge":2701,"sound":"sosumi.aiff","content-available":1}}'])
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should include an item #3 with the identifier' do
|
152
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
153
|
+
items = parse_items(items_stream)
|
154
|
+
items.should include([3, 4, [42].pack('N')])
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should include an item #4 with the expiry' do
|
158
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
159
|
+
items = parse_items(items_stream)
|
160
|
+
items.should include([4, 4, [1234567890].pack('N')])
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should include an item #4 with the priority' do
|
164
|
+
_1, _2, items_stream = subject.message.unpack('cNa*')
|
165
|
+
items = parse_items(items_stream)
|
166
|
+
items.should include([5, 1, [10].pack('c')])
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'might be missing the identifier item' do
|
170
|
+
notification_options.delete(:id)
|
171
|
+
notification = Houston::Notification.new(notification_options)
|
172
|
+
msg = notification.message
|
173
|
+
_1, _2, items_stream = notification.message.unpack('cNa*')
|
174
|
+
items = parse_items(items_stream)
|
175
|
+
items.should have(4).items
|
176
|
+
items.find { |item| item[0] == 3 }.should be_nil
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'might be missing the expiry item' do
|
180
|
+
notification_options.delete(:expiry)
|
181
|
+
notification = Houston::Notification.new(notification_options)
|
182
|
+
msg = notification.message
|
183
|
+
_1, _2, items_stream = notification.message.unpack('cNa*')
|
184
|
+
items = parse_items(items_stream)
|
185
|
+
items.should have(4).items
|
186
|
+
items.find { |item| item[0] == 4 }.should be_nil
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'might be missing the priority item' do
|
190
|
+
notification_options.delete(:priority)
|
191
|
+
notification = Houston::Notification.new(notification_options)
|
192
|
+
msg = notification.message
|
193
|
+
_1, _2, items_stream = notification.message.unpack('cNa*')
|
194
|
+
items = parse_items(items_stream)
|
195
|
+
items.should have(4).items
|
196
|
+
items.find { |item| item[0] == 5 }.should be_nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: houston
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattt Thompson
|
@@ -42,30 +42,44 @@ dependencies:
|
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: Houston is a simple gem for sending Apple Push Notifications. Pass your
|
70
84
|
credentials, construct your message, and send it.
|
71
85
|
email: m@mattt.me
|
@@ -86,6 +100,8 @@ files:
|
|
86
100
|
- ./LICENSE
|
87
101
|
- ./Rakefile
|
88
102
|
- ./README.md
|
103
|
+
- spec/notification_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
89
105
|
- bin/apn
|
90
106
|
homepage: http://github.com/mattt/houston
|
91
107
|
licenses:
|
@@ -111,4 +127,6 @@ rubygems_version: 2.0.3
|
|
111
127
|
signing_key:
|
112
128
|
specification_version: 4
|
113
129
|
summary: Send Apple Push Notifications
|
114
|
-
test_files:
|
130
|
+
test_files:
|
131
|
+
- spec/notification_spec.rb
|
132
|
+
- spec/spec_helper.rb
|