ass 0.0.21 → 0.0.24

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.
Files changed (8) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +14 -2
  3. data/VERSION +1 -1
  4. data/ass.yml +3 -1
  5. data/lib/ass.rb +2 -404
  6. data/lib/ass/app.rb +277 -0
  7. data/lib/ass/conf.rb +165 -0
  8. metadata +57 -86
@@ -0,0 +1,277 @@
1
+ #encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'logger'
6
+ require 'sequel'
7
+ require 'socket'
8
+ require 'openssl'
9
+ require 'cgi'
10
+
11
+ require 'rufus/scheduler'
12
+ require 'eventmachine'
13
+ require 'sinatra'
14
+ require 'sinatra/base'
15
+ require 'sinatra/synchrony'
16
+ require 'rack/mobile-detect'
17
+
18
+ require 'uri'
19
+ require 'uri-handler'
20
+ require 'net/http'
21
+
22
+ require 'active_support'
23
+ require 'json'
24
+ require 'digest/sha2'
25
+
26
+ require 'will_paginate'
27
+ require 'will_paginate/sequel'
28
+
29
+ ############################################################
30
+ ## Apple Service Server based on Sinatra
31
+ ############################################################
32
+
33
+ class App < Sinatra::Base
34
+
35
+ register Sinatra::Synchrony
36
+
37
+ use Rack::MobileDetect
38
+
39
+ enable :logging
40
+
41
+ LOGGER = Logger.new("ass-#{$mode}.log", 'a+')
42
+
43
+ case $loglevel.upcase
44
+ when 'FATAL'
45
+ set :logging, Logger::FATAL
46
+ when 'ERROR'
47
+ set :logging, Logger::ERROR
48
+ when 'WARN'
49
+ set :logging, Logger::WARN
50
+ when 'INFO'
51
+ set :logging, Logger::INFO
52
+ when 'DEBUG'
53
+ set :logging, Logger::DEBUG
54
+ else
55
+ set :logging, Logger::DEBUG
56
+ end
57
+
58
+ if "#{$mode}".strip == 'production' then
59
+ set :environment, :production
60
+ else
61
+ set :environment, :development
62
+ end
63
+
64
+ set :root, File.expand_path('../../../', __FILE__)
65
+ set :port, "#{$port}".to_i
66
+ set :public_folder, File.dirname(__FILE__) + '/../../public'
67
+ set :views, File.dirname(__FILE__) + '/../../views'
68
+
69
+ helpers do
70
+
71
+ include Rack::Utils
72
+ alias_method :h, :escape_html
73
+
74
+ def connect_socket(app)
75
+ openSSLContext = $certkey["#{app}"]
76
+ sock = nil
77
+ sslSocket = nil;
78
+ if $mode == 'production' then
79
+ sock = TCPSocket.new('gateway.push.apple.com', 2195)
80
+ else
81
+ sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
82
+ end
83
+ sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext)
84
+ sslSocket.connect
85
+
86
+ [sock, sslSocket]
87
+ end
88
+
89
+ def logger
90
+ LOGGER
91
+ end
92
+
93
+ def checkFlood?(req)
94
+ if $client_ip != "#{req.ip}" then
95
+ $client_ip = "#{req.ip}"
96
+ return false
97
+ else
98
+ if $last_access == 0 then
99
+ return false
100
+ else
101
+ return isFlood?
102
+ end
103
+ end
104
+ end
105
+
106
+ def isFlood?
107
+ result = (Time.now - $last_access) < $flood * 60
108
+ $last_access = Time.now
109
+ return result
110
+ end
111
+
112
+ def iOS?
113
+ result = case request.env['X_MOBILE_DEVICE']
114
+ when /iPhone|iPod|iPad/ then
115
+ true
116
+ else false
117
+ end
118
+ return result
119
+ end
120
+
121
+ def authorized?
122
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
123
+ @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["#{$user}", "#{$pass}"]
124
+ end
125
+
126
+ def protected!
127
+ unless authorized?
128
+ response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
129
+ throw(:halt, [401, "Oops... we need your login name & password\n"])
130
+ end
131
+ end
132
+
133
+ def push(parameter)
134
+ message = CGI::unescape(parameter[:alert].encode("UTF-8") || "")[0..107]
135
+ pid = "#{parameter[:pid]}"
136
+ badge = 1
137
+ badge = parameter[:badge].to_i if parameter[:badge] and parameter[:badge] != ''
138
+ sound = CGI::unescape(parameter[:sound] || "")
139
+ extra = CGI::unescape(parameter[:extra] || "")
140
+
141
+ @tokens = Token.where(:app => "#{app}").reverse_order(:id)
142
+ @exist = Push.first(:pid => "#{pid}", :app => "#{app}")
143
+
144
+ unless @exist
145
+ Push.insert(:pid => pid,
146
+ :message => message,
147
+ :created_at => Time.now,
148
+ :app => "#{app}",
149
+ :ip => "#{parameter.ip}" )
150
+
151
+ sock, sslSocket = connect_socket("#{app}")
152
+
153
+ # write our packet to the stream
154
+ @tokens.each do |o|
155
+ begin
156
+ tokenText = "#{o[:token]}"
157
+ tokenData = [tokenText].pack('H*')
158
+ aps = {'aps'=> {}}
159
+ aps['aps']['alert'] = message
160
+ aps['aps']['badge'] = badge
161
+ aps['aps']['sound'] = sound
162
+ aps['aps']['extra'] = extra
163
+ pm = aps.to_json
164
+ packet = [0,0,32,devData,0,pm.bytesize,pm].pack("ccca*cca*")
165
+ sslSocket.write(packet)
166
+ rescue Errno::EPIPE, OpenSSL::SSL::SSLError => e
167
+ puts "e: #{e} from id:#{o[:id]}"
168
+ sleep 3
169
+ sock, sslSocket = connect_socket("#{app}")
170
+ next
171
+ #retry
172
+ end
173
+ end
174
+ # cleanup
175
+ sslSocket.close
176
+ sock.close
177
+ end
178
+ end
179
+
180
+ def localonly(req)
181
+ protected! unless req.host == 'localhost'
182
+ end
183
+
184
+ end
185
+
186
+ before do
187
+ if "#{$mode}".strip == 'development' then
188
+ enable :dump_errors, :raise_errors, :show_exceptions
189
+ else
190
+ disable :dump_errors, :raise_errors, :show_exceptions
191
+ end
192
+ end
193
+
194
+ get '/' do
195
+ erb :index
196
+ end
197
+
198
+ get '/about' do
199
+ erb :about
200
+ end
201
+
202
+ not_found do
203
+ erb :not_found
204
+ end
205
+
206
+ error do
207
+ @error = "";
208
+ @error = params['captures'].first.inspect if development?
209
+ end
210
+
211
+ post '/v1/send' do
212
+ app = params[:app]
213
+ message = CGI::escape(params[:message] || "").encode("UTF-8")
214
+ pid = "#{Time.now.to_i}"
215
+ # begin
216
+ # url = URI.parse("http://localhost:#{$port}/v1/apps/#{app}/push")
217
+ # post_args1 = { :alert => "#{message}".encode('UTF-8'), :pid => "#{pid}" }
218
+ # Net::HTTP.post_form(url, post_args1)
219
+ # rescue =>err
220
+ # puts "#{err.class} ##{err}"
221
+ # end
222
+ system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{message}/#{pid}"
223
+ redirect '/v1/admin/push' if (params[:app] and params[:message])
224
+ end
225
+
226
+ get "/v1/admin/:db" do
227
+ protected!
228
+ db = params[:db] || 'token'
229
+ page = 1
230
+ page = params[:page].to_i if params[:page]
231
+ if (db == 'token') then
232
+ @o = []
233
+ $apps.each_with_index { |app, index|
234
+ @o << Token.where(:app => app).order(:id).reverse.paginate(page, 20)
235
+ }
236
+ erb :token
237
+ elsif (db == 'push') then
238
+ @p = []
239
+ $apps.each_with_index { |app, index|
240
+ @p << Push.where(:app => app).order(:id).reverse.paginate(page, 20)
241
+ }
242
+ erb :push
243
+ else
244
+ erb :not_found
245
+ end
246
+ end
247
+
248
+ $apps.each { |app|
249
+
250
+ ## register token api
251
+ get "/v1/apps/#{app}/:token" do
252
+ if (("#{params[:token]}".length == 64) and iOS? and checkFlood?(request) ) then
253
+ o = Token.first(:app => app, :token => params[:token])
254
+ unless o
255
+ Token.insert(
256
+ :app => app,
257
+ :token => params[:token],
258
+ :created_at => Time.now
259
+ )
260
+ end
261
+ end
262
+ end
263
+
264
+ ## http POST method push api
265
+ post "/v1/apps/#{app}/push" do
266
+ localonly(request)
267
+ push(params)
268
+ end
269
+
270
+ ## http GET method push api
271
+ ## POST method get more options
272
+ get "/v1/apps/#{app}/push/:message/:pid" do
273
+ localonly(request)
274
+ push(params)
275
+ end
276
+ }
277
+ end
@@ -0,0 +1,165 @@
1
+ #encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'logger'
6
+ require 'sequel'
7
+ require 'socket'
8
+ require 'openssl'
9
+ require 'cgi'
10
+
11
+ require 'rufus/scheduler'
12
+
13
+ require 'uri'
14
+ require 'uri-handler'
15
+ require 'net/http'
16
+
17
+ require 'active_support'
18
+ require 'json'
19
+ require 'digest/sha2'
20
+
21
+ require 'will_paginate'
22
+ require 'will_paginate/sequel'
23
+
24
+ ############################################################
25
+ ## Initilization Setup
26
+ ############################################################
27
+ LIBDIR = File.expand_path(File.join(File.dirname(__FILE__), '../..', 'lib'))
28
+ ROOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
29
+ unless $LOAD_PATH.include?(LIBDIR)
30
+ $LOAD_PATH << LIBDIR
31
+ end
32
+
33
+ unless File.exist?("#{Dir.pwd}/ass.yml") then
34
+ puts 'create config file: ass.yml'
35
+ system "cp #{ROOTDIR}/ass.yml #{Dir.pwd}/ass.yml"
36
+ end
37
+
38
+ unless File.exist?("#{Dir.pwd}/cron") then
39
+ puts "create a demo 'cron' script"
40
+ system "cp #{ROOTDIR}/cron #{Dir.pwd}/cron"
41
+ end
42
+
43
+ ############################################################
44
+ ## Configuration Setup
45
+ ############################################################
46
+ env = ENV['SINATRA_ENV'] || "development"
47
+ config = YAML.load_file("#{Dir.pwd}/ass.yml")
48
+ $timer = "#{config['timer']}".to_i
49
+ $cron = config['cron'] || 'cron'
50
+ $port = "#{config['port']}".to_i || 4567
51
+ $mode = config['mode'] || env
52
+ $VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
53
+ $apps = config['apps'] || []
54
+ $log = config['log'] || 'off'
55
+ $user = config['user'] || 'admin'
56
+ $pass = config['pass'] || 'pass'
57
+ $pempass = config['pempass'] || ''
58
+ $loglevel = config['loglevel'] || 'info'
59
+ $flood = "#{config['flood']}".to_i || 1 # default 1 minute
60
+
61
+ $client_ip = '127.0.0.1'
62
+ $last_access = 0
63
+
64
+ ############################################################
65
+ ## Certificate Key Setup
66
+ ############################################################
67
+
68
+ $certkey = {}
69
+
70
+ def check_cert
71
+ $apps.each { |app|
72
+ unless File.exist?("#{Dir.pwd}/#{app}_#{$mode}.pem") then
73
+ puts "Please provide #{app}_#{$mode}.pem under '#{Dir.pwd}/' directory"
74
+ return false;
75
+ else
76
+ puts "'#{app}'s #{$mode} PEM: (#{app}_#{$mode}.pem)"
77
+ certfile = File.read("#{Dir.pwd}/#{app}_#{$mode}.pem")
78
+ openSSLContext = OpenSSL::SSL::SSLContext.new
79
+ openSSLContext.cert = OpenSSL::X509::Certificate.new(certfile)
80
+ if $pempass == '' then
81
+ openSSLContext.key = OpenSSL::PKey::RSA.new(certfile)
82
+ else
83
+ openSSLContext.key = OpenSSL::PKey::RSA.new(certfile,"#{$pempass}")
84
+ end
85
+ $certkey["#{app}"] = openSSLContext
86
+ end
87
+ }
88
+ return true
89
+ end
90
+
91
+ unless check_cert then
92
+ html = <<-END
93
+ 1: please provide certificate key pem file under current directory, name should be: appid_development.pem for development and appid_production.pem for production
94
+ 2: edit your ass.yml under current directory
95
+ 3: run ass
96
+ 4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:
97
+ END
98
+ $apps.each { |app|
99
+ html << "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
100
+ }
101
+ html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
102
+ puts html
103
+ exit
104
+ else
105
+ html = <<-END
106
+ #{'*'*80}
107
+ Apple Service Server(#{$VERSION}) is Running ...
108
+ Push Notification Service: Enabled
109
+ Mode: #{$mode}
110
+ Port: #{$port}
111
+ END
112
+ html << "#{'*'*80}"
113
+ html << "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
114
+ html << "\n"
115
+ html << "access http://localhost:#{$port}/ for more information"
116
+ html << "\n"
117
+ html << "#{'*'*80}"
118
+ puts html
119
+ end
120
+
121
+ ############################################################
122
+ ## Sequel Database Setup
123
+ ############################################################
124
+
125
+ unless File.exist?("#{Dir.pwd}/ass-#{$mode}.db") then
126
+ $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass-#{$mode}.db")
127
+ $DB.create_table :tokens do
128
+ primary_key :id
129
+ String :app, :unique => false, :null => false
130
+ String :token, :unique => false, :null => false, :size => 100
131
+ Time :created_at
132
+ index [:app, :token]
133
+ end
134
+ $DB.create_table :pushes do
135
+ primary_key :id
136
+ String :pid, :unique => false, :null => false, :size => 100
137
+ String :app, :unique => false, :null => false, :size => 30
138
+ String :message, :unique => false, :null => false, :size => 107
139
+ String :ip, :unique => false, :null => false, :size => 20
140
+ Time :created_at
141
+ index [:pid, :app, :message]
142
+ end
143
+ else
144
+ $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass-#{$mode}.db")
145
+ end
146
+
147
+ class Token < Sequel::Model
148
+ Sequel.extension :pagination
149
+ end
150
+
151
+ class Push < Sequel::Model
152
+ Sequel.extension :pagination
153
+ end
154
+
155
+ ############################################################
156
+ ## Timer Job Setup
157
+ ############################################################
158
+ scheduler = Rufus::Scheduler.start_new
159
+
160
+ unless $timer == 0 then
161
+ scheduler.every "#{$timer}m" do
162
+ puts "running job: '#{Dir.pwd}/#{$cron}' every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'}"
163
+ system "./#{$cron}"
164
+ end
165
+ end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ass
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.21
5
- prerelease:
4
+ version: 0.0.24
6
5
  platform: ruby
7
6
  authors:
8
7
  - Eiffel Qiu
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-02 00:00:00.000000000 Z
11
+ date: 2013-10-23 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: sqlite3
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: thin
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: json
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -62,23 +55,20 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: sinatra
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ~>
59
+ - - ! '>='
68
60
  - !ruby/object:Gem::Version
69
- version: 1.3.0
61
+ version: '0'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ~>
66
+ - - ! '>='
76
67
  - !ruby/object:Gem::Version
77
- version: 1.3.0
68
+ version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: sequel
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ! '>='
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ! '>='
92
81
  - !ruby/object:Gem::Version
@@ -94,7 +83,6 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rufus-scheduler
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ! '>='
100
88
  - !ruby/object:Gem::Version
@@ -102,7 +90,6 @@ dependencies:
102
90
  type: :runtime
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ! '>='
108
95
  - !ruby/object:Gem::Version
@@ -110,7 +97,6 @@ dependencies:
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: eventmachine
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
101
  - - ! '>='
116
102
  - !ruby/object:Gem::Version
@@ -118,7 +104,6 @@ dependencies:
118
104
  type: :runtime
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
108
  - - ! '>='
124
109
  - !ruby/object:Gem::Version
@@ -126,23 +111,20 @@ dependencies:
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: activesupport
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
115
  - - ! '>='
132
116
  - !ruby/object:Gem::Version
133
- version: 3.2.8
117
+ version: '0'
134
118
  type: :runtime
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
122
  - - ! '>='
140
123
  - !ruby/object:Gem::Version
141
- version: 3.2.8
124
+ version: '0'
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: uri-handler
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
129
  - - ! '>='
148
130
  - !ruby/object:Gem::Version
@@ -150,7 +132,6 @@ dependencies:
150
132
  type: :runtime
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
136
  - - ! '>='
156
137
  - !ruby/object:Gem::Version
@@ -158,23 +139,20 @@ dependencies:
158
139
  - !ruby/object:Gem::Dependency
159
140
  name: will_paginate
160
141
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
142
  requirements:
163
- - - ~>
143
+ - - ! '>='
164
144
  - !ruby/object:Gem::Version
165
- version: '3.0'
145
+ version: '0'
166
146
  type: :runtime
167
147
  prerelease: false
168
148
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
149
  requirements:
171
- - - ~>
150
+ - - ! '>='
172
151
  - !ruby/object:Gem::Version
173
- version: '3.0'
152
+ version: '0'
174
153
  - !ruby/object:Gem::Dependency
175
154
  name: sinatra-contrib
176
155
  requirement: !ruby/object:Gem::Requirement
177
- none: false
178
156
  requirements:
179
157
  - - ! '>='
180
158
  - !ruby/object:Gem::Version
@@ -182,7 +160,6 @@ dependencies:
182
160
  type: :runtime
183
161
  prerelease: false
184
162
  version_requirements: !ruby/object:Gem::Requirement
185
- none: false
186
163
  requirements:
187
164
  - - ! '>='
188
165
  - !ruby/object:Gem::Version
@@ -190,7 +167,6 @@ dependencies:
190
167
  - !ruby/object:Gem::Dependency
191
168
  name: rack-mobile-detect
192
169
  requirement: !ruby/object:Gem::Requirement
193
- none: false
194
170
  requirements:
195
171
  - - ! '>='
196
172
  - !ruby/object:Gem::Version
@@ -198,7 +174,6 @@ dependencies:
198
174
  type: :runtime
199
175
  prerelease: false
200
176
  version_requirements: !ruby/object:Gem::Requirement
201
- none: false
202
177
  requirements:
203
178
  - - ! '>='
204
179
  - !ruby/object:Gem::Version
@@ -206,23 +181,20 @@ dependencies:
206
181
  - !ruby/object:Gem::Dependency
207
182
  name: sinatra-synchrony
208
183
  requirement: !ruby/object:Gem::Requirement
209
- none: false
210
184
  requirements:
211
- - - ~>
185
+ - - ! '>='
212
186
  - !ruby/object:Gem::Version
213
- version: 0.4.1
187
+ version: '0'
214
188
  type: :runtime
215
189
  prerelease: false
216
190
  version_requirements: !ruby/object:Gem::Requirement
217
- none: false
218
191
  requirements:
219
- - - ~>
192
+ - - ! '>='
220
193
  - !ruby/object:Gem::Version
221
- version: 0.4.1
194
+ version: '0'
222
195
  - !ruby/object:Gem::Dependency
223
196
  name: shoulda
224
197
  requirement: !ruby/object:Gem::Requirement
225
- none: false
226
198
  requirements:
227
199
  - - ! '>='
228
200
  - !ruby/object:Gem::Version
@@ -230,7 +202,6 @@ dependencies:
230
202
  type: :development
231
203
  prerelease: false
232
204
  version_requirements: !ruby/object:Gem::Requirement
233
- none: false
234
205
  requirements:
235
206
  - - ! '>='
236
207
  - !ruby/object:Gem::Version
@@ -238,23 +209,20 @@ dependencies:
238
209
  - !ruby/object:Gem::Dependency
239
210
  name: rdoc
240
211
  requirement: !ruby/object:Gem::Requirement
241
- none: false
242
212
  requirements:
243
- - - ~>
213
+ - - ! '>='
244
214
  - !ruby/object:Gem::Version
245
- version: '3.12'
215
+ version: '0'
246
216
  type: :development
247
217
  prerelease: false
248
218
  version_requirements: !ruby/object:Gem::Requirement
249
- none: false
250
219
  requirements:
251
- - - ~>
220
+ - - ! '>='
252
221
  - !ruby/object:Gem::Version
253
- version: '3.12'
222
+ version: '0'
254
223
  - !ruby/object:Gem::Dependency
255
224
  name: bundler
256
225
  requirement: !ruby/object:Gem::Requirement
257
- none: false
258
226
  requirements:
259
227
  - - ! '>='
260
228
  - !ruby/object:Gem::Version
@@ -262,7 +230,6 @@ dependencies:
262
230
  type: :development
263
231
  prerelease: false
264
232
  version_requirements: !ruby/object:Gem::Requirement
265
- none: false
266
233
  requirements:
267
234
  - - ! '>='
268
235
  - !ruby/object:Gem::Version
@@ -270,23 +237,20 @@ dependencies:
270
237
  - !ruby/object:Gem::Dependency
271
238
  name: jeweler
272
239
  requirement: !ruby/object:Gem::Requirement
273
- none: false
274
240
  requirements:
275
- - - ~>
241
+ - - ! '>='
276
242
  - !ruby/object:Gem::Version
277
- version: 1.8.4
243
+ version: '0'
278
244
  type: :development
279
245
  prerelease: false
280
246
  version_requirements: !ruby/object:Gem::Requirement
281
- none: false
282
247
  requirements:
283
- - - ~>
248
+ - - ! '>='
284
249
  - !ruby/object:Gem::Version
285
- version: 1.8.4
250
+ version: '0'
286
251
  - !ruby/object:Gem::Dependency
287
252
  name: sqlite3
288
253
  requirement: !ruby/object:Gem::Requirement
289
- none: false
290
254
  requirements:
291
255
  - - ! '>='
292
256
  - !ruby/object:Gem::Version
@@ -294,7 +258,6 @@ dependencies:
294
258
  type: :runtime
295
259
  prerelease: false
296
260
  version_requirements: !ruby/object:Gem::Requirement
297
- none: false
298
261
  requirements:
299
262
  - - ! '>='
300
263
  - !ruby/object:Gem::Version
@@ -302,7 +265,6 @@ dependencies:
302
265
  - !ruby/object:Gem::Dependency
303
266
  name: thin
304
267
  requirement: !ruby/object:Gem::Requirement
305
- none: false
306
268
  requirements:
307
269
  - - ! '>='
308
270
  - !ruby/object:Gem::Version
@@ -310,7 +272,6 @@ dependencies:
310
272
  type: :runtime
311
273
  prerelease: false
312
274
  version_requirements: !ruby/object:Gem::Requirement
313
- none: false
314
275
  requirements:
315
276
  - - ! '>='
316
277
  - !ruby/object:Gem::Version
@@ -318,7 +279,6 @@ dependencies:
318
279
  - !ruby/object:Gem::Dependency
319
280
  name: sinatra
320
281
  requirement: !ruby/object:Gem::Requirement
321
- none: false
322
282
  requirements:
323
283
  - - ! '>='
324
284
  - !ruby/object:Gem::Version
@@ -326,7 +286,6 @@ dependencies:
326
286
  type: :runtime
327
287
  prerelease: false
328
288
  version_requirements: !ruby/object:Gem::Requirement
329
- none: false
330
289
  requirements:
331
290
  - - ! '>='
332
291
  - !ruby/object:Gem::Version
@@ -334,7 +293,6 @@ dependencies:
334
293
  - !ruby/object:Gem::Dependency
335
294
  name: sequel
336
295
  requirement: !ruby/object:Gem::Requirement
337
- none: false
338
296
  requirements:
339
297
  - - ! '>='
340
298
  - !ruby/object:Gem::Version
@@ -342,7 +300,6 @@ dependencies:
342
300
  type: :runtime
343
301
  prerelease: false
344
302
  version_requirements: !ruby/object:Gem::Requirement
345
- none: false
346
303
  requirements:
347
304
  - - ! '>='
348
305
  - !ruby/object:Gem::Version
@@ -350,7 +307,6 @@ dependencies:
350
307
  - !ruby/object:Gem::Dependency
351
308
  name: rufus-scheduler
352
309
  requirement: !ruby/object:Gem::Requirement
353
- none: false
354
310
  requirements:
355
311
  - - ! '>='
356
312
  - !ruby/object:Gem::Version
@@ -358,7 +314,6 @@ dependencies:
358
314
  type: :runtime
359
315
  prerelease: false
360
316
  version_requirements: !ruby/object:Gem::Requirement
361
- none: false
362
317
  requirements:
363
318
  - - ! '>='
364
319
  - !ruby/object:Gem::Version
@@ -366,7 +321,6 @@ dependencies:
366
321
  - !ruby/object:Gem::Dependency
367
322
  name: eventmachine
368
323
  requirement: !ruby/object:Gem::Requirement
369
- none: false
370
324
  requirements:
371
325
  - - ! '>='
372
326
  - !ruby/object:Gem::Version
@@ -374,7 +328,6 @@ dependencies:
374
328
  type: :runtime
375
329
  prerelease: false
376
330
  version_requirements: !ruby/object:Gem::Requirement
377
- none: false
378
331
  requirements:
379
332
  - - ! '>='
380
333
  - !ruby/object:Gem::Version
@@ -382,7 +335,6 @@ dependencies:
382
335
  - !ruby/object:Gem::Dependency
383
336
  name: activesupport
384
337
  requirement: !ruby/object:Gem::Requirement
385
- none: false
386
338
  requirements:
387
339
  - - ! '>='
388
340
  - !ruby/object:Gem::Version
@@ -390,7 +342,6 @@ dependencies:
390
342
  type: :runtime
391
343
  prerelease: false
392
344
  version_requirements: !ruby/object:Gem::Requirement
393
- none: false
394
345
  requirements:
395
346
  - - ! '>='
396
347
  - !ruby/object:Gem::Version
@@ -398,7 +349,6 @@ dependencies:
398
349
  - !ruby/object:Gem::Dependency
399
350
  name: uri-handler
400
351
  requirement: !ruby/object:Gem::Requirement
401
- none: false
402
352
  requirements:
403
353
  - - ! '>='
404
354
  - !ruby/object:Gem::Version
@@ -406,7 +356,6 @@ dependencies:
406
356
  type: :runtime
407
357
  prerelease: false
408
358
  version_requirements: !ruby/object:Gem::Requirement
409
- none: false
410
359
  requirements:
411
360
  - - ! '>='
412
361
  - !ruby/object:Gem::Version
@@ -414,7 +363,6 @@ dependencies:
414
363
  - !ruby/object:Gem::Dependency
415
364
  name: will_paginate
416
365
  requirement: !ruby/object:Gem::Requirement
417
- none: false
418
366
  requirements:
419
367
  - - ~>
420
368
  - !ruby/object:Gem::Version
@@ -422,7 +370,6 @@ dependencies:
422
370
  type: :runtime
423
371
  prerelease: false
424
372
  version_requirements: !ruby/object:Gem::Requirement
425
- none: false
426
373
  requirements:
427
374
  - - ~>
428
375
  - !ruby/object:Gem::Version
@@ -430,7 +377,6 @@ dependencies:
430
377
  - !ruby/object:Gem::Dependency
431
378
  name: sinatra-contrib
432
379
  requirement: !ruby/object:Gem::Requirement
433
- none: false
434
380
  requirements:
435
381
  - - ! '>='
436
382
  - !ruby/object:Gem::Version
@@ -438,7 +384,34 @@ dependencies:
438
384
  type: :runtime
439
385
  prerelease: false
440
386
  version_requirements: !ruby/object:Gem::Requirement
441
- none: false
387
+ requirements:
388
+ - - ! '>='
389
+ - !ruby/object:Gem::Version
390
+ version: '0'
391
+ - !ruby/object:Gem::Dependency
392
+ name: rack-mobile-detect
393
+ requirement: !ruby/object:Gem::Requirement
394
+ requirements:
395
+ - - ! '>='
396
+ - !ruby/object:Gem::Version
397
+ version: '0'
398
+ type: :runtime
399
+ prerelease: false
400
+ version_requirements: !ruby/object:Gem::Requirement
401
+ requirements:
402
+ - - ! '>='
403
+ - !ruby/object:Gem::Version
404
+ version: '0'
405
+ - !ruby/object:Gem::Dependency
406
+ name: sinatra-synchrony
407
+ requirement: !ruby/object:Gem::Requirement
408
+ requirements:
409
+ - - ! '>='
410
+ - !ruby/object:Gem::Version
411
+ version: '0'
412
+ type: :runtime
413
+ prerelease: false
414
+ version_requirements: !ruby/object:Gem::Requirement
442
415
  requirements:
443
416
  - - ! '>='
444
417
  - !ruby/object:Gem::Version
@@ -458,6 +431,8 @@ files:
458
431
  - ass.yml
459
432
  - cron
460
433
  - lib/ass.rb
434
+ - lib/ass/app.rb
435
+ - lib/ass/conf.rb
461
436
  - public/css/bootstrap-responsive.css
462
437
  - public/css/bootstrap-responsive.min.css
463
438
  - public/css/bootstrap.css
@@ -478,29 +453,25 @@ files:
478
453
  homepage: http://github.com/eiffelqiu/ass
479
454
  licenses:
480
455
  - MIT
456
+ metadata: {}
481
457
  post_install_message:
482
458
  rdoc_options: []
483
459
  require_paths:
484
460
  - lib
485
461
  required_ruby_version: !ruby/object:Gem::Requirement
486
- none: false
487
462
  requirements:
488
463
  - - ! '>='
489
464
  - !ruby/object:Gem::Version
490
465
  version: '0'
491
- segments:
492
- - 0
493
- hash: -3291505526784258788
494
466
  required_rubygems_version: !ruby/object:Gem::Requirement
495
- none: false
496
467
  requirements:
497
468
  - - ! '>='
498
469
  - !ruby/object:Gem::Version
499
470
  version: '0'
500
471
  requirements: []
501
472
  rubyforge_project: ass
502
- rubygems_version: 1.8.24
473
+ rubygems_version: 2.1.9
503
474
  signing_key:
504
- specification_version: 3
475
+ specification_version: 4
505
476
  summary: Apple Service Server
506
477
  test_files: []