diff --git a/README.textile b/README.textile index c6426b1..54a76ee 100644 --- a/README.textile +++ b/README.textile @@ -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 +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: +

+  >> notification.devices << device
+
+as it was shown in the previous example. + +The following Rake task can then be used to deliver urgent notifications: +

+  $ rake gcm:notifications:deliver["urgent"]
+
-The following Rake task can then be used to deliver notifications: +If you want to deliver every notification that wasn't delivered yet:

   $ rake gcm:notifications:deliver
 
@@ -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. diff --git a/VERSION b/VERSION index 7693c96..845639e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.3 \ No newline at end of file +0.1.4 diff --git a/gcm_on_rails.gemspec b/gcm_on_rails.gemspec index 5fc23ee..082e025 100644 --- a/gcm_on_rails.gemspec +++ b/gcm_on_rails.gemspec @@ -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"] @@ -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" diff --git a/lib/gcm_on_rails/app/models/gcm/device.rb b/lib/gcm_on_rails/app/models/gcm/device.rb index 165aeaa..579624d 100644 --- a/lib/gcm_on_rails/app/models/gcm/device.rb +++ b/lib/gcm_on_rails/app/models/gcm/device.rb @@ -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 @@ -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 \ No newline at end of file +end diff --git a/lib/gcm_on_rails/app/models/gcm/notification.rb b/lib/gcm_on_rails/app/models/gcm/notification.rb index 8b144ac..4c3d8c7 100644 --- a/lib/gcm_on_rails/app/models/gcm/notification.rb +++ b/lib/gcm_on_rails/app/models/gcm/notification.rb @@ -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 @@ -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 \ No newline at end of file +end diff --git a/lib/gcm_on_rails/libs/connection.rb b/lib/gcm_on_rails/libs/connection.rb index c240329..bb5f6fc 100644 --- a/lib/gcm_on_rails/libs/connection.rb +++ b/lib/gcm_on_rails/libs/connection.rb @@ -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}" @@ -40,4 +41,4 @@ def open end end end -end \ No newline at end of file +end diff --git a/lib/gcm_on_rails/tasks/gcm.rake b/lib/gcm_on_rails/tasks/gcm.rake index 1df4faa..a645282 100644 --- a/lib/gcm_on_rails/tasks/gcm.rake +++ b/lib/gcm_on_rails/tasks/gcm.rake @@ -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 diff --git a/lib/generators/gcm_migrations_generator.rb b/lib/generators/gcm_migrations_generator.rb index a8d95a3..0b43c9e 100644 --- a/lib/generators/gcm_migrations_generator.rb +++ b/lib/generators/gcm_migrations_generator.rb @@ -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| @@ -29,4 +30,4 @@ def create_migrations end end end -end # GcmMigrationsGenerator \ No newline at end of file +end # GcmMigrationsGenerator diff --git a/lib/generators/templates/gcm_migrations/create_gcm_devices_notifications.rb b/lib/generators/templates/gcm_migrations/create_gcm_devices_notifications.rb new file mode 100644 index 0000000..1abb1ff --- /dev/null +++ b/lib/generators/templates/gcm_migrations/create_gcm_devices_notifications.rb @@ -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 diff --git a/lib/generators/templates/gcm_migrations/create_gcm_notifications.rb b/lib/generators/templates/gcm_migrations/create_gcm_notifications.rb index 72b49ab..fda6e58 100644 --- a/lib/generators/templates/gcm_migrations/create_gcm_notifications.rb +++ b/lib/generators/templates/gcm_migrations/create_gcm_notifications.rb @@ -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 \ No newline at end of file +end