gwong-apn_on_rails 0.4.2

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.
Files changed (60) hide show
  1. data/.rspec +2 -0
  2. data/.specification +80 -0
  3. data/Gemfile +19 -0
  4. data/Gemfile.lock +47 -0
  5. data/LICENSE +21 -0
  6. data/README +179 -0
  7. data/README.textile +209 -0
  8. data/Rakefile +49 -0
  9. data/VERSION +1 -0
  10. data/apn_on_rails.gemspec +144 -0
  11. data/autotest/discover.rb +1 -0
  12. data/generators/apn_migrations_generator.rb +31 -0
  13. data/generators/templates/apn_migrations/001_create_apn_devices.rb +13 -0
  14. data/generators/templates/apn_migrations/002_create_apn_notifications.rb +23 -0
  15. data/generators/templates/apn_migrations/003_alter_apn_devices.rb +25 -0
  16. data/generators/templates/apn_migrations/004_create_apn_apps.rb +18 -0
  17. data/generators/templates/apn_migrations/005_create_groups.rb +23 -0
  18. data/generators/templates/apn_migrations/006_alter_apn_groups.rb +11 -0
  19. data/generators/templates/apn_migrations/007_create_device_groups.rb +27 -0
  20. data/generators/templates/apn_migrations/008_create_apn_group_notifications.rb +23 -0
  21. data/generators/templates/apn_migrations/009_create_pull_notifications.rb +16 -0
  22. data/generators/templates/apn_migrations/010_alter_apn_notifications.rb +21 -0
  23. data/generators/templates/apn_migrations/011_make_device_token_index_nonunique.rb +11 -0
  24. data/generators/templates/apn_migrations/012_add_launch_notification_to_apn_pull_notifications.rb +9 -0
  25. data/lib/apn_on_rails.rb +4 -0
  26. data/lib/apn_on_rails/apn_on_rails.rb +81 -0
  27. data/lib/apn_on_rails/app/models/apn/app.rb +150 -0
  28. data/lib/apn_on_rails/app/models/apn/base.rb +9 -0
  29. data/lib/apn_on_rails/app/models/apn/device.rb +49 -0
  30. data/lib/apn_on_rails/app/models/apn/device_grouping.rb +16 -0
  31. data/lib/apn_on_rails/app/models/apn/group.rb +12 -0
  32. data/lib/apn_on_rails/app/models/apn/group_notification.rb +79 -0
  33. data/lib/apn_on_rails/app/models/apn/notification.rb +93 -0
  34. data/lib/apn_on_rails/app/models/apn/pull_notification.rb +28 -0
  35. data/lib/apn_on_rails/libs/connection.rb +70 -0
  36. data/lib/apn_on_rails/libs/feedback.rb +39 -0
  37. data/lib/apn_on_rails/tasks/apn.rake +30 -0
  38. data/lib/apn_on_rails/tasks/db.rake +19 -0
  39. data/lib/apn_on_rails_tasks.rb +3 -0
  40. data/spec/active_record/setup_ar.rb +19 -0
  41. data/spec/apn_on_rails/app/models/apn/app_spec.rb +226 -0
  42. data/spec/apn_on_rails/app/models/apn/device_spec.rb +60 -0
  43. data/spec/apn_on_rails/app/models/apn/group_notification_spec.rb +66 -0
  44. data/spec/apn_on_rails/app/models/apn/notification_spec.rb +71 -0
  45. data/spec/apn_on_rails/app/models/apn/pull_notification_spec.rb +100 -0
  46. data/spec/apn_on_rails/libs/connection_spec.rb +40 -0
  47. data/spec/apn_on_rails/libs/feedback_spec.rb +43 -0
  48. data/spec/extensions/string.rb +10 -0
  49. data/spec/factories/app_factory.rb +27 -0
  50. data/spec/factories/device_factory.rb +29 -0
  51. data/spec/factories/device_grouping_factory.rb +22 -0
  52. data/spec/factories/group_factory.rb +27 -0
  53. data/spec/factories/group_notification_factory.rb +22 -0
  54. data/spec/factories/notification_factory.rb +22 -0
  55. data/spec/factories/pull_notification_factory.rb +22 -0
  56. data/spec/fixtures/hexa.bin +1 -0
  57. data/spec/fixtures/message_for_sending.bin +0 -0
  58. data/spec/rails_root/config/apple_push_notification_development.pem +19 -0
  59. data/spec/spec_helper.rb +55 -0
  60. metadata +282 -0
