rpush 2.0.0.rc1-java → 2.0.1-java
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +29 -14
- data/bin/rpush +2 -2
- data/lib/generators/templates/rpush.rb +4 -1
- data/lib/generators/templates/rpush_2_0_0_updates.rb +2 -2
- data/lib/rpush/client/redis/app.rb +2 -0
- data/lib/rpush/daemon.rb +1 -1
- data/lib/rpush/daemon/app_runner.rb +12 -1
- data/lib/rpush/daemon/signal_handler.rb +24 -19
- data/lib/rpush/daemon/synchronizer.rb +21 -5
- data/lib/rpush/daemon/tcp_connection.rb +6 -0
- data/lib/rpush/reflection.rb +1 -1
- data/lib/rpush/version.rb +1 -1
- data/spec/functional/apns_spec.rb +10 -11
- data/spec/functional/synchronization_spec.rb +22 -0
- data/spec/unit/daemon/app_runner_spec.rb +2 -2
- data/spec/unit/daemon/signal_handler_spec.rb +23 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +17 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13082e421fbc9ede84fe1dea84373de5ccab8bd5
|
4
|
+
data.tar.gz: c1b695784b56de4a796e75402c808d015f7d9c8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc6c33f58cb98c3f5a0958fc8a11b554b375aae30e3ad0f44d61a5ddb91da5ae970e0fd4c7b8fe35d773771dedd37b011a4527030df023e7587fa2b5eb49f04c
|
7
|
+
data.tar.gz: 44ebe64b26b051db0e0ae5acb1bfb91cd1ec0efc6a2658138f2a1c493be013f8785683e46ba9a39d2c4c93f7f4bbef101c2a4a7dddca70278fa43544a3861c93
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
## 2.0.
|
1
|
+
## 2.0.1 (Sept 13, 2014)
|
2
|
+
* Add ssl_certificate_revoked reflection (#68).
|
3
|
+
* Fix for Postgis support in 2.0.0 migration (#70).
|
4
|
+
|
5
|
+
## 2.0.0 (Sept 6, 2014)
|
2
6
|
* Use APNs enhanced binary format version 2.
|
3
7
|
* Support running multiple Rpush processes when using ActiveRecord and Redis.
|
4
8
|
* APNs error detection is now performed asynchronously, 'check_for_errors' is therefore deprecated.
|
@@ -11,6 +15,7 @@
|
|
11
15
|
* The 'batch_storage_updates' config option has been deprecated, storage backends will now always batch updates where appropriate.
|
12
16
|
* The rpush process title updates with number of queued notifications and number of dispatchers.
|
13
17
|
* Rpush::Apns::Feedback#app has been renamed to app_id and is now an Integer.
|
18
|
+
* An app is restarted when the HUP signal is received if its certificate or environment attribute changed.
|
14
19
|
|
15
20
|
## 1.0.0 (Feb 9, 2014)
|
16
21
|
* Renamed to Rpush (from Rapns). Version number reset to 1.0.0.
|
data/README.md
CHANGED
@@ -7,15 +7,21 @@
|
|
7
7
|
|
8
8
|
### Rpush. The push notification service for Ruby.
|
9
9
|
|
10
|
-
*
|
11
|
-
* **Apple Push Notification Service**
|
12
|
-
* **Google Cloud Messaging**
|
13
|
-
* **Amazon Device Messaging**
|
14
|
-
* **Windows Phone Push Notification Service
|
15
|
-
|
16
|
-
*
|
17
|
-
*
|
18
|
-
*
|
10
|
+
* Supported services:
|
11
|
+
* [**Apple Push Notification Service**](#apple-push-notification-service)
|
12
|
+
* [**Google Cloud Messaging**](#google-cloud-messaging)
|
13
|
+
* [**Amazon Device Messaging**](#amazon-device-messaging)
|
14
|
+
* [**Windows Phone Push Notification Service**](#windows-phone-notification-service)
|
15
|
+
|
16
|
+
* Supported storage backends:
|
17
|
+
* **ActiveRecord**
|
18
|
+
* **Redis**
|
19
|
+
* More coming!
|
20
|
+
|
21
|
+
* Seamless Rails integration (3 & 4) .
|
22
|
+
* Scales vertically (threading) and horizontally (multiple processes).
|
23
|
+
* Designed for uptime - new apps are loaded automatically, signal `HUP` to update running apps.
|
24
|
+
* Run as a daemon or inside an [existing process](https://github.com/rpush/rpush/wiki/Embedding-API).
|
19
25
|
* Use in a scheduler for low-workload deployments ([Push API](https://github.com/rpush/rpush/wiki/Push-API)).
|
20
26
|
* Hooks for fine-grained instrumentation and error handling ([Reflection API](https://github.com/rpush/rpush/wiki/Reflection-API)).
|
21
27
|
* Works with MRI, JRuby and Rubinius.
|
@@ -122,31 +128,40 @@ n.alert = "..."
|
|
122
128
|
n.save!
|
123
129
|
```
|
124
130
|
|
125
|
-
###
|
131
|
+
### Running Rpush
|
126
132
|
|
127
|
-
|
133
|
+
It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
|
134
|
+
|
135
|
+
#### As a daemon (recommended):
|
128
136
|
|
129
137
|
cd /path/to/rails/app
|
130
138
|
rpush <Rails environment> [options]
|
131
139
|
|
132
|
-
|
140
|
+
#### Embedded inside an existing process
|
133
141
|
|
134
142
|
```ruby
|
143
|
+
# Call this during startup of your application, for example, by adding it to the end of config/initializers/rpush.rb
|
135
144
|
Rpush.embed
|
136
145
|
```
|
137
146
|
|
138
|
-
|
147
|
+
See [Embedding API](https://github.com/rpush/rpush/wiki/Embedding-API) for more details.
|
148
|
+
|
149
|
+
#### Manually (in a scheduler)
|
139
150
|
|
140
151
|
```ruby
|
141
152
|
Rpush.push
|
142
153
|
Rpush.apns_feedback
|
143
154
|
```
|
144
155
|
|
156
|
+
See [Push API](https://github.com/rpush/rpush/wiki/Push-API) for more details.
|
157
|
+
|
158
|
+
### Configuration
|
159
|
+
|
145
160
|
See [Configuration](https://github.com/rpush/rpush/wiki/Configuration) for a list of options, or run `rpush --help`.
|
146
161
|
|
147
162
|
### Updating Rpush
|
148
163
|
|
149
|
-
|
164
|
+
If you're using ActiveRecord, you should run `rails g rpush` after upgrading Rpush to check for any new migrations.
|
150
165
|
|
151
166
|
### Wiki
|
152
167
|
|
data/bin/rpush
CHANGED
@@ -12,10 +12,10 @@ options = ARGV.options do |opts|
|
|
12
12
|
opts.on('-f', '--foreground', 'Run in the foreground.') { config.foreground = true }
|
13
13
|
opts.on('-P N', '--db-poll N', Integer, "Frequency in seconds to check for new notifications.") { |n| config.push_poll = n }
|
14
14
|
opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback.") { |n| config.feedback_poll = n }
|
15
|
-
opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false }
|
15
|
+
opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false } # deprecated
|
16
16
|
opts.on('-p PATH', '--pid-file PATH', String, 'Path to write PID file. Relative to Rails root unless absolute.') { |path| config.pid_file = path }
|
17
17
|
opts.on('-b N', '--batch-size N', Integer, 'Storage backend notification batch size.') { |n| config.batch_size = n }
|
18
|
-
opts.on('-B', '--[no-]batch-storage-updates', 'Perform storage updates in batches.') { |v| config.batch_storage_updates = v }
|
18
|
+
opts.on('-B', '--[no-]batch-storage-updates', 'Perform storage updates in batches.') { |v| config.batch_storage_updates = v } # deprecated
|
19
19
|
opts.on('-v', '--version', 'Print the version.') do
|
20
20
|
puts "rpush #{Rpush::VERSION}"
|
21
21
|
exit
|
@@ -106,6 +106,10 @@ Rpush.reflect do |on|
|
|
106
106
|
# on.ssl_certificate_will_expire do |app, expiration_time|
|
107
107
|
# end
|
108
108
|
|
109
|
+
# Called when an SSL certificate has been revoked.
|
110
|
+
# on.ssl_certificate_revoked do |app, error|
|
111
|
+
# end
|
112
|
+
|
109
113
|
# Called when the ADM returns a canonical registration ID.
|
110
114
|
# You will need to replace old_id with canonical_id in your records.
|
111
115
|
# on.adm_canonical_id do |old_id, canonical_id|
|
@@ -119,7 +123,6 @@ Rpush.reflect do |on|
|
|
119
123
|
# on.adm_failed_to_recipient do |notification, registration_id, reason|
|
120
124
|
# end
|
121
125
|
|
122
|
-
|
123
126
|
# Called when an exception is raised.
|
124
127
|
# on.error do |error|
|
125
128
|
# end
|
@@ -7,7 +7,7 @@ class Rpush200Updates < ActiveRecord::Migration
|
|
7
7
|
remove_index :rpush_notifications, name: :index_rpush_notifications_multi
|
8
8
|
end
|
9
9
|
|
10
|
-
add_index :rpush_notifications, [:
|
10
|
+
add_index :rpush_notifications, [:delivered, :failed], name: 'index_rpush_notifications_multi', where: 'NOT delivered AND NOT failed'
|
11
11
|
|
12
12
|
rename_column :rpush_feedback, :app, :app_id
|
13
13
|
|
@@ -37,6 +37,6 @@ class Rpush200Updates < ActiveRecord::Migration
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.postgresql?
|
40
|
-
adapter_name =~ /postgresql/
|
40
|
+
adapter_name =~ /postgresql|postgis/
|
41
41
|
end
|
42
42
|
end
|
data/lib/rpush/daemon.rb
CHANGED
@@ -4,7 +4,9 @@ module Rpush
|
|
4
4
|
extend Reflectable
|
5
5
|
include Reflectable
|
6
6
|
include Loggable
|
7
|
+
extend Loggable
|
7
8
|
include StringHelpers
|
9
|
+
extend StringHelpers
|
8
10
|
|
9
11
|
@runners = {}
|
10
12
|
|
@@ -24,6 +26,7 @@ module Rpush
|
|
24
26
|
def self.start_app(app)
|
25
27
|
@runners[app.id] = new(app)
|
26
28
|
@runners[app.id].start
|
29
|
+
log_info("[#{app.name}] Started, #{pluralize(app.connections, 'dispatcher')}.")
|
27
30
|
rescue StandardError => e
|
28
31
|
@runners.delete(app.id)
|
29
32
|
Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
|
@@ -32,7 +35,15 @@ module Rpush
|
|
32
35
|
end
|
33
36
|
|
34
37
|
def self.stop_app(app_id)
|
35
|
-
@runners.delete(app_id)
|
38
|
+
runner = @runners.delete(app_id)
|
39
|
+
if runner
|
40
|
+
runner.stop
|
41
|
+
log_info("[#{runner.app.name}] Stopped.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.app_with_id(app_id)
|
46
|
+
@runners[app_id].app
|
36
47
|
end
|
37
48
|
|
38
49
|
def self.app_running?(app)
|
@@ -7,42 +7,47 @@ module Rpush
|
|
7
7
|
|
8
8
|
def self.start
|
9
9
|
return unless trap_signals?
|
10
|
-
|
10
|
+
|
11
11
|
read_io, @write_io = IO.pipe
|
12
12
|
start_handler(read_io)
|
13
13
|
%w(INT TERM HUP USR2).each do |signal|
|
14
|
-
Signal.trap(signal) { @write_io.
|
14
|
+
Signal.trap(signal) { @write_io.puts(signal) }
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.stop
|
19
|
-
@write_io.
|
19
|
+
@write_io.puts('break') if @write_io
|
20
20
|
@thread.join if @thread
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.start_handler(read_io)
|
24
24
|
@thread = Thread.new do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
while readable_io = IO.select([read_io]) # rubocop:disable AssignmentInCondition
|
26
|
+
signal = readable_io.first[0].gets.strip
|
27
|
+
|
28
|
+
begin
|
29
|
+
case signal
|
30
|
+
when 'HUP'
|
31
|
+
Synchronizer.sync
|
32
|
+
Feeder.wakeup
|
33
|
+
when 'USR2'
|
34
|
+
AppRunner.debug
|
35
|
+
when 'INT', 'TERM'
|
36
|
+
Thread.new { Rpush::Daemon.shutdown }
|
37
|
+
break
|
38
|
+
when 'break'
|
39
|
+
break
|
40
|
+
else
|
41
|
+
Rpush.logger.error("Unhandled signal: #{signal}")
|
42
|
+
end
|
43
|
+
rescue StandardError => e
|
44
|
+
Rpush.logger.error("Error raised when hndling signal '#{signal}'")
|
45
|
+
Rpush.logger.error(e)
|
36
46
|
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
|
41
|
-
def self.handle_shutdown_signal
|
42
|
-
@shutting_down = true
|
43
|
-
Rpush::Daemon.shutdown
|
44
|
-
end
|
45
|
-
|
46
51
|
def self.trap_signals?
|
47
52
|
!Rpush.config.embedded
|
48
53
|
end
|
@@ -14,13 +14,19 @@ module Rpush
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.sync_app(app)
|
17
|
-
|
17
|
+
if !AppRunner.app_running?(app)
|
18
18
|
AppRunner.start_app(app)
|
19
|
-
|
20
|
-
|
19
|
+
elsif certificate_changed?(app)
|
20
|
+
log_info("[#{app.name}] Certificate changed, restarting...")
|
21
|
+
AppRunner.stop_app(app.id)
|
22
|
+
AppRunner.start_app(app)
|
23
|
+
elsif environment_changed?(app)
|
24
|
+
log_info("[#{app.name}] Environment changed, restarting...")
|
25
|
+
AppRunner.stop_app(app.id)
|
26
|
+
AppRunner.start_app(app)
|
27
|
+
else
|
28
|
+
sync_dispatcher_count(app)
|
21
29
|
end
|
22
|
-
|
23
|
-
sync_dispatcher_count(app)
|
24
30
|
end
|
25
31
|
|
26
32
|
def self.sync_dispatcher_count(app)
|
@@ -39,6 +45,16 @@ module Rpush
|
|
39
45
|
num_dispatchers = AppRunner.num_dispatchers_for_app(app)
|
40
46
|
log_info("[#{app.name}] #{start_stop_str} #{pluralize(diff.abs, 'dispatcher')}. #{num_dispatchers} running.")
|
41
47
|
end
|
48
|
+
|
49
|
+
def self.certificate_changed?(app)
|
50
|
+
old_app = AppRunner.app_with_id(app.id)
|
51
|
+
app.certificate != old_app.certificate
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.environment_changed?(app)
|
55
|
+
old_app = AppRunner.app_with_id(app.id)
|
56
|
+
app.environment != old_app.environment
|
57
|
+
end
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
@@ -108,6 +108,12 @@ module Rpush
|
|
108
108
|
ssl_socket.sync = true
|
109
109
|
ssl_socket.connect
|
110
110
|
[tcp_socket, ssl_socket]
|
111
|
+
rescue StandardError => error
|
112
|
+
if error.message =~ /certificate revoked/i
|
113
|
+
log_warn('Certificate has been revoked.')
|
114
|
+
reflect(:ssl_certificate_revoked, @app, error)
|
115
|
+
end
|
116
|
+
raise
|
111
117
|
end
|
112
118
|
|
113
119
|
def check_certificate_expiration
|
data/lib/rpush/reflection.rb
CHANGED
@@ -15,7 +15,7 @@ module Rpush
|
|
15
15
|
:notification_failed, :notification_will_retry, :gcm_delivered_to_recipient,
|
16
16
|
:gcm_failed_to_recipient, :gcm_canonical_id, :gcm_invalid_registration_id,
|
17
17
|
:error, :adm_canonical_id, :adm_failed_to_recipient,
|
18
|
-
:tcp_connection_lost, :ssl_certificate_will_expire,
|
18
|
+
:tcp_connection_lost, :ssl_certificate_will_expire, :ssl_certificate_revoked,
|
19
19
|
:notification_id_will_retry, :notification_id_failed
|
20
20
|
]
|
21
21
|
|
data/lib/rpush/version.rb
CHANGED
@@ -127,17 +127,16 @@ describe 'APNs' do
|
|
127
127
|
notification1.delivered.should be_true
|
128
128
|
end
|
129
129
|
|
130
|
-
it 'marks notifications following the failed one as retryable'
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# end
|
130
|
+
it 'marks notifications following the failed one as retryable' do
|
131
|
+
Rpush.config.push_poll = 1_000_000
|
132
|
+
|
133
|
+
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
134
|
+
fail_notification(notification2)
|
135
|
+
|
136
|
+
[notification3, notification4].each do |n|
|
137
|
+
wait_for_notification_to_retry(n)
|
138
|
+
end
|
139
|
+
end
|
141
140
|
|
142
141
|
describe 'without an error response' do
|
143
142
|
it 'marks all notifications as failed' do
|
@@ -16,6 +16,9 @@ describe 'Synchronization' do
|
|
16
16
|
app.name = 'test'
|
17
17
|
app.auth_key = 'abc123'
|
18
18
|
app.connections = 2
|
19
|
+
app.certificate = TEST_CERT_WITH_PASSWORD
|
20
|
+
app.password = 'fubar'
|
21
|
+
app.environment = 'sandbox'
|
19
22
|
app.save!
|
20
23
|
|
21
24
|
Rpush.embed
|
@@ -43,4 +46,23 @@ describe 'Synchronization' do
|
|
43
46
|
Rpush.sync
|
44
47
|
Rpush::Daemon::AppRunner.app_running?(app).should be_false
|
45
48
|
end
|
49
|
+
|
50
|
+
it 'restarts an app when the certificate is changed' do
|
51
|
+
app.certificate = TEST_CERT
|
52
|
+
app.password = nil
|
53
|
+
app.save!
|
54
|
+
Rpush.sync
|
55
|
+
|
56
|
+
running_app = Rpush::Daemon::AppRunner.app_with_id(app.id)
|
57
|
+
expect(running_app.certificate).to eql(TEST_CERT)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'restarts an app when the environment is changed' do
|
61
|
+
app.environment = 'production'
|
62
|
+
app.save!
|
63
|
+
Rpush.sync
|
64
|
+
|
65
|
+
running_app = Rpush::Daemon::AppRunner.app_with_id(app.id)
|
66
|
+
expect(running_app.environment).to eql('production')
|
67
|
+
end
|
46
68
|
end
|
@@ -32,7 +32,7 @@ module Rpush
|
|
32
32
|
end
|
33
33
|
|
34
34
|
describe Rpush::Daemon::AppRunner, 'enqueue' do
|
35
|
-
let(:app) { double(id: 1) }
|
35
|
+
let(:app) { double(id: 1, name: 'Test', connections: 1) }
|
36
36
|
let(:notification) { double(app_id: 1) }
|
37
37
|
let(:runner) { double(Rpush::Daemon::AppRunner, enqueue: nil, start: nil, stop: nil) }
|
38
38
|
let(:logger) { double(Rpush::Logger, error: nil, info: nil) }
|
@@ -53,7 +53,7 @@ describe Rpush::Daemon::AppRunner, 'enqueue' do
|
|
53
53
|
|
54
54
|
it 'starts the app if a runner does not exist' do
|
55
55
|
notification = double(app_id: 3)
|
56
|
-
new_app = double(Rpush::App, id: 3)
|
56
|
+
new_app = double(Rpush::App, id: 3, name: 'NewApp', connections: 1)
|
57
57
|
Rpush::Daemon.store = double(app: new_app)
|
58
58
|
Rpush::Daemon::AppRunner.enqueue([notification])
|
59
59
|
Rpush::Daemon::AppRunner.app_running?(new_app).should be_true
|
@@ -69,4 +69,27 @@ describe Rpush::Daemon::SignalHandler do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
describe 'error handing' do
|
74
|
+
let(:error) { StandardError.new('test') }
|
75
|
+
|
76
|
+
before { Rpush.stub(logger: double(error: nil)) }
|
77
|
+
|
78
|
+
it 'logs errors received when handling a signal' do
|
79
|
+
Rpush::Daemon::Synchronizer.stub(:sync).and_raise(error)
|
80
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
81
|
+
with_handler_start_stop do
|
82
|
+
signal_handler('HUP')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not interrupt processing of further errors' do
|
87
|
+
Rpush::Daemon::Synchronizer.stub(:sync).and_raise(error)
|
88
|
+
expect(Rpush::Daemon::AppRunner).to receive(:debug)
|
89
|
+
with_handler_start_stop do
|
90
|
+
signal_handler('HUP')
|
91
|
+
signal_handler('USR2')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
72
95
|
end
|
@@ -113,6 +113,23 @@ describe Rpush::Daemon::TcpConnection do
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
|
+
|
117
|
+
describe 'certificate revocation' do
|
118
|
+
let(:cert_revoked_error) { OpenSSL::SSL::SSLError.new('certificate revoked') }
|
119
|
+
before do
|
120
|
+
ssl_socket.stub(:connect).and_raise(cert_revoked_error)
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'reflects that the certificate has been revoked' do
|
124
|
+
connection.should_receive(:reflect).with(:ssl_certificate_revoked, app, cert_revoked_error)
|
125
|
+
expect { connection.connect }.to raise_error(cert_revoked_error)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'logs that the certificate has been revoked' do
|
129
|
+
logger.should_receive(:warn).with('[Connection 0] Certificate has been revoked.')
|
130
|
+
expect { connection.connect }.to raise_error(cert_revoked_error)
|
131
|
+
end
|
132
|
+
end
|
116
133
|
end
|
117
134
|
|
118
135
|
describe "when shuting down the connection" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Ian Leitch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -277,9 +277,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
277
277
|
version: '0'
|
278
278
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
279
279
|
requirements:
|
280
|
-
- - '
|
280
|
+
- - '>='
|
281
281
|
- !ruby/object:Gem::Version
|
282
|
-
version:
|
282
|
+
version: '0'
|
283
283
|
requirements: []
|
284
284
|
rubyforge_project:
|
285
285
|
rubygems_version: 2.1.9
|