Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,25 @@ Please note that a more detailed introduction to GCM is located at "http://devel
$ rails console
>> device = Gcm::Device.create(:registration_id => "XXXXXXXXXXXXXXXXXXXXXX")
>> notification = Gcm::Notification.new
>> notification.device = device
>> notification.devices << device
>> notification.collapse_key = "updates_available"
>> notification.delay_while_idle = true
>> notification.data = {:registration_ids => ["RegistrationID"], :data => {:message_text => "Get on cloud nine"}}
>> notification.data = {:data => {:message_text => "Get on cloud nine"}}
>> notification.notification_type = "urgent"
>> notification.save
</code></pre>
In this version you don't have to add the registration ids of the devices you're going to send the notifications. You just add them to notifications_devices relation by doing:
<pre><code>
>> notification.devices << device
</code></pre>
as it was shown in the previous example.

The following Rake task can then be used to deliver urgent notifications:
<pre><code>
$ rake gcm:notifications:deliver["urgent"]
</code></pre>

The following Rake task can then be used to deliver notifications:
If you want to deliver every notification that wasn't delivered yet:
<pre><code>
$ rake gcm:notifications:deliver
</code></pre>
Expand Down Expand Up @@ -128,7 +139,7 @@ h2. To-Do's:
in the message object of the return json.

- Tests, tests then some more tests. These need to be implemented.
- Implement "broadcasting" sending and processing responses to multiple registration id's within one request. Currently only one message to a single registration id is implemented.
- Implement some sort of mechanism to update from previous versions


Released under the MIT license.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.3
0.1.4
3 changes: 2 additions & 1 deletion gcm_on_rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Gem::Specification.new do |s|
s.name = "gcm_on_rails"
s.version = "0.1.3"
s.version = "0.1.4"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Dennis Ondeng"]
Expand Down Expand Up @@ -35,6 +35,7 @@ Gem::Specification.new do |s|
"lib/gcm_on_rails_tasks.rb",
"lib/generators/gcm_migrations_generator.rb",
"lib/generators/templates/gcm_migrations/create_gcm_devices.rb",
"lib/generators/templates/gcm_migrations/create_gcm_devices_notifications.rb",
"lib/generators/templates/gcm_migrations/create_gcm_notifications.rb"
]
s.homepage = "http://github.com/dondeng/gcm_on_rails"
Expand Down
4 changes: 2 additions & 2 deletions lib/gcm_on_rails/app/models/gcm/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Gcm::Device < Gcm::Base

attr_accessible :registration_id

has_many :notifications, :class_name => 'Gcm::Notification', :dependent => :destroy
has_and_belongs_to_many :notifications, :class_name => 'Gcm::Notification', :join_table => :gcm_devices_notifications
validates_presence_of :registration_id
validates_uniqueness_of :registration_id

Expand All @@ -28,4 +28,4 @@ class Gcm::Device < Gcm::Base
def set_last_registered_at
self.last_registered_at = Time.now if self.last_registered_at.nil?
end
end
end
128 changes: 69 additions & 59 deletions lib/gcm_on_rails/app/models/gcm/notification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Gcm::Notification < Gcm::Base
extend ::ActionView::Helpers::TextHelper
serialize :data

belongs_to :device, :class_name => 'Gcm::Device'
has_and_belongs_to_many :devices, :class_name => 'Gcm::Device', :join_table => :gcm_devices_notifications
validates_presence_of :collapse_key if :time_to_live?

class << self
Expand All @@ -28,73 +28,83 @@ class << self
# {:message=>"{\"multicast_id\":6085691036338669615,\"success\":1,\"failure\":0,\"canonical_ids\":0,\"results\":[{\"message_id\":\"0:1349723376618187%d702725e98d39af3\"}]}", :code=>200}
#
#
def send_notifications(notifications = Gcm::Notification.all(:conditions => {:sent_at => nil}, :joins => :device, :readonly => false))
def send_notifications_by_type(notification_type, notifications = Gcm::Notification.all(:conditions => {:sent_at => nil, notification_type: notification_type}, :include => :devices, :readonly => false))
notify(notifications)
end

if configatron.gcm_on_rails.delivery_format and configatron.gcm_on_rails.delivery_format == 'plain_text'
format = "plain_text"
else
format = "json"
end
def send_notifications(notifications = Gcm::Notification.all(:conditions => {:sent_at => nil}, :include => :devices, :readonly => false))
notify(notifications)
end

unless notifications.nil? || notifications.empty?
api_key = Gcm::Connection.open
if api_key
notifications.each do |notification|
private

logger.info "notification = #{notification.inspect}"
response = Gcm::Connection.send_notification(notification, api_key, format)
logger.info "response = #{response.inspect}"
def notify notifications

if response[:code] == 200
if response[:message].nil?
# TODO - Making this assumption might not be right. HTTP status code 200 does not really signify success
# if Gcm servers returned nil for the message
error = "success"
elsif format == "json"
error = ""
message_data = JSON.parse response[:message]
success = message_data['success']
error = message_data['results'][0]['error'] if success == 0
elsif format == "plain_text" #format is plain text
message_data = response[:message]
error = response[:message].split('=')[1]
end
if configatron.gcm_on_rails.delivery_format and configatron.gcm_on_rails.delivery_format == 'plain_text'
format = "plain_text"
else
format = "json"
end

unless notifications.nil? || notifications.empty?
api_key = Gcm::Connection.open
if api_key
notifications.each do |notification|

logger.info "notification = #{notification.inspect}"
response = Gcm::Connection.send_notification(notification, api_key, format)
logger.info "response = #{response.inspect}"