@@ -0,0 +1,11 @@
1
+ class MakeDeviceTokenIndexNonunique < ActiveRecord::Migration
2
+ def self.up
3
+ remove_index :apn_devices, :column => :token
4
+ add_index :apn_devices, :token
5
+ end
6
+
7
+ def self.down
8
+ remove_index :apn_devices, :column => :token
9
+ add_index :apn_devices, :token, :unique => true
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class AddLaunchNotificationToApnPullNotifications < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :apn_pull_notifications, :launch_notification, :boolean
4
+ end
5
+
6
+ def self.down
7
+ remove_column :apn_pull_notifications, :launch_notification
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), 'apn_on_rails', '**/*.rb')).sort.each do |f|
2
+ require File.expand_path(f)
3
+ end
4
+
@@ -0,0 +1,81 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'configatron'
4
+
5
+ rails_root = File.join(FileUtils.pwd, 'rails_root')
6
+ if defined?(RAILS_ROOT)
7
+ rails_root = RAILS_ROOT
8
+ end
9
+
10
+ rails_env = 'development'
11
+ if defined?(RAILS_ENV)
12
+ rails_env = RAILS_ENV
13
+ end
14
+
15
+ configatron.apn.set_default(:passphrase, '')
16
+ configatron.apn.set_default(:port, 2195)
17
+
18
+ configatron.apn.feedback.set_default(:passphrase, configatron.apn.passphrase)
19
+ configatron.apn.feedback.set_default(:port, 2196)
20
+
21
+ if rails_env == 'production'
22
+ configatron.apn.set_default(:host, 'gateway.push.apple.com')
23
+ configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_production.pem'))
24
+
25
+ configatron.apn.feedback.set_default(:host, 'feedback.push.apple.com')
26
+ configatron.apn.feedback.set_default(:cert, configatron.apn.cert)
27
+ else
28
+ configatron.apn.set_default(:host, 'gateway.sandbox.push.apple.com')
29
+ configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_development.pem'))
30
+
31
+ configatron.apn.feedback.set_default(:host, 'feedback.sandbox.push.apple.com')
32
+ configatron.apn.feedback.set_default(:cert, configatron.apn.cert)
33
+ end
34
+
35
+ module APN # :nodoc:
36
+
37
+ module Errors # :nodoc:
38
+
39
+ # Raised when a notification message to Apple is longer than 256 bytes.
40
+ class ExceededMessageSizeError < StandardError
41
+
42
+ def initialize(message) # :nodoc:
43
+ super("The maximum size allowed for a notification payload is 256 bytes: '#{message}'")
44
+ end
45
+
46
+ end
47
+
48
+ class MissingCertificateError < StandardError
49
+ def initialize
50
+ super("This app has no certificate")
51
+ end
52
+ end
53
+
54
+ end # Errors
55
+
56
+ end # APN
57
+
58
+ base = File.join(File.dirname(__FILE__), 'app', 'models', 'apn', 'base.rb')
59
+ require base
60
+
61
+ Dir.glob(File.join(File.dirname(__FILE__), 'app', 'models', 'apn', '*.rb')).sort.each do |f|
62
+ require f
63
+ end
64
+
65
+ %w{ models controllers helpers }.each do |dir|
66
+ path = File.join(File.dirname(__FILE__), 'app', dir)
67
+ $LOAD_PATH << path
68
+ # puts "Adding #{path}"
69
+ begin
70
+ if ActiveSupport::Dependencies.respond_to? :autoload_paths
71
+ ActiveSupport::Dependencies.autoload_paths << path
72
+ ActiveSupport::Dependencies.autoload_once_paths.delete(path)
73
+ else
74
+ ActiveSupport::Dependencies.load_paths << path
75
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
76
+ end
77
+ rescue NameError
78
+ Dependencies.load_paths << path
79
+ Dependencies.load_once_paths.delete(path)
80
+ end
81
+ end
@@ -0,0 +1,150 @@
1
+ class APN::App < APN::Base
2
+
3
+ has_many :groups, :class_name => 'APN::Group', :dependent => :destroy
4
+ has_many :devices, :class_name => 'APN::Device', :dependent => :destroy
5
+ has_many :notifications, :through => :devices, :dependent => :destroy
6
+ has_many :unsent_notifications, :through => :devices
7
+ has_many :group_notifications, :through => :groups
8
+ has_many :unsent_group_notifications, :through => :groups
9
+
10
+ def cert
11
+ (RAILS_ENV == 'production' ? apn_prod_cert : apn_dev_cert)
12
+ end
13
+
14
+ # Opens a connection to the Apple APN server and attempts to batch deliver
15
+ # an Array of group notifications.
16
+ #
17
+ #
18
+ # As each APN::GroupNotification is sent the <tt>sent_at</tt> column will be timestamped,
19
+ # so as to not be sent again.
20
+ #
21
+ def send_notifications
22
+ if self.cert.nil?
23
+ raise APN::Errors::MissingCertificateError.new
24
+ return
25
+ end
26
+ APN::App.send_notifications_for_cert(self.cert, self.id)
27
+ end
28
+
29
+ def self.send_notifications
30
+ apps = APN::App.all
31
+ apps.each do |app|
32
+ app.send_notifications
33
+ end
34
+ if !configatron.apn.cert.blank?
35
+ global_cert = File.read(configatron.apn.cert)
36
+ send_notifications_for_cert(global_cert, nil)
37
+ end
38
+ end
39
+
40
+ def self.send_notifications_for_cert(the_cert, app_id)
41
+ # unless self.unsent_notifications.nil? || self.unsent_notifications.empty?
42
+ if (app_id == nil)
43
+ conditions = "app_id is null and sent_at is null"
44
+ else
45
+ conditions = ["app_id = ? and sent_at is null", app_id]
46
+ end
47
+ begin
48
+ APN::Connection.open_for_delivery({:cert => the_cert}) do |conn, sock|
49
+ notifications = APN::Notification.find(:all, :select => "apn_notifications.*", :conditions => conditions, :joins => " INNER JOIN apn_devices ON apn_devices.id = apn_notifications.device_id")
50
+ notifications.each do |noty|
51
+ conn.write(noty.message_for_sending)
52
+ noty.sent_at = Time.now
53
+ noty.save
54
+ end
55
+ end
56
+ rescue Exception => e
57
+ log_connection_exception(e)
58
+ end
59
+ # end
60
+ end
61
+
62
+ def send_group_notifications
63
+ if self.cert.nil?
64
+ raise APN::Errors::MissingCertificateError.new
65
+ return
66
+ end
67
+ unless self.unsent_group_notifications.nil? || self.unsent_group_notifications.empty?
68
+ APN::Connection.open_for_delivery({:cert => self.cert}) do |conn, sock|
69
+ unsent_group_notifications.each do |gnoty|
70
+ gnoty.devices.find_each do |device|
71
+ conn.write(gnoty.message_for_sending(device))
72
+ end
73
+ gnoty.sent_at = Time.now
74
+ gnoty.save
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def send_group_notification(gnoty)
81
+ if self.cert.nil?
82
+ raise APN::Errors::MissingCertificateError.new
83
+ return
84
+ end
85
+ unless gnoty.nil?
86
+ APN::Connection.open_for_delivery({:cert => self.cert}) do |conn, sock|
87
+ gnoty.devices.find_each do |device|
88
+ conn.write(gnoty.message_for_sending(device))
89
+ end
90
+ gnoty.sent_at = Time.now
91
+ gnoty.save
92
+ end
93
+ end
94
+ end
95
+
96
+ def self.send_group_notifications
97
+ apps = APN::App.all
98
+ apps.each do |app|
99
+ app.send_group_notifications
100
+ end
101
+ end
102
+
103
+ # Retrieves a list of APN::Device instnces from Apple using
104
+ # the <tt>devices</tt> method. It then checks to see if the
105
+ # <tt>last_registered_at</tt> date of each APN::Device is
106
+ # before the date that Apple says the device is no longer
107
+ # accepting notifications then the device is deleted. Otherwise
108
+ # it is assumed that the application has been re-installed
109
+ # and is available for notifications.
110
+ #
111
+ # This can be run from the following Rake task:
112
+ # $ rake apn:feedback:process
113
+ def process_devices
114
+ if self.cert.nil?
115
+ raise APN::Errors::MissingCertificateError.new
116
+ return
117
+ end
118
+ APN::App.process_devices_for_cert(self.cert)
119
+ end # process_devices
120
+
121
+ def self.process_devices
122
+ apps = APN::App.all
123
+ apps.each do |app|
124
+ app.process_devices
125
+ end
126
+ if !configatron.apn.cert.blank?
127
+ global_cert = File.read(configatron.apn.cert)
128
+ APN::App.process_devices_for_cert(global_cert)
129
+ end
130
+ end
131
+
132
+ def self.process_devices_for_cert(the_cert)
133
+ puts "in APN::App.process_devices_for_cert"
134
+ APN::Feedback.devices(the_cert).each do |device|
135
+ if device.last_registered_at < device.feedback_at
136
+ puts "device #{device.id} -> #{device.last_registered_at} < #{device.feedback_at}"
137
+ device.destroy
138
+ else
139
+ puts "device #{device.id} -> #{device.last_registered_at} not < #{device.feedback_at}"
140
+ end
141
+ end
142
+ end
143
+
144
+
145
+ protected
146
+ def log_connection_exception(ex)
147
+ puts ex.message
148
+ end
149
+
150
+ end
@@ -0,0 +1,9 @@
1
+ module APN
2
+ class Base < ActiveRecord::Base # :nodoc:
3
+
4
+ def self.table_name # :nodoc:
5
+ self.to_s.gsub("::", "_").tableize
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ # Represents an iPhone (or other APN enabled device).
2
+ # An APN::Device can have many APN::Notification.
3
+ #
4
+ # In order for the APN::Feedback system to work properly you *MUST*
5
+ # touch the <tt>last_registered_at</tt> column everytime someone opens
6
+ # your application. If you do not, then it is possible, and probably likely,
7
+ # that their device will be removed and will no longer receive notifications.
8
+ #
9
+ # Example:
10
+ # Device.create(:token => '5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz')
11
+ class APN::Device < APN::Base
12
+
13
+ belongs_to :app, :class_name => 'APN::App'
14
+ has_many :notifications, :class_name => 'APN::Notification'
15
+ has_many :unsent_notifications, :class_name => 'APN::Notification', :conditions => 'sent_at is null'
16
+
17
+ validates_uniqueness_of :token, :scope => :app_id
18
+ validates_format_of :token, :with => /^[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}$/
19
+
20
+ before_create :set_last_registered_at
21
+
22
+ # The <tt>feedback_at</tt> accessor is set when the
23
+ # device is marked as potentially disconnected from your
24
+ # application by Apple.
25
+ attr_accessor :feedback_at
26
+
27
+ # Stores the token (Apple's device ID) of the iPhone (device).
28
+ #
29
+ # If the token comes in like this:
30
+ # '<5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz>'
31
+ # Then the '<' and '>' will be stripped off.
32
+ def token=(token)
33
+ res = token.scan(/\<(.+)\>/).first
34
+ unless res.nil? || res.empty?
35
+ token = res.first
36
+ end
37
+ write_attribute('token', token)
38
+ end
39
+
40
+ # Returns the hexadecimal representation of the device's token.
41
+ def to_hexa
42
+ [self.token.delete(' ')].pack('H*')
43
+ end
44
+
45
+ def set_last_registered_at
46
+ self.last_registered_at = Time.now #if self.last_registered_at.nil?
47
+ end
48
+
49
+ end
@@ -0,0 +1,16 @@
1
+ class APN::DeviceGrouping < APN::Base
2
+
3
+ belongs_to :group, :class_name => 'APN::Group'
4
+ belongs_to :device, :class_name => 'APN::Device'
5
+
6
+ validates_presence_of :device_id, :group_id
7
+ validate :same_app_id
8
+ validates_uniqueness_of :device_id, :scope => :group_id
9
+
10
+ def same_app_id
11
+ unless self.group and self.device and self.group.app_id == self.device.app_id
12
+ errors.add_to_base("device and group must belong to the same app")
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,12 @@
1
+ class APN::Group < APN::Base
2
+
3
+ belongs_to :app, :class_name => 'APN::App'
4
+ has_many :device_groupings, :class_name => "APN::DeviceGrouping", :dependent => :destroy
5
+ has_many :devices, :class_name => 'APN::Device', :through => :device_groupings
6
+ has_many :group_notifications, :class_name => 'APN::GroupNotification'
7
+ has_many :unsent_group_notifications, :class_name => 'APN::GroupNotification', :conditions => 'sent_at is null'
8
+
9
+ validates_presence_of :app_id
10
+ validates_uniqueness_of :name, :scope => :app_id
11
+
12
+ end
@@ -0,0 +1,79 @@
1
+ class APN::GroupNotification < APN::Base
2
+ include ::ActionView::Helpers::TextHelper
3
+ extend ::ActionView::Helpers::TextHelper
4
+ serialize :custom_properties
5
+
6
+ belongs_to :group, :class_name => 'APN::Group'
7
+ has_one :app, :class_name => 'APN::App', :through => :group
8
+ has_many :device_groupings, :through => :group
9
+
10
+ validates_presence_of :group_id
11
+
12
+ def devices
13
+ self.group.devices
14
+ end
15
+
16
+ # Stores the text alert message you want to send to the device.
17
+ #
18
+ # If the message is over 150 characters long it will get truncated
19
+ # to 150 characters with a <tt>...</tt>
20
+ def alert=(message)
21
+ if !message.blank? && message.size > 150
22
+ message = truncate(message, :length => 150)
23
+ end
24
+ write_attribute('alert', message)
25
+ end
26
+
27
+ # Creates a Hash that will be the payload of an APN.
28
+ #
29
+ # Example:
30
+ # apn = APN::GroupNotification.new
31
+ # apn.badge = 5
32
+ # apn.sound = 'my_sound.aiff'
33
+ # apn.alert = 'Hello!'
34
+ # apn.apple_hash # => {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}}
35
+ #
36
+ # Example 2:
37
+ # apn = APN::GroupNotification.new
38
+ # apn.badge = 0
39
+ # apn.sound = true
40
+ # apn.custom_properties = {"typ" => 1}
41
+ # apn.apple_hash # => {"aps" => {"badge" => 0, "sound" => 1.aiff},"typ" => "1"}
42
+ def apple_hash
43
+ result = {}
44
+ result['aps'] = {}
45
+ result['aps']['alert'] = self.alert if self.alert
46
+ result['aps']['badge'] = self.badge.to_i if self.badge
47
+ if self.sound
48
+ result['aps']['sound'] = self.sound if self.sound.is_a? String
49
+ result['aps']['sound'] = "1.aiff" if self.sound.is_a?(TrueClass)
50
+ end
51
+ if self.custom_properties
52
+ self.custom_properties.each do |key,value|
53
+ result["#{key}"] = "#{value}"
54
+ end
55
+ end
56
+ result
57
+ end
58
+
59
+ # Creates the JSON string required for an APN message.
60
+ #
61
+ # Example:
62
+ # apn = APN::Notification.new
63
+ # apn.badge = 5
64
+ # apn.sound = 'my_sound.aiff'
65
+ # apn.alert = 'Hello!'
66
+ # apn.to_apple_json # => '{"aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}'
67
+ def to_apple_json
68
+ self.apple_hash.to_json
69
+ end
70
+
71
+ # Creates the binary message needed to send to Apple.
72
+ def message_for_sending(device)
73
+ json = self.to_apple_json
74
+ message = "\0\0 #{device.to_hexa}\0#{json.length.chr}#{json}"
75
+ raise APN::Errors::ExceededMessageSizeError.new(message) if message.size.to_i > 256
76
+ message
77
+ end
78
+
79
+ end # APN::Notification
@@ -0,0 +1,93 @@
1
+ # Represents the message you wish to send.
2
+ # An APN::Notification belongs to an APN::Device.
3
+ #
4
+ # Example:
5
+ # apn = APN::Notification.new
6
+ # apn.badge = 5
7
+ # apn.sound = 'my_sound.aiff'
8
+ # apn.alert = 'Hello!'
9
+ # apn.device = APN::Device.find(1)
10
+ # apn.save
11
+ #
12
+ # To deliver call the following method:
13
+ # APN::Notification.send_notifications
14
+ #
15
+ # As each APN::Notification is sent the <tt>sent_at</tt> column will be timestamped,
16
+ # so as to not be sent again.
17
+ class APN::Notification < APN::Base
18
+ include ::ActionView::Helpers::TextHelper
19
+ extend ::ActionView::Helpers::TextHelper
20
+ serialize :custom_properties
21
+
22
+ belongs_to :device, :class_name => 'APN::Device'
23
+ has_one :app, :class_name => 'APN::App', :through => :device
24
+
25
+ # Stores the text alert message you want to send to the device.
26
+ #
27
+ # If the message is over 150 characters long it will get truncated
28
+ # to 150 characters with a <tt>...</tt>
29
+ def alert=(message)
30
+ if !message.blank? && message.size > 150
31
+ message = truncate(message, :length => 150)
32
+ end
33
+ write_attribute('alert', message)
34
+ end
35
+
36
+ # Creates a Hash that will be the payload of an APN.
37
+ #
38
+ # Example:
39
+ # apn = APN::Notification.new
40
+ # apn.badge = 5
41
+ # apn.sound = 'my_sound.aiff'
42
+ # apn.alert = 'Hello!'
43
+ # apn.apple_hash # => {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}}
44
+ #
45
+ # Example 2:
46
+ # apn = APN::Notification.new
47
+ # apn.badge = 0
48
+ # apn.sound = true
49
+ # apn.custom_properties = {"typ" => 1}
50
+ # apn.apple_hash # => {"aps" => {"badge" => 0, "sound" => "1.aiff"}, "typ" => "1"}
51
+ def apple_hash
52
+ result = {}
53
+ result['aps'] = {}
54
+ result['aps']['alert'] = self.alert if self.alert
55
+ result['aps']['badge'] = self.badge.to_i if self.badge
56
+ if self.sound
57
+ result['aps']['sound'] = self.sound if self.sound.is_a? String
58
+ result['aps']['sound'] = "1.aiff" if self.sound.is_a?(TrueClass)
59
+ end
60
+ if self.custom_properties
61
+ self.custom_properties.each do |key,value|
62
+ result["#{key}"] = "#{value}"
63
+ end
64
+ end
65
+ result
66
+ end
67
+
68
+ # Creates the JSON string required for an APN message.
69
+ #
70
+ # Example:
71
+ # apn = APN::Notification.new
72
+ # apn.badge = 5
73
+ # apn.sound = 'my_sound.aiff'
74
+ # apn.alert = 'Hello!'
75
+ # apn.to_apple_json # => '{"aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}'
76
+ def to_apple_json
77
+ self.apple_hash.to_json
78
+ end
79
+
80
+ # Creates the binary message needed to send to Apple.
81
+ def message_for_sending
82
+ json = self.to_apple_json
83
+ message = "\0\0 #{self.device.to_hexa}\0#{json.length.chr}#{json}"
84
+ raise APN::Errors::ExceededMessageSizeError.new(message) if message.size.to_i > 256
85
+ message
86
+ end
87
+
88
+ def self.send_notifications
89
+ ActiveSupport::Deprecation.warn("The method APN::Notification.send_notifications is deprecated. Use APN::App.send_notifications instead.")
90
+ APN::App.send_notifications
91
+ end
92
+
93
+ end # APN::Notification