simperium 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/simperium.rb +425 -0
  2. data/lib/simperium/version.rb +3 -0
  3. metadata +123 -0
data/lib/simperium.rb ADDED
@@ -0,0 +1,425 @@
1
+ # Simperium Ruby bindings
2
+ # API spec at https://simperium.com/docs/reference
3
+ require 'rubygems'
4
+ require 'rest_client'
5
+ require 'json'
6
+ require 'uuid'
7
+ require File.join(File.dirname(__FILE__), 'simperium/error_handling')
8
+
9
+ #state file is not shared between processes on Heroku
10
+ UUID.state_file = false
11
+
12
+ class Auth
13
+ def initialize(appname, api_key, host=nil,scheme='https')
14
+ if host == nil
15
+ host = ENV['SIMPERIUM_AUTHHOST'] || 'auth.simperium.com'
16
+ end
17
+
18
+ @appname = appname
19
+ @api_key = api_key
20
+ @host = host
21
+ @scheme = scheme
22
+ end
23
+
24
+ def _auth_header
25
+ return {"X-Simperium-API-Key" => "#{@api_key}"}
26
+ end
27
+
28
+ def _request(url, data=nil, headers=nil, method=nil)
29
+ url = "#{@scheme}://#{@host}/1/#{url}"
30
+ opts = {:url => url,
31
+ :method => :post,
32
+ :open_timeout => 30,
33
+ :timeout => 80}
34
+
35
+ if data
36
+ opts = opts.merge({:payload => data})
37
+ end
38
+
39
+ if headers.nil?
40
+ headers = {}
41
+ end
42
+ opts = opts.merge({:headers => headers})
43
+
44
+ if method
45
+ opts = opts.merge({:method => method})
46
+ end
47
+
48
+ begin
49
+ response = RestClient::Request.execute(opts)
50
+ rescue SocketError => e
51
+ ErrorHandling.handle_restclient_error(e)
52
+ rescue NoMethodError => e
53
+ if e.message =~ /\WRequestFailed\W/
54
+ e = StandardError.new('Unexpected HTTP response code')
55
+ ErrorHandling.handle_restclient_error(e)
56
+ else
57
+ raise
58
+ end
59
+ rescue RestClient::ExceptionWithResponse => e
60
+ if rcode = e.http_code and rbody = e.http_body
61
+ ErrorHandling.handle_api_error(rcode, rbody)
62
+ else
63
+ ErrorHandling.handle_restclient_error(e)
64
+ end
65
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
66
+ ErrorHandling.handle_restclient_error(e)
67
+ end
68
+
69
+ return response
70
+ end
71
+
72
+ def create(username, password)
73
+ data = {
74
+ 'client_id' => @api_key,
75
+ 'username' => username,
76
+ 'password'=> password }
77
+
78
+ response = self._request(@appname+'/create/', data)
79
+ return JSON.load(response.body)['access_token']
80
+ end
81
+
82
+ def authorize(username, password)
83
+ data = {
84
+ 'username' => username,
85
+ 'password' => password }
86
+ response = self._request(@appname+'/authorize/', data, headers=_auth_header())
87
+ return JSON.load(response.body)['access_token']
88
+ end
89
+ end
90
+
91
+ class Bucket
92
+ def initialize(appname, auth_token, bucket, options={})
93
+ defaults = { :userid => nil, :host => nil, :scheme => 'https', :clientid => nil }
94
+ unless options.empty?
95
+ options = defaults.merge(options)
96
+ else
97
+ options = defaults
98
+ end
99
+
100
+ if options[:host] == nil
101
+ options[:host] = ENV['SIMPERIUM_APIHOST'] || 'api.simperium.com'
102
+ end
103
+
104
+ @userid = options[:userid]
105
+ @host = options[:host]
106
+ @scheme = options[:scheme]
107
+ @appname = appname
108
+ @bucket = bucket
109
+ @auth_token = auth_token
110
+
111
+ if options[:clientid] == nil
112
+ uuid = UUID.new
113
+ random_string = uuid.generate(:compact)
114
+ @clientid = "rb-#{random_string}"
115
+ else
116
+ @clientid = options[:clientid]
117
+ end
118
+ end
119
+
120
+ def _auth_header
121
+ headers = {"X-Simperium-Token" => "#{@auth_token}"}
122
+ unless @userid.nil?
123
+ headers["X-Simperium-User"] = @userid
124
+ end
125
+ return headers
126
+ end
127
+
128
+ def _gen_ccid
129
+ ccid = UUID.new
130
+ return ccid.generate(:compact)
131
+ end
132
+
133
+ def _request(url, data=nil, headers=nil, method=nil, timeout=nil)
134
+ url = "#{@scheme}://#{@host}/1/#{url}"
135
+ opts = {:url => url,
136
+ :method => :post,
137
+ :open_timeout => 30,
138
+ :timeout => 80}
139
+
140
+ if data
141
+ opts = opts.merge({:payload => data})
142
+ end
143
+
144
+ if headers.nil?
145
+ headers = {}
146
+ end
147
+ opts = opts.merge({:headers => headers})
148
+
149
+ if method
150
+ opts = opts.merge({:method => method})
151
+ end
152
+
153
+ if timeout
154
+ opts = opts.merge({:timeout => timeout})
155
+ end
156
+
157
+ puts opts
158
+ begin
159
+ response = RestClient::Request.execute(opts)
160
+ rescue SocketError => e
161
+ ErrorHandling.handle_restclient_error(e)
162
+ rescue NoMethodError => e
163
+ if e.message =~ /\WRequestFailed\W/
164
+ e = StandardError.new('Unexpected HTTP response code')
165
+ ErrorHandling.handle_restclient_error(e)
166
+ else
167
+ raise
168
+ end
169
+ rescue RestClient::ExceptionWithResponse => e
170
+ if rcode = e.http_code and rbody = e.http_body
171
+ ErrorHandling.handle_api_error(rcode, rbody)
172
+ else
173
+ ErrorHandling.handle_restclient_error(e)
174
+ end
175
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
176
+ ErrorHandling.handle_restclient_error(e)
177
+ end
178
+
179
+ return response
180
+ end
181
+
182
+ def index(options={})
183
+ defaults = {:data=>nil, :mark=>nil, :limit=>nil, :since=>nil}
184
+ unless options.empty?
185
+ options = defaults.merge(options)
186
+ else
187
+ options = defaults
188
+ end
189
+
190
+ data = options[:data]
191
+ mark = options[:mark]
192
+ limit = options[:limit]
193
+ since = options[:since]
194
+
195
+ url = "#{@appname}/#{@bucket}/index?"
196
+
197
+ if data
198
+ url += "&data=1"
199
+ end
200
+
201
+ if mark
202
+ url += "&mark=#{mark.to_str}"
203
+ end
204
+
205
+ if limit
206
+ url += "&limit=#{limit.to_s}"
207
+ end
208
+
209
+ if since
210
+ url += "&since=#{since.to_str}"
211
+ end
212
+
213
+ response = self._request(url, data=nil, headers=_auth_header(), method='GET')
214
+ return JSON.load(response.body)
215
+ end
216
+
217
+ def get(item, options={})
218
+ defaults = {:default=>nil, :version=>nil}
219
+ unless options.empty?
220
+ options = defaults.merge(options)
221
+ else
222
+ options = defaults
223
+ end
224
+ default = options[:default]
225
+ version = options[:version]
226
+
227
+ url = "#{@appname}/#{@bucket}/i/#{item}"
228
+ unless version.nil?
229
+ url += "/v/#{version}"
230
+ end
231
+
232
+ response = self._request(url, data=nil, headers=_auth_header(), method='GET')
233
+ return JSON.load(response.body)
234
+ end
235
+
236
+ def post(item, data, options={})
237
+ defaults = {:version=>nil, :ccid=>nil, :include_response=>false, :replace=>false}
238
+ unless options.empty?
239
+ options = defaults.merge(options)
240
+ else
241
+ options = defaults
242
+ end
243
+
244
+ version = options[:version]
245
+ ccid = options[:ccid]
246
+ include_response = options[:include_response]
247
+ replace = options[:replace]
248
+
249
+ if ccid.nil?
250
+ ccid = self._gen_ccid()
251
+ end
252
+ url = "#{@appname}/#{@bucket}/i/#{item}"
253
+
254
+ if version
255
+ url += "/v/#{version}"
256
+ end
257
+ url += "?clientid=#{@clientid}&ccid=#{ccid}"
258
+
259
+ if include_response
260
+ url += "&response=1"
261
+ end
262
+
263
+ if replace
264
+ url += "&replace=1"
265
+ end
266
+ data = JSON.dump(data)
267
+
268
+ response = self._request(url, data, headers=_auth_header())
269
+ if include_response
270
+ return item, JSON.load(response.body)
271
+ else
272
+ return item
273
+ end
274
+ end
275
+
276
+ def new(data, ccid=nil)
277
+ uuid = UUID.new
278
+ return self.post(uuid.generate(:compact), data, ccid=ccid)
279
+ end
280
+
281
+ def set(item, data, options={})
282
+ return self.post(item, data, options)
283
+ end
284
+
285
+ def delete(item, version=nil)
286
+ ccid = self._gen_ccid()
287
+ url = "#{@appname}/#{@bucket}/i/{item}"
288
+
289
+ if version
290
+ url += "/v/#{version}"
291
+ end
292
+
293
+ url += "?clientid=#{@clientid}&ccid=#{ccid}"
294
+ response = self._request(url, data=nil, headers=_auth_header(), method='DELETE')
295
+ if response.body.strip.nil?
296
+ return ccid
297
+ end
298
+ end
299
+
300
+ def changes(options={})
301
+ defautls = {:cv=>nil, :timeout=>nil}
302
+ unless options.empty?
303
+ options = defaults.merge(options)
304
+ else
305
+ options = defaults
306
+ end
307
+
308
+ cv = option[:cv]
309
+ timeout = option[:timeout]
310
+
311
+ url = "#{@appname}/#{@bucket}/changes?clientid=#{@clientid}"
312
+ unless cv.nil?
313
+ url += "&cv=#{cv}"
314
+ end
315
+ headers = _auth_header()
316
+
317
+ response = self._request(url, data=nil, headers=headers, method='GET', timeout=timeout)
318
+ return JSON.load(response.body)
319
+ end
320
+
321
+ def all(options={})
322
+ defaults = {:cv=>nil, :data=>nil, :username=>false, :most_recent=>false, :timeout=>nil}
323
+ unless options.empty?
324
+ options = defaults.merge(options)
325
+ else
326
+ options = defaults
327
+ end
328
+
329
+ cv = options[:cv]
330
+ data = options[:data]
331
+ username = options[:username]
332
+ most_recent = options[:most_recent]
333
+ timeout = options[:timeout]
334
+
335
+ url = "#{@appname}/#{@bucket}/all?clientid=#{@clientid}"
336
+ unless cv.nil?
337
+ url += "&cv=#{cv}"
338
+ end
339
+
340
+ if username
341
+ url += "&username=1"
342
+ end
343
+
344
+ if data
345
+ url += "&data=1"
346
+ end
347
+
348
+ if most_recent
349
+ url += "&most_recent=1"
350
+ end
351
+
352
+ headers = _auth_header()
353
+
354
+ response = self._request(url, data=nil, headers=headers, method='GET', timeout=timeout)
355
+ return JSON.load(response.body)
356
+ end
357
+ end
358
+
359
+ class User
360
+ def initialize(appname, auth_token, options={})
361
+ defaults = {:host=>nil, :scheme=>'https', :clientid=>nil}
362
+ unless options.empty?
363
+ options = defaults.merge(options)
364
+ else
365
+ options = defaults
366
+ end
367
+
368
+ @bucket = Bucket.new(appname, auth_token, 'user',
369
+ options=options)
370
+
371
+ url = "#{appname}/user"
372
+ response = @bucket._request(url, data=nil, headers=@bucket._auth_header(), method='GET')
373
+ response = JSON.load(response.body)
374
+ @userid = response['userid']
375
+ end
376
+
377
+ def get
378
+ return @bucket.get(@userid)
379
+ end
380
+
381
+ def post(data)
382
+ @bucket.post(@userid, data)
383
+ end
384
+ end
385
+
386
+ class Api
387
+ def initialize(appname, auth_token, options={})
388
+ @appname = appname
389
+ @token = auth_token
390
+ @_options = options
391
+
392
+ @getitem = {}
393
+ end
394
+
395
+ def method_missing(method_sym, *arguments, &block)
396
+ #the first argument is a Symbol, so you need to_s it you want to pattern match
397
+ unless method_sym.to_s =~ /=$/
398
+ if method_sym.to_s == 'user'
399
+ @getitem[method_sym] ||= User.new(@appname, @token)
400
+ else
401
+ @getitem[method_sym] ||= Bucket.new(@appname, @token, method_sym)
402
+ end
403
+ end
404
+ end
405
+
406
+ def respond_to?(method_sym, include_private = false)
407
+ if method_sym.to_s =~ /^(.*)$/
408
+ true
409
+ else
410
+ super
411
+ end
412
+ end
413
+ end
414
+
415
+ class Admin < Api
416
+ def initialize(appname, admin_token, options={})
417
+ @appname = appname
418
+ @token = admin_token
419
+ @_options = options
420
+ end
421
+
422
+ def as_user(userid)
423
+ return Api.new(@appname, @token, userid=userid, @_options)
424
+ end
425
+ end
@@ -0,0 +1,3 @@
1
+ module Simperium
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simperium
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Ray Ventura
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-06-19 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 1
31
+ - 4
32
+ version: "1.4"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: uuid
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: json
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: mongo
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :runtime
76
+ version_requirements: *id004
77
+ description: Simperium moves data instantly and automatically everywhere it's needed. See https://simperium.com for details.
78
+ email:
79
+ - ray@simperium.com
80
+ executables: []
81
+
82
+ extensions: []
83
+
84
+ extra_rdoc_files: []
85
+
86
+ files:
87
+ - lib/simperium.rb
88
+ - lib/simperium/version.rb
89
+ homepage: https://simperium.com/docs/reference
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options: []
94
+
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project:
118
+ rubygems_version: 1.8.24
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Ruby bindings for the Simperium API
122
+ test_files: []
123
+