diff --git a/.gitignore b/.gitignore index 0202b51..c615632 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,9 @@ test/tmp test/version_tmp tmp example/.sass-cache +.env # YARD artifacts .yardoc _yardoc -doc/ \ No newline at end of file +doc/ diff --git a/README.md b/README.md index 158bce4..6c1b010 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Bundler.require run Helios::Application.new do service :data, model: 'path/to/DataModel.xcdatamodel' service :push_notification, apn_certificate: 'path/to/apple_push_notification.pem', apn_environment: 'development' + service :gcm, gcm_api_key: 'xxxxxxx' service :in_app_purchase service :passbook end @@ -103,6 +104,7 @@ Helios can be run as Rails middleware by adding this to the configuration block config.middleware.use Helios::Application do service :data, model: 'path/to/DataModel.xcdatamodel' service :push_notification, apn_certificate: 'path/to/apple_push_notification.pem', apn_environment: 'development' + service :gcm, gcm_api_key: 'xxxxxxx' service :in_app_purchase service :passbook end @@ -157,15 +159,39 @@ Each entity in the specified data model will have a `Sequel::Model` subclass cre - + - + - + + + +
Endpoints
PUT /devices/:tokenPUT /push_notification/devices/:token Register or update existing device for push notifications
DELETE /devices/:tokenDELETE /push_notification/devices/:token Unregister a device from receiving push notifications
POST /messagePOST /push_notification/messageSend out a push notification to some devices
+ +--- + +`gcm`: Adds Android push notification(Google Cloud Messaging) registration / unregistration endpoints. + +**Associated Classes** + +- `Rack::GCM::Device` + + + + + + + + + + + + +
Endpoints
PUT /gcm/devices/:tokenRegister or update existing device for push notifications
DELETE /gcm/devices/:tokenUnregister a device from receiving push notifications
POST /gcm/message Send out a push notification to some devices
@@ -274,7 +300,13 @@ To run Helios in development mode on `localhost`, run the `server` command: Once you have registered a device and set up your certificate, try this: - $ curl -X POST -d 'payload={"aps": {"alert":"Blastoff!"}}' http://localhost:5000/message + $ curl -X POST -d 'payload={"aps": {"alert":"Blastoff!"}}' http://localhost:5000/push-notification/message + +### Testing Android Push Notifications(Google Cloud Messaging) + +Once you have registered a device and set up your certificate, try this: + + $ curl -X POST -d 'payload={"alert":"Blastoff!"}' http://localhost:5000/gcm/message ### Running the Helios Console @@ -336,6 +368,13 @@ Now covert the p12 file to a pem file: $ openssl pkcs12 -in cert.p12 -out apple_push_notification.pem -nodes -clcerts +## Integrating with an Android Application + +### Google Cloud Messaging Registration + +See more detail [Google Cloud Messaging Tutorial](http://developer.android.com/google/gcm/gs.html) + + ## Coming Attractions There's still a lot to do to make Helios even better. Here are some ideas that are at the top of the list: @@ -346,7 +385,7 @@ There's still a lot to do to make Helios even better. Here are some ideas that a - Better RubyMotion integration - Support for multiple schema definitions (not just Core Data) - Send push notifications from the UI -- Support for additional platforms (Android, WP7) +- Support for additional platforms (WP7) --- diff --git a/example/Gemfile b/example/Gemfile index f8388ac..2afef97 100644 --- a/example/Gemfile +++ b/example/Gemfile @@ -3,3 +3,4 @@ source "https://rubygems.org" gem 'helios', path: File.join(__FILE__, "../..") gem 'unicorn' gem 'pg' +gem 'thin' diff --git a/example/config.ru b/example/config.ru index afe2e92..aa00d76 100644 --- a/example/config.ru +++ b/example/config.ru @@ -3,7 +3,8 @@ Bundler.require app = Helios::Application.new do service :data, model: Dir['*.xcdatamodel*'].first, only: [:create, :read] - service :push_notification, frontend: false + service :push_notification, frontend: true + service :gcm, gcm_api_key: ENV['GCM_API_KEY'] service :in_app_purchase service :passbook service :newsstand, storage: ({ @@ -33,4 +34,4 @@ end # } # end -run app +run app \ No newline at end of file diff --git a/helios.gemspec b/helios.gemspec index 5f0b1f1..d4e6798 100644 --- a/helios.gemspec +++ b/helios.gemspec @@ -36,6 +36,8 @@ Gem::Specification.new do |s| s.add_dependency "rails-database-url", "~> 1.0" s.add_dependency "fog", "~> 1.10" s.add_dependency "houston", "~> 0.2" + s.add_dependency "rack-gcm", "~> 0.0.2" + s.add_dependency "sanjose", "~> 0.0.3" s.add_development_dependency "rake" s.add_development_dependency "rspec" diff --git a/lib/helios/backend.rb b/lib/helios/backend.rb index aec7a17..8dd41e2 100644 --- a/lib/helios/backend.rb +++ b/lib/helios/backend.rb @@ -62,3 +62,4 @@ def constantize(identifier) require 'helios/backend/passbook' require 'helios/backend/push-notification' require 'helios/backend/newsstand' +require 'helios/backend/gcm' diff --git a/lib/helios/backend/gcm.rb b/lib/helios/backend/gcm.rb new file mode 100644 index 0000000..0aff596 --- /dev/null +++ b/lib/helios/backend/gcm.rb @@ -0,0 +1,106 @@ +require 'rack/gcm' + +require 'sinatra/base' +require 'sinatra/param' + +require 'sanjose' + +class Helios::Backend::Gcm < Sinatra::Base + helpers Sinatra::Param + attr_reader :gcm_api_key + + def initialize(app, options = {}, &block) + super(Rack::GCM.new) + + @api_key = options[:gcm_api_key] || ENV['GCM_API_KEY'] + end + + before do + content_type :json + end + + get '/devices/?' do + param :q, String + + devices = ::Rack::GCM::Device.dataset + devices = devices.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty? + + if params[:page] or params[:per_page] + param :page, Integer, default: 1, min: 1 + param :per_page, Integer, default: 100, in: (1..100) + + { + devices: devices.limit(params[:per_page], (params[:page] - 1) * params[:per_page]), + page: params[:page], + total: devices.count + }.to_json + else + param :limit, Integer, default: 100, in: (1..100) + param :offset, Integer, default: 0, min: 0 + + { + devices: devices.limit(params[:limit], params[:offset]) + }.to_json + end + end + + get '/devices/:token/?' do + record = ::Rack::GCM::Device.find(token: params[:token]) + + if record + {device: record}.to_json + else + status 404 + end + end + + head '/message' do + status 503 and return unless client + + status 204 + end + + post '/message' do + status 503 and return unless client + + param :payload, String, empty: false + param :tokens, Array, empty: false + + tokens = params[:tokens] || ::Rack::GCM::Device.all.collect(&:token) + + options = JSON.parse(params[:payload]) + puts tokens.inspect + puts options + + # Create a notification that alerts a message to the user. + notification = Sanjose::Notification.new(devices: tokens) + notification.data = options + + begin + results = client.push(notification) + + status 204 + rescue => error + status 500 + + {error: error}.to_json + end + + status 200 + end + + private + + def client + begin + return nil unless api_key + + client = Sanjose::Client.new + client.gcm_api_key = api_key + + return client + rescue + return nil + end + end +end diff --git a/lib/helios/frontend.rb b/lib/helios/frontend.rb index 7bd7f1c..a151599 100644 --- a/lib/helios/frontend.rb +++ b/lib/helios/frontend.rb @@ -25,31 +25,31 @@ class Frontend < Sinatra::Base serve '/fonts', from: '/fonts' js :application, '/javascripts/application.js', [ - 'javascripts/vendor/jquery.js', - 'javascripts/vendor/jquery/jquery.ui.widget.js', - 'javascripts/vendor/jquery/jquery.fileupload.js', - 'javascripts/vendor/jquery/jquery.fileupload-ui.js', - 'javascripts/vendor/underscore.js', - 'javascripts/vendor/backbone.js', - 'javascripts/vendor/backbone.paginator.js', - 'javascripts/vendor/backbone.datagrid.js', - 'javascripts/vendor/codemirror.js', - 'javascripts/vendor/codemirror.javascript.js', - 'javascripts/vendor/foundation.js', - 'javascripts/vendor/foundation/foundation.dropdown.js', - 'javascripts/vendor/foundation/foundation.reveal.js', - 'javascripts/vendor/date.js', - 'javascripts/vendor/linkheaders.js', - 'javascripts/helios.js', - 'javascripts/helios/models.js', - 'javascripts/helios/collections.js', - 'javascripts/helios/templates.js', - 'javascripts/helios/views.js', - 'javascripts/helios/router.js', + '/javascripts/vendor/jquery.js', + '/javascripts/vendor/jquery/jquery.ui.widget.js', + '/javascripts/vendor/jquery/jquery.fileupload.js', + '/javascripts/vendor/jquery/jquery.fileupload-ui.js', + '/javascripts/vendor/underscore.js', + '/javascripts/vendor/backbone.js', + '/javascripts/vendor/backbone.paginator.js', + '/javascripts/vendor/backbone.datagrid.js', + '/javascripts/vendor/codemirror.js', + '/javascripts/vendor/codemirror.javascript.js', + '/javascripts/vendor/foundation.js', + '/javascripts/vendor/foundation/foundation.dropdown.js', + '/javascripts/vendor/foundation/foundation.reveal.js', + '/javascripts/vendor/date.js', + '/javascripts/vendor/linkheaders.js', + '/javascripts/helios.js', + '/javascripts/helios/models.js', + '/javascripts/helios/collections.js', + '/javascripts/helios/templates.js', + '/javascripts/helios/views.js', + '/javascripts/helios/router.js', ] css :application, '/stylesheets/application.css', [ - 'stylesheets/screen.css' + '/stylesheets/screen.css' ] end @@ -64,3 +64,12 @@ class Frontend < Sinatra::Base end end end + + +# Workaround for Sinatra::Assetpack bug +# See https://github.com/helios-framework/helios/issues/68 +class Sinatra::AssetPack::Package + def to_production_html(path_prefix, options={}) + to_development_html(path_prefix, options) + end +end diff --git a/lib/helios/frontend/javascripts/helios.coffee b/lib/helios/frontend/javascripts/helios.coffee index 69e589b..16f18bb 100644 --- a/lib/helios/frontend/javascripts/helios.coffee +++ b/lib/helios/frontend/javascripts/helios.coffee @@ -63,6 +63,8 @@ $ -> Helios.services['newsstand'] = href when "Helios::Backend::PushNotification" Helios.services['push-notification'] = href + when "Helios::Backend::Gcm" + Helios.services['gcm'] = href when "Helios::Backend::Passbook" Helios.services['passbook'] = href diff --git a/lib/helios/frontend/javascripts/helios/collections.coffee b/lib/helios/frontend/javascripts/helios/collections.coffee index 1c6c84f..621ce57 100644 --- a/lib/helios/frontend/javascripts/helios/collections.coffee +++ b/lib/helios/frontend/javascripts/helios/collections.coffee @@ -67,7 +67,6 @@ class Helios.Collections.Devices extends Helios.Collection comparator: (device) -> device.get('token') - class Helios.Collections.Receipts extends Helios.Collection model: Helios.Models.Receipt diff --git a/lib/helios/frontend/javascripts/helios/router.coffee b/lib/helios/frontend/javascripts/helios/router.coffee index 26e642c..8b221ce 100644 --- a/lib/helios/frontend/javascripts/helios/router.coffee +++ b/lib/helios/frontend/javascripts/helios/router.coffee @@ -13,6 +13,7 @@ class Helios.Routers.Root extends Backbone.Router routes: '': 'index' 'data': 'data' + 'gcm': 'gcm' 'push-notification': 'push_notification' 'in-app-purchase': 'in_app_purchase' 'passbook': 'passbook' @@ -25,6 +26,12 @@ class Helios.Routers.Root extends Backbone.Router Helios.entities.fetch(type: 'OPTIONS') @views.entities.render() + gcm: -> + @android_devices ?= new Helios.Collections.Devices + @android_devices.paginator_core.url = Helios.services['gcm'] + '/devices' + @views.andoid_devices ?= new Helios.Views.AndroidDevices(collection: @android_devices) + @views.andoid_devices.render() + push_notification: -> @devices ?= new Helios.Collections.Devices @devices.paginator_core.url = Helios.services['push-notification'] + '/devices' diff --git a/lib/helios/frontend/javascripts/helios/views.coffee b/lib/helios/frontend/javascripts/helios/views.coffee index a2eb4c4..d25d0a9 100644 --- a/lib/helios/frontend/javascripts/helios/views.coffee +++ b/lib/helios/frontend/javascripts/helios/views.coffee @@ -71,6 +71,35 @@ class Helios.Views.Devices extends Backbone.View @collection.query = $(e.target).val() @collection.fetch() +class Helios.Views.AndroidDevices extends Backbone.View + template: JST['gcm/devices'] + el: "[role='main']" + + events: + 'keyup form.filter input': 'filter' + + initialize: -> + @datagrid = new Backbone.Datagrid({ + collection: @collection, + columns: @collection.fields, + paginated: true, + perPage: 20 + }) + + render: => + @$el.html(@template()) + + # @composeView ?= new Helios.Views.Compose() + # @composeView.render() + @$el.find("#datagrid").html(@datagrid.el) + + @ + + filter: (e) -> + e.preventDefault() + @collection.query = $(e.target).val() + @collection.fetch() + class Helios.Views.Compose extends Backbone.View template: JST['push-notification/compose'] el: "#compose-modal" diff --git a/lib/helios/frontend/stylesheets/_fonts.sass b/lib/helios/frontend/stylesheets/_fonts.sass index 21a8a48..885defe 100644 --- a/lib/helios/frontend/stylesheets/_fonts.sass +++ b/lib/helios/frontend/stylesheets/_fonts.sass @@ -70,6 +70,9 @@ i.heroku:before i.push-notification:before content: "\6f" +i.gcm:before + content: "\6f" + i.passbook:before content: "\78" diff --git a/lib/helios/frontend/templates/gcm/compose.jst.tpl b/lib/helios/frontend/templates/gcm/compose.jst.tpl new file mode 100644 index 0000000..bed38a3 --- /dev/null +++ b/lib/helios/frontend/templates/gcm/compose.jst.tpl @@ -0,0 +1,70 @@ +
+

