mercurius 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 65ff200559fb5130010b47d7d2be2cc1e524b0de
4
+ data.tar.gz: 8a28049240b58317298775619721978b80cb7ce7
5
+ SHA512:
6
+ metadata.gz: ce55894987fecb4a496c014681d07dbea9375e805e2a1523b2d2495fe09a8bfb9a13254f6a78fd720ab3d994311903c5a0ace2415a2edf8b21b7b81dfaf10bd1
7
+ data.tar.gz: 334217c4278902311a45fed9ae873b912246354f25972d04dab17356fdca3f68fd0d6d0a214f103d7fd399d617253d99858ddc35a3bc19038d0c7edadbb34173
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ NEW_README.md
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ mercurius
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.5
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ # - 1.9.3
4
+ - 2.0.0
5
+ # - jruby-19mode # JRuby in 1.9 mode
6
+ # uncomment this line if your project needs to run something other than `rake`:
7
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mercurius.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Mercurius
2
+
3
+ ### Send push messages to Android, iOS devices.
4
+
5
+ ## Installation
6
+
7
+ $ gem install mercurius
8
+
9
+ or add to your ``Gemfile``
10
+
11
+ gem 'mercurius'
12
+
13
+ and install it with
14
+
15
+ $ bundle install
16
+
17
+ ## Contributing
18
+
19
+ Please fork, modify, and send a pull request if you want to help improve this gem.
20
+
21
+ ## Thanks
22
+
23
+ This gem is based off of the 'pushmeup' gem by Nicos Karalis
24
+
25
+ https://github.com/NicosKaralis/pushmeup
26
+
27
+ ## License
28
+
29
+ Mercurius is released under the MIT license:
30
+
31
+ http://www.opensource.org/licenses/MIT
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
8
+ task :test => :spec
9
+
10
+ desc 'Open an IRB session preloaded with this library'
11
+ task :console do
12
+ sh "irb -rubygems -I lib -r mercurius.rb"
13
+ end
@@ -0,0 +1,2 @@
1
+ require "mercurius/gcm/core"
2
+ require "mercurius/gcm/notification"
@@ -0,0 +1,128 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'json'
4
+
5
+ module APNS
6
+ class Error < Exception; end
7
+ class ConfigurationError < Error; end
8
+
9
+ @host = 'gateway.sandbox.push.apple.com'
10
+ @port = 2195
11
+ @pem_path = nil
12
+ @pem_password = nil
13
+ @pem_data = nil
14
+
15
+ @persistent = false
16
+ @mutex = Mutex.new
17
+ @retries = 3 # TODO: check if we really need this
18
+
19
+ @sock = nil
20
+ @ssl = nil
21
+
22
+ class << self
23
+ attr_accessor :host, :port, :pem_path, :pem_password, :pem_data
24
+ end
25
+
26
+ def self.start_persistence
27
+ @persistent = true
28
+ end
29
+
30
+ def self.stop_persistence
31
+ @persistent = false
32
+ self.close_connection
33
+ end
34
+
35
+ def self.send_notification(device_token, message)
36
+ notification = APNS::Notification.new(device_token, message)
37
+ send_notifications([notification])
38
+ end
39
+
40
+ def self.send_notifications(notifications)
41
+ @mutex.synchronize do
42
+ with_connection do
43
+ notifications.each do |n|
44
+ @ssl.write(n.packaged_notification)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.feedback
51
+ sock, ssl = feedback_connection
52
+ apns_feedback = []
53
+
54
+ # read lines from the socket
55
+ while line = ssl.read(38)
56
+ line.strip!
57
+ f = line.unpack('N1n1H140')
58
+ apns_feedback << { timestamp: Time.at(f[0]), token: f[2] }
59
+ end
60
+
61
+ ssl.close
62
+ sock.close
63
+
64
+ apns_feedback
65
+ end
66
+
67
+ protected
68
+
69
+ def self.with_connection
70
+ attempts = 1
71
+
72
+ begin
73
+ open_connection if connection_closed?
74
+ yield
75
+ rescue StandardError, Errno::EPIPE
76
+ raise Error.new("Failed after #{@retries} attempts.") unless attempts < @retries
77
+ close_connection
78
+ attempts += 1
79
+ retry
80
+ end
81
+
82
+ close_connection unless @persistent
83
+ end
84
+
85
+ def self.open_connection
86
+ @sock = TCPSocket.new(self.host, self.port)
87
+ @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
88
+ @ssl.connect
89
+ end
90
+
91
+ def self.close_connection
92
+ @ssl.close
93
+ @ssl = nil
94
+ @sock.close
95
+ @sock = nil
96
+ end
97
+
98
+ def self.connection_closed?
99
+ @ssl.nil? || @sock.nil? || @ssl.closed? || @sock.closed?
100
+ end
101
+
102
+ def self.feedback_connection
103
+ fhost = self.host.gsub('gateway','feedback')
104
+ sock = TCPSocket.new(fhost, 2196)
105
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
106
+ ssl.connect
107
+ return sock, ssl
108
+ end
109
+
110
+ def self.context
111
+ context = OpenSSL::SSL::SSLContext.new
112
+ context.cert = OpenSSL::X509::Certificate.new(pem_data)
113
+ context.key = OpenSSL::PKey::RSA.new(pem_data, pem_password)
114
+ context
115
+ end
116
+
117
+ def self.pem_data
118
+ return @pem_data if @pem_data
119
+
120
+ if @pem_path
121
+ raise ConfigurationError.new('The specified PEM file does not exist.') unless File.exist?(@pem_path)
122
+ @pem_data = File.read(@pem_path)
123
+ else
124
+ raise ConfigurationError.new('PEM not configured properly.')
125
+ end
126
+ end
127
+
128
+ end
@@ -0,0 +1,47 @@
1
+ module APNS
2
+ class Notification
3
+ attr_accessor :device_token, :alert, :badge, :sound, :other
4
+
5
+ def initialize(device_token, message)
6
+ self.device_token = device_token
7
+ if message.is_a?(Hash)
8
+ self.alert = message[:alert]
9
+ self.badge = message[:badge]
10
+ self.sound = message[:sound]
11
+ self.other = message[:other]
12
+ elsif message.is_a?(String)
13
+ self.alert = message
14
+ else
15
+ raise "Notification needs to have either a Hash or String"
16
+ end
17
+ end
18
+
19
+ def packaged_notification
20
+ pt = self.packaged_token
21
+ pm = self.packaged_message
22
+ [0, 0, 32, pt, 0, pm.bytesize, pm].pack("ccca*cca*")
23
+ end
24
+
25
+ def packaged_token
26
+ [device_token.gsub(/[\s|<|>]/,'')].pack('H*')
27
+ end
28
+
29
+ def packaged_message
30
+ aps = {'aps'=> {} }
31
+ aps['aps']['alert'] = self.alert if self.alert
32
+ aps['aps']['badge'] = self.badge if self.badge
33
+ aps['aps']['sound'] = self.sound if self.sound
34
+ aps.merge!(self.other) if self.other
35
+ aps.to_json.gsub(/\\u([\da-fA-F]{4})/) {|m| [$1].pack("H*").unpack("n*").pack("U*")}
36
+ end
37
+
38
+ def ==(that)
39
+ device_token == that.device_token &&
40
+ alert == that.alert &&
41
+ badge == that.badge &&
42
+ sound == that.sound &&
43
+ other == that.other
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,2 @@
1
+ require "mercurius/apns/core"
2
+ require "mercurius/apns/notification"
@@ -0,0 +1,113 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module GCM
5
+ include HTTParty
6
+
7
+ @host = 'https://android.googleapis.com/gcm/send'
8
+ @format = :json
9
+ @key = nil
10
+
11
+ class << self
12
+ attr_accessor :host, :format, :key
13
+
14
+ def key(identity = nil)
15
+ if @key.is_a?(Hash)
16
+ raise %{If your key is a hash of keys you'll need to pass a identifier to the notification!} if identity.nil?
17
+ return @key[identity]
18
+ else
19
+ return @key
20
+ end
21
+ end
22
+
23
+ def key_identities
24
+ if @key.is_a?(Hash)
25
+ return @key.keys
26
+ else
27
+ return nil
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.send_notification(device_tokens, data = {}, options = {})
33
+ n = GCM::Notification.new(device_tokens, data, options)
34
+ self.send_notifications([n])
35
+ end
36
+
37
+ def self.send_notifications(notifications)
38
+ responses = []
39
+ notifications.each do |n|
40
+ responses << self.prepare_and_send(n)
41
+ end
42
+ responses
43
+ end
44
+
45
+ private
46
+
47
+ def self.prepare_and_send(n)
48
+ if n.device_tokens.count < 1 || n.device_tokens.count > 1000
49
+ raise "Number of device tokens must be between 1 and 1000"
50
+ end
51
+ if !n.collapse_key.nil? && n.time_to_live.nil?
52
+ raise %q{If you are defining a "collapse key" you need a "time to live"}
53
+ end
54
+ if @key.is_a?(Hash) && n.identity.nil?
55
+ raise %{If your key is a Hash of keys you'll need to pass a identifier to the notification!}
56
+ end
57
+
58
+ if self.format == :json
59
+ self.send_push_as_json(n)
60
+ elsif self.format == :text
61
+ self.send_push_as_plain_text(n)
62
+ else
63
+ raise "Invalid format"
64
+ end
65
+ end
66
+
67
+ def self.send_push_as_json(n)
68
+ headers = {
69
+ 'Authorization' => "key=#{ self.key(n.identity) }",
70
+ 'Content-Type' => 'application/json',
71
+ }
72
+ body = {
73
+ registration_ids: n.device_tokens,
74
+ data: n.data,
75
+ collapse_key: n.collapse_key,
76
+ time_to_live: n.time_to_live,
77
+ delay_while_idle: n.delay_while_idle
78
+ }
79
+ return self.send_to_server(headers, body.to_json)
80
+ end
81
+
82
+ def self.send_push_as_plain_text(n)
83
+ raise "Still has to be done: http://developer.android.com/guide/google/gcm/gcm.html"
84
+ headers = {
85
+ # TODO: Aceitar key ser um hash
86
+ 'Authorization' => "key=#{ self.key(n.identity) }",
87
+ 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
88
+ }
89
+ return self.send_to_server(headers, body)
90
+ end
91
+
92
+ def self.send_to_server(headers, body)
93
+ params = { headers: headers, body: body}
94
+ response = self.post(self.host, params)
95
+ build_response(response)
96
+ end
97
+
98
+ def self.build_response(response)
99
+ case response.code
100
+ when 200
101
+ { response: 'success', body: JSON.parse(response.body), headers: response.headers, status_code: response.code }
102
+ when 400
103
+ { response: 'Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.', status_code: response.code }
104
+ when 401
105
+ { response: 'There was an error authenticating the sender account.', status_code: response.code }
106
+ when 500
107
+ { response: 'There was an internal error in the GCM server while trying to process the request.', status_code: response.code }
108
+ when 503
109
+ { response: 'Server is temporarily unavailable.', status_code: response.code }
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,55 @@
1
+ module GCM
2
+ class Notification
3
+ attr_accessor :device_tokens, :data, :collapse_key, :time_to_live, :delay_while_idle, :identity
4
+
5
+ def initialize(tokens, data, options = {})
6
+ self.device_tokens = tokens
7
+ self.data = data
8
+
9
+ @collapse_key = options[:collapse_key]
10
+ @time_to_live = options[:time_to_live]
11
+ @delay_while_idle = options[:delay_while_idle]
12
+ @identity = options[:identity]
13
+ end
14
+
15
+ def device_tokens=(tokens)
16
+ if tokens.is_a?(Array)
17
+ @device_tokens = tokens
18
+ elsif tokens.is_a?(String)
19
+ @device_tokens = [tokens]
20
+ else
21
+ raise "device_tokens needs to be either an Array or a String"
22
+ end
23
+ end
24
+
25
+ def data=(data)
26
+ if data.is_a?(Hash)
27
+ @data = data
28
+ else
29
+ raise "data parameter must be the type of Hash"
30
+ end
31
+ end
32
+
33
+ def delay_while_idle=(delay_while_idle)
34
+ @delay_while_idle = (delay_while_idle == true || delay_while_idle == :true)
35
+ end
36
+
37
+ def time_to_live=(time_to_live)
38
+ if time_to_live.is_a?(Integer)
39
+ @time_to_live = time_to_live
40
+ else
41
+ raise %q{"time_to_live" must be seconds as an integer value, like "100"}
42
+ end
43
+ end
44
+
45
+ def ==(that)
46
+ device_tokens == that.device_tokens &&
47
+ data == that.data &&
48
+ collapse_key == that.collapse_key &&
49
+ time_to_live == that.time_to_live &&
50
+ delay_while_idle == that.delay_while_idle &&
51
+ identity == that.identity
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module Mercurius
2
+ VERSION = '0.0.1'
3
+ end
data/lib/mercurius.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "mercurius/version"
2
+ require "mercurius/apple"
3
+ require "mercurius/android"
data/mercurius.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mercurius/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'mercurius'
7
+ s.version = Mercurius::VERSION
8
+ s.authors = ["John Beck"]
9
+ s.email = ["jbeck@live.com"]
10
+
11
+ s.homepage = "https://github.com/jrbeck/mercurius"
12
+ s.summary = %q{Send push notifications to mobile devices through native services}
13
+ s.description = <<-DESC
14
+ This gem is a wrapper to send push notifications to devices through native push services.
15
+ DESC
16
+
17
+ s.rubyforge_project = "mercurius"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+
23
+ s.require_paths = ["lib"]
24
+
25
+ s.add_dependency 'httparty'
26
+ s.add_dependency 'json'
27
+
28
+ s.add_development_dependency 'rake'
29
+ s.add_development_dependency 'rspec'
30
+ end
@@ -0,0 +1,148 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mercurius do
4
+ describe "APNS" do
5
+ it "should have an APNS object" do
6
+ defined?(APNS).should_not be_false
7
+ end
8
+
9
+ it "should not forget the APNS default parameters" do
10
+ APNS.host.should == "gateway.sandbox.push.apple.com"
11
+ APNS.port.should == 2195
12
+ APNS.pem_path.should be_equal(nil)
13
+ APNS.pem_password.should be_equal(nil)
14
+ end
15
+
16
+ describe "Notifications" do
17
+ describe "#==" do
18
+ it "should properly equate objects without caring about object identity" do
19
+ a = APNS::Notification.new("123", {:alert => "hi"})
20
+ b = APNS::Notification.new("123", {:alert => "hi"})
21
+ a.should eq(b)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '.send_notification' do
27
+ let(:token) { 'token' }
28
+ let(:message) { 'message' }
29
+ let(:pem_data) { 'pem_data' }
30
+ let(:cert) { double }
31
+ let(:key) { double }
32
+ let(:sock) { double(close: nil) }
33
+ let(:ssl) { double(connect: nil, write: nil, close: nil) }
34
+ let(:packaged) { 'packaged' }
35
+
36
+ after do
37
+ APNS.pem_path = nil
38
+ APNS.pem_data = nil
39
+ end
40
+
41
+ before do
42
+ allow(OpenSSL::X509::Certificate).to receive(:new).
43
+ with(pem_data).and_return(cert)
44
+ allow(OpenSSL::PKey::RSA).to receive(:new).
45
+ with(pem_data, anything).and_return(key)
46
+ allow(TCPSocket).to receive(:new).and_return(sock)
47
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl)
48
+ end
49
+
50
+ shared_examples 'notifications' do
51
+ it 'notifications are sent' do
52
+ expect(ssl).to have_received(:write).with(/"#{message}"/)
53
+ end
54
+ end
55
+
56
+ context 'with pem setting' do
57
+ context 'with an existing pem file' do
58
+ let(:path) { '/good/path' }
59
+
60
+ before do
61
+ allow(File).to receive(:exist?).with(path).and_return(true)
62
+ allow(File).to receive(:read).with(path).and_return(pem_data)
63
+ end
64
+
65
+ before { APNS.pem_path = '/good/path' }
66
+ before { APNS.send_notification(token, message) }
67
+
68
+ include_examples 'notifications'
69
+ end
70
+
71
+ context 'when the pem does not exist' do
72
+ before { APNS.pem_path = '/bad/path' }
73
+
74
+ it 'fails' do
75
+ expect do
76
+ APNS.send_notification(token, message)
77
+ end.to raise_error(APNS::ConfigurationError, /does not exist/)
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'with pem_data' do
83
+ before { APNS.pem_data = pem_data }
84
+ before { APNS.send_notification(token, message) }
85
+
86
+ include_examples 'notifications'
87
+ end
88
+
89
+ context 'without pem or pem_data' do
90
+ before { APNS.pem_path = nil }
91
+
92
+ it 'fails' do
93
+ expect do
94
+ APNS.send_notification(token, message)
95
+ end.to raise_error(APNS::ConfigurationError, /PEM not configured properly/)
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ describe "GCM" do
103
+ it "should have a GCM object" do
104
+ defined?(GCM).should_not be_false
105
+ end
106
+
107
+ describe "Notifications" do
108
+
109
+ before do
110
+ @options = {:data => "dummy data"}
111
+ end
112
+
113
+ it "should allow only notifications with device_tokens as array" do
114
+ n = GCM::Notification.new("id", @options)
115
+ n.device_tokens.is_a?(Array).should be_true
116
+
117
+ n.device_tokens = ["a" "b", "c"]
118
+ n.device_tokens.is_a?(Array).should be_true
119
+
120
+ n.device_tokens = "a"
121
+ n.device_tokens.is_a?(Array).should be_true
122
+ end
123
+
124
+ it "should allow only notifications with data as hash with :data root" do
125
+ n = GCM::Notification.new("id", { :data => "data" })
126
+
127
+ n.data.is_a?(Hash).should be_true
128
+ n.data.should == {:data => "data"}
129
+
130
+ n.data = {:a => ["a", "b", "c"]}
131
+ n.data.is_a?(Hash).should be_true
132
+ n.data.should == {:a => ["a", "b", "c"]}
133
+
134
+ n.data = {:a => "a"}
135
+ n.data.is_a?(Hash).should be_true
136
+ n.data.should == {:a => "a"}
137
+ end
138
+
139
+ describe "#==" do
140
+ it "should properly equate objects without caring about object identity" do
141
+ a = GCM::Notification.new("id", { :data => "data" })
142
+ b = GCM::Notification.new("id", { :data => "data" })
143
+ a.should eq(b)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1 @@
1
+ require 'mercurius'
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mercurius
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Beck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: |2
70
+ This gem is a wrapper to send push notifications to devices through native push services.
71
+ email:
72
+ - jbeck@live.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".ruby-gemset"
79
+ - ".ruby-version"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE
83
+ - README.md
84
+ - Rakefile
85
+ - lib/mercurius.rb
86
+ - lib/mercurius/android.rb
87
+ - lib/mercurius/apns/core.rb
88
+ - lib/mercurius/apns/notification.rb
89
+ - lib/mercurius/apple.rb
90
+ - lib/mercurius/gcm/core.rb
91
+ - lib/mercurius/gcm/notification.rb
92
+ - lib/mercurius/version.rb
93
+ - mercurius.gemspec
94
+ - spec/lib/mercurius_spec.rb
95
+ - spec/spec_helper.rb
96
+ homepage: https://github.com/jrbeck/mercurius
97
+ licenses: []
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project: mercurius
115
+ rubygems_version: 2.2.2
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Send push notifications to mobile devices through native services
119
+ test_files:
120
+ - spec/lib/mercurius_spec.rb
121
+ - spec/spec_helper.rb