push-core 0.0.1.pre4 → 1.0.0

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.
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Push
2
2
 
3
- Please note this gem not yet used in production. If you want to help, please contact me.
3
+ ## Features
4
+
5
+ * Multi-App
6
+ * Multi-Provider ([APNS](https://github.com/tompesman/push-apns), [GCM](https://github.com/tompesman/push-gcm), [C2DM](https://github.com/tompesman/push-c2dm))
7
+ * Integrated feedback processing
8
+ * Rake task to cleanup the database
9
+ * Database for storage (no external dependencies)
4
10
 
5
11
  ## Installation
6
12
 
@@ -13,8 +19,8 @@ and add the push provider to you Gemfile:
13
19
  For __APNS__ (iOS: Apple Push Notification Services):
14
20
 
15
21
  gem push-apns
16
-
17
- For __C2DM__ (Android: Cloud to Device Messaging, deprecated):
22
+
23
+ For __C2DM__ (Android: Cloud to Device Messaging, deprecated by Google, not this gem):
18
24
 
19
25
  gem push-c2dm
20
26
 
@@ -33,25 +39,30 @@ To generate the migration and the configuration files run:
33
39
 
34
40
  The configuration is in the database and you add the configuration per push provider with the console (`rails c`):
35
41
 
36
- APNS:
42
+ APNS ([see](https://github.com/tompesman/push-core#generating-certificates)):
37
43
  ```ruby
38
- Push::ConfigurationApns.create(:app => 'app_name', :connections => 2, :certificate => File.read("certificate.pem"), :feedback_poll => 60, :enabled => true).save
44
+ Push::ConfigurationApns.create(app: 'app_name', connections: 2, enabled: true,
45
+ certificate: File.read('certificate.pem'),
46
+ feedback_poll: 60,
47
+ sandbox: false)
39
48
  ```
40
49
 
41
- C2DM
50
+ C2DM ([see](https://developers.google.com/android/c2dm/)):
42
51
  ```ruby
43
- Push::ConfigurationC2dm.create(:app => 'app_name', :connections => 2, :email => "<email address here>", :password => "<password here>", :enabled => true).save
52
+ Push::ConfigurationC2dm.create(app: 'app_name', connections: 2, enabled: true,
53
+ email: '<email address here>',
54
+ password: '<password here>')
44
55
  ```
45
56
 
46
- GCM
57
+ GCM ([see](http://developer.android.com/guide/google/gcm/gs.html)):
47
58
  ```ruby
48
- Push::ConfigurationGcm.create(:app => 'app_name', :connections => 2, :key => '<api key here>').save
59
+ Push::ConfigurationGcm.create(app: 'app_name', connections: 2, enabled: true,
60
+ key: '<api key here>')
49
61
  ```
50
62
 
51
- You can have each provider per app_name and you can have more than one app_name. Use the instructions below to generate the certificate for the APNS provider.
63
+ You can have each provider per app_name and you can have more than one app_name. Use the instructions below to generate the certificate for the APNS provider. If you only want to prepare the database with the configurations, you can set the `enabled` switch to `false`. Only enabled configurations will be used by the daemon.
52
64
 
53
-
54
- ### Generating Certificates
65
+ ### Generating Certificates for APNS
55
66
 
56
67
  1. Open up Keychain Access and select the `Certificates` category in the sidebar.
57
68
  2. Expand the disclosure arrow next to the iOS Push Services certificate you want to export.
@@ -62,60 +73,78 @@ You can have each provider per app_name and you can have more than one app_name.
62
73
  7. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
63
74
 
64
75
  `openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
65
-
66
- 8. Move the .pem file into your Rails application under `config/push`.
67
76
 
77
+ 8. Move the .pem file somewhere where you can use the `File.read` to load the file in the database.
68
78
 
69
79
  ## Daemon
70
80
 
71
81
  To start the daemon:
72
82
 
73
83
  bundle exec push <environment> <options>
74
-
84
+
75
85
  Where `<environment>` is your Rails environment and `<options>` can be:
76
86
 
77
- -f, --foreground Run in the foreground.
78
- -p, --pid-file PATH Path to write PID file. Relative to Rails root unless absolute.
79
- -P, --push-poll N Frequency in seconds to check for new notifications. Default: 2.
80
- -n, --airbrake-notify Enables error notifications via Airbrake.
81
- -F, --feedback-poll N Frequency in seconds to check for feedback for the feedback processor. Default: 60. Use 0 to disable.
82
- -b, --feedback-processor PATH Path to the feedback processor. Default: lib/push/feedback_processor.
83
- -v, --version Print this version of push.
84
- -h, --help You're looking at it.
87
+ -f, --foreground Run in the foreground. Log is not written.
88
+ -p, --pid-file PATH Path to write PID file. Relative to Rails root unless absolute.
89
+ -P, --push-poll N Frequency in seconds to check for new notifications. Default: 2.
90
+ -n, --error-notification Enables error notifications via Airbrake or Bugsnag.
91
+ -F, --feedback-poll N Frequency in seconds to check for feedback for the feedback processor. Default: 60. Use 0 to disable.
92
+ -b, --feedback-processor PATH Path to the feedback processor. Default: lib/push/feedback_processor.
93
+ -v, --version Print this version of push.
94
+ -h, --help You're looking at it.
85
95
 
86
96
 
87
97
  ## Sending notifications
88
98
  APNS:
89
99
  ```ruby
90
- Push::MessageApns.new(:app => 'app_name', device: '<APNS device_token here>', alert: 'Hello World', expiry: 1.day.to_i, attributes_for_device: {key: 'MSG'}).save
100
+ Push::MessageApns.create(
101
+ app: 'app_name',
102
+ device: '<APNS device_token here>',
103
+ alert: 'Hello World',
104
+ sound: '1.aiff',
105
+ badge: 1,
106
+ expiry: 1.day.to_i,
107
+ attributes_for_device: {key: 'MSG'})
91
108
  ```
92
109
  C2DM:
93
110
  ```ruby
94
- Push::MessageC2dm.new(:app => 'app_name', device: '<C2DM registration_id here>', payload: { message: 'Hello World' }, collapse_key: 'MSG').save
111
+ Push::MessageC2dm.create(
112
+ app: 'app_name',
113
+ device: '<C2DM registration_id here>',
114
+ payload: { message: 'Hello World' },
115
+ collapse_key: 'MSG')
95
116
  ```
96
117
 
97
118
  GCM:
98
119
  ```ruby
99
- Push::MessageGcm.new(:app => 'app_name', device: '<GCM registration_id here>', payload: { message: 'Hello World' }, collapse_key: 'MSG').save
120
+ Push::MessageGcm.create(
121
+ app: 'app_name',
122
+ device: '<GCM registration_id here>',
123
+ payload: { message: 'Hello World' },
124
+ collapse_key: 'MSG')
100
125
  ```
101
126
 
102
127
  ## Feedback processing
103
128
 
104
129
  The push providers return feedback in various ways and these are captured and stored in the `push_feedback` table. The installer installs the `lib/push/feedback_processor.rb` file which is by default called every 60 seconds. In this file you can process the feedback which is different for every application.
105
130
 
106
- ## Rake Task
131
+ ## Maintenance
107
132
 
108
- The push-core also comes with a rake task to delete all the messages and feedback of the last 7 days or by the DAYS parameter.
133
+ The push-core comes with a rake task to delete all the messages and feedback of the last 7 days or by the DAYS parameter.
109
134
 
110
- bundle exec rake push:clean DAYS=2
135
+ bundle exec rake push:clean DAYS=2
111
136
 
112
- ## Prerequisites
137
+ ## Heroku
138
+
139
+ Push runs on Heroku with the following line in the `Procfile`.
113
140
 
114
- * Rails 3.2 +
115
- * Ruby 1.9
141
+ push: bundle exec push $RACK_ENV -f
142
+
143
+ ## Prerequisites
116
144
 
145
+ * Rails 3.2.x
146
+ * Ruby 1.9.x
117
147
 
118
148
  ## Thanks
119
149
 
120
150
  This project started as a fork of Ian Leitch [RAPNS](https://github.com/ileitch/rapns) project. The differences between this project and RAPNS is the support for C2DM and the modularity of the push providers.
121
-
data/bin/push CHANGED
@@ -5,20 +5,20 @@ require 'push'
5
5
 
6
6
  environment = ARGV[0]
7
7
 
8
- config = Struct.new(:foreground, :pid_file, :push_poll, :airbrake_notify, :feedback_poll, :feedback_processor).new
8
+ config = Struct.new(:foreground, :pid_file, :push_poll, :error_notification, :feedback_poll, :feedback_processor).new
9
9
  config.foreground = false
10
10
  config.push_poll = 2
11
- config.airbrake_notify = false
11
+ config.error_notification = false
12
12
  config.feedback_poll = 60
13
13
  config.feedback_processor = 'lib/push/feedback_processor'
14
14
 
15
15
  banner = 'Usage: push <Rails environment> [options]'
16
16
  ARGV.options do |opts|
17
17
  opts.banner = banner
18
- opts.on('-f', '--foreground', 'Run in the foreground.') { config.foreground = true }
18
+ opts.on('-f', '--foreground', 'Run in the foreground. Log is not written.') { config.foreground = true }
19
19
  opts.on('-p PATH', '--pid-file PATH', String, 'Path to write PID file. Relative to Rails root unless absolute.') { |path| config.pid_file = path }
20
20
  opts.on('-P N', '--push-poll N', Integer, "Frequency in seconds to check for new notifications. Default: #{config.push_poll}.") { |n| config.push_poll = n }
21
- opts.on('-n', '--airbrake-notify', 'Enables error notifications via Airbrake.') { config.check_for_errors = true }
21
+ opts.on('-n', '--error-notification', 'Enables error notifications via Airbrake or Bugsnag.') { config.error_notification = true }
22
22
  opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback for the feedback processor. Default: #{config.feedback_poll}. Use 0 to disable.") { |n| config.feedback_poll = n }
23
23
  opts.on('-b PATH', '--feedback-processor PATH', String, "Path to the feedback processor. Default: #{config.feedback_processor}.") { |n| config.feedback_processor = n }
24
24
  opts.on('-v', '--version', 'Print this version of push.') { puts "push #{Push::VERSION}"; exit }
@@ -40,4 +40,4 @@ if config.pid_file && !Pathname.new(config.pid_file).absolute?
40
40
  config.pid_file = File.join(Rails.root, config.pid_file)
41
41
  end
42
42
 
43
- Push::Daemon.start(environment, config)
43
+ Push::Daemon.start(config)
data/lib/push/daemon.rb CHANGED
@@ -19,9 +19,9 @@ module Push
19
19
  attr_accessor :logger, :config
20
20
  end
21
21
 
22
- def self.start(environment, config)
22
+ def self.start(config)
23
23
  self.config = config
24
- self.logger = Logger.new(:foreground => config.foreground, :airbrake_notify => config.airbrake_notify)
24
+ self.logger = Logger.new(:foreground => config.foreground, :error_notification => config.error_notification)
25
25
  setup_signal_hooks
26
26
  daemonize unless config.foreground
27
27
  write_pid_file
@@ -1,15 +1,17 @@
1
- class PGError < StandardError; end if !defined?(PGError)
2
- module Mysql2; class Error < StandardError; end; end if !defined?(Mysql2)
3
-
4
1
  module Push
5
2
  module Daemon
6
3
  module DatabaseReconnectable
7
- ADAPTER_ERRORS = [ActiveRecord::StatementInvalid, PGError, Mysql2::Error]
4
+ def adaptor_errors
5
+ errors = [ActiveRecord::StatementInvalid]
6
+ errors << PGError if defined?(PGError)
7
+ errors << Mysql2::Error if defined?(Mysql2)
8
+ errors
9
+ end
8
10
 
9
11
  def with_database_reconnect_and_retry(name)
10
12
  begin
11
13
  yield
12
- rescue *ADAPTER_ERRORS => e
14
+ rescue *adaptor_errors => e
13
15
  Push::Daemon.logger.error(e)
14
16
  database_connection_lost(name)
15
17
  retry
@@ -25,8 +27,8 @@ module Push
25
27
  reconnect_database
26
28
  check_database_is_connected
27
29
  break
28
- rescue *ADAPTER_ERRORS => e
29
- Push::Daemon.logger.error(e, :airbrake_notify => false)
30
+ rescue *adaptor_errors => e
31
+ Push::Daemon.logger.error(e, :error_notification => false)
30
32
  sleep_to_avoid_thrashing
31
33
  end
32
34
  end
@@ -28,8 +28,8 @@ module Push
28
28
  def self.enqueue_notifications
29
29
  begin
30
30
  with_database_reconnect_and_retry(name) do
31
+ ready_apps = Push::Daemon::App.ready
31
32
  Push::Message.ready_for_delivery.find_each do |notification|
32
- ready_apps = Push::Daemon::App.ready
33
33
  Push::Daemon::App.deliver(notification) if ready_apps.include?(notification.app)
34
34
  end
35
35
  end
@@ -3,10 +3,7 @@ module Push
3
3
  class Logger
4
4
  def initialize(options)
5
5
  @options = options
6
- log_file = File.open(File.join(Rails.root, 'log', 'push.log'), 'w')
7
- log_file.sync = true
8
- @logger = ActiveSupport::BufferedLogger.new(log_file, Rails.logger.level)
9
- @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
6
+ open_log unless @options[:foreground]
10
7
  end
11
8
 
12
9
  def info(msg)
@@ -14,7 +11,7 @@ module Push
14
11
  end
15
12
 
16
13
  def error(msg, options = {})
17
- airbrake_notify(msg) if notify_via_airbrake?(msg, options)
14
+ error_notification(msg, options)
18
15
  log(:error, msg, 'ERROR')
19
16
  end
20
17
 
@@ -26,28 +23,41 @@ module Push
26
23
 
27
24
  def log(where, msg, prefix = nil)
28
25
  if msg.is_a?(Exception)
29
- msg = "#{msg.class.name}, #{msg.message}: #{msg.backtrace.join("\n")}"
26
+ msg = "#{msg.class.name}, #{msg.message}: #{msg.backtrace.join("\n") if msg.backtrace}"
30
27
  end
31
28
 
32
29
  formatted_msg = "[#{Time.now.to_s(:db)}] "
33
30
  formatted_msg << "[#{prefix}] " if prefix
34
31
  formatted_msg << msg
35
- puts formatted_msg if @options[:foreground]
36
- @logger.send(where, formatted_msg)
32
+
33
+ if @options[:foreground]
34
+ puts formatted_msg
35
+ else
36
+ @logger.send(where, formatted_msg)
37
+ end
38
+ end
39
+
40
+ def open_log
41
+ log_file = File.open(File.join(Rails.root, 'log', 'push.log'), 'w')
42
+ log_file.sync = true
43
+ @logger = ActiveSupport::BufferedLogger.new(log_file, Rails.logger.level)
44
+ @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
37
45
  end
38
46
 
39
- def airbrake_notify(e)
40
- return unless @options[:airbrake_notify] == true
47
+ def error_notification(e, options)
48
+ return unless do_error_notification?(e, options)
41
49
 
42
50
  if defined?(Airbrake)
43
51
  Airbrake.notify_or_ignore(e)
44
52
  elsif defined?(HoptoadNotifier)
45
53
  HoptoadNotifier.notify_or_ignore(e)
54
+ elsif defined?(Bugsnag)
55
+ Bugsnag.notify(e)
46
56
  end
47
57
  end
48
58
 
49
- def notify_via_airbrake?(msg, options)
50
- msg.is_a?(Exception) && options[:airbrake_notify] != false
59
+ def do_error_notification?(msg, options)
60
+ @options[:error_notification] and options[:error_notification] != false and msg.is_a?(Exception)
51
61
  end
52
62
  end
53
63
  end
data/lib/push/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Push
2
- VERSION = "0.0.1.pre4"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -2,7 +2,7 @@
2
2
  namespace :push do
3
3
  desc "Delete all push messages and feedback older than 7 days or DAYS=x if defined (where x is an integer)"
4
4
  task :clean => :environment do
5
- days = ENV["DAYS"].to_i || 7
5
+ days = ENV["DAYS"] ? ENV["DAYS"].to_i : 7
6
6
  puts "Removing #{days} days of push messages and feedback"
7
7
  feedback = Push::Feedback.where("created_at < ?", days.days.ago).delete_all
8
8
  messages = Push::Message.where("created_at < ?", days.days.ago).delete_all
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: push-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre4
5
- prerelease: 6
4
+ version: 1.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tom Pesman
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-21 00:00:00.000000000 Z
12
+ date: 2012-09-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70213224802660 !ruby/object:Gem::Requirement
16
+ requirement: &70261995219020 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70213224802660
24
+ version_requirements: *70261995219020
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &70213224801920 !ruby/object:Gem::Requirement
27
+ requirement: &70261995218260 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70213224801920
35
+ version_requirements: *70261995218260
36
36
  description: Push daemon for push notification services like APNS (iOS/Apple) and
37
37
  GCM/C2DM (Android).
38
38
  email:
@@ -85,12 +85,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
- - - ! '>'
88
+ - - ! '>='
89
89
  - !ruby/object:Gem::Version
90
- version: 1.3.1
90
+ version: '0'
91
91
  requirements: []
92
92
  rubyforge_project:
93
- rubygems_version: 1.8.5
93
+ rubygems_version: 1.8.17
94
94
  signing_key:
95
95
  specification_version: 3
96
96
  summary: Core of the push daemon.