Send a Google Cloud Message

+
+ +
+ + +
+ Payload + +
+ + +
+ : +
+
+
+ +
+ + +
+ +
+
+
+ + Carrier + 100% +
+ + +
+ +
+

App Name

+

+
+ + +
diff --git a/lib/helios/frontend/templates/gcm/devices.jst.tpl b/lib/helios/frontend/templates/gcm/devices.jst.tpl new file mode 100644 index 0000000..9b3d0f1 --- /dev/null +++ b/lib/helios/frontend/templates/gcm/devices.jst.tpl @@ -0,0 +1,17 @@ +
+

Devices

+ +
+ +
+ <% if (false) { %> + + <% } %> +
+ +
+
+
+
+ +
diff --git a/lib/helios/frontend/templates/navigation.jst.tpl b/lib/helios/frontend/templates/navigation.jst.tpl index 2f2a3f4..6292ac5 100644 --- a/lib/helios/frontend/templates/navigation.jst.tpl +++ b/lib/helios/frontend/templates/navigation.jst.tpl @@ -11,6 +11,12 @@ <% } %> + <% if(Helios.services['gcm']) { %> +
  • + Google Cloud Message +
  • + <% } %> + <% if(Helios.services['in-app-purchase']) { %>
  • In-App Purchase diff --git a/lib/helios/version.rb b/lib/helios/version.rb index 7917f5b..d6897e1 100644 --- a/lib/helios/version.rb +++ b/lib/helios/version.rb @@ -1,3 +1,3 @@ module Helios - VERSION = "0.2.3" + VERSION = "0.2.4" end