houston 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +7 -1
- data/README.md +37 -25
- data/Rakefile +1 -1
- data/bin/apn +76 -0
- data/houston-0.1.0.gem +0 -0
- data/houston.gemspec +6 -3
- data/lib/houston.rb +5 -145
- data/lib/houston/client.rb +75 -0
- data/lib/houston/connection.rb +32 -0
- data/lib/houston/notification.rb +41 -0
- metadata +39 -10
data/Gemfile.lock
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
houston (0.0
|
4
|
+
houston (0.1.0)
|
5
|
+
commander (~> 4.1.2)
|
6
|
+
json (~> 1.7.3)
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: http://rubygems.org/
|
8
10
|
specs:
|
11
|
+
commander (4.1.2)
|
12
|
+
highline (~> 1.6.11)
|
13
|
+
highline (1.6.13)
|
14
|
+
json (1.7.5)
|
9
15
|
rake (0.9.2.2)
|
10
16
|
rspec (0.6.4)
|
11
17
|
|
data/README.md
CHANGED
@@ -3,35 +3,51 @@
|
|
3
3
|
|
4
4
|
> Houston, We Have Liftoff!
|
5
5
|
|
6
|
-
Push Notifications don't have to be difficult.
|
6
|
+
Push Notifications don't have to be difficult.
|
7
7
|
|
8
8
|
Houston is a simple gem for sending Apple Push Notifications. Pass your credentials, construct your message, and send it.
|
9
9
|
|
10
10
|
In a production application, you will probably want to schedule or queue notifications into a background job. Whether you're using [queue_classic](https://github.com/ryandotsmith/queue_classic), [resque](https://github.com/defunkt/resque), or rolling you own infrastructure, integrating Houston couldn't be simpler.
|
11
11
|
|
12
|
-
Another caveat is that Houston doesn't manage device tokens for you.
|
12
|
+
Another caveat is that Houston doesn't manage device tokens for you. Infrastructures can vary dramatically for these kinds of things, so being agnostic and not forcing any conventions here is more a feature than a bug, perhaps. Treat it the same way as you would an e-mail address, associating one or many for each user account.
|
13
|
+
|
14
|
+
_That said, a simple web service adapter, similar to [Rack::CoreData](https://github.com/mattt/rack-core-data) may be in the cards._
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
```
|
19
|
+
$ gem install houston
|
20
|
+
```
|
13
21
|
|
14
22
|
## Usage
|
15
23
|
|
16
24
|
```ruby
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
# Environment variables are automatically read, or can be overridden by any specified options
|
26
|
+
APN = Houston::Client.new
|
27
|
+
APN.certificate = File.read("/path/to/apple_push_notification.pem")
|
28
|
+
|
29
|
+
# An example of the token sent back when a device registers for notifications
|
30
|
+
token = "<ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b5969>"
|
31
|
+
|
32
|
+
# Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
|
33
|
+
notification = Houston::Notification.new(device: token)
|
34
|
+
notification.alert = "Hello, World!"
|
35
|
+
|
36
|
+
# Notifications can also change the badge count, have a custom sound, or pass along arbitrary data.
|
37
|
+
notification.badge = 57
|
38
|
+
notification.sound = "sosumi.aiff"
|
39
|
+
notification.custom_data = {foo: "bar"}
|
40
|
+
|
41
|
+
# And... sent! That's all it takes.
|
42
|
+
APN.push(notification)
|
43
|
+
```
|
44
|
+
|
45
|
+
## Command Line Tool
|
46
|
+
|
47
|
+
Houston also comes with the `apn` binary, which provides a convenient way to test notifications from the command line.
|
48
|
+
|
49
|
+
```
|
50
|
+
$ apn push "<token>" -c /path/to/apple_push_notification.pem -m "Hello from the command line!"
|
35
51
|
```
|
36
52
|
|
37
53
|
## Converting Your Certificate
|
@@ -48,13 +64,9 @@ and the apple certificate as p12 files. Here is a quick walkthrough on how to do
|
|
48
64
|
Now covert the p12 file to a pem file:
|
49
65
|
|
50
66
|
```
|
51
|
-
|
67
|
+
$ openssl pkcs12 -in cert.p12 -out apple_push_notification.pem -nodes -clcerts
|
52
68
|
```
|
53
69
|
|
54
|
-
If you are using a development certificate, then change the name to apple_push_notification_development.pem instead.
|
55
|
-
|
56
|
-
Store the contents of the certificate files on the app model for the app you want to send notifications to.
|
57
|
-
|
58
70
|
## Contact
|
59
71
|
|
60
72
|
Mattt Thompson
|
data/Rakefile
CHANGED
@@ -7,5 +7,5 @@ task :build => "#{gemspec.full_name}.gem"
|
|
7
7
|
|
8
8
|
file "#{gemspec.full_name}.gem" => gemspec.files + ["houston.gemspec"] do
|
9
9
|
system "gem build houston.gemspec"
|
10
|
-
system "gem install houston-#{Houston::
|
10
|
+
system "gem install houston-#{Houston::VERSION}.gem"
|
11
11
|
end
|
data/bin/apn
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'commander/import'
|
4
|
+
|
5
|
+
require 'houston'
|
6
|
+
|
7
|
+
HighLine.track_eof = false # Fix for built-in Ruby
|
8
|
+
Signal.trap("INT") {} # Suppress backtrace when exiting command
|
9
|
+
|
10
|
+
program :version, Houston::VERSION
|
11
|
+
program :description, 'A command-line interface for sending push notifications'
|
12
|
+
|
13
|
+
program :help, 'Author', 'Mattt Thompson <m@mattt.me>'
|
14
|
+
program :help, 'Website', 'https://github.com/mattt'
|
15
|
+
program :help_formatter, :compact
|
16
|
+
|
17
|
+
default_command :help
|
18
|
+
|
19
|
+
command :push do |c|
|
20
|
+
c.syntax = 'apn push DEVICE [...]'
|
21
|
+
c.summary = 'Sends an Apple Push Notification to specified devices'
|
22
|
+
c.description = ''
|
23
|
+
|
24
|
+
c.example 'description', 'apn push <token> -m "Hello, World" -b 57 -s sosumi.aiff'
|
25
|
+
c.option '-m', '--alert ALERT', 'Body of the alert to send in the push notification'
|
26
|
+
c.option '-b', '--badge NUMBER', 'Badge number to set with the push notification'
|
27
|
+
c.option '-s', '--sound SOUND', 'Sound to play with the notification'
|
28
|
+
c.option '-e', '--environment [production|development]', 'Environment to send push notification (defaults to development)'
|
29
|
+
c.option '-c', '--certificate CERTIFICATE', 'Path to certificate (.pem) file'
|
30
|
+
c.option '-p', '--[no]-passphrase', 'Prompt for a certificate passphrase'
|
31
|
+
|
32
|
+
c.action do |args, options|
|
33
|
+
say_error "One or more device tokens required" and abort if args.empty?
|
34
|
+
|
35
|
+
@environment = options.environment.downcase.to_sym rescue :development
|
36
|
+
say_error "Invalid environment,'#{@environment}' (should be either :development or :production)" and abort unless [:development, :production].include?(@environment)
|
37
|
+
|
38
|
+
@certificate = options.certificate
|
39
|
+
say_error "Missing certificate file option (-c /path/to/certificate.pem)" and abort unless @certificate
|
40
|
+
say_error "Could not find certificate file '#{@certificate}'" and abort unless File.exists?(@certificate)
|
41
|
+
|
42
|
+
@passphrase = options.passphrase ? password : ""
|
43
|
+
|
44
|
+
@alert = options.alert
|
45
|
+
@badge = options.badge.nil? ? nil : options.badge.to_i
|
46
|
+
@sound = options.sound
|
47
|
+
|
48
|
+
unless @alert or @badge
|
49
|
+
placeholder = "Enter your alert message"
|
50
|
+
@alert = ask_editor placeholder
|
51
|
+
say_error "Alert message or badge required" and abort if @alert.nil? or @alert == placeholder
|
52
|
+
end
|
53
|
+
|
54
|
+
notifications = []
|
55
|
+
args.each do |token|
|
56
|
+
notification = Houston::Notification.new(device: token)
|
57
|
+
notification.alert = @alert
|
58
|
+
notification.badge = @badge
|
59
|
+
notification.sound = @sound
|
60
|
+
|
61
|
+
notifications << notification
|
62
|
+
end
|
63
|
+
|
64
|
+
client = @environment == :production ? Houston::Client.production : Houston::Client.development
|
65
|
+
client.certificate = File.read(@certificate)
|
66
|
+
client.passphrase = @passphrase
|
67
|
+
|
68
|
+
begin
|
69
|
+
client.push(*notifications)
|
70
|
+
rescue => e
|
71
|
+
say_error "Exception sending notification: #{e}" and abort
|
72
|
+
end
|
73
|
+
|
74
|
+
say_ok "Push notifications successfully sent"
|
75
|
+
end
|
76
|
+
end
|
data/houston-0.1.0.gem
ADDED
File without changes
|
data/houston.gemspec
CHANGED
@@ -7,10 +7,13 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.authors = ["Mattt Thompson"]
|
8
8
|
s.email = "m@mattt.me"
|
9
9
|
s.homepage = "http://github.com/mattt/houston"
|
10
|
-
s.version = Houston::
|
10
|
+
s.version = Houston::VERSION
|
11
11
|
s.platform = Gem::Platform::RUBY
|
12
|
-
s.summary = "
|
13
|
-
s.description = ""
|
12
|
+
s.summary = "Send Apple Push Notifications"
|
13
|
+
s.description = "Houston is a simple gem for sending Apple Push Notifications. Pass your credentials, construct your message, and send it."
|
14
|
+
|
15
|
+
s.add_dependency "commander", "~> 4.1.2"
|
16
|
+
s.add_dependency "json", "~> 1.7.3"
|
14
17
|
|
15
18
|
s.add_development_dependency "rspec", "~> 0.6.1"
|
16
19
|
s.add_development_dependency "rake", "~> 0.9.2"
|
data/lib/houston.rb
CHANGED
@@ -1,147 +1,7 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'socket'
|
3
|
-
require 'openssl'
|
4
|
-
require 'json'
|
5
|
-
|
6
1
|
module Houston
|
7
|
-
|
8
|
-
|
9
|
-
APPLE_PRODUCTION_GATEWAY_URI = "apn://gateway.push.apple.com:2195"
|
10
|
-
APPLE_PRODUCTION_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
|
11
|
-
|
12
|
-
APPLE_DEVELOPMENT_GATEWAY_URI = "apn://gateway.sandbox.push.apple.com:2195"
|
13
|
-
APPLE_DEVELOPMENT_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
|
14
|
-
|
15
|
-
class Client
|
16
|
-
attr_accessor :gateway_uri, :feedback_uri, :certificate, :passphrase
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
@gateway_uri = ENV['APN_GATEWAY_URI']
|
20
|
-
@feedback_uri = ENV['APN_FEEDBACK_URI']
|
21
|
-
@certificate = ENV['APN_CERTIFICATE']
|
22
|
-
@passphrase = ENV['APN_CERTIFICATE_PASSPHRASE']
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.development
|
26
|
-
client = self.new
|
27
|
-
client.gateway_uri = APPLE_DEVELOPMENT_GATEWAY_URI
|
28
|
-
client.feedback_uri = APPLE_DEVELOPMENT_FEEDBACK_URI
|
29
|
-
client
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.production
|
33
|
-
client = self.new
|
34
|
-
client.gateway_uri = APPLE_PRODUCTION_GATEWAY_URI
|
35
|
-
client.feedback_uri = APPLE_PRODUCTION_FEEDBACK_URI
|
36
|
-
client
|
37
|
-
end
|
38
|
-
|
39
|
-
def push(*notifications)
|
40
|
-
Connection.open(connection_options_for_endpoint(:gateway)) do |connection, socket|
|
41
|
-
notifications.each do |notification|
|
42
|
-
next if notification.sent?
|
43
|
-
|
44
|
-
connection.write(notification.message)
|
45
|
-
notification.mark_as_sent!
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def devices
|
51
|
-
devices = []
|
52
|
-
|
53
|
-
Connection.open(connection_options_for_endpoint(:feedback)) do |connection, socket|
|
54
|
-
while line = connection.read(38)
|
55
|
-
feedback = line.unpack('N1n1H140')
|
56
|
-
token = feedback[2].scan(/.{0,8}/).join(' ').strip
|
57
|
-
devices << token if token
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
devices
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def connection_options_for_endpoint(endpoint = :gateway)
|
67
|
-
uri = case endpoint
|
68
|
-
when :gateway then URI(@gateway_uri)
|
69
|
-
when :feedback then URI(@feedback_uri)
|
70
|
-
else
|
71
|
-
raise ArgumentError
|
72
|
-
end
|
73
|
-
|
74
|
-
{
|
75
|
-
certificate: @certificate,
|
76
|
-
passphrase: @passphrase,
|
77
|
-
host: uri.host,
|
78
|
-
port: uri.port
|
79
|
-
}
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class Notification
|
84
|
-
attr_accessor :device, :alert, :badge, :sound, :custom_data
|
85
|
-
attr_reader :sent_at
|
86
|
-
|
87
|
-
def initialize(options = {})
|
88
|
-
@device = options.delete(:device)
|
89
|
-
@alert = options.delete(:alert)
|
90
|
-
@badge = options.delete(:badge)
|
91
|
-
@sound = options.delete(:sound)
|
92
|
-
|
93
|
-
@custom_data = options
|
94
|
-
end
|
95
|
-
|
96
|
-
def payload
|
97
|
-
json = {}.merge(@custom_data || {})
|
98
|
-
json['aps'] = {}
|
99
|
-
json['aps']['alert'] = @alert
|
100
|
-
json['aps']['badge'] = @badge.to_i rescue 0
|
101
|
-
json['aps']['sound'] = @sound
|
102
|
-
|
103
|
-
json
|
104
|
-
end
|
105
|
-
|
106
|
-
def message
|
107
|
-
json = payload.to_json
|
108
|
-
|
109
|
-
"\0\0 #{[@device.gsub(/[<\s>]/, '')].pack('H*')}\0#{json.length.chr}#{json}"
|
110
|
-
end
|
111
|
-
|
112
|
-
def mark_as_sent!
|
113
|
-
@sent_at = Time.now
|
114
|
-
end
|
115
|
-
|
116
|
-
def sent?
|
117
|
-
!!@sent_at
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class Connection
|
122
|
-
class << self
|
123
|
-
def open(options = {})
|
124
|
-
return unless block_given?
|
125
|
-
|
126
|
-
[:certificate, :passphrase, :host, :port].each do |option|
|
127
|
-
raise ArgumentError, "Missing connection parameter: #{option}" unless option
|
128
|
-
end
|
129
|
-
|
130
|
-
socket = TCPSocket.new(options[:host], options[:port])
|
131
|
-
|
132
|
-
context = OpenSSL::SSL::SSLContext.new
|
133
|
-
context.key = OpenSSL::PKey::RSA.new(options[:certificate], options[:passphrase])
|
134
|
-
context.cert = OpenSSL::X509::Certificate.new(options[:certificate])
|
135
|
-
|
136
|
-
ssl = OpenSSL::SSL::SSLSocket.new(socket, context)
|
137
|
-
ssl.sync = true
|
138
|
-
ssl.connect
|
139
|
-
|
140
|
-
yield ssl, socket
|
141
|
-
|
142
|
-
ssl.close
|
143
|
-
socket.close
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
2
|
+
VERSION = "0.1.0"
|
147
3
|
end
|
4
|
+
|
5
|
+
require 'houston/client'
|
6
|
+
require 'houston/notification'
|
7
|
+
require 'houston/connection'
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Houston
|
2
|
+
APPLE_PRODUCTION_GATEWAY_URI = "apn://gateway.push.apple.com:2195"
|
3
|
+
APPLE_PRODUCTION_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
|
4
|
+
|
5
|
+
APPLE_DEVELOPMENT_GATEWAY_URI = "apn://gateway.sandbox.push.apple.com:2195"
|
6
|
+
APPLE_DEVELOPMENT_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
|
7
|
+
|
8
|
+
class Client
|
9
|
+
attr_accessor :gateway_uri, :feedback_uri, :certificate, :passphrase
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@gateway_uri = ENV['APN_GATEWAY_URI']
|
13
|
+
@feedback_uri = ENV['APN_FEEDBACK_URI']
|
14
|
+
@certificate = ENV['APN_CERTIFICATE']
|
15
|
+
@passphrase = ENV['APN_CERTIFICATE_PASSPHRASE']
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.development
|
19
|
+
client = self.new
|
20
|
+
client.gateway_uri = APPLE_DEVELOPMENT_GATEWAY_URI
|
21
|
+
client.feedback_uri = APPLE_DEVELOPMENT_FEEDBACK_URI
|
22
|
+
client
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.production
|
26
|
+
client = self.new
|
27
|
+
client.gateway_uri = APPLE_PRODUCTION_GATEWAY_URI
|
28
|
+
client.feedback_uri = APPLE_PRODUCTION_FEEDBACK_URI
|
29
|
+
client
|
30
|
+
end
|
31
|
+
|
32
|
+
def push(*notifications)
|
33
|
+
Connection.open(connection_options_for_endpoint(:gateway)) do |connection, socket|
|
34
|
+
notifications.each do |notification|
|
35
|
+
next if notification.sent?
|
36
|
+
|
37
|
+
connection.write(notification.message)
|
38
|
+
notification.mark_as_sent!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def devices
|
44
|
+
devices = []
|
45
|
+
|
46
|
+
Connection.open(connection_options_for_endpoint(:feedback)) do |connection, socket|
|
47
|
+
while line = connection.read(38)
|
48
|
+
feedback = line.unpack('N1n1H140')
|
49
|
+
token = feedback[2].scan(/.{0,8}/).join(' ').strip
|
50
|
+
devices << token if token
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
devices
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def connection_options_for_endpoint(endpoint = :gateway)
|
60
|
+
uri = case endpoint
|
61
|
+
when :gateway then URI(@gateway_uri)
|
62
|
+
when :feedback then URI(@feedback_uri)
|
63
|
+
else
|
64
|
+
raise ArgumentError
|
65
|
+
end
|
66
|
+
|
67
|
+
{
|
68
|
+
certificate: @certificate,
|
69
|
+
passphrase: @passphrase,
|
70
|
+
host: uri.host,
|
71
|
+
port: uri.port
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'socket'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Houston
|
6
|
+
class Connection
|
7
|
+
class << self
|
8
|
+
def open(options = {})
|
9
|
+
return unless block_given?
|
10
|
+
|
11
|
+
[:certificate, :passphrase, :host, :port].each do |option|
|
12
|
+
raise ArgumentError, "Missing connection parameter: #{option}" unless option
|
13
|
+
end
|
14
|
+
|
15
|
+
socket = TCPSocket.new(options[:host], options[:port])
|
16
|
+
|
17
|
+
context = OpenSSL::SSL::SSLContext.new
|
18
|
+
context.key = OpenSSL::PKey::RSA.new(options[:certificate], options[:passphrase])
|
19
|
+
context.cert = OpenSSL::X509::Certificate.new(options[:certificate])
|
20
|
+
|
21
|
+
ssl = OpenSSL::SSL::SSLSocket.new(socket, context)
|
22
|
+
ssl.sync = true
|
23
|
+
ssl.connect
|
24
|
+
|
25
|
+
yield ssl, socket
|
26
|
+
|
27
|
+
ssl.close
|
28
|
+
socket.close
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Houston
|
4
|
+
class Notification
|
5
|
+
attr_accessor :device, :alert, :badge, :sound, :custom_data
|
6
|
+
attr_reader :sent_at
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@device = options.delete(:device)
|
10
|
+
@alert = options.delete(:alert)
|
11
|
+
@badge = options.delete(:badge)
|
12
|
+
@sound = options.delete(:sound)
|
13
|
+
|
14
|
+
@custom_data = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def payload
|
18
|
+
json = {}.merge(@custom_data || {})
|
19
|
+
json['aps'] = {}
|
20
|
+
json['aps']['alert'] = @alert
|
21
|
+
json['aps']['badge'] = @badge.to_i rescue 0
|
22
|
+
json['aps']['sound'] = @sound
|
23
|
+
|
24
|
+
json
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
json = payload.to_json
|
29
|
+
|
30
|
+
"\0\0 #{[@device.gsub(/[<\s>]/, '')].pack('H*')}\0#{json.length.chr}#{json}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def mark_as_sent!
|
34
|
+
@sent_at = Time.now
|
35
|
+
end
|
36
|
+
|
37
|
+
def sent?
|
38
|
+
!!@sent_at
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: houston
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,9 +11,31 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
date: 2012-09-07 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: commander
|
16
|
+
requirement: &70209585432560 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 4.1.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70209585432560
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
requirement: &70209585432040 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.7.3
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70209585432040
|
14
36
|
- !ruby/object:Gem::Dependency
|
15
37
|
name: rspec
|
16
|
-
requirement: &
|
38
|
+
requirement: &70209585431420 !ruby/object:Gem::Requirement
|
17
39
|
none: false
|
18
40
|
requirements:
|
19
41
|
- - ~>
|
@@ -21,10 +43,10 @@ dependencies:
|
|
21
43
|
version: 0.6.1
|
22
44
|
type: :development
|
23
45
|
prerelease: false
|
24
|
-
version_requirements: *
|
46
|
+
version_requirements: *70209585431420
|
25
47
|
- !ruby/object:Gem::Dependency
|
26
48
|
name: rake
|
27
|
-
requirement: &
|
49
|
+
requirement: &70209585430780 !ruby/object:Gem::Requirement
|
28
50
|
none: false
|
29
51
|
requirements:
|
30
52
|
- - ~>
|
@@ -32,21 +54,28 @@ dependencies:
|
|
32
54
|
version: 0.9.2
|
33
55
|
type: :development
|
34
56
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
description:
|
57
|
+
version_requirements: *70209585430780
|
58
|
+
description: Houston is a simple gem for sending Apple Push Notifications. Pass your
|
59
|
+
credentials, construct your message, and send it.
|
37
60
|
email: m@mattt.me
|
38
|
-
executables:
|
61
|
+
executables:
|
62
|
+
- apn
|
39
63
|
extensions: []
|
40
64
|
extra_rdoc_files: []
|
41
65
|
files:
|
42
66
|
- ./apple_push_notification_production.pem
|
43
67
|
- ./Gemfile
|
44
68
|
- ./Gemfile.lock
|
69
|
+
- ./houston-0.1.0.gem
|
45
70
|
- ./houston.gemspec
|
71
|
+
- ./lib/houston/client.rb
|
72
|
+
- ./lib/houston/connection.rb
|
73
|
+
- ./lib/houston/notification.rb
|
46
74
|
- ./lib/houston.rb
|
47
75
|
- ./LICENSE
|
48
76
|
- ./Rakefile
|
49
77
|
- ./README.md
|
78
|
+
- bin/apn
|
50
79
|
homepage: http://github.com/mattt/houston
|
51
80
|
licenses: []
|
52
81
|
post_install_message:
|
@@ -61,7 +90,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
90
|
version: '0'
|
62
91
|
segments:
|
63
92
|
- 0
|
64
|
-
hash:
|
93
|
+
hash: 4053213412686882167
|
65
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
95
|
none: false
|
67
96
|
requirements:
|
@@ -70,11 +99,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
99
|
version: '0'
|
71
100
|
segments:
|
72
101
|
- 0
|
73
|
-
hash:
|
102
|
+
hash: 4053213412686882167
|
74
103
|
requirements: []
|
75
104
|
rubyforge_project:
|
76
105
|
rubygems_version: 1.8.15
|
77
106
|
signing_key:
|
78
107
|
specification_version: 3
|
79
|
-
summary:
|
108
|
+
summary: Send Apple Push Notifications
|
80
109
|
test_files: []
|