From afa71981f0eb5bc65d36ed33cf5fa902dcc0c6a5 Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Tue, 4 Jun 2013 15:20:45 +0100 Subject: [PATCH 1/6] 'android support' implemented --- .env | 1 + README.md | 47 +++++++- example/config.ru | 5 +- helios.gemspec | 2 + lib/helios/backend.rb | 1 + lib/helios/backend/gcm.rb | 106 ++++++++++++++++++ lib/helios/frontend.rb | 44 ++++---- lib/helios/frontend/javascripts/helios.coffee | 2 + .../javascripts/helios/collections.coffee | 1 - .../frontend/javascripts/helios/router.coffee | 7 ++ .../frontend/javascripts/helios/views.coffee | 29 +++++ lib/helios/frontend/stylesheets/_fonts.sass | 3 + .../frontend/templates/gcm/compose.jst.tpl | 70 ++++++++++++ .../frontend/templates/gcm/devices.jst.tpl | 17 +++ .../frontend/templates/navigation.jst.tpl | 6 + 15 files changed, 311 insertions(+), 30 deletions(-) create mode 100644 .env create mode 100644 lib/helios/backend/gcm.rb create mode 100644 lib/helios/frontend/templates/gcm/compose.jst.tpl create mode 100644 lib/helios/frontend/templates/gcm/devices.jst.tpl diff --git a/.env b/.env new file mode 100644 index 0000000..c16973b --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL=postgres://minkim@localhost/livestation_development diff --git a/README.md b/README.md index 158bce4..1beef2a 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 @@ -321,6 +353,11 @@ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken } ``` +### Google Cloud Messaging Registration + +See more detail [Google Cloud Messaging Tutorial](http://developer.android.com/google/gcm/gs.html) + + ### Converting Your Push Notification Certificate > These instructions come from the [APN on Rails](https://github.com/PRX/apn_on_rails) project. @@ -346,7 +383,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/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..d22eaea 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 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

+

+
+ +
+
+ + slide to view +
+
+
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 From 327ae1704afa07ebfc512798a9492ccb9a7cd374 Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Tue, 4 Jun 2013 15:22:41 +0100 Subject: [PATCH 2/6] readme file updated --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1beef2a..b9f8d5c 100644 --- a/README.md +++ b/README.md @@ -353,11 +353,6 @@ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken } ``` -### Google Cloud Messaging Registration - -See more detail [Google Cloud Messaging Tutorial](http://developer.android.com/google/gcm/gs.html) - - ### Converting Your Push Notification Certificate > These instructions come from the [APN on Rails](https://github.com/PRX/apn_on_rails) project. @@ -373,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: From ecf9550f62ea0d91a15c0827bb6c1f8c88875294 Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Tue, 4 Jun 2013 15:25:17 +0100 Subject: [PATCH 3/6] fixed typos in readme file --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b9f8d5c..6c1b010 100644 --- a/README.md +++ b/README.md @@ -159,15 +159,15 @@ Each entity in the specified data model will have a `Sequel::Model` subclass cre - + - + - +
    Endpoints
    PUT /push-notification/devices/:tokenPUT /push_notification/devices/:token Register or update existing device for push notifications
    DELETE /push-notification/devices/:tokenDELETE /push_notification/devices/:token Unregister a device from receiving push notifications
    POST /push-notification/messagePOST /push_notification/message Send out a push notification to some devices
    From 79355b6b692713767e2d03a41519c27172fae732 Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Wed, 5 Jun 2013 11:55:05 +0100 Subject: [PATCH 4/6] test --- example/config.ru | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/example/config.ru b/example/config.ru index aa00d76..a32b739 100644 --- a/example/config.ru +++ b/example/config.ru @@ -2,16 +2,16 @@ require 'bundler' Bundler.require app = Helios::Application.new do - service :data, model: Dir['*.xcdatamodel*'].first, only: [:create, :read] + # service :data, model: Dir['*.xcdatamodel*'].first, only: [:create, :read] service :push_notification, frontend: true service :gcm, gcm_api_key: ENV['GCM_API_KEY'] - service :in_app_purchase - service :passbook - service :newsstand, storage: ({ - provider: 'AWS', - aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], - aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] - } if ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY']) + # service :in_app_purchase + # service :passbook + # service :newsstand, storage: ({ + # provider: 'AWS', + # aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], + # aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] + # } if ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY']) end # Customization through composability From 0b7c8bd622443bdf178d9f3ffe55d1d0fb10e6cd Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Mon, 12 Aug 2013 15:47:05 +0100 Subject: [PATCH 5/6] fixed the issue Assetpack bug --- .env | 1 - example/Gemfile | 1 + example/config.ru | 16 ++++++++-------- lib/helios/frontend.rb | 9 +++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index c16973b..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL=postgres://minkim@localhost/livestation_development 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 a32b739..aa00d76 100644 --- a/example/config.ru +++ b/example/config.ru @@ -2,16 +2,16 @@ require 'bundler' Bundler.require app = Helios::Application.new do - # service :data, model: Dir['*.xcdatamodel*'].first, only: [:create, :read] + service :data, model: Dir['*.xcdatamodel*'].first, only: [:create, :read] service :push_notification, frontend: true service :gcm, gcm_api_key: ENV['GCM_API_KEY'] - # service :in_app_purchase - # service :passbook - # service :newsstand, storage: ({ - # provider: 'AWS', - # aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], - # aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] - # } if ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY']) + service :in_app_purchase + service :passbook + service :newsstand, storage: ({ + provider: 'AWS', + aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], + aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] + } if ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY']) end # Customization through composability diff --git a/lib/helios/frontend.rb b/lib/helios/frontend.rb index d22eaea..a151599 100644 --- a/lib/helios/frontend.rb +++ b/lib/helios/frontend.rb @@ -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 From e70e8d3855bad6810d5f86cc0e03c8b089907b74 Mon Sep 17 00:00:00 2001 From: minsikzzang Date: Mon, 12 Aug 2013 16:02:29 +0100 Subject: [PATCH 6/6] fixed the issue Assetpack bug / version up --- .gitignore | 3 ++- lib/helios/version.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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/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