bmo 0.8.10 → 0.8.11
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 +8 -8
- data/Gemfile.lock +1 -1
- data/lib/bmo/apns/notification.rb +31 -12
- data/lib/bmo/gcm/notification.rb +1 -1
- data/lib/bmo/utils.rb +28 -0
- data/lib/bmo/version.rb +1 -1
- data/lib/bmo.rb +8 -2
- data/spec/bmo/apns/notification_spec.rb +37 -15
- data/spec/bmo/utils_spec.rb +59 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YWVjNzg2YTQ0NmNkMWNkYjI5NTYyZjZkNzg2NGM1NjE5NGI4OGY3Zg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZjdmZjgxZDNkYTgzZmE5Yzc0MjY4ZTg1M2E0MDQzYTI3ZmQzOTY1YQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTI5ZmYwOTBlYTRmNWMyNWEwMjAzNWY4MWFjNWEwNDc4MjFiN2RlMjQ4MGI2
|
10
|
+
ZmE5ODA2MzVhOTU5NzY1OGIwODk0NTdkZjFkN2RmNzk1MDI0M2E0YTMxMDY1
|
11
|
+
NDA4OWU0ZWZlODc2YjlkYjNmZWE0Y2UxM2Y4ODQwZTg1OGQwNDg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZmI0NWNhZGI0MGFlYjczZmI5ZGI4YmRlNjFiZTFjZTQ3MmVhZGY0OTA2ZTFk
|
14
|
+
ZjhiNWZlNzg2NWI2MmNiMDdjNjg0OGFkNGM1ZmEyMDBlMmUzZGE5NzVkNzVj
|
15
|
+
NGVhNTlhMjhkNTMwZWY0NWQ0MGZmNjBhYzVkYjAxYmNlNTBhM2Q=
|
data/Gemfile.lock
CHANGED
@@ -8,9 +8,9 @@ module BMO
|
|
8
8
|
|
9
9
|
attr_reader :device_token, :payload
|
10
10
|
|
11
|
-
def initialize(device_token, payload)
|
11
|
+
def initialize(device_token, payload, options = {})
|
12
12
|
@device_token = DeviceToken.new(device_token)
|
13
|
-
@payload = Payload.new(payload)
|
13
|
+
@payload = Payload.new(payload, options)
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_package
|
@@ -40,23 +40,42 @@ module BMO
|
|
40
40
|
# Define ==
|
41
41
|
include Equalizer.new(:data)
|
42
42
|
|
43
|
-
attr_reader :data
|
43
|
+
attr_reader :data, :truncable_field, :options
|
44
44
|
|
45
|
-
def initialize(data)
|
46
|
-
@data
|
45
|
+
def initialize(data, options = {})
|
46
|
+
@data = data
|
47
|
+
@truncable_field = options[:truncable_field]
|
48
|
+
@options = options
|
47
49
|
end
|
48
50
|
|
49
51
|
def to_package
|
50
|
-
|
52
|
+
truncate_field! if truncable_field && !valid?
|
53
|
+
validate!
|
54
|
+
package
|
51
55
|
end
|
52
56
|
|
53
57
|
def validate!
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
return true if valid?
|
59
|
+
str = "Payload byte size (#{package.bytesize})" \
|
60
|
+
" should be less than #{Payload::MAX_BYTE_SIZE} bytes"
|
61
|
+
fail PayloadTooLarge, str
|
62
|
+
end
|
63
|
+
|
64
|
+
def package
|
65
|
+
data.to_json.bytes.to_a.pack('C*')
|
66
|
+
end
|
67
|
+
|
68
|
+
def truncate_field!
|
69
|
+
return unless data[:apns] && data[:apns][truncable_field]
|
70
|
+
string = data[:apns][truncable_field]
|
71
|
+
diff_bytesize = package.bytesize - MAX_BYTE_SIZE
|
72
|
+
desirable_bytesize = (string.bytesize - diff_bytesize) - 1
|
73
|
+
data[:apns][truncable_field] = Utils
|
74
|
+
.bytesize_force_truncate(string, desirable_bytesize, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def valid?
|
78
|
+
package.bytesize < MAX_BYTE_SIZE
|
60
79
|
end
|
61
80
|
end # class Payload
|
62
81
|
|
data/lib/bmo/gcm/notification.rb
CHANGED
data/lib/bmo/utils.rb
CHANGED
@@ -11,5 +11,33 @@ module BMO
|
|
11
11
|
end
|
12
12
|
hash_symbolized
|
13
13
|
end
|
14
|
+
|
15
|
+
# Byte aware truncation
|
16
|
+
def self.bytesize_force_truncate(string, bytesize, options = {})
|
17
|
+
if bytesize <= 0
|
18
|
+
return ''
|
19
|
+
elsif bytesize > string.bytesize
|
20
|
+
return string.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
options[:omission] ||= '...'
|
24
|
+
computed_bytesize = bytesize - options[:omission].bytesize
|
25
|
+
return bytesize_force_truncate(options[:omission],
|
26
|
+
bytesize,
|
27
|
+
omission: '') if computed_bytesize <= 0
|
28
|
+
new_string = ''
|
29
|
+
|
30
|
+
string.chars.each_with_index do |char, i|
|
31
|
+
break if (new_string + char).bytesize > computed_bytesize
|
32
|
+
new_string << char
|
33
|
+
end
|
34
|
+
|
35
|
+
stop = if options[:separator]
|
36
|
+
new_string.rindex(options[:separator]) || new_string.length
|
37
|
+
else
|
38
|
+
new_string.length
|
39
|
+
end
|
40
|
+
(new_string[0...stop] + options[:omission]).to_s
|
41
|
+
end
|
14
42
|
end
|
15
43
|
end
|
data/lib/bmo/version.rb
CHANGED
data/lib/bmo.rb
CHANGED
@@ -25,14 +25,19 @@ module BMO
|
|
25
25
|
#
|
26
26
|
# @param device_token [String]
|
27
27
|
# @param data [Hash] The data you want to send
|
28
|
+
# @option options :truncable_field If the payload is too large
|
29
|
+
# this field will be truncated, it must be in a apns hash
|
30
|
+
# @option options :omission ('...') The omission in truncate
|
31
|
+
# @option options :separator The separator use in truncation
|
28
32
|
#
|
29
33
|
# @return The Socket#write return
|
30
34
|
#
|
31
35
|
# @see https://developer.apple.com/library/ios/documentation/
|
32
36
|
# NetworkingInternet/Conceptual/RemoteNotificationsPG/
|
33
37
|
# Chapters/ApplePushService.html
|
34
|
-
def self.send_ios_notification(device_token, data)
|
35
|
-
|
38
|
+
def self.send_ios_notification(device_token, data, options = {})
|
39
|
+
data = Utils.coerce_to_symbols(data)
|
40
|
+
notification = APNS::Notification.new(device_token, data, options)
|
36
41
|
notification.validate!
|
37
42
|
apns_client.send_notification(notification)
|
38
43
|
end
|
@@ -55,6 +60,7 @@ module BMO
|
|
55
60
|
#
|
56
61
|
# @see http://developer.android.com/google/gcm/server.html]
|
57
62
|
def self.send_android_notification(device_token, data)
|
63
|
+
data = Utils.coerce_to_symbols(data)
|
58
64
|
notification = GCM::Notification.new(device_token, data)
|
59
65
|
notification.validate!
|
60
66
|
gcm_client.send_notification(notification)
|
@@ -31,21 +31,6 @@ describe BMO::APNS::Notification::DeviceToken do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
describe BMO::APNS::Notification::Payload do
|
34
|
-
it 'coerce hash keys to symbols' do
|
35
|
-
payload = described_class.new('Finn' => 'The Human')
|
36
|
-
expect(payload.data).to eq(Finn: 'The Human')
|
37
|
-
end
|
38
|
-
|
39
|
-
it "doesn't coerce incompatible types" do
|
40
|
-
payload = described_class.new(1 => 'For Money')
|
41
|
-
expect(payload.data).to eq(1 => 'For Money')
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'returns true for equality between coerced hash and symbolized hash ' do
|
45
|
-
payload = described_class.new('Jake' => 'The Dog')
|
46
|
-
expect(payload).to eq(described_class.new(Jake: 'The Dog'))
|
47
|
-
end
|
48
|
-
|
49
34
|
describe '#validate!' do
|
50
35
|
it 'returns true if the payload is valid' do
|
51
36
|
payload = described_class.new(apns: 'a' * 200)
|
@@ -57,4 +42,41 @@ describe BMO::APNS::Notification::Payload do
|
|
57
42
|
BMO::APNS::Notification::Payload::PayloadTooLarge)
|
58
43
|
end
|
59
44
|
end
|
45
|
+
|
46
|
+
describe '#to_package' do
|
47
|
+
it 'truncates if there is a truncable field and this is not valid' do
|
48
|
+
options = { truncable_field: :message }
|
49
|
+
payload = described_class.new({ apns: { message: 'a' * 256 } }, options)
|
50
|
+
expect(payload.to_package).to eq("{\"apns\":{\"message\":\"#{'a' * 229}...\"}}")
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'truncates to respect the MAX_BYTE_SIZE' do
|
54
|
+
options = { truncable_field: :message }
|
55
|
+
payload = described_class.new({ apns: { message: 'a' * 256 } }, options)
|
56
|
+
expect(payload.to_package.bytesize).to be < BMO::APNS::Notification::Payload::MAX_BYTE_SIZE
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#truncable_field!' do
|
61
|
+
it 'truncates the corresponding field in apns' do
|
62
|
+
options = { truncable_field: :message }
|
63
|
+
payload = described_class.new({ apns: { message: 'a' * 256 } }, options)
|
64
|
+
payload.truncate_field!
|
65
|
+
expect(payload.data[:apns][:message]).to eq(('a' * 229) + '...')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'truncates with omission' do
|
69
|
+
options = { truncable_field: :message, omission: '[more]' }
|
70
|
+
payload = described_class.new({ apns: { message: 'a' * 256 } }, options)
|
71
|
+
payload.truncate_field!
|
72
|
+
expect(payload.data[:apns][:message]).to eq(('a' * 226) + '[more]')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'truncates with separator' do
|
76
|
+
options = { truncable_field: :message, separator: ' ' }
|
77
|
+
payload = described_class.new({ apns: { message: 'test ' + ('a' * 255) } }, options)
|
78
|
+
payload.truncate_field!
|
79
|
+
expect(payload.data[:apns][:message]).to eq('test...')
|
80
|
+
end
|
81
|
+
end
|
60
82
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe BMO::Utils do
|
5
|
+
describe '#bytesize_force_truncate' do
|
6
|
+
|
7
|
+
it 'truncates the string' do
|
8
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 10, omission: ''))
|
9
|
+
.to eq('Ça a déb')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'adds a default omission' do
|
13
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 10))
|
14
|
+
.to eq('Ça a d...')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'adds accepts an optional omission params' do
|
18
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 10, omission: '[more]'))
|
19
|
+
.to eq('Ça [more]')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'forces the returned string to be less or equal to the length' do
|
23
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 10, omission: '[more]').bytesize)
|
24
|
+
.to be 10
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not mutate the string' do
|
28
|
+
string = 'Ça a débuté comme ça.'
|
29
|
+
BMO::Utils.bytesize_force_truncate(string, 10)
|
30
|
+
expect(string.bytesize)
|
31
|
+
.to be > 10
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns an empty string if the length if less than 1' do
|
35
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', -2))
|
36
|
+
.to eq('')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns the whole string if the length is more than the string length' do
|
40
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 100000))
|
41
|
+
.to eq('Ça a débuté comme ça.')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'accepts a separator param' do
|
45
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 20, separator: ' '))
|
46
|
+
.to eq('Ça a débuté...')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns an empty string if the length if less than 1' do
|
50
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', -2))
|
51
|
+
.to eq('')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'truncates the omission too' do
|
55
|
+
expect(BMO::Utils.bytesize_force_truncate('Ça a débuté comme ça.', 2))
|
56
|
+
.to eq('..')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bmo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Antoine Lyset
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: equalizer
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- lib/bmo/utils.rb
|
96
96
|
- lib/bmo/version.rb
|
97
97
|
- spec/bmo/apns/notification_spec.rb
|
98
|
+
- spec/bmo/utils_spec.rb
|
98
99
|
- spec/spec_helper.rb
|
99
100
|
homepage: https://github.com/antoinelyset/bmo
|
100
101
|
licenses:
|
@@ -122,5 +123,6 @@ specification_version: 4
|
|
122
123
|
summary: Push notifications to iOS and Android devices
|
123
124
|
test_files:
|
124
125
|
- spec/bmo/apns/notification_spec.rb
|
126
|
+
- spec/bmo/utils_spec.rb
|
125
127
|
- spec/spec_helper.rb
|
126
128
|
has_rdoc:
|