case error
when "MissingRegistration"
ex = Gcm::Errors::MissingRegistration.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "InvalidRegistration"
ex = Gcm::Errors::InvalidRegistration.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "MismatchedSenderId"
ex = Gcm::Errors::MismatchSenderId.new(response[:message])
logger.warn(ex.message)
when "NotRegistered"
ex = Gcm::Errors::NotRegistered.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "MessageTooBig"
ex = Gcm::Errors::MessageTooBig.new(response[:message])
logger.warn(ex.message)
else
notification.sent_at = Time.now
notification.save!
if response[:code] == 200
if response[:message].nil?
# TODO - Making this assumption might not be right. HTTP status code 200 does not really signify success
# if Gcm servers returned nil for the message
error = "success"
elsif format == "json"
error = ""
message_data = JSON.parse response[:message]
success = message_data['success']
error = message_data['results'][0]['error'] if success == 0
elsif format == "plain_text" #format is plain text
message_data = response[:message]
error = response[:message].split('=')[1]
end


case error
when "MissingRegistration"
ex = Gcm::Errors::MissingRegistration.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "InvalidRegistration"
ex = Gcm::Errors::InvalidRegistration.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "MismatchedSenderId"
ex = Gcm::Errors::MismatchSenderId.new(response[:message])
logger.warn(ex.message)
when "NotRegistered"
ex = Gcm::Errors::NotRegistered.new(response[:message])
logger.warn("#{ex.message}, destroying gcm_device with id #{notification.device.id}")
notification.device.destroy
when "MessageTooBig"
ex = Gcm::Errors::MessageTooBig.new(response[:message])
logger.warn(ex.message)
else
#notification.sent_at = Time.now
notification.save!
end
elsif response[:code] == 401
raise Gcm::Errors::InvalidAuthToken.new(message_data)
elsif response[:code] == 503
raise Gcm::Errors::ServiceUnavailable.new(message_data)
elsif response[:code] == 500
raise Gcm::Errors::InternalServerError.new(message_data)
end
elsif response[:code] == 401
raise Gcm::Errors::InvalidAuthToken.new(message_data)
elsif response[:code] == 503
raise Gcm::Errors::ServiceUnavailable.new(message_data)
elsif response[:code] == 500
raise Gcm::Errors::InternalServerError.new(message_data)
end

end
end
end
end
end
end
end
end
5 changes: 3 additions & 2 deletions lib/gcm_on_rails/libs/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ def send_notification(notification, api_key, format)
data = notification.data.merge({:collapse_key => notification.collapse_key}) unless notification.collapse_key.nil?
data = data.merge({:delay_while_idle => notification.delay_while_idle}) unless notification.delay_while_idle.nil?
data = data.merge({:time_to_live => notification.time_to_live}) unless notification.time_to_live.nil?
data = data.merge({:registration_ids => notification.devices.map{ |d| d.registration_id } })
data = data.to_json
else #plain text format
headers = {"Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8",
"Authorization" => "key=#{api_key}"}

post_data = notification.data[:data].map{|k, v| "&data.#{k}=#{URI.escape(v)}".reduce{|k, v| k + v}}[0]
extra_data = "registration_id=#{notification.data[:registration_ids][0]}"
extra_data = "registration_ids=#{notification.devices.map{ |d| d.registration_id }}"
extra_data = "#{extra_data}&collapse_key=#{notification.collapse_key}" unless notification.collapse_key.nil?
extra_data = "#{extra_data}&delay_while_idle=1" if notification.delay_while_idle
data = "#{extra_data}#{post_data}"
Expand All @@ -40,4 +41,4 @@ def open
end
end
end
end
end
9 changes: 7 additions & 2 deletions lib/gcm_on_rails/tasks/gcm.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ namespace :gcm do
namespace :notifications do

desc "Deliver all unsent Gcm notifications."
task :deliver => [:environment] do
Gcm::Notification.send_notifications
task :deliver, [:type] => [:environment] do |t, args|
if args.type.nil?
Gcm::Notification.send_notifications
else
Gcm::Notification.send_notifications_by_type(args.type)
end
end

end
end
5 changes: 3 additions & 2 deletions lib/generators/gcm_migrations_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def self.base_root
def create_migrations
templates = {
'create_gcm_devices.rb' => 'db/migrate/create_gcm_devices.rb',
'create_gcm_notifications.rb' => 'db/migrate/create_gcm_notifications.rb'
'create_gcm_notifications.rb' => 'db/migrate/create_gcm_notifications.rb',
'create_gcm_devices_notifications.rb' => 'db/migrate/create_gcm_devices_notifications.rb'
}

templates.each_pair do |name, path|
Expand All @@ -29,4 +30,4 @@ def create_migrations
end
end
end
end # GcmMigrationsGenerator
end # GcmMigrationsGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class CreateGcmDevicesNotifications < ActiveRecord::Migration # :nodoc:

def self.up

create_table :gcm_devices_notifications, :id => false do |t|
t.integer :device_id, :null => false
t.integer :notification_id, :null => false
end

add_index :gcm_devices_notifications, [:device_id, :notification_id], :unique => true
end

def self.down
drop_table :gcm_devices_notifications
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ class CreateGcmNotifications < ActiveRecord::Migration # :nodoc:
def self.up

create_table :gcm_notifications do |t|
t.integer :device_id, :null => false
t.string :collapse_key
t.text :data
t.boolean :delay_while_idle
t.datetime :sent_at
t.integer :time_to_live
t.string :notification_type
t.timestamps
end

add_index :gcm_notifications, :device_id
end

def self.down
drop_table :gcm_notifications
end
end
end