sanjose 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +50 -0
- data/Rakefile +11 -0
- data/bin/gcm +69 -0
- data/lib/sanjose/client.rb +154 -0
- data/lib/sanjose/connection.rb +26 -0
- data/lib/sanjose/notification.rb +104 -0
- data/lib/sanjose/version.rb +3 -0
- data/lib/sanjose.rb +8 -0
- data/sanjose.gemspec +25 -0
- metadata +123 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Min Kim
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Sanjose
|
2
|
+
**Google Cloud Message.**
|
3
|
+
|
4
|
+
Sanjose is a simple gem for sending Google Cloud Message.
|
5
|
+
|
6
|
+
> This project is inspired by [houston](https://github.com/mattt/houston) and named as following the series of world-class command-line utilities.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'sanjose'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install sanjose
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
GCM = Sanjose::Client.new
|
26
|
+
GCM.gcm_api_key = "AIzaSyApdin5fMuTSGtW-OTC9gJrm2qz1hJPUzk"
|
27
|
+
|
28
|
+
# An example of the registration id sent back when a device registers for notifications
|
29
|
+
registration_id = "APA91bH35MqYza3xfc2hfag6Rr8VQPSOmi2nrUOPABlFwowfVMZNHaBGBpx-zQ7nuv9qzCEosepUMPKyOrVn0UncZMa__E2sWuM2Q53fjJ5loqIY1QKCza3MkxAu1rvyhhzJP3meEqpmv-kjBuRTeWe_ysRUICupE-awrK1eiStmmm2Y_VBBSs4"
|
30
|
+
|
31
|
+
# Create a notification that alerts a message to the user.
|
32
|
+
notification = Sanjose::Notification.new(devices: [registration_id])
|
33
|
+
notification.collapse_key = "Hello, World!"
|
34
|
+
notification.data = {foo: "bar"}
|
35
|
+
|
36
|
+
# And... sent! That's all it takes.
|
37
|
+
GCM.push(notification)
|
38
|
+
```
|
39
|
+
|
40
|
+
## Contact
|
41
|
+
|
42
|
+
Min Kim
|
43
|
+
|
44
|
+
- http://github.com/minsikzzang
|
45
|
+
- http://twitter.com/minsikzzang
|
46
|
+
- minsikzzang@gmail.com
|
47
|
+
|
48
|
+
## License
|
49
|
+
|
50
|
+
Sanjose is available under the MIT license. See the LICENSE file for more info.
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
gemspec = eval(File.read("sanjose.gemspec"))
|
5
|
+
|
6
|
+
task :build => "#{gemspec.full_name}.gem"
|
7
|
+
|
8
|
+
file "#{gemspec.full_name}.gem" => gemspec.files + ["sanjose.gemspec"] do
|
9
|
+
system "gem build houston.gemspec"
|
10
|
+
system "gem install sanjose-#{Houston::VERSION}.gem"
|
11
|
+
end
|
data/bin/gcm
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'commander/import'
|
4
|
+
|
5
|
+
require 'sanjose'
|
6
|
+
|
7
|
+
HighLine.track_eof = false # Fix for built-in Ruby
|
8
|
+
Signal.trap("INT") {} # Suppress backtrace when exiting command
|
9
|
+
|
10
|
+
program :version, Sanjose::VERSION
|
11
|
+
program :description, 'A command-line interface for sending push notifications for Android'
|
12
|
+
|
13
|
+
program :help, 'Author', 'Min Kim <minsikzzang@gmail.com>'
|
14
|
+
program :help, 'Website', 'https://github.com/minsikzzang'
|
15
|
+
program :help_formatter, :compact
|
16
|
+
|
17
|
+
default_command :help
|
18
|
+
|
19
|
+
command :push do |c|
|
20
|
+
c.syntax = 'gcm push DEVICE [...]'
|
21
|
+
c.summary = 'Sends an Google Cloud Message to specified devices'
|
22
|
+
c.description = ''
|
23
|
+
|
24
|
+
c.example 'description', 'gcm push <registration_id> -d "{\"foo\":\"bar\"}"" -k "AIzaSyApdin5fMuTSGtW-OTC9gJrm2qz1hJPUzk" -c "Hello, World!"'
|
25
|
+
c.option '-k', '--key KEY', 'Google Cloud Message api key'
|
26
|
+
c.option '-c', '--collapse_key CK', 'An arbitrary string (such as "Updates Available") that is used to collapse a group of like messages when the device is offline'
|
27
|
+
c.option '-r', '--dry_run DRY_RUN', 'It allows developers to test their request without actually sending a message'
|
28
|
+
c.option '-t', '--time_to_live TTL', 'How long (in seconds) the message should be kept on GCM storage if the device is offline.'
|
29
|
+
c.option '-p', '--restricted_package_name RPN', 'A string containing the package name of your application.'
|
30
|
+
c.option '-d', '--data DATA', 'A JSON object whose fields represents the key-value pairs of the message\'s payload data'
|
31
|
+
|
32
|
+
c.action do |args, options|
|
33
|
+
say_error "One or more device registration_id required" and abort if args.empty?
|
34
|
+
|
35
|
+
@key = options.key
|
36
|
+
say_error "Google Cloud api key is required" and abort unless @key
|
37
|
+
|
38
|
+
@data = options.data
|
39
|
+
@time_to_live = options.time_to_live
|
40
|
+
@collapse_key = options.collapse_key
|
41
|
+
@restricted_package_name = options.restricted_package_name
|
42
|
+
@dry_run = options.dry_run
|
43
|
+
|
44
|
+
client = Sanjose::Client.new
|
45
|
+
client.gcm_api_key = @key
|
46
|
+
a = "APA91bH35MqYza3xfc2hfag6Rr8VQPSOmi2nrUOPABlFwowfVMZNHaBGBpx-zQ7nuv9qzCEosepUMPKyOrVn0UncZMa__E2sWuM2Q53fjJ5loqIY1QKCza3MkxAu1rvyhhzJP3meEqpmv-kjBuRTeWe_ysRUICupE-awrK1eiStmmm2Y_VBBSs4"
|
47
|
+
b = "APA91bHAX-Owu0Ulm5XubHs7BYy6a4O87Gk8-2JOW3eJ2TauVzWsB0d4yCxjzxMXl40elIIwG3E0Lpvd1_5xGldZm64UO4fqC0RNpvGWKUSr7hMNhavRS8w2NS2XUwYsluysVaXZU0rvmq0zSCH6JHGZiqd0qlUsmg"
|
48
|
+
|
49
|
+
notification = Sanjose::Notification.new
|
50
|
+
args.each do |device|
|
51
|
+
notification.add_device(device)
|
52
|
+
end
|
53
|
+
|
54
|
+
notification.data = JSON.parse(@data)
|
55
|
+
notification.collapse_key = @collapse_key
|
56
|
+
notification.dry_run = @dry_run
|
57
|
+
notification.restricted_package_name = @restricted_package_name
|
58
|
+
notification.time_to_live = @time_to_live
|
59
|
+
|
60
|
+
begin
|
61
|
+
results = client.push(notification)
|
62
|
+
puts results.inspect
|
63
|
+
rescue => e
|
64
|
+
say_error "Exception sending notification: #{e}" and abort
|
65
|
+
end
|
66
|
+
|
67
|
+
say_ok "Push notifications successfully sent"
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Sanjose
|
2
|
+
GCM_GATEWAY_URI = "https://android.googleapis.com/gcm/send"
|
3
|
+
MULTICAST_SIZE = 1000
|
4
|
+
TOKEN_CANONICAL_REG_ID = 'registration_id'
|
5
|
+
JSON_SUCCESS = 'success'
|
6
|
+
JSON_FAILURE ='failure'
|
7
|
+
JSON_CANONICAL_IDS = 'canonical_ids'
|
8
|
+
JSON_MULTICAST_ID = 'multicast_id'
|
9
|
+
JSON_RESULTS = 'results'
|
10
|
+
JSON_MESSAGE_ID = 'message_id'
|
11
|
+
JSON_ERROR = 'error'
|
12
|
+
ERROR_UNAVAILABLE = "Unavailable";
|
13
|
+
|
14
|
+
# Initial delay before first retry
|
15
|
+
BACKOFF_INITIAL_DELAY = 1000
|
16
|
+
|
17
|
+
# Maximum delay before a retry.
|
18
|
+
MAX_BACKOFF_DELAY = 1024000
|
19
|
+
|
20
|
+
class Client
|
21
|
+
attr_accessor :gcm_gateway_uri, :gcm_api_key
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@gcm_gateway_uri = GCM_GATEWAY_URI
|
25
|
+
@gcm_api_key = ENV['GCM_API_KEY']
|
26
|
+
end
|
27
|
+
|
28
|
+
def push(notification, retries = 5)
|
29
|
+
return if not notification or notification.sent?
|
30
|
+
|
31
|
+
attempt = 0
|
32
|
+
try_again = true
|
33
|
+
muticast_ids = []
|
34
|
+
results = {}
|
35
|
+
unsent_reg_ids = notification.devices
|
36
|
+
backoff = BACKOFF_INITIAL_DELAY
|
37
|
+
|
38
|
+
while try_again do
|
39
|
+
attempt += 1
|
40
|
+
multicast_result = push_no_retry(notification,
|
41
|
+
connection_options_for_endpoint(:gateway), unsent_reg_ids)
|
42
|
+
|
43
|
+
if multicast_result
|
44
|
+
muticast_ids << multicast_result.multicast_id
|
45
|
+
unsent_reg_ids = update_status(unsent_reg_ids, results, multicast_result)
|
46
|
+
end
|
47
|
+
|
48
|
+
try_again = !unsent_reg_ids.empty? and attempt <= retries
|
49
|
+
|
50
|
+
if try_again
|
51
|
+
sleep_time = backoff / 2 + rand(backoff)
|
52
|
+
sleep(sleep_time / 1000)
|
53
|
+
if 2 * backoff < MAX_BACKOFF_DELAY
|
54
|
+
backoff *= 2
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# calculate summary
|
60
|
+
success = 0
|
61
|
+
failure = 0
|
62
|
+
canonical_ids = 0
|
63
|
+
|
64
|
+
results.values.each do |result|
|
65
|
+
if result.message_id
|
66
|
+
success += 1
|
67
|
+
if result.canonical_reg_id
|
68
|
+
canonical_ids += 1
|
69
|
+
end
|
70
|
+
else
|
71
|
+
failure += 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# build a new object with the overall result
|
76
|
+
multicast_id = muticast_ids.pop(0)
|
77
|
+
builder = MulticastResult.new(
|
78
|
+
:success => success,
|
79
|
+
:failure => failure,
|
80
|
+
:canonical_ids => canonical_ids,
|
81
|
+
:multicast_id => multicast_id)
|
82
|
+
builder.retry_multicast_ids = muticast_ids
|
83
|
+
|
84
|
+
# add results, in the same order as the input
|
85
|
+
notification.devices.each do |device|
|
86
|
+
builder.add_result(results[device])
|
87
|
+
end
|
88
|
+
|
89
|
+
builder
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def update_status(unsent_reg_ids, all_results, multicast_result)
|
94
|
+
results = multicast_result.results
|
95
|
+
if results.length != unsent_reg_ids.length
|
96
|
+
# should never heppan, unless there is a flaw in the algorithm
|
97
|
+
raise RuntimeError
|
98
|
+
"internal error: sizes do not match. current_results: #{results}; unsent_reg_ids: #{unsent_reg_ids}"
|
99
|
+
end
|
100
|
+
|
101
|
+
new_unsent_reg_ids = []
|
102
|
+
i = 0
|
103
|
+
unsent_reg_ids.each do |reg_id|
|
104
|
+
result = results.at(i)
|
105
|
+
all_results[reg_id] = result
|
106
|
+
i += 1
|
107
|
+
if result.error and result.error == ERROR_UNAVAILABLE
|
108
|
+
new_unsent_reg_ids << reg_id
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
new_unsent_reg_ids
|
113
|
+
end
|
114
|
+
|
115
|
+
def push_no_retry(notification, uri, reg_ids)
|
116
|
+
multicast_result = nil
|
117
|
+
messages = notification.clone
|
118
|
+
messages.devices = reg_ids
|
119
|
+
|
120
|
+
Connection.open(uri) do |connection|
|
121
|
+
message = messages.message
|
122
|
+
|
123
|
+
# Need to put gcm api keyand content length in header
|
124
|
+
headers = {"Authorization" => "key=#{@gcm_api_key}",
|
125
|
+
"Content-type" => "application/json"}
|
126
|
+
response = connection.post(uri.path, message, headers)
|
127
|
+
return nil if response.code != "200"
|
128
|
+
|
129
|
+
json = JSON.parse(response.body)
|
130
|
+
multicast_result = MulticastResult.new(
|
131
|
+
:success => json[JSON_SUCCESS],
|
132
|
+
:failure => json[JSON_FAILURE],
|
133
|
+
:canonical_ids => json[JSON_CANONICAL_IDS],
|
134
|
+
:multicast_id => json[JSON_MULTICAST_ID])
|
135
|
+
results = json[JSON_RESULTS]
|
136
|
+
results.each do |result|
|
137
|
+
multicast_result.add_result(
|
138
|
+
Result.new(:message_id => result[JSON_MESSAGE_ID],
|
139
|
+
:canonical_reg_id => result[TOKEN_CANONICAL_REG_ID],
|
140
|
+
:error => result[JSON_ERROR]))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
multicast_result
|
144
|
+
end
|
145
|
+
|
146
|
+
def connection_options_for_endpoint(endpoint = :gateway)
|
147
|
+
uri = case endpoint
|
148
|
+
when :gateway then URI(@gcm_gateway_uri)
|
149
|
+
else
|
150
|
+
raise ArgumentError
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Sanjose
|
4
|
+
class Connection
|
5
|
+
class << self
|
6
|
+
def open(uri)
|
7
|
+
return if uri == nil
|
8
|
+
|
9
|
+
connection = connection_for_uri(uri)
|
10
|
+
connection.start
|
11
|
+
|
12
|
+
yield connection
|
13
|
+
|
14
|
+
connection.finish
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.connection_for_uri(uri)
|
21
|
+
connection = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
connection.use_ssl = uri.scheme == 'https'
|
23
|
+
connection
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Sanjose
|
4
|
+
class Notification
|
5
|
+
attr_accessor :data, :collapse_key, :delay_when_idle, :time_to_live,
|
6
|
+
:dry_run, :restricted_package_name, :devices
|
7
|
+
attr_reader :sent_at
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
# Required field
|
11
|
+
# A string array with the list of devices (registration IDs) receiving
|
12
|
+
# the message. It must contain at least 1 and at most 1000 registration IDs.
|
13
|
+
if !options[:devices]
|
14
|
+
@devices = []
|
15
|
+
else
|
16
|
+
@devices = options[:devices]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Optional fields
|
20
|
+
# An arbitraru string (such as "Updates Available") that is used to
|
21
|
+
# collapse a group of like messages when the device is offline, so
|
22
|
+
# that only the last message gets sent to the client.
|
23
|
+
@collapse_key = options[:collapse_key]
|
24
|
+
|
25
|
+
# If included, indicates that the message should not be sent immediately
|
26
|
+
# if the device is idle.
|
27
|
+
@delay_when_idle = options[:delay_when_idle]
|
28
|
+
|
29
|
+
# How long (in seconds) the message should be kept on GCM storage if the
|
30
|
+
# device is offline.
|
31
|
+
@time_to_live = options[:time_to_live]
|
32
|
+
|
33
|
+
# If included, allows developers to test their request without actually
|
34
|
+
# sending a message. Optional. The default value is false, and must be a
|
35
|
+
# JSON boolean.
|
36
|
+
@dry_run = options[:dry_run]
|
37
|
+
|
38
|
+
# A string containing the package name of your application. When set,
|
39
|
+
# messages will only be sent to registration IDs that match the package
|
40
|
+
# name.
|
41
|
+
@restricted_package_name = options[:restricted_package_name]
|
42
|
+
|
43
|
+
# A JSON object whose fields represents the key-value pairs of the message's
|
44
|
+
# payload data. There is no limit on the number of key/value pairs, though
|
45
|
+
# there is a limit on the total size of the message (4kb).
|
46
|
+
@data = options[:data]
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_device(device)
|
50
|
+
@devices << device
|
51
|
+
end
|
52
|
+
|
53
|
+
def device_size
|
54
|
+
@devices.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def message
|
58
|
+
# plan-text format
|
59
|
+
# 'registration_id=APA91bH35MqYza3xfc2hfag6Rr8VQPSOmi2nrUOPABlFwowfVMZNHaBGBpx-zQ7nuv9qzCEosepUMPKyOrVn0UncZMa__E2sWuM2Q53fjJ5loqIY1QKCza3MkxAu1rvyhhzJP3meEqpmv-kjBuRTeWe_ysRUICupE-awrK1eiStmmm2Y_VBBSs4'
|
60
|
+
# json format
|
61
|
+
# '{"registration_ids":["APA91bH35MqYza3xfc2hfag6Rr8VQPSOmi2nrUOPABlFwowfVMZNHaBGBpx-zQ7nuv9qzCEosepUMPKyOrVn0UncZMa__E2sWuM2Q53fjJ5loqIY1QKCza3MkxAu1rvyhhzJP3meEqpmv-kjBuRTeWe_ysRUICupE-awrK1eiStmmm2Y_VBBSs4","APA91bHAX-Owu0Ulm5XubHs7BYy6a4O87Gk8-2JOW3eJ2TauVzWsB0d4yCxjzxMXl40elIIwG3E0Lpvd1_5xGldZm64UO4fqC0RNpvGWKUSr7hMNhavRS8w2NS2XUwYsluysVaXZU0rvmq0zSCH6JHGZiqd0qlUsmg"]}'
|
62
|
+
|
63
|
+
json = {}
|
64
|
+
if @devices.empty?
|
65
|
+
raise ArgumentError 'devices cannot be empty'
|
66
|
+
else
|
67
|
+
json['registration_ids'] = @devices
|
68
|
+
end
|
69
|
+
|
70
|
+
if @collapse_key
|
71
|
+
json['collapse_key'] = @collapse_key
|
72
|
+
end
|
73
|
+
if @delay_when_idle
|
74
|
+
json['delay_when_idle'] = @delay_when_idle
|
75
|
+
end
|
76
|
+
|
77
|
+
if @time_to_live
|
78
|
+
json['time_to_live'] = @time_to_live
|
79
|
+
end
|
80
|
+
|
81
|
+
if @data
|
82
|
+
json['data'] = @data
|
83
|
+
end
|
84
|
+
|
85
|
+
if @restricted_package_name
|
86
|
+
json['restricted_package_name'] = @restricted_package_name
|
87
|
+
end
|
88
|
+
|
89
|
+
if @dry_run
|
90
|
+
json['dry_run'] = @dry_run
|
91
|
+
end
|
92
|
+
|
93
|
+
json.to_json
|
94
|
+
end
|
95
|
+
|
96
|
+
def mark_as_sent!
|
97
|
+
@sent_at = Time.now
|
98
|
+
end
|
99
|
+
|
100
|
+
def sent?
|
101
|
+
!!@sent_at
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/sanjose.rb
ADDED
data/sanjose.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sanjose'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "sanjose"
|
8
|
+
s.version = Sanjose::VERSION
|
9
|
+
s.authors = ["Min Kim"]
|
10
|
+
s.email = ["minsikzzang@gmail.com", "minsik.kim@livestation.com"]
|
11
|
+
s.description = "Send Google Cloud Message"
|
12
|
+
s.summary = "Sanjose is a simple gem for sending Google Cloud Message"
|
13
|
+
s.homepage = "http://github.com/minsikzzang/sanjose"
|
14
|
+
|
15
|
+
s.add_dependency "commander", "~> 4.1.2"
|
16
|
+
s.add_dependency "json", "~> 1.7.3"
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec", "~> 0.6.1"
|
19
|
+
s.add_development_dependency "rake", "~> 0.9.2"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split($/)
|
22
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sanjose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Min Kim
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: commander
|
16
|
+
requirement: !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: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.1.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.7.3
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.7.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.6.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.6.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.9.2
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.9.2
|
78
|
+
description: Send Google Cloud Message
|
79
|
+
email:
|
80
|
+
- minsikzzang@gmail.com
|
81
|
+
- minsik.kim@livestation.com
|
82
|
+
executables:
|
83
|
+
- gcm
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE.txt
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- bin/gcm
|
93
|
+
- lib/sanjose.rb
|
94
|
+
- lib/sanjose/client.rb
|
95
|
+
- lib/sanjose/connection.rb
|
96
|
+
- lib/sanjose/notification.rb
|
97
|
+
- lib/sanjose/version.rb
|
98
|
+
- sanjose.gemspec
|
99
|
+
homepage: http://github.com/minsikzzang/sanjose
|
100
|
+
licenses: []
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.24
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Sanjose is a simple gem for sending Google Cloud Message
|
123
|
+
test_files: []
|