predictionio 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require "predictionio/client"
@@ -0,0 +1,22 @@
1
+ module PredictionIO
2
+ # This class contains the URI path and query parameters that is consumed by PredictionIO::Connection for asynchronous HTTP requests.
3
+ class AsyncRequest
4
+
5
+ # The path portion of the request URI.
6
+ attr_reader :path
7
+
8
+ # Query parameters, or form data.
9
+ attr_reader :params
10
+
11
+ # Populates the package with request URI path, and optionally query parameters or form data.
12
+ def initialize(path, params = {})
13
+ @params = params
14
+ @path = path
15
+ end
16
+
17
+ # Returns an URI path with query parameters encoded for HTTP GET requests.
18
+ def qpath
19
+ "#{@path}?#{URI::encode_www_form(@params)}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ require 'thread'
2
+
3
+ module PredictionIO
4
+ # This class encapsulates an asynchronous response that will block the caller until the response is available.
5
+ class AsyncResponse
6
+
7
+ # The PredictionIO::AsyncRequest instance that created the current PredictionIO::AsyncResponse instance.
8
+ attr_reader :request
9
+
10
+ # Create the response by saving the request, and optionally the Net::HTTPResponse object.
11
+ def initialize(request, response = nil)
12
+ @request = request
13
+ @response = Queue.new
14
+ if response != nil then
15
+ set(response)
16
+ end
17
+ end
18
+
19
+ # Save a Net::HTTPResponse instance to the current instance.
20
+ # This will unblock any caller that called #get.
21
+ def set(response)
22
+ @response.push(response)
23
+ end
24
+
25
+ # Get the Net::HTTPResponse instance. This will block if the response is not yet available.
26
+ def get
27
+ @response.pop
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,681 @@
1
+ # Ruby SDK for convenient access of PredictionIO Output API.
2
+ #
3
+ # Author:: TappingStone (help@tappingstone.com)
4
+ # Copyright:: Copyright (c) 2013 TappingStone
5
+ # License:: Apache License, Version 2.0
6
+
7
+ require 'date'
8
+ require 'json'
9
+ require 'net/http'
10
+ require 'predictionio/async_request'
11
+ require 'predictionio/async_response'
12
+ require 'predictionio/connection'
13
+
14
+ # The PredictionIO module contains classes that provide convenient access of the PredictionIO output API over HTTP/HTTPS.
15
+ #
16
+ # To create an app and perform predictions, please download the PredictionIO suite from http://prediction.io.
17
+ #
18
+ # Most functionality is provided by the PredictionIO::Client class.
19
+ module PredictionIO
20
+
21
+ # This class contains methods that access PredictionIO via REST requests.
22
+ #
23
+ # Many REST request methods support optional arguments.
24
+ # They can be supplied to these methods as Hash'es.
25
+ # For a complete reference, please visit http://prediction.io.
26
+ #
27
+ # == High-performance Asynchronous Backend
28
+ #
29
+ # All REST request methods come in both synchronous and asynchronous flavors.
30
+ # Both flavors accept the same set of arguments.
31
+ # In addition, all synchronous request methods can instead accept a PredictionIO::AsyncResponse object generated from asynchronous request methods as its first argument.
32
+ # In this case, the method will block until a response is received from it.
33
+ #
34
+ # Any network reconnection and request retry is automatically handled in the background.
35
+ # Exceptions will be thrown after a request times out to avoid infinite blocking.
36
+ #
37
+ # == Special Handling of Some Optional Arguments
38
+ # Some optional arguments have additional special handling:
39
+ # - For all requests that accept "itypes" as input, the value can be supplied as either an Array of String's, or a comma-delimited String.
40
+ # - For all requests that accept "latlng" as input, they will also accept "latitude" and "longitude".
41
+ # When these are supplied, they will override any existing "latlng" value.
42
+ # - All time arguments (e.g. t, startT, endT, etc) can be supplied as either a Time or Float object.
43
+ # When supplied as a Float, the SDK will interpret it as a UNIX UTC timestamp in seconds.
44
+ # The SDK will automatically round to the nearest millisecond, e.g. 3.14159 => 3.142.
45
+ #
46
+ # == Installation
47
+ # Download the PredictionIO Ruby Gem from http://prediction.io
48
+ # gem install predictionio-0.1.0.gem
49
+ #
50
+ # == Synopsis
51
+ # The recommended usage of the SDK is to fire asynchronous requests as early as you can in your code,
52
+ # and check results later when you need them.
53
+ #
54
+ # === Instantiate PredictionIO Client
55
+ # # Include the PredictionIO SDK
56
+ # require "PredictionIO"
57
+ #
58
+ # client = PredictionIO::Client.new(<appkey>)
59
+ #
60
+ # === Import a User Record from Your App (with asynchronous/non-blocking requests)
61
+ #
62
+ # #
63
+ # # (your user registration logic)
64
+ # #
65
+ #
66
+ # uid = get_user_from_your_db()
67
+ #
68
+ # # PredictionIO call to create user
69
+ # response = client.acreate_user(uid)
70
+ #
71
+ # #
72
+ # # (other work to do for the rest of the page)
73
+ # #
74
+ #
75
+ # begin
76
+ # # PredictionIO call to retrieve results from an asynchronous response
77
+ # result = client.create_user(response)
78
+ # rescue UserNotCreatedError => e
79
+ # log_and_email_error(...)
80
+ # end
81
+ #
82
+ # === Import a User Action (View) form Your App (with synchronous/blocking requests)
83
+ # # PredictionIO call to record the view action
84
+ # begin
85
+ # result = client.user_view_item(4, 15)
86
+ # rescue U2IActionNotCreatedError => e
87
+ # ...
88
+ # end
89
+ #
90
+ # === Retrieving Top N Recommendations for a User
91
+ # # PredictionIO call to get recommendations
92
+ # response = client.aget_recommendation(4, 10)
93
+ #
94
+ # #
95
+ # # work you need to do for the page (rendering, db queries, etc)
96
+ # #
97
+ #
98
+ # begin
99
+ # result = client.get_recommendations(response)
100
+ # # display results, store results, or your other work...
101
+ # rescue RecommendationsNotFoundError => e
102
+ # # graceful error handling
103
+ # end
104
+
105
+ class Client
106
+
107
+ # Appkey can be changed on-the-fly after creation of the client.
108
+ attr_accessor :appkey
109
+
110
+ # API version can be changed on-the-fly after creation of the client.
111
+ attr_accessor :apiversion
112
+
113
+ # Only JSON is currently supported as API response format.
114
+ attr_accessor :apiformat
115
+
116
+ # Raised when a user is not created after a synchronous API call.
117
+ class UserNotCreatedError < StandardError; end
118
+
119
+ # Raised when a user is not found after a synchronous API call.
120
+ class UserNotFoundError < StandardError; end
121
+
122
+ # Raised when a user is not deleted after a synchronous API call.
123
+ class UserNotDeletedError < StandardError; end
124
+
125
+ # Raised when an item is not created after a synchronous API call.
126
+ class ItemNotCreatedError < StandardError; end
127
+
128
+ # Raised when an item is not found after a synchronous API call.
129
+ class ItemNotFoundError < StandardError; end
130
+
131
+ # Raised when an item is not deleted after a synchronous API call.
132
+ class ItemNotDeletedError < StandardError; end
133
+
134
+ # Raised when ItemRec results cannot be found for a user after a synchronous API call.
135
+ class ItemRecNotFoundError < StandardError; end
136
+
137
+ # Raised when an user-to-item action is not created after a synchronous API call.
138
+ class U2IActionNotCreatedError < StandardError; end
139
+
140
+ # Create a new PredictionIO client with default:
141
+ # - API entry point at http://localhost:8000
142
+ # - API return data format of json
143
+ # - 10 concurrent HTTP(S) connections
144
+ def initialize(appkey, threads = 10, apiurl = "http://localhost:8000", apiversion = "")
145
+ @appkey = appkey
146
+ @apiversion = apiversion
147
+ @apiformat = "json"
148
+ @http = PredictionIO::Connection.new(URI(apiurl), threads)
149
+ end
150
+
151
+ # Returns the number of pending requests within the current client.
152
+ def pending_requests
153
+ @http.packages.size
154
+ end
155
+
156
+ # Returns PredictionIO's status in string.
157
+ def get_status
158
+ status = @http.aget(PredictionIO::AsyncRequest.new("/")).get()
159
+ begin
160
+ status.body
161
+ rescue Exception
162
+ status
163
+ end
164
+ end
165
+
166
+ # :category: Asynchronous Methods
167
+ # Asynchronously request to create a user and return a PredictionIO::AsyncResponse object immediately.
168
+ #
169
+ # Corresponding REST API method: POST /users
170
+ #
171
+ # See also #create_user.
172
+ def acreate_user(uid, params = {})
173
+ rparams = params
174
+ rparams["appkey"] = @appkey
175
+ rparams["uid"] = uid
176
+ if params["latitude"] != nil && params["longitude"] != nil then
177
+ rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
178
+ end
179
+
180
+ @http.apost(PredictionIO::AsyncRequest.new(versioned_path("/users.#{@apiformat}"), rparams))
181
+ end
182
+
183
+ # :category: Synchronous Methods
184
+ # Synchronously request to create a user and block until a response is received.
185
+ #
186
+ # See also #acreate_user.
187
+ #
188
+ # call-seq:
189
+ # create_user(uid, params = {})
190
+ # create_user(async_response)
191
+ def create_user(*args)
192
+ uid_or_res = args[0]
193
+ if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
194
+ uid = uid_or_res.request.params["uid"]
195
+ response = uid_or_res.get
196
+ else
197
+ uid = uid_or_res
198
+ response = acreate_user(*args).get
199
+ end
200
+ unless response.is_a?(Net::HTTPCreated) then
201
+ begin
202
+ msg = response.body
203
+ rescue Exception
204
+ raise UserNotCreatedError, response
205
+ end
206
+ raise UserNotCreatedError, msg
207
+ end
208
+ end
209
+
210
+ # :category: Asynchronous Methods
211
+ # Asynchronously request to get a user and return a PredictionIO::AsyncResponse object immediately.
212
+ #
213
+ # Creation time of the user will be returned as a Time object.
214
+ #
215
+ # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
216
+ #
217
+ # Corresponding REST API method: GET /users/:uid
218
+ #
219
+ # See also #get_user.
220
+ def aget_user(uid)
221
+ @http.aget(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
222
+ "appkey" => @appkey,
223
+ "uid" => uid))
224
+ end
225
+
226
+ # :category: Synchronous Methods
227
+ # Synchronously request to get a user and block until a response is received.
228
+ #
229
+ # Creation time of the user will be returned as a Time object.
230
+ #
231
+ # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
232
+ #
233
+ # See also #aget_user.
234
+ #
235
+ # call-seq:
236
+ # get_user(uid)
237
+ # get_user(async_response)
238
+ def get_user(uid_or_res)
239
+ if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
240
+ response = uid_or_res.get
241
+ else
242
+ response = aget_user(uid_or_res).get
243
+ end
244
+ if response.is_a?(Net::HTTPOK) then
245
+ res = JSON.parse(response.body)
246
+ ct = Rational(res["ct"], 1000)
247
+ res["ct"] = Time.at(ct)
248
+ if res["latlng"] != nil then
249
+ latlng = res["latlng"]
250
+ res["latitude"] = latlng[0]
251
+ res["longitude"] = latlng[1]
252
+ end
253
+ res
254
+ else
255
+ begin
256
+ msg = response.body
257
+ rescue Exception
258
+ raise UserNotFoundError, response
259
+ end
260
+ raise UserNotFoundError, msg
261
+ end
262
+ end
263
+
264
+ # :category: Asynchronous Methods
265
+ # Asynchronously request to delete a user and return a PredictionIO::AsyncResponse object immediately.
266
+ #
267
+ # Corresponding REST API method: DELETE /users/:uid
268
+ #
269
+ # See also #delete_user.
270
+ def adelete_user(uid)
271
+ @http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/users/#{uid}.#{@apiformat}"),
272
+ "appkey" => @appkey,
273
+ "uid" => uid))
274
+ end
275
+
276
+ # :category: Synchronous Methods
277
+ # Synchronously request to delete a user and block until a response is received.
278
+ #
279
+ # See also #adelete_user.
280
+ #
281
+ # call-seq:
282
+ # delete_user(uid)
283
+ # delete_user(async_response)
284
+ def delete_user(uid_or_res)
285
+ if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
286
+ response = uid_or_res.get
287
+ else
288
+ response = adelete_user(uid_or_res).get
289
+ end
290
+ unless response.is_a?(Net::HTTPOK) then
291
+ begin
292
+ msg = response.body
293
+ rescue Exception
294
+ raise UserNotDeletedError, response
295
+ end
296
+ raise msg
297
+ end
298
+ end
299
+
300
+ # :category: Asynchronous Methods
301
+ # Asynchronously request to create an item and return a PredictionIO::AsyncResponse object immediately.
302
+ #
303
+ # Corresponding REST API method: POST /items
304
+ #
305
+ # See also #create_item.
306
+ def acreate_item(iid, itypes, params = {})
307
+ rparams = params
308
+ rparams["appkey"] = @appkey
309
+ rparams["iid"] = iid
310
+ begin
311
+ rparams["itypes"] = itypes.join(",")
312
+ rescue Exception
313
+ rparams["itypes"] = itypes
314
+ end
315
+ if params["latitude"] != nil && params["longitude"] != nil then
316
+ rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
317
+ end
318
+ if params["startT"] != nil then
319
+ rparams["startT"] = ((params["startT"].to_r) * 1000).round(0).to_s
320
+ end
321
+ if params["endT"] != nil then
322
+ rparams["endT"] = ((params["endT"].to_r) * 1000).round(0).to_s
323
+ end
324
+
325
+ @http.apost(PredictionIO::AsyncRequest.new(versioned_path("/items.#{@apiformat}"), rparams))
326
+ end
327
+
328
+ # :category: Synchronous Methods
329
+ # Synchronously request to create an item and block until a response is received.
330
+ #
331
+ # See #acreate_item for a description of other accepted arguments.
332
+ #
333
+ # call-seq:
334
+ # create_item(iid, itypes, params = {})
335
+ # create_item(async_response)
336
+ def create_item(*args)
337
+ iid_or_res = args[0]
338
+ if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
339
+ response = iid_or_res.get
340
+ else
341
+ response = acreate_item(*args).get
342
+ end
343
+ unless response.is_a?(Net::HTTPCreated) then
344
+ begin
345
+ msg = response.body
346
+ rescue Exception
347
+ raise ItemNotCreatedError, response
348
+ end
349
+ raise ItemNotCreatedError, msg
350
+ end
351
+ end
352
+
353
+ # :category: Asynchronous Methods
354
+ # Asynchronously request to get an item and return a PredictionIO::AsyncResponse object immediately.
355
+ #
356
+ # Creation time of the user will be returned as a Time object.
357
+ #
358
+ # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
359
+ #
360
+ # Corresponding REST API method: GET /items/:iid
361
+ #
362
+ # See also #get_item.
363
+ def aget_item(iid)
364
+ @http.aget(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
365
+ "appkey" => @appkey,
366
+ "iid" => iid))
367
+ end
368
+
369
+ # :category: Synchronous Methods
370
+ # Synchronously request to get an item and block until a response is received.
371
+ #
372
+ # Creation time of the item will be returned as a Time object.
373
+ #
374
+ # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
375
+ #
376
+ # See also #aget_item.
377
+ #
378
+ # call-seq:
379
+ # get_item(iid)
380
+ # get_item(async_response)
381
+ def get_item(iid_or_res)
382
+ if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
383
+ response = iid_or_res.get
384
+ else
385
+ response = aget_item(iid_or_res).get
386
+ end
387
+ if response.is_a?(Net::HTTPOK) then
388
+ res = JSON.parse(response.body)
389
+ ct = Rational(res["ct"], 1000)
390
+ res["ct"] = Time.at(ct)
391
+ if res["latlng"] != nil then
392
+ latlng = res["latlng"]
393
+ res["latitude"] = latlng[0]
394
+ res["longitude"] = latlng[1]
395
+ end
396
+ if res["startT"] != nil then
397
+ startT = Rational(res["startT"], 1000)
398
+ res["startT"] = Time.at(startT)
399
+ end
400
+ if res["endT"] != nil then
401
+ endT = Rational(res["endT"], 1000)
402
+ res["endT"] = Time.at(endT)
403
+ end
404
+ res
405
+ else
406
+ begin
407
+ msg = response.body
408
+ rescue Exception
409
+ raise ItemNotFoundError, response
410
+ end
411
+ raise ItemNotFoundError, msg
412
+ end
413
+ end
414
+
415
+ # :category: Asynchronous Methods
416
+ # Asynchronously request to delete an item and return a PredictionIO::AsyncResponse object immediately.
417
+ #
418
+ # Corresponding REST API method: DELETE /items/:iid
419
+ #
420
+ # See also #delete_item.
421
+ def adelete_item(iid)
422
+ @http.adelete(PredictionIO::AsyncRequest.new(versioned_path("/items/#{iid}.#{@apiformat}"),
423
+ "appkey" => @appkey,
424
+ "iid" => iid))
425
+ end
426
+
427
+ # :category: Synchronous Methods
428
+ # Synchronously request to delete an item and block until a response is received.
429
+ #
430
+ # See also #adelete_item.
431
+ #
432
+ # call-seq:
433
+ # delete_item(iid)
434
+ # delete_item(async_response)
435
+ def delete_item(iid_or_res)
436
+ if iid_or_res.is_a?(PredictionIO::AsyncResponse) then
437
+ response = iid_or_res.get
438
+ else
439
+ response = adelete_item(iid_or_res).get
440
+ end
441
+ unless response.is_a?(Net::HTTPOK) then
442
+ begin
443
+ msg = response.body
444
+ rescue Exception
445
+ raise ItemNotDeletedError, response
446
+ end
447
+ raise ItemNotDeletedError, msg
448
+ end
449
+ end
450
+
451
+ # :category: Asynchronous Methods
452
+ # Asynchronously request to get the top n recommendations for a user from an ItemRec engine and return a PredictionIO::AsyncResponse object immediately.
453
+ #
454
+ # Corresponding REST API method: GET /engines/itemrec/:engine/topn
455
+ #
456
+ # See also #get_itemrec_top_n.
457
+ def aget_itemrec_top_n(engine, uid, n, params = {})
458
+ rparams = Hash.new
459
+ rparams["appkey"] = @appkey
460
+ rparams["uid"] = uid
461
+ rparams["n"] = n
462
+ if params["itypes"] != nil &&
463
+ params["itypes"].kind_of?(Array) &&
464
+ params["itypes"].length > 0 then
465
+ rparams["itypes"] = params["itypes"].join(",")
466
+ else
467
+ rparams["itypes"] = params["itypes"]
468
+ end
469
+ if params["latitude"] != nil && params["longitude"] != nil then
470
+ rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
471
+ end
472
+ if params["within"] != nil then
473
+ rparams["within"] = params["within"]
474
+ end
475
+ if params["unit"] != nil then
476
+ rparams["unit"] = params["unit"]
477
+ end
478
+ @http.aget(PredictionIO::AsyncRequest.new(versioned_path("/engines/itemrec/#{engine}/topn.#{@apiformat}"), rparams))
479
+ end
480
+
481
+ # :category: Synchronous Methods
482
+ # Synchronously request to get the top n recommendations for a user from an ItemRec engine and block until a response is received.
483
+ #
484
+ # See #aget_itemrec_top_n for a description of special argument handling.
485
+ #
486
+ # call-seq:
487
+ # get_recommendations(uid, n, params = {})
488
+ # get_recommendations(async_response)
489
+ def get_itemrec_top_n(*args)
490
+ uid_or_res = args[0]
491
+ if uid_or_res.is_a?(PredictionIO::AsyncResponse) then
492
+ response = uid_or_res.get
493
+ else
494
+ response = aget_itemrec_top_n(*args).get
495
+ end
496
+ if response.is_a?(Net::HTTPOK) then
497
+ res = JSON.parse(response.body)
498
+ res["iids"]
499
+ else
500
+ begin
501
+ msg = response.body
502
+ rescue Exception
503
+ raise ItemRecNotFoundError, response
504
+ end
505
+ raise ItemRecNotFoundError, msg
506
+ end
507
+ end
508
+
509
+ # :category: Asynchronous Methods
510
+ # Asynchronously request to record a user-rate-item action and return a PredictionIO::AsyncResponse object immediately.
511
+ #
512
+ # Corresponding REST API method: POST /actions/u2i/rate
513
+ #
514
+ # See also #user_rate_item.
515
+ def auser_rate_item(uid, iid, rate, params = {})
516
+ params["rate"] = rate
517
+ auser_action_item("rate", uid, iid, params)
518
+ end
519
+
520
+ # :category: Synchronous Methods
521
+ # Synchronously request to record a user-rate-item action and block until a response is received.
522
+ #
523
+ # See #auser_rate_item.
524
+ #
525
+ # call-seq:
526
+ # user_rate_item(uid, iid, rate, params = {})
527
+ # user_rate_item(async_response)
528
+ def user_rate_item(*args)
529
+ if !args[0].is_a?(PredictionIO::AsyncResponse) then
530
+ args.unshift("rate")
531
+ params = args[4]
532
+ if params == nil then
533
+ params = Hash.new
534
+ end
535
+ params["rate"] = args[3]
536
+ args[3] = params
537
+ end
538
+ user_action_item(*args)
539
+ end
540
+
541
+ # :category: Asynchronous Methods
542
+ # Asynchronously request to record a user-like-item action and return a PredictionIO::AsyncResponse object immediately.
543
+ #
544
+ # Corresponding REST API method: POST /actions/u2i/like
545
+ #
546
+ # See also #user_like_item.
547
+ def auser_like_item(uid, iid, params = {})
548
+ auser_action_item("like", uid, iid, params)
549
+ end
550
+
551
+ # :category: Synchronous Methods
552
+ # Synchronously request to record a user-like-item action and block until a response is received.
553
+ #
554
+ # See also #auser_like_item.
555
+ #
556
+ # call-seq:
557
+ # user_like_item(uid, iid, params = {})
558
+ # user_like_item(async_response)
559
+ def user_like_item(*args)
560
+ if !args[0].is_a?(PredictionIO::AsyncResponse) then
561
+ args.unshift("like")
562
+ end
563
+ user_action_item(*args)
564
+ end
565
+
566
+ # :category: Asynchronous Methods
567
+ # Asynchronously request to record a user-dislike-item action and return a PredictionIO::AsyncResponse object immediately.
568
+ #
569
+ # Corresponding REST API method: POST /actions/u2i/dislike
570
+ #
571
+ # See also #user_dislike_item.
572
+ def auser_dislike_item(uid, iid, params = {})
573
+ auser_action_item("dislike", uid, iid, params)
574
+ end
575
+
576
+ # :category: Synchronous Methods
577
+ # Synchronously request to record a user-dislike-item action and block until a response is received.
578
+ #
579
+ # See also #auser_dislike_item.
580
+ #
581
+ # call-seq:
582
+ # user_dislike_item(uid, iid, params = {})
583
+ # user_dislike_item(async_response)
584
+ def user_dislike_item(*args)
585
+ if !args[0].is_a?(PredictionIO::AsyncResponse) then
586
+ args.unshift("dislike")
587
+ end
588
+ user_action_item(*args)
589
+ end
590
+
591
+ # :category: Asynchronous Methods
592
+ # Asynchronously request to record a user-view-item action and return a PredictionIO::AsyncResponse object immediately.
593
+ #
594
+ # Corresponding REST API method: POST /actions/u2i/view
595
+ #
596
+ # See also #user_view_item.
597
+ def auser_view_item(uid, iid, params = {})
598
+ auser_action_item("view", uid, iid, params)
599
+ end
600
+
601
+ # :category: Synchronous Methods
602
+ # Synchronously request to record a user-view-item action and block until a response is received.
603
+ #
604
+ # See also #auser_view_item.
605
+ #
606
+ # call-seq:
607
+ # user_view_item(uid, iid, params = {})
608
+ # user_view_item(async_response)
609
+ def user_view_item(*args)
610
+ if !args[0].is_a?(PredictionIO::AsyncResponse) then
611
+ args.unshift("view")
612
+ end
613
+ user_action_item(*args)
614
+ end
615
+
616
+ # :category: Asynchronous Methods
617
+ # Asynchronously request to record a user-conversion-item action and return a PredictionIO::AsyncResponse object immediately.
618
+ #
619
+ # Corresponding REST API method: POST /actions/u2i/conversion
620
+ #
621
+ # See also #user_conversion_item.
622
+ def auser_conversion_item(uid, iid, params = {})
623
+ auser_action_item("conversion", uid, iid, params)
624
+ end
625
+
626
+ # :category: Synchronous Methods
627
+ # Synchronously request to record a user-conversion-item action and block until a response is received.
628
+ #
629
+ # See also #auser_conversion_item.
630
+ #
631
+ # call-seq:
632
+ # user_conversion_item(uid, iid, params = {})
633
+ # user_conversion_item(async_response)
634
+ def user_conversion_item(*args)
635
+ if !args[0].is_a?(PredictionIO::AsyncResponse) then
636
+ args.unshift("conversion")
637
+ end
638
+ user_action_item(*args)
639
+ end
640
+
641
+ # :nodoc: all
642
+ private
643
+
644
+ def versioned_path(path)
645
+ # disabled for now
646
+ # "/#{@apiversion}#{path}"
647
+ path
648
+ end
649
+
650
+ def auser_action_item(action, uid, iid, params = {})
651
+ rparams = params
652
+ rparams["appkey"] = @appkey
653
+ rparams["uid"] = uid
654
+ rparams["iid"] = iid
655
+ if params["t"] != nil then
656
+ rparams["t"] = ((params["t"].to_r) * 1000).round(0).to_s
657
+ end
658
+ if params["latitude"] != nil && params["longitude"] != nil then
659
+ rparams["latlng"] = "#{params["latitude"]},#{params["longitude"]}"
660
+ end
661
+ @http.apost(PredictionIO::AsyncRequest.new(versioned_path("/actions/u2i/#{action}.#{@apiformat}"), rparams))
662
+ end
663
+
664
+ def user_action_item(*args)
665
+ action_or_res = args[0]
666
+ if action_or_res.is_a?(PredictionIO::AsyncResponse) then
667
+ response = action_or_res.get
668
+ else
669
+ response = auser_action_item(*args).get
670
+ end
671
+ unless response.is_a?(Net::HTTPCreated) then
672
+ begin
673
+ msg = response.body
674
+ rescue Exception
675
+ raise U2IActionNotCreatedError, response
676
+ end
677
+ raise U2IActionNotCreatedError, msg
678
+ end
679
+ end
680
+ end
681
+ end
@@ -0,0 +1,112 @@
1
+ module PredictionIO
2
+
3
+ # This class handles multithreading and asynchronous requests transparently for the REST client.
4
+ class Connection
5
+
6
+ # Number of pending asynchronous request and response packages.
7
+ attr_reader :packages
8
+
9
+ # Number of connections active
10
+ attr_reader :connections
11
+
12
+ # Timeout in seconds
13
+ attr_reader :timeout
14
+
15
+ # Spawns a number of threads with persistent HTTP connection to the specified URI.
16
+ # Sets a default timeout of 5 seconds.
17
+ def initialize(uri, threads = 1, timeout = 5)
18
+ @packages = Queue.new
19
+ @counter_lock = Mutex.new
20
+ @connections = 0
21
+ @timeout = timeout
22
+ threads.times do
23
+ Thread.new do
24
+ begin
25
+ Net::HTTP.start(uri.host, uri.port,
26
+ :use_ssl => uri.scheme == 'https') do |http|
27
+ @counter_lock.synchronize do
28
+ @connections += 1
29
+ end
30
+ catch(:exit) do
31
+ http.read_timeout = @timeout
32
+ loop do
33
+ package = @packages.pop
34
+ request = package[:request]
35
+ response = package[:response]
36
+ case package[:method]
37
+ when "get"
38
+ http_req = Net::HTTP::Get.new("#{uri.path}#{request.qpath}")
39
+ begin
40
+ response.set(http.request(http_req))
41
+ rescue Exception => details
42
+ response.set(details)
43
+ end
44
+ when "post"
45
+ http_req = Net::HTTP::Post.new("#{uri.path}#{request.path}")
46
+ http_req.set_form_data(request.params)
47
+ begin
48
+ response.set(http.request(http_req))
49
+ rescue Exception => details
50
+ response.set(details)
51
+ end
52
+ when "delete"
53
+ http_req = Net::HTTP::Delete.new("#{uri.path}#{request.qpath}")
54
+ begin
55
+ response.set(http.request(http_req))
56
+ rescue Exception => details
57
+ response.set(details)
58
+ end
59
+ when "exit"
60
+ @counter_lock.synchronize do
61
+ @connections -= 1
62
+ end
63
+ throw :exit
64
+ end
65
+ end
66
+ end
67
+ end
68
+ rescue Exception => detail
69
+ @counter_lock.synchronize do
70
+ if @connections == 0 then
71
+ # Use non-blocking pop to avoid dead-locking the current
72
+ # thread when there is no request, and give it a chance to re-connect.
73
+ begin
74
+ package = @packages.pop(true)
75
+ response = package[:response]
76
+ response.set(detail)
77
+ rescue Exception
78
+ end
79
+ end
80
+ end
81
+ sleep(1)
82
+ retry
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # Create an asynchronous request and response package, put it in the pending queue, and return the response object.
89
+ def request(method, request)
90
+ response = AsyncResponse.new(request)
91
+ @packages.push(:method => method,
92
+ :request => request,
93
+ :response => response)
94
+ response
95
+ end
96
+
97
+ # Shortcut to create an asynchronous GET request with the response object returned.
98
+ def aget(areq)
99
+ request("get", areq)
100
+ end
101
+
102
+ # Shortcut to create an asynchronous POST request with the response object returned.
103
+ def apost(areq)
104
+ request("post", areq)
105
+ end
106
+
107
+ # Shortcut to create an asynchronous DELETE request with the response object returned.
108
+ def adelete(areq)
109
+ request("delete", areq)
110
+ end
111
+ end
112
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: predictionio
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - The PredictionIO Team
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-25 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'PredictionIO is a prediction server for building smart applications.
15
+ This gem
16
+
17
+ provides convenient access of the PredictionIO API to Ruby programmers so that
18
+
19
+ they can focus on their application logic.
20
+
21
+ '
22
+ email: support@prediction.io
23
+ executables: []
24
+ extensions: []
25
+ extra_rdoc_files: []
26
+ files:
27
+ - lib/predictionio/client.rb
28
+ - lib/predictionio/async_request.rb
29
+ - lib/predictionio/connection.rb
30
+ - lib/predictionio/async_response.rb
31
+ - lib/predictionio.rb
32
+ homepage: http://prediction.io
33
+ licenses: []
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '1.9'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.23
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: PredictionIO Ruby SDK
56
+ test_files: []