Merge branch 'develop'
diff --git a/.travis.yml b/.travis.yml
index 1849328..34f69c5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,4 +3,4 @@
- 1.9.3
- 2.1.1
script:
- - rspec
+ - rake
diff --git a/lib/predictionio.rb b/lib/predictionio.rb
index 73250bd..9879266 100644
--- a/lib/predictionio.rb
+++ b/lib/predictionio.rb
@@ -1,4 +1,3 @@
-require 'predictionio/client'
require 'predictionio/event_client'
require 'predictionio/engine_client'
diff --git a/lib/predictionio/client.rb b/lib/predictionio/client.rb
deleted file mode 100644
index e2b405b..0000000
--- a/lib/predictionio/client.rb
+++ /dev/null
@@ -1,703 +0,0 @@
-# Ruby SDK for convenient access of PredictionIO Output API.
-#
-# Author:: TappingStone (help@tappingstone.com)
-# Copyright:: Copyright (c) 2013 TappingStone
-# License:: Apache License, Version 2.0
-
-require 'date'
-require 'json'
-require 'net/http'
-require 'predictionio/async_request'
-require 'predictionio/async_response'
-require 'predictionio/connection'
-
-module PredictionIO
- # This class contains methods that access PredictionIO via REST requests.
- #
- # Many REST request methods support optional arguments.
- # They can be supplied to these methods as Hash'es.
- # For a complete reference, please visit http://prediction.io.
- #
- # == High-performance Asynchronous Backend
- #
- # All REST request methods come in both synchronous and asynchronous flavors.
- # Both flavors accept the same set of arguments.
- # In addition, all synchronous request methods can instead accept a PredictionIO::AsyncResponse object generated from asynchronous request methods as its first argument.
- # In this case, the method will block until a response is received from it.
- #
- # Any network reconnection and request retry is automatically handled in the background.
- # Exceptions will be thrown after a request times out to avoid infinite blocking.
- #
- # == Special Handling of Some Optional Arguments
- # Some optional arguments have additional special handling:
- # - 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.
- # - For all requests that accept "pio_latlng" as input, they will also accept "pio_latitude" and "pio_longitude".
- # When these are supplied, they will override any existing "pio_latlng" value.
- # - All time arguments (e.g. t, pio_startT, pio_endT, etc) can be supplied as either a Time or Float object.
- # When supplied as a Float, the SDK will interpret it as a UNIX UTC timestamp in seconds.
- # The SDK will automatically round to the nearest millisecond, e.g. 3.14159 => 3.142.
- #
- # == Installation
- # The easiest way is to use RubyGems:
- # gem install predictionio
- #
- # == Synopsis
- # The recommended usage of the SDK is to fire asynchronous requests as early as you can in your code,
- # and check results later when you need them.
- #
- # === Instantiate PredictionIO Client
- # # Include the PredictionIO SDK
- # require "predictionio"
- #
- # client = PredictionIO::Client.new(<appkey>)
- #
- # === Import a User Record from Your App (with asynchronous/non-blocking requests)
- #
- # #
- # # (your user registration logic)
- # #
- #
- # uid = get_user_from_your_db()
- #
- # # PredictionIO call to create user
- # response = client.acreate_user(uid)
- #
- # #
- # # (other work to do for the rest of the page)
- # #
- #
- # begin
- # # PredictionIO call to retrieve results from an asynchronous response
- # result = client.create_user(response)
- # rescue UserNotCreatedError => e
- # log_and_email_error(...)
- # end
- #
- # === Import a User Action (Rate) from Your App (with synchronous/blocking requests)
- # # PredictionIO call to record the view action
- # begin
- # client.identify("foouser")
- # result = client.record_action_on_item("rate", "baritem", "pio_rate" => 4)
- # rescue U2IActionNotCreatedError => e
- # ...
- # end
- #
- # === Retrieving Top N Recommendations for a User
- # # PredictionIO call to get recommendations
- # client.identify("foouser")
- # response = client.aget_itemrec_top_n("barengine", 10)
- #
- # #
- # # work you need to do for the page (rendering, db queries, etc)
- # #
- #
- # begin
- # result = client.get_itemrec_top_n(response)
- # # display results, store results, or your other work...
- # rescue ItemRecNotFoundError => e
- # # graceful error handling
- # end
- #
- # === Retrieving Top N Similar Items for an Item
- # # PredictionIO call to get similar items
- # response = client.aget_itemsim_top_n("barengine", "fooitem", 10)
- #
- # #
- # # work you need to do for the page (rendering, db queries, etc)
- # #
- #
- # begin
- # result = client.get_itemsim_top_n(response)
- # # display results, store results, or your other work...
- # rescue ItemSimNotFoundError => e
- # # graceful error handling
- # end
-
- class Client
-
- # Appkey can be changed on-the-fly after creation of the client.
- attr_accessor :appkey
-
- # Only JSON is currently supported as API response format.
- attr_accessor :apiformat
-
- # The UID used for recording user-to-item actions and retrieving recommendations.
- attr_accessor :apiuid
-
- # Raised when a user is not created after a synchronous API call.
- class UserNotCreatedError < StandardError; end
-
- # Raised when a user is not found after a synchronous API call.
- class UserNotFoundError < StandardError; end
-
- # Raised when a user is not deleted after a synchronous API call.
- class UserNotDeletedError < StandardError; end
-
- # Raised when an item is not created after a synchronous API call.
- class ItemNotCreatedError < StandardError; end
-
- # Raised when an item is not found after a synchronous API call.
- class ItemNotFoundError < StandardError; end
-
- # Raised when an item is not deleted after a synchronous API call.
- class ItemNotDeletedError < StandardError; end
-
- # Raised when ItemRec results cannot be found for a user after a synchronous API call.
- class ItemRecNotFoundError < StandardError; end
-
- # Raised when ItemRank results cannot be found for a user after a synchronous API call.
- class ItemRankNotFoundError < StandardError; end
-
- # Raised when ItemSim results cannot be found for an item after a synchronous API call.
- class ItemSimNotFoundError < StandardError; end
-
- # Raised when an user-to-item action is not created after a synchronous API call.
- class U2IActionNotCreatedError < StandardError; end
-
- # Create a new PredictionIO client with default:
- # - 10 concurrent HTTP(S) connections (threads)
- # - API entry point at http://localhost:8000 (apiurl)
- # - a 60-second timeout for each HTTP(S) connection (thread_timeout)
- def initialize(appkey, threads = 10, apiurl = "http://localhost:8000", thread_timeout = 60)
- @appkey = appkey
- @apiformat = "json"
- @http = PredictionIO::Connection.new(URI(apiurl), threads, thread_timeout)
- end
-
- # Returns the number of pending requests within the current client.
- def pending_requests
- @http.packages.size
- end
-
- # Returns PredictionIO's status in string.
- def get_status
- status = @http.aget(PredictionIO::AsyncRequest.new("/")).get()
- begin
- status.body
- rescue Exception
- status
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to create a user and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /users
- #
- # See also #create_user.
- def acreate_user(uid, params = {})
- rparams = params
- rparams["pio_appkey"] = @appkey
- rparams["pio_uid"] = uid
- if params["pio_latitude"] && params["pio_longitude"]
- rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
- end
-
- @http.apost(PredictionIO::AsyncRequest.new("/users.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to create a user and block until a response is received.
- #
- # See also #acreate_user.
- #
- # call-seq:
- # create_user(uid, params = {})
- # create_user(async_response)
- def create_user(*args)
- uid_or_res = args[0]
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res.get
- else
- uid = uid_or_res
- response = acreate_user(*args).get
- end
- unless response.is_a?(Net::HTTPCreated)
- begin
- msg = response.body
- rescue Exception
- raise UserNotCreatedError, response
- end
- raise UserNotCreatedError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to get a user and return a PredictionIO::AsyncResponse object immediately.
- #
- # Creation time of the user will be returned as a Time object.
- #
- # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
- #
- # Corresponding REST API method: GET /users/:uid
- #
- # See also #get_user.
- def aget_user(uid)
- @http.aget(PredictionIO::AsyncRequest.new("/users/#{uid}.#{@apiformat}",
- "pio_appkey" => @appkey,
- "pio_uid" => uid))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to get a user and block until a response is received.
- #
- # Creation time of the user will be returned as a Time object.
- #
- # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
- #
- # See also #aget_user.
- #
- # call-seq:
- # get_user(uid)
- # get_user(async_response)
- def get_user(uid_or_res)
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res.get
- else
- response = aget_user(uid_or_res).get
- end
- if response.is_a?(Net::HTTPOK)
- res = JSON.parse(response.body)
- if res["pio_latlng"]
- latlng = res["pio_latlng"]
- res["pio_latitude"] = latlng[0]
- res["pio_longitude"] = latlng[1]
- end
- res
- else
- begin
- msg = response.body
- rescue Exception
- raise UserNotFoundError, response
- end
- raise UserNotFoundError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to delete a user and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: DELETE /users/:uid
- #
- # See also #delete_user.
- def adelete_user(uid)
- @http.adelete(PredictionIO::AsyncRequest.new("/users/#{uid}.#{@apiformat}",
- "pio_appkey" => @appkey,
- "pio_uid" => uid))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to delete a user and block until a response is received.
- #
- # See also #adelete_user.
- #
- # call-seq:
- # delete_user(uid)
- # delete_user(async_response)
- def delete_user(uid_or_res)
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res.get
- else
- response = adelete_user(uid_or_res).get
- end
- unless response.is_a?(Net::HTTPOK)
- begin
- msg = response.body
- rescue Exception
- raise UserNotDeletedError, response
- end
- raise msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to create an item and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /items
- #
- # See also #create_item.
- def acreate_item(iid, itypes, params = {})
- rparams = params
- rparams["pio_appkey"] = @appkey
- rparams["pio_iid"] = iid
- begin
- rparams["pio_itypes"] = itypes.join(",")
- rescue Exception
- rparams["pio_itypes"] = itypes
- end
- if params["pio_latitude"] && params["pio_longitude"]
- rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
- end
- rparams["pio_startT"] = ((params["pio_startT"].to_r) * 1000).round(0).to_s if params["pio_startT"]
- rparams["pio_endT"] = ((params["pio_endT"].to_r) * 1000).round(0).to_s if params["pio_endT"]
-
- @http.apost(PredictionIO::AsyncRequest.new("/items.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to create an item and block until a response is received.
- #
- # See #acreate_item for a description of other accepted arguments.
- #
- # call-seq:
- # create_item(iid, itypes, params = {})
- # create_item(async_response)
- def create_item(*args)
- iid_or_res = args[0]
- if iid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = iid_or_res.get
- else
- response = acreate_item(*args).get
- end
- unless response.is_a?(Net::HTTPCreated)
- begin
- msg = response.body
- rescue Exception
- raise ItemNotCreatedError, response
- end
- raise ItemNotCreatedError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to get an item and return a PredictionIO::AsyncResponse object immediately.
- #
- # Creation time of the user will be returned as a Time object.
- #
- # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
- #
- # Corresponding REST API method: GET /items/:iid
- #
- # See also #get_item.
- def aget_item(iid)
- @http.aget(PredictionIO::AsyncRequest.new("/items/#{iid}.#{@apiformat}",
- "pio_appkey" => @appkey,
- "pio_iid" => iid))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to get an item and block until a response is received.
- #
- # Creation time of the item will be returned as a Time object.
- #
- # If the result contains a latlng key, both latitude and longitude will also be available as separate keys.
- #
- # See also #aget_item.
- #
- # call-seq:
- # get_item(iid)
- # get_item(async_response)
- def get_item(iid_or_res)
- if iid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = iid_or_res.get
- else
- response = aget_item(iid_or_res).get
- end
- if response.is_a?(Net::HTTPOK)
- res = JSON.parse(response.body)
- if res["pio_latlng"]
- latlng = res["pio_latlng"]
- res["pio_latitude"] = latlng[0]
- res["pio_longitude"] = latlng[1]
- end
- if res["pio_startT"]
- startT = Rational(res["pio_startT"], 1000)
- res["pio_startT"] = Time.at(startT)
- end
- if res["pio_endT"]
- endT = Rational(res["pio_endT"], 1000)
- res["pio_endT"] = Time.at(endT)
- end
- res
- else
- begin
- msg = response.body
- rescue Exception
- raise ItemNotFoundError, response
- end
- raise ItemNotFoundError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to delete an item and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: DELETE /items/:iid
- #
- # See also #delete_item.
- def adelete_item(iid)
- @http.adelete(PredictionIO::AsyncRequest.new("/items/#{iid}.#{@apiformat}",
- "pio_appkey" => @appkey,
- "pio_iid" => iid))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to delete an item and block until a response is received.
- #
- # See also #adelete_item.
- #
- # call-seq:
- # delete_item(iid)
- # delete_item(async_response)
- def delete_item(iid_or_res)
- if iid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = iid_or_res.get
- else
- response = adelete_item(iid_or_res).get
- end
- unless response.is_a?(Net::HTTPOK)
- begin
- msg = response.body
- rescue Exception
- raise ItemNotDeletedError, response
- end
- raise ItemNotDeletedError, msg
- end
- end
-
- # Set the user ID for use in all subsequent user-to-item action recording and user recommendation retrieval.
- def identify(uid)
- @apiuid = uid
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to get the top n recommendations for a user from an ItemRec engine and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: GET /engines/itemrec/:engine/topn
- #
- # See also #get_itemrec_top_n.
- def aget_itemrec_top_n(engine, n, params = {})
- rparams = Hash.new
- rparams["pio_appkey"] = @appkey
- rparams["pio_uid"] = @apiuid
- rparams["pio_n"] = n
- if params["pio_itypes"]
- if params["pio_itypes"].kind_of?(Array) && params["pio_itypes"].any?
- rparams["pio_itypes"] = params["pio_itypes"].join(",")
- else
- rparams["pio_itypes"] = params["pio_itypes"]
- end
- end
- if params["pio_latitude"] && params["pio_longitude"]
- rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
- end
- rparams["pio_within"] = params["pio_within"] if params["pio_within"]
- rparams["pio_unit"] = params["pio_unit"] if params["pio_unit"]
- if params["pio_attributes"]
- if params["pio_attributes"].kind_of?(Array) && params["pio_attributes"].any?
- rparams["pio_attributes"] = params["pio_attributes"].join(",")
- else
- rparams["pio_attributes"] = params["pio_attributes"]
- end
- end
- @http.aget(PredictionIO::AsyncRequest.new("/engines/itemrec/#{engine}/topn.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to get the top n recommendations for a user from an ItemRec engine and block until a response is received.
- #
- # See #aget_itemrec_top_n for a description of special argument handling.
- #
- # call-seq:
- # get_itemrec_top_n(engine, n, params = {})
- # get_itemrec_top_n(async_response)
- def get_itemrec_top_n(*args)
- uid_or_res = args[0]
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res
- else
- response = aget_itemrec_top_n(*args)
- end
- http_response = response.get
- if http_response.is_a?(Net::HTTPOK)
- res = JSON.parse(http_response.body)
- if response.request.params.has_key?('pio_attributes')
- attributes = response.request.params['pio_attributes'].split(',')
- list_of_attribute_values = attributes.map { |attrib| res[attrib] }
- res["pio_iids"].zip(*list_of_attribute_values).map { |v| Hash[(['pio_iid'] + attributes).zip(v)] }
- else
- res["pio_iids"]
- end
- else
- begin
- msg = response.body
- rescue Exception
- raise ItemRecNotFoundError, response
- end
- raise ItemRecNotFoundError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to get the ranking for a user from an ItemRank engine and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: GET /engines/itemrank/:engine/ranked
- #
- # See also #get_itemrank_ranked.
- def aget_itemrank_ranked(engine, iids, params = {})
- rparams = Hash.new
- rparams["pio_appkey"] = @appkey
- rparams["pio_uid"] = @apiuid
- if iids.kind_of?(Array) && iids.any?
- rparams["pio_iids"] = iids.join(",")
- else
- rparams["pio_iids"] = iids
- end
- if params["pio_attributes"]
- if params["pio_attributes"].kind_of?(Array) && params["pio_attributes"].any?
- rparams["pio_attributes"] = params["pio_attributes"].join(",")
- else
- rparams["pio_attributes"] = params["pio_attributes"]
- end
- end
- @http.aget(PredictionIO::AsyncRequest.new("/engines/itemrank/#{engine}/ranked.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to get the ranking for a user from an ItemRank engine and block until a response is received.
- #
- # See #aget_itemrank_ranked for a description of special argument handling.
- #
- # call-seq:
- # get_itemrank_ranked(engine, n, params = {})
- # get_itemrank_ranked(async_response)
- def get_itemrank_ranked(*args)
- uid_or_res = args[0]
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res
- else
- response = aget_itemrank_ranked(*args)
- end
- http_response = response.get
- if http_response.is_a?(Net::HTTPOK)
- res = JSON.parse(http_response.body)
- if response.request.params.has_key?('pio_attributes')
- attributes = response.request.params['pio_attributes'].split(',')
- list_of_attribute_values = attributes.map { |attrib| res[attrib] }
- res["pio_iids"].zip(*list_of_attribute_values).map { |v| Hash[(['pio_iid'] + attributes).zip(v)] }
- else
- res["pio_iids"]
- end
- else
- begin
- msg = response.body
- rescue Exception
- raise ItemRankNotFoundError, response
- end
- raise ItemRankNotFoundError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to get the top n similar items for an item from an ItemSim engine and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: GET /engines/itemsim/:engine/topn
- #
- # See also #get_itemsim_top_n.
- def aget_itemsim_top_n(engine, iid, n, params = {})
- rparams = Hash.new
- rparams["pio_appkey"] = @appkey
- rparams["pio_iid"] = iid
- rparams["pio_n"] = n
- if params["pio_itypes"]
- if params["pio_itypes"].kind_of?(Array) && params["pio_itypes"].any?
- rparams["pio_itypes"] = params["pio_itypes"].join(",")
- else
- rparams["pio_itypes"] = params["pio_itypes"]
- end
- end
- if params["pio_latitude"] && params["pio_longitude"]
- rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
- end
- rparams["pio_within"] = params["pio_within"] if params["pio_within"]
- rparams["pio_unit"] = params["pio_unit"] if params["pio_unit"]
- if params["pio_attributes"]
- if params["pio_attributes"].kind_of?(Array) && params["pio_attributes"].any?
- rparams["pio_attributes"] = params["pio_attributes"].join(",")
- else
- rparams["pio_attributes"] = params["pio_attributes"]
- end
- end
- @http.aget(PredictionIO::AsyncRequest.new("/engines/itemsim/#{engine}/topn.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to get the top n similar items for an item from an ItemSim engine and block until a response is received.
- #
- # See #aget_itemsim_top_n for a description of special argument handling.
- #
- # call-seq:
- # get_itemsim_top_n(engine, iid, n, params = {})
- # get_itemsim_top_n(async_response)
- def get_itemsim_top_n(*args)
- uid_or_res = args[0]
- if uid_or_res.is_a?(PredictionIO::AsyncResponse)
- response = uid_or_res
- else
- response = aget_itemsim_top_n(*args)
- end
- http_response = response.get
- if http_response.is_a?(Net::HTTPOK)
- res = JSON.parse(http_response.body)
- if response.request.params.has_key?('pio_attributes')
- attributes = response.request.params['pio_attributes'].split(',')
- list_of_attribute_values = attributes.map { |attrib| res[attrib] }
- res["pio_iids"].zip(*list_of_attribute_values).map { |v| Hash[(['pio_iid'] + attributes).zip(v)] }
- else
- res["pio_iids"]
- end
- else
- begin
- msg = response.body
- rescue Exception
- raise ItemSimNotFoundError, response
- end
- raise ItemSimNotFoundError, msg
- end
- end
-
- # :category: Asynchronous Methods
- # Asynchronously request to record an action on an item and return a PredictionIO::AsyncResponse object immediately.
- #
- # Corresponding REST API method: POST /actions/u2i
- #
- # See also #record_action_on_item.
- def arecord_action_on_item(action, iid, params = {})
- rparams = params
- rparams["pio_appkey"] = @appkey
- rparams["pio_action"] = action
- rparams["pio_uid"] = @apiuid
- rparams["pio_iid"] = iid
- rparams["pio_t"] = ((params["pio_t"].to_r) * 1000).round(0).to_s if params["pio_t"]
- if params["pio_latitude"] && params["pio_longitude"]
- rparams["pio_latlng"] = "#{params["pio_latitude"]},#{params["pio_longitude"]}"
- end
- @http.apost(PredictionIO::AsyncRequest.new("/actions/u2i.#{@apiformat}", rparams))
- end
-
- # :category: Synchronous Methods
- # Synchronously request to record an action on an item and block until a response is received.
- #
- # See also #arecord_action_on_item.
- #
- # call-seq:
- # record_action_on_item(action, iid, params = {})
- # record_action_on_item(async_response)
- def record_action_on_item(*args)
- action_or_res = args[0]
- if action_or_res.is_a?(PredictionIO::AsyncResponse)
- response = action_or_res.get
- else
- response = arecord_action_on_item(*args).get
- end
- unless response.is_a?(Net::HTTPCreated)
- begin
- msg = response.body
- rescue Exception
- raise U2IActionNotCreatedError, response
- end
- raise U2IActionNotCreatedError, msg
- end
- end
- end
-end
diff --git a/lib/predictionio/engine_client.rb b/lib/predictionio/engine_client.rb
index 021db1d..65ee8ad 100644
--- a/lib/predictionio/engine_client.rb
+++ b/lib/predictionio/engine_client.rb
@@ -49,7 +49,7 @@
# Create a new PredictionIO Event Client with defaults:
# - 1 concurrent HTTP(S) connections (threads)
- # - API entry point at http://localhost:7070 (apiurl)
+ # - API entry point at http://localhost:8000 (apiurl)
# - a 60-second timeout for each HTTP(S) connection (thread_timeout)
def initialize(apiurl = 'http://localhost:8000', threads = 1,
thread_timeout = 60)
diff --git a/lib/predictionio/event_client.rb b/lib/predictionio/event_client.rb
index 7deaa53..83c73a5 100644
--- a/lib/predictionio/event_client.rb
+++ b/lib/predictionio/event_client.rb
@@ -41,7 +41,7 @@
# # Include the PredictionIO SDK
# require 'predictionio'
#
- # client = PredictionIO::EventClient.new(<app_id>)
+ # client = PredictionIO::EventClient.new(<access_key>)
#
# === Import a User Record from Your App (with asynchronous/non-blocking
# requests)
@@ -84,9 +84,9 @@
# - 1 concurrent HTTP(S) connections (threads)
# - API entry point at http://localhost:7070 (apiurl)
# - a 60-second timeout for each HTTP(S) connection (thread_timeout)
- def initialize(app_id, apiurl = 'http://localhost:7070', threads = 1,
+ def initialize(access_key, apiurl = 'http://localhost:7070', threads = 1,
thread_timeout = 60)
- @app_id = app_id
+ @access_key = access_key
@http = PredictionIO::Connection.new(URI(apiurl), threads, thread_timeout)
end
@@ -135,11 +135,12 @@
def acreate_event(event, entity_type, entity_id, optional = {})
h = optional
h.key?('eventTime') || h['eventTime'] = DateTime.now.to_s
- h['appId'] = @app_id
h['event'] = event
h['entityType'] = entity_type
h['entityId'] = entity_id
- @http.apost(PredictionIO::AsyncRequest.new('/events.json', h.to_json))
+ @http.apost(PredictionIO::AsyncRequest.new(
+ "/events.json?accessKey=#{@access_key}", h.to_json
+ ))
end
# :category: Synchronous Methods
diff --git a/predictionio.gemspec b/predictionio.gemspec
index b9a2596..8bbb9f7 100644
--- a/predictionio.gemspec
+++ b/predictionio.gemspec
@@ -6,7 +6,7 @@
provides convenient access of the PredictionIO API to Ruby programmers so that
they can focus on their application logic.
EOF
- s.version = '0.8.0'
+ s.version = '0.8.2'
s.licenses = ['Apache-2.0']
s.author = 'The PredictionIO Team'
s.email = 'support@prediction.io'
diff --git a/spec/predictionio_spec.rb b/spec/predictionio_spec.rb
index 911dc93..b7f2045 100644
--- a/spec/predictionio_spec.rb
+++ b/spec/predictionio_spec.rb
@@ -1,7 +1,6 @@
require 'predictionio'
require 'spec_helper'
-client = PredictionIO::Client.new('foobar', 10, 'http://fakeapi.com:8000')
event_client = PredictionIO::EventClient.new(1, 'http://fakeapi.com:8000', 10)
engine_client = PredictionIO::EngineClient.new('http://fakeapi.com:8000', 10)
@@ -50,92 +49,4 @@
expect(predictions).to eq('iids' => %w(dead beef))
end
end
-
- describe 'Users API' do
- it 'create_user should create a user' do
- response = client.create_user('foo')
- expect(response).to_not raise_error
- end
- it 'get_user should get a user' do
- response = client.get_user('foo')
- expect(response).to eq('pio_uid' => 'foo')
- end
- it 'delete_user should delete a user' do
- response = client.delete_user('foo')
- expect(response).to_not raise_error
- end
- end
-
- describe 'Items API' do
- it 'create_item should create a item' do
- response = client.create_item('bar', %w(dead beef))
- expect(response).to_not raise_error
- end
- it 'get_item should get a item' do
- response = client.get_item('bar')
- expect(response).to eq('pio_iid' => 'bar',
- 'pio_itypes' => %w(dead beef))
- end
- it 'delete_item should delete a item' do
- response = client.delete_item('bar')
- expect(response).to_not raise_error
- end
- end
-
- describe 'U2I API' do
- it 'record_action_on_item should record an action' do
- client.identify('foo')
- response = client.record_action_on_item('view', 'bar')
- expect(response).to_not raise_error
- end
- end
-
- describe 'Item Recommendation API' do
- it 'provides recommendations to a user without attributes' do
- client.identify('foo')
- response = client.get_itemrec_top_n('itemrec-engine', 10)
- expect(response).to eq(%w(x y z))
- end
- it 'provides recommendations to a user with attributes' do
- client.identify('foo')
- response = client.get_itemrec_top_n('itemrec-engine', 10,
- 'pio_attributes' => 'name')
- expect(response).to eq([
- { 'pio_iid' => 'x', 'name' => 'a' },
- { 'pio_iid' => 'y', 'name' => 'b' },
- { 'pio_iid' => 'z', 'name' => 'c' }])
- end
- end
-
- describe 'Item Rank API' do
- it 'provides ranking to a user without attributes' do
- client.identify('foo')
- response = client.get_itemrank_ranked('itemrank-engine', %w(y z x))
- expect(response).to eq(%w(x y z))
- end
- it 'provides ranking to a user with attributes' do
- client.identify('foo')
- response = client.get_itemrank_ranked('itemrank-engine', %w(y x z),
- 'pio_attributes' => 'name')
- expect(response).to eq([
- { 'pio_iid' => 'x', 'name' => 'a' },
- { 'pio_iid' => 'y', 'name' => 'b' },
- { 'pio_iid' => 'z', 'name' => 'c' }])
- end
- end
-
- describe 'Item Similarity API' do
- it 'provides similarities to an item without attributes' do
- response = client.get_itemsim_top_n('itemsim-engine', 'bar', 10)
- expect(response).to eq(%w(x y z))
- end
- it 'provides similarities to an item with attributes' do
- response = client.get_itemsim_top_n('itemsim-engine', 'bar', 10,
- 'pio_attributes' => 'name')
- expect(response).to eq([
- { 'pio_iid' => 'x', 'name' => 'a' },
- { 'pio_iid' => 'y', 'name' => 'b' },
- { 'pio_iid' => 'z', 'name' => 'c' }])
- end
- end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 19d9d32..917ce65 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -8,41 +8,41 @@
RSpec.configure do |config|
config.before(:each) do
# Events API
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: 'register',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: 'register',
entityType: 'user', entityId: 'foobar'))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef00'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$set',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$set',
entityType: 'pio_user', entityId: 'foobar'))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef01'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$unset',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$unset',
entityType: 'pio_user', entityId: 'foobar',
properties: { bar: 'baz' }))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef02'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$set',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$set',
entityType: 'pio_item', entityId: 'foobar'))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef03'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$unset',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$unset',
entityType: 'pio_item', entityId: 'foobar',
properties: { bar: 'baz' }))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef04'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: 'greet',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: 'greet',
entityType: 'pio_user', entityId: 'foobar',
targetEntityType: 'pio_item',
targetEntityId: 'barbaz',
properties: { dead: 'beef' }))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef05'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$delete',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$delete',
entityType: 'pio_user', entityId: 'foobar'))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef06'))
- stub_request(:post, 'http://fakeapi.com:8000/events.json')
- .with(body: hash_including(appId: 1, event: '$delete',
+ stub_request(:post, 'http://fakeapi.com:8000/events.json?accessKey=1')
+ .with(body: hash_including(event: '$delete',
entityType: 'pio_item', entityId: 'foobar'))
.to_return(status: 201, body: JSON.generate(eventId: 'deadbeef07'))
@@ -51,92 +51,5 @@
.with(body: { uid: 'foobar' })
.to_return(status: 200, body: JSON.generate(iids: %w(dead beef)),
headers: {})
-
- # Users API
- stub_request(:post, 'http://fakeapi.com:8000/users.json')
- .with(body: { pio_appkey: 'foobar', pio_uid: 'foo' })
- .to_return(status: 201, body: '', headers: {})
- stub_request(:get, 'http://fakeapi.com:8000/users/foo.json')
- .with(query: hash_including(pio_appkey: 'foobar'))
- .to_return(status: 200, body: JSON.generate(pio_uid: 'foo'), headers: {})
- stub_request(:delete, 'http://fakeapi.com:8000/users/foo.json')
- .with(query: hash_including(pio_appkey: 'foobar'))
- .to_return(status: 200, body: '', headers: {})
-
- # Items API
- stub_request(:post, 'http://fakeapi.com:8000/items.json')
- .with(body: { pio_appkey: 'foobar', pio_iid: 'bar',
- pio_itypes: 'dead,beef' })
- .to_return(status: 201, body: '', headers: {})
- stub_request(:get, 'http://fakeapi.com:8000/items/bar.json')
- .with(query: hash_including(pio_appkey: 'foobar'))
- .to_return(status: 200,
- body: JSON.generate(pio_iid: 'bar', pio_itypes: %w(dead beef)),
- headers: {})
- stub_request(:delete, 'http://fakeapi.com:8000/items/bar.json')
- .with(query: hash_including(pio_appkey: 'foobar'))
- .to_return(status: 200, body: '', headers: {})
-
- # U2I Actions API
- stub_request(:post, 'http://fakeapi.com:8000/actions/u2i.json')
- .with(body: { pio_action: 'view', pio_appkey: 'foobar', pio_iid: 'bar',
- pio_uid: 'foo' })
- .to_return(status: 201, body: '', headers: {})
-
- # Item Recommendation API
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemrec/itemrec-engine/topn.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_n: '10',
- pio_uid: 'foo'))
- .to_return(status: 200, body: JSON.generate(pio_iids: %w(x y z)),
- headers: {})
-
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemrec/itemrec-engine/topn.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_n: '10',
- pio_uid: 'foo', pio_attributes: 'name'))
- .to_return(status: 200,
- body: JSON.generate(pio_iids: %w(x y z), name: %w(a b c)),
- headers: {})
-
- # Item Recommendation API
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemrank/itemrank-engine/ranked.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_iids: 'y,z,x',
- pio_uid: 'foo'))
- .to_return(status: 200, body: JSON.generate(pio_iids: %w(x y z)),
- headers: {})
-
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemrank/itemrank-engine/ranked.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_iids: 'y,x,z',
- pio_uid: 'foo', pio_attributes: 'name'))
- .to_return(status: 200,
- body: JSON.generate(pio_iids: %w(x y z), name: %w(a b c)),
- headers: {})
-
- # Item Similarity API
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemsim/itemsim-engine/topn.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_n: '10',
- pio_iid: 'bar'))
- .to_return(status: 200,
- body: JSON.generate(pio_iids: %w(x y z)),
- headers: {})
-
- stub_request(
- :get,
- 'http://fakeapi.com:8000/engines/itemsim/itemsim-engine/topn.json')
- .with(query: hash_including(pio_appkey: 'foobar', pio_n: '10',
- pio_iid: 'bar', pio_attributes: 'name'))
- .to_return(status: 200,
- body: JSON.generate('pio_iids' => %w(x y z),
- 'name' => %w(a b c)),
- headers: {})
end
end