simperium 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.
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
+