predictable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjNkNTE5YTc1NjA5MDEyNjc4YTk1NDViMThmYjBiZjY5ZDkzNTQ3Ng==
5
+ data.tar.gz: !binary |-
6
+ MmY0YmRmMjgxZDhhMjU1OTBjMjhkYmMwMjc2YjM3YmMyYjA3MGQ2NA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NWI0NGUzYjkyZThjMzY5ZWEwODA1YmIzMTg3ZjQ4NDUzZGM1OWExOWRmNzVm
10
+ NzhlOTJjNWYxMjAxMWU1MTk2NzY2YmQ2NzExMDM1OWIyYjFiYTExYzcwMWYz
11
+ M2RjODllNDRjMjBkZGNmZDFmODlmMzdhYzQxY2I0OTJiMmYyNDc=
12
+ data.tar.gz: !binary |-
13
+ YjBkZjExNGRhYzU2NjU3NjUxYTMzMmY4NzYzYzAyNDE1MzY4OGYxYjlhMjM2
14
+ MzY3MDViYTkyYWMyMzJmYzZkZTI0NTc4MDBmMTJiMTEzYzRjYmExYzRmOTgw
15
+ NTk3NGU5MWEyMjY3MjY1YmNmZjJjODY2NDU1ZTJmMzA2OWE4ZDY=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in predictable.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 JS Boulanger
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Predictable
2
+
3
+ Predictable is a Ruby DSL for Prediction.io, an open source machine learning server built on top of Hadoop. It makes it easy to implement predictive features such as personalization, recommendations, and content discovery in your Ruby/Rails application.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'predictable'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install predictable
18
+
19
+ ## Usage
20
+
21
+ Include Predictable::User in your application's User model.
22
+
23
+ class User < ActiveRecord::Base
24
+ include Predictable::User
25
+
26
+ ...
27
+ end
28
+
29
+ user = User.create(email: "js@perkhub.com")
30
+ user.add_to_recommender
31
+
32
+ Include the Predictable::Item in the models of the items you want to recommend.
33
+
34
+ class Offer < ActiveRecord::Base
35
+ include Predictable::Item
36
+
37
+ after_create { add_to_recommender }
38
+ after_destroy { delete_from_recommender }
39
+
40
+ ...
41
+ end
42
+
43
+ Get 10 recommended items for a user.
44
+
45
+ Offer.recommended_for(user)
46
+ current_user.recommended_offers.where("created_at > ?", 1.week.ago).limit(10)
47
+
48
+ Get 10 items most similar to an existing item.
49
+
50
+ Offer.similar_to(offer)
51
+
52
+
53
+ current_user.recommended_offers.limit(10)
54
+
55
+ ## Contributing
56
+
57
+ 1. Fork it
58
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
59
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
60
+ 4. Push to the branch (`git push origin my-new-feature`)
61
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,40 @@
1
+ require "predictable/item"
2
+ require "predictable/recommender"
3
+ require "predictable/user"
4
+ require "predictable/version"
5
+
6
+ module Predictable
7
+ class << self
8
+ def default_config
9
+ {
10
+ :api_url => "http://localhost:8000",
11
+ :threads => 10,
12
+ :api_version => ""
13
+ }.dup
14
+ end
15
+
16
+ def client
17
+ @client ||= PredictionIO::Client.new(
18
+ config[:app_key],
19
+ config[:threads],
20
+ config[:api_url],
21
+ config[:api_version]
22
+ )
23
+ end
24
+
25
+ def config
26
+ @config ||= default_config
27
+ end
28
+
29
+ def config=(new_config)
30
+ config.merge!(new_config)
31
+ end
32
+
33
+ def engines
34
+ {
35
+ :recommendation_engine => config[:recommendation_engine],
36
+ :similarity_engine => config[:similarity_engine]
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,97 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/inflector'
3
+
4
+ module Predictable
5
+ # == Predictable Item Role
6
+ #
7
+ # Defines the Item role for the recommender. This module should
8
+ # be included in any model that represents items that will
9
+ # be recommended.
10
+ #
11
+ # class Offer < ActiveRecord::Base
12
+ # include Predictable::Item
13
+ # ...
14
+ # end
15
+ #
16
+ # class Article < ActiveRecord::Base
17
+ # include Predictable::Item
18
+ # ...
19
+ # end
20
+ #
21
+ module Item
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ # if active_record? defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
26
+ after_create { add_to_recommender }
27
+ after_destroy { delete_from_recommender }
28
+ end
29
+
30
+ # Returns the item id
31
+ def pio_iid
32
+ "#{self.class.pio_itype}-#{id}"
33
+ end
34
+
35
+ # Returns the item types
36
+ def pio_itypes
37
+ [self.class.pio_itype]
38
+ end
39
+
40
+ # Creates or updates the item in prediction.io
41
+ # When the item already exists, it updates it.
42
+ # The operation is done asynchronously.
43
+ def add_to_recommender(attrs={})
44
+ recommender.create_item(self, attrs)
45
+ nil
46
+ end
47
+
48
+ # Removes the item from prediction.io
49
+ # The operation is done asynchronously.
50
+ def delete_from_recommender
51
+ recommender.delete_item(self)
52
+ nil
53
+ end
54
+
55
+ protected
56
+
57
+ def recommender
58
+ self.class.recommender
59
+ end
60
+
61
+ module ClassMethods
62
+ attr_writer :pio_engines, :recommender
63
+
64
+ def pio_engines
65
+ @pio_engines || Predictable.engines
66
+ end
67
+
68
+ def recommender
69
+ @recommender ||= Recommender.new(Predictable.client, pio_engines)
70
+ end
71
+
72
+ def pio_itype
73
+ to_s.underscore
74
+ end
75
+
76
+ def recommended_for(user, opts = {})
77
+ options = { "itypes" => [pio_itype] }.merge(opts)
78
+ limit = options.delete("limit") || 10
79
+
80
+ item_ids = recommender.recommended_items(user, limit, options)
81
+ item_ids = item_ids.map { |id| id.gsub! /[^\d]/, '' }
82
+ where(:id => item_ids)
83
+ end
84
+
85
+ def similar_to(item, opts={})
86
+ options = opts.stringify_keys
87
+ options["pio_itypes"] ||= [pio_itype]
88
+ limit = options.delete("limit") || 10
89
+
90
+ item_ids = recommender.similar_items(item, limit, options)
91
+ item_ids = item_ids.map { |id| id.gsub! /[^\d]/, '' }
92
+ where(:id => item_ids)
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,72 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'predictionio'
3
+
4
+ module Predictable
5
+ # == Recommender
6
+ #
7
+ # Defines the Recommender interface and implements it using
8
+ # the Prediction.io API.
9
+ #
10
+ # recommender = Recommender.new(client)
11
+ # recommender.create_item(item)
12
+ # recommender.create_user(user)
13
+ # recommender.record_action(user, "conversion", item)
14
+ # recommender.recommended_items(user, 10)
15
+ #
16
+ class Recommender
17
+ attr_reader :client
18
+ attr_accessor :recommendation_engine, :similarity_engine
19
+
20
+ def initialize(client, engines={})
21
+ @client = client
22
+ @recommendation_engine = engines[:recommendation_engine]
23
+ @similarity_engine = engines[:similarity_engine]
24
+ end
25
+
26
+ def create_item(item, attrs = {})
27
+ client.acreate_item(item.pio_iid, item.pio_itypes, attrs)
28
+ end
29
+
30
+ def delete_item(item)
31
+ client.adelete_item(item.pio_iid)
32
+ end
33
+
34
+ def create_user(user, attrs = {})
35
+ client.acreate_user(user.pio_uid, attrs)
36
+ end
37
+
38
+ def delete_user(user)
39
+ client.adelete_user(user.pio_uid)
40
+ end
41
+
42
+ def record_action(user, action, item, opts = {})
43
+ client.identify(user.pio_uid)
44
+ client.arecord_action_on_item(action.to_s, item.pio_iid, opts)
45
+ end
46
+
47
+ def recommended_items(user, n, opts = {})
48
+ options = opts.stringify_keys
49
+ client.identify(user.pio_uid)
50
+ begin
51
+ client.get_itemrec_top_n(recommendation_engine, n, opts)
52
+ rescue PredictionIO::Client::ItemRecNotFoundError => e
53
+ []
54
+ end
55
+ end
56
+
57
+ def similar_items(item, n, opts = {})
58
+ begin
59
+ client.get_itemsim_top_n(similarity_engine, item.pio_iid, n, opts)
60
+ rescue PredictionIO::Client::ItemSimNotFoundError
61
+ []
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+
69
+
70
+
71
+
72
+
@@ -0,0 +1,78 @@
1
+ require 'active_support/concern'
2
+
3
+ module Predictable
4
+ # == Predictable User Role
5
+ #
6
+ # Defines the User role for the recommender. This module should
7
+ # be included in the User model of the appliation.
8
+ #
9
+ # class User < ActiveRecord::Base
10
+ # include Predictable::User
11
+ # ...
12
+ # end
13
+ #
14
+ module User
15
+ extend ActiveSupport::Concern
16
+
17
+ included do
18
+ # if active_record? defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
19
+ after_create { add_to_recommender }
20
+ after_destroy { delete_from_recommender }
21
+ end
22
+
23
+ # Returns the user id
24
+ def pio_uid
25
+ self.id
26
+ end
27
+
28
+ # Record a user action in the recommender.
29
+ # Actions cannot be overwritten or deleted in prediction.io
30
+ # The operation is done asynchronously.
31
+ #
32
+ # user.record_action(:conversion, item)
33
+ #
34
+ def record_action(action, item, opts={})
35
+ recommender.record_action(self, action, item, opts)
36
+ nil
37
+ end
38
+
39
+ def record_conversion(item, opts={})
40
+ record_action(:conversion, item, opts)
41
+ nil
42
+ end
43
+
44
+ # Creates or updates a user in prediction.io
45
+ # When the user id already exists it updates the user.
46
+ # The operation is done asynchronously.
47
+ def add_to_recommender(attrs={})
48
+ recommender.create_user(self, attrs)
49
+ nil
50
+ end
51
+
52
+ # Removes the user from prediction.io.
53
+ # The operation is done asynchronously.
54
+ def delete_from_recommender
55
+ recommender.delete_user(self)
56
+ nil
57
+ end
58
+
59
+ protected
60
+
61
+ def recommender
62
+ self.class.recommender
63
+ end
64
+
65
+ module ClassMethods
66
+ attr_writer :pio_engines, :recommender
67
+
68
+ def pio_engines
69
+ @pio_engines || Predictable.engines
70
+ end
71
+
72
+ def recommender
73
+ @recommender ||= Recommender.new(Predictable.client, pio_engines)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,3 @@
1
+ module Predictable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'predictable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "predictable"
8
+ spec.version = Predictable::VERSION
9
+ spec.authors = ["JS Boulanger"]
10
+ spec.email = ["jsboulanger@gmail.com"]
11
+ spec.description = "Ruby/Rails DSL for PredictionIO, an open source machine learning server."
12
+ spec.summary = "Ruby/Rails DSL for PredictionIO"
13
+ spec.homepage = "https://github.com/jsboulanger/predictable"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency('activerecord')
22
+ spec.add_dependency('predictionio')
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ end
@@ -0,0 +1,105 @@
1
+ require 'ostruct'
2
+
3
+ describe Predictable::Item do
4
+ let(:recommender) { Predictable::Recommender.new(double("Client").as_null_object) }
5
+
6
+ # TODO: Test with ActiveRecord::Base
7
+ class MyItem < OpenStruct # ActiveRecord::Base
8
+ include Predictable::Item
9
+
10
+ def self.where(args)
11
+ args[:id].map do |i|
12
+ self.new(:id => i.to_i)
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:item) { MyItem.new(:id => 1) }
18
+ let(:user) { double("User", :pio_uid => 2) }
19
+
20
+ before do
21
+ MyItem.recommender = recommender
22
+ end
23
+ subject { item }
24
+
25
+ its(:pio_iid) { should == "my_item-#{item.id}" }
26
+ its(:pio_itypes) { should == ['my_item'] }
27
+
28
+ describe "#add_to_recommender" do
29
+ it "should create the item in the recommender" do
30
+ recommender.should_receive(:create_item).with(item, {})
31
+ item.add_to_recommender
32
+ end
33
+
34
+ it "should return nil" do
35
+ item.add_to_recommender.should be_nil
36
+ end
37
+ end
38
+
39
+ describe "#delete_from_recommender" do
40
+ it "should delete the item from the recommender" do
41
+ recommender.should_receive(:delete_item).with(item)
42
+ item.delete_from_recommender
43
+ end
44
+
45
+ it "should return nil" do
46
+ item.delete_from_recommender.should be_nil
47
+ end
48
+ end
49
+
50
+ describe ".recommended_for" do
51
+ it "should get recommended items from the recommender" do
52
+ recommender.should_receive(:recommended_items).with(user, 10, {"itypes" => ["my_item"] }).and_return []
53
+ MyItem.recommended_for(user, 10)
54
+ end
55
+
56
+ context "when there are recommendations" do
57
+ before do
58
+ recommender.stub(:recommended_items).and_return [item.pio_iid]
59
+ end
60
+
61
+ it "should return the recommended items" do
62
+ MyItem.recommended_for(user, 1).should == [item]
63
+ end
64
+ end
65
+
66
+ context "when there are no recommendations" do
67
+ before do
68
+ recommender.stub(:recommended_items).and_return []
69
+ end
70
+
71
+ it "should return an empty array" do
72
+ MyItem.recommended_for(user, 1).should == []
73
+ end
74
+ end
75
+ end
76
+
77
+ describe ".similar_to" do
78
+ let(:similar_item) { MyItem.new(:id => 1) }
79
+
80
+ it "should get similar items from the recommender" do
81
+ recommender.should_receive(:similar_items).with(item, 10, {"pio_itypes"=>["my_item"]}).and_return []
82
+ MyItem.similar_to(item, 10)
83
+ end
84
+
85
+ context "when there are similar items" do
86
+ before do
87
+ recommender.stub(:similar_items).and_return [similar_item.pio_iid]
88
+ end
89
+
90
+ it "should return the similar items" do
91
+ MyItem.similar_to(item, 10).should == [similar_item]
92
+ end
93
+ end
94
+
95
+ context "when there are no similar items" do
96
+ before do
97
+ recommender.stub(:similar_items).and_return []
98
+ end
99
+
100
+ it "should an empty array" do
101
+ MyItem.similar_to(item, 10).should == []
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,122 @@
1
+ describe Predictable::Recommender do
2
+ let(:client) { double("PredictionIO Client") }
3
+ let(:recommender) { Predictable::Recommender.new(client) }
4
+
5
+ let(:item) { double("Item", :pio_iid => "item-123", :pio_itypes => ["item"]) }
6
+
7
+ # TODO: item should behave as an item
8
+
9
+ describe "#create_item" do
10
+ it "should call the api" do
11
+ client.should_receive(:acreate_item)
12
+ .with("item-123", ["item"], {})
13
+ recommender.create_item(item)
14
+ end
15
+ end
16
+
17
+ describe "#delete_item" do
18
+ it "should call the api" do
19
+ client.should_receive(:adelete_item)
20
+ .with("item-123")
21
+ recommender.delete_item(item)
22
+ end
23
+ end
24
+
25
+ let(:user) { double("User", :pio_uid => "user-1") }
26
+ # TODO: user should behave as a user
27
+
28
+ describe "#create_user" do
29
+ it "should call the api" do
30
+ client.should_receive(:acreate_user)
31
+ .with("user-1", {})
32
+ recommender.create_user(user)
33
+ end
34
+ end
35
+
36
+ describe "#delete_user" do
37
+ it "should call the api" do
38
+ client.should_receive(:adelete_user)
39
+ .with("user-1")
40
+ recommender.delete_user(user)
41
+ end
42
+ end
43
+
44
+ describe "#record_action" do
45
+ it "should call the api" do
46
+ client.should_receive(:identify).with("user-1")
47
+ client.should_receive(:arecord_action_on_item)
48
+ .with("conversion", "item-123", {})
49
+ recommender.record_action(user, :conversion, item)
50
+ end
51
+ end
52
+
53
+ describe "#recommended_items" do
54
+ before do
55
+ client.stub(:identify)
56
+ recommender.recommendation_engine = "itemrec"
57
+ end
58
+
59
+ it "should call the api" do
60
+ client.should_receive(:identify).with("user-1")
61
+ client.should_receive(:get_itemrec_top_n)
62
+ .with("itemrec", 10, {})
63
+ .and_return([])
64
+ recommender.recommended_items(user, 10)
65
+ end
66
+
67
+ context "when items are found" do
68
+ before do
69
+ client.stub(:get_itemrec_top_n).and_return(["item-1", "item-2"])
70
+ end
71
+
72
+ it "should return an array of item ids" do
73
+ recommender.recommended_items(user, 10).should == ["item-1", "item-2"]
74
+ end
75
+ end
76
+
77
+ context "when no items are found" do
78
+ before do
79
+ client.stub(:get_itemrec_top_n).and_raise(PredictionIO::Client::ItemRecNotFoundError)
80
+ end
81
+
82
+ it "should return an empty array" do
83
+ recommender.recommended_items(user, 10).should == []
84
+ end
85
+ end
86
+ end # recommended_items
87
+
88
+ describe "#similar_items" do
89
+ before do
90
+ client.stub(:identify)
91
+ recommender.similarity_engine = "itemsim"
92
+ end
93
+
94
+ it "should call the api" do
95
+ client.should_receive(:get_itemsim_top_n)
96
+ .with("itemsim", "item-123", 10, {})
97
+ .and_return([])
98
+
99
+ recommender.similar_items(item, 10)
100
+ end
101
+
102
+ context "when items are found" do
103
+ before do
104
+ client.stub(:get_itemsim_top_n).and_return(["item-1", "item-2"])
105
+ end
106
+
107
+ it "should return an array of item ids" do
108
+ recommender.similar_items(item, 10).should == ["item-1", "item-2"]
109
+ end
110
+ end
111
+
112
+ context "when no items are found" do
113
+ before do
114
+ client.stub(:get_itemsim_top_n).and_raise(PredictionIO::Client::ItemSimNotFoundError)
115
+ end
116
+
117
+ it "should return an empty array" do
118
+ recommender.similar_items(item, 10).should == []
119
+ end
120
+ end
121
+ end # similar_items
122
+ end
@@ -0,0 +1,55 @@
1
+ require 'ostruct'
2
+
3
+ describe Predictable::User do
4
+ let(:recommender) { Predictable::Recommender.new(double("Client").as_null_object) }
5
+
6
+ # TODO: Test with ActiveRecord::Base
7
+ class MyUser < OpenStruct
8
+ include Predictable::User
9
+ end
10
+
11
+ before do
12
+ MyUser.recommender = recommender
13
+ end
14
+
15
+ let(:user) { MyUser.new(:id => 1) }
16
+ let(:item) { double("Item", :pio_iid => 2) }
17
+ subject { user }
18
+
19
+ its(:pio_uid) { should == 1 }
20
+
21
+ describe "#record_action" do
22
+ it "should record an action with the recommender" do
23
+ recommender.should_receive(:record_action)
24
+ .with(user, :rate, item, :rating => 4)
25
+
26
+ user.record_action(:rate, item, :rating => 4)
27
+ end
28
+
29
+ it "should return nil" do
30
+ user.record_action(:rate, item).should be_nil
31
+ end
32
+ end
33
+
34
+ describe "#add_to_recommender" do
35
+ it "should create a user in the recommender" do
36
+ recommender.should_receive(:create_user).with(user, {})
37
+ user.add_to_recommender
38
+ end
39
+
40
+ it "should return nil" do
41
+ user.add_to_recommender.should be_nil
42
+ end
43
+ end
44
+
45
+ describe "#delete_from_recommender" do
46
+ it "should delete the user from the recommender" do
47
+ recommender.should_receive(:delete_user).with(user)
48
+ user.delete_from_recommender
49
+ end
50
+
51
+ it "should return nil" do
52
+ user.delete_from_recommender.should be_nil
53
+ end
54
+ end
55
+ end
@@ -0,0 +1 @@
1
+ require 'predictable'
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: predictable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - JS Boulanger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: predictionio
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Ruby/Rails DSL for PredictionIO, an open source machine learning server.
84
+ email:
85
+ - jsboulanger@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/predictable.rb
97
+ - lib/predictable/item.rb
98
+ - lib/predictable/recommender.rb
99
+ - lib/predictable/user.rb
100
+ - lib/predictable/version.rb
101
+ - predictable.gemspec
102
+ - spec/predictable/item_spec.rb
103
+ - spec/predictable/recommender_spec.rb
104
+ - spec/predictable/user_spec.rb
105
+ - spec/spec_helper.rb
106
+ homepage: https://github.com/jsboulanger/predictable
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.1.11
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Ruby/Rails DSL for PredictionIO
130
+ test_files:
131
+ - spec/predictable/item_spec.rb
132
+ - spec/predictable/recommender_spec.rb
133
+ - spec/predictable/user_spec.rb
134
+ - spec/spec_helper.rb