gamifier 1.0.3

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 (41) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +60 -0
  5. data/Rakefile +2 -0
  6. data/examples/dev.rb +186 -0
  7. data/examples/preprod.rb +218 -0
  8. data/gamifier.gemspec +24 -0
  9. data/lib/gamifier/collection.rb +26 -0
  10. data/lib/gamifier/dsl/network.rb +33 -0
  11. data/lib/gamifier/dsl/site.rb +134 -0
  12. data/lib/gamifier/dsl.rb +24 -0
  13. data/lib/gamifier/engine.rb +84 -0
  14. data/lib/gamifier/errors.rb +6 -0
  15. data/lib/gamifier/model.rb +212 -0
  16. data/lib/gamifier/models/activity.rb +8 -0
  17. data/lib/gamifier/models/activity_definition.rb +6 -0
  18. data/lib/gamifier/models/group.rb +6 -0
  19. data/lib/gamifier/models/player.rb +20 -0
  20. data/lib/gamifier/models/reward.rb +6 -0
  21. data/lib/gamifier/models/reward_definition.rb +14 -0
  22. data/lib/gamifier/models/site.rb +6 -0
  23. data/lib/gamifier/models/track.rb +6 -0
  24. data/lib/gamifier/models/unit.rb +16 -0
  25. data/lib/gamifier/models/user.rb +6 -0
  26. data/lib/gamifier/version.rb +3 -0
  27. data/lib/gamifier.rb +76 -0
  28. data/spec/integration/dsl_integration_spec.rb +76 -0
  29. data/spec/integration/player_integration_spec.rb +41 -0
  30. data/spec/integration/user_integration_spec.rb +19 -0
  31. data/spec/spec_helper.rb +7 -0
  32. data/spec/spec_integration_helper.rb +51 -0
  33. data/spec/unit/collection_spec.rb +21 -0
  34. data/spec/unit/dsl/network_spec.rb +34 -0
  35. data/spec/unit/dsl/site_spec.rb +54 -0
  36. data/spec/unit/dsl_spec.rb +9 -0
  37. data/spec/unit/engine_spec.rb +135 -0
  38. data/spec/unit/gamifier_spec.rb +60 -0
  39. data/spec/unit/model_spec.rb +182 -0
  40. data/spec/unit/models/player_spec.rb +40 -0
  41. metadata +179 -0
@@ -0,0 +1,76 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe "DSL" do
4
+ before do
5
+ @network = Gamifier.dsl do
6
+ site 'mydomain' do
7
+ set :url, 'mydomain.com'
8
+
9
+ unit :karma do
10
+ set :label, 'Karma'
11
+ set :abbreviation, 'kar'
12
+ end
13
+
14
+ behavior 'new comment' do
15
+ set :description, "new comment behaviour"
16
+ set :selector, {
17
+ :verb => 'new_comment',
18
+ :category_id => 'hello'
19
+ }
20
+ set :adjustment, {
21
+ :points => 15, :karma => 10
22
+ }
23
+
24
+ set :enable_count_limiting, true
25
+ set :limit_per_player, 10
26
+ set :limit_field_scope, 'category_id'
27
+
28
+ set :enable_rate_limiting, true
29
+ set :bucket_max_capacity, 2
30
+ set :bucket_drain_rate, bucket_max_capacity/1.5
31
+
32
+ set :hide_in_widgets, false
33
+ set :icon, "http://crohr.me/images/cyrilrohr.jpg"
34
+ end
35
+
36
+
37
+ r1 = reward 'top commenter' do
38
+ set :reward_template, {
39
+ :message => "Congrats!"
40
+ }
41
+ set :components, [
42
+ {:command => "count", :comparator => 1, :where => {:verb => 'new_comment', :player_id => "%player_id"}},
43
+ # the first player who performs at least twice the 'new_question' behaviour
44
+ {:command => "count", :comparator => {"$gt" => 2}, :where => {:verb => 'new_question', :site_id => "%site_id"}}
45
+ ]
46
+ set :adjustment, {
47
+ :points => 5
48
+ }
49
+ set :active, true
50
+ set :allow_duplicates, false
51
+ end
52
+
53
+ m1 = mission 'community member' do
54
+ # allow a player to complete this mission more than once?
55
+ set :repeatable, false
56
+ set :active, true
57
+ # give points and reward upon completing mission? (This is required for track progression)
58
+ set :enable_rewards, true
59
+ # points and secondary units to award when completing a mission
60
+ set :adjustment, {
61
+ :points => 10
62
+ }
63
+ set :reward_definition_ids do [r1._id] end
64
+ end
65
+
66
+ track 'commenting' do
67
+ set :group_definition_ids do [m1._id] end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ it "should work twice" do
73
+ expect{ @network.save! }.to_not raise_error
74
+ expect{ @network.save! }.to_not raise_error
75
+ end
76
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe "managing players" do
4
+ before :all do
5
+ destroy_test_player!
6
+ end
7
+
8
+ it "should list all players" do
9
+ players = ENGINE.players.all
10
+ players.all?{|p| p.kind_of?(Gamifier::Player)}.should be_true
11
+ end
12
+
13
+ it "should create a new player" do
14
+ find_or_create_test_user
15
+ player = ENGINE.players.build(player_attributes)
16
+ player.save
17
+ ENGINE.players.find(player._id).should_not be_nil
18
+ end
19
+
20
+ describe "with an existing player" do
21
+ before do
22
+ @player = find_or_create_test_player
23
+ end
24
+
25
+ it "should credit an activity for the player" do
26
+ activity = @player.credit("visit", :contents_attributes => [{:content_url => "http://somwhere.ltd", :content_type => "page", :title => "Test1"}])
27
+ activity.should_not be_false
28
+ activity.contents.first["title"].should == "Test1"
29
+ activity.rewards.should be_empty
30
+ activity.points.should == 1
31
+ end
32
+
33
+ it "should update the picture_url" do
34
+ url = "http://s3.amazonaws.com/crohr.dimelo/identity_avatars/2ffbf54be4af49e7/avatar_normal.png?f59510d"
35
+ @player.picture_url = url
36
+ @player.save.should be_true
37
+ ENGINE.players.find(@player._id).picture_url.should == url
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe "managing users" do
4
+
5
+ before :all do
6
+ user = ENGINE.users.find(user_attributes[:email])
7
+ if user
8
+ puts "Found user #{user}. Deleting..."
9
+ user.destroy.should be_true
10
+ end
11
+ end
12
+
13
+ it "should create a new user" do
14
+ user = ENGINE.users.build(user_attributes)
15
+ user.should be_new
16
+ user.save.should be_true
17
+ user.should_not be_new
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec'
2
+ require 'webmock/rspec'
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
5
+ require 'gamifier'
6
+
7
+ WebMock.disable_net_connect!
@@ -0,0 +1,51 @@
1
+ require 'rspec'
2
+ require 'webmock/rspec'
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
5
+ require 'gamifier'
6
+
7
+ WebMock.allow_net_connect!
8
+
9
+ RSpec.configure do |config|
10
+
11
+ config.before :all do
12
+ Gamifier.set :uri, ENV.fetch("BADGEVILLE_URI") { "http://sandbox.v2.badgeville.com/api/berlin/" }
13
+ Gamifier.set :key, ENV.fetch("BADGEVILLE_KEY") { "1234" }
14
+ ENGINE = Gamifier.engine
15
+ Gamifier.logger.level = Logger::DEBUG
16
+ end
17
+
18
+ def player_attributes
19
+ {:email => "1234@test.com", :site => "ibadgedyou.dev", :display_name => "John Doe"}
20
+ end
21
+
22
+ def user_attributes
23
+ {:email => "1234@test.com"}
24
+ end
25
+
26
+ def find_or_create_test_user
27
+ user = ENGINE.users.find(user_attributes[:email]) || ENGINE.users.build(user_attributes).save
28
+ user.should_not be_nil
29
+ end
30
+
31
+ def destroy_test_player!
32
+ player = ENGINE.players.find_by_site_and_email(player_attributes[:site], player_attributes[:email])
33
+ if player
34
+ puts "Found player #{player}. Deleting..."
35
+ player.destroy.should be_true
36
+ end
37
+ end
38
+
39
+ def find_or_create_test_player
40
+ find_or_create_test_user
41
+ player = ENGINE.players.find_by_site_and_email(player_attributes[:site], player_attributes[:email])
42
+ if player.nil?
43
+ player = ENGINE.players.build(player_attributes).save
44
+ player.should_not be_false
45
+ player
46
+ else
47
+ player
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier::Collection do
4
+ before do
5
+ @engine = mock(Gamifier::Engine)
6
+ @collection = Gamifier::Collection.new(@engine, Gamifier::Model)
7
+ end
8
+
9
+ it "should have included the FinderMethods of the model" do
10
+ Gamifier::Model::FinderMethods.public_instance_methods.each do |method|
11
+ @collection.should respond_to(method)
12
+ end
13
+ end
14
+
15
+ it "should correctly build a new object with a correct reference to the current engine" do
16
+ object = @collection.build(:key => "value")
17
+ object.should be_a(Gamifier::Model)
18
+ object.key.should == "value"
19
+ end
20
+
21
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier::DSL::Network do
4
+ before do
5
+ Gamifier.set :uri => "http://somewhere.ltd/path/", :key => "1234"
6
+ @network = Gamifier::DSL::Network.new
7
+ end
8
+ it "should be linked to the gamifier engine" do
9
+ @network.engine.should == Gamifier.engine
10
+ end
11
+ it "should have an empty list of sites" do
12
+ @network.sites.should be_empty
13
+ end
14
+
15
+ describe "creating sites" do
16
+ it "should add a new site to its list when calling #site" do
17
+ expect {
18
+ @network.site 'site-name'
19
+ }.to change{ @network.sites.count }.by(1)
20
+ end
21
+
22
+ it "creates a site with the given name" do
23
+ new_site = @network.site 'site-name'
24
+ new_site.name.should == 'site-name'
25
+ end
26
+
27
+ it "should call eval_with_context" do
28
+ block = proc{}
29
+ Gamifier::DSL.should_receive(:eval_with_context).with(instance_of(Gamifier::DSL::Site), &block)
30
+ @network.site 'site-name', &block
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier::DSL::Site do
4
+ before do
5
+ @site = Gamifier::DSL::Site.new(:name => "site-name")
6
+ end
7
+
8
+ describe "creating units" do
9
+
10
+ it "adds a unit to the site" do
11
+ expect{ @site.unit 'unit-name' }.to change{ @site.units.count }.by(1)
12
+ end
13
+ end
14
+
15
+ describe "creating behaviours" do
16
+
17
+ it "adds a behavior to the site" do
18
+ expect{ @site.behavior 'behavior-name' }.to change{ @site.behaviors.count }.by(1)
19
+ end
20
+
21
+ it "should call eval_with_context" do
22
+ block = proc{}
23
+ Gamifier::DSL.should_receive(:eval_with_context).with(instance_of(Gamifier::ActivityDefinition), &block)
24
+ @site.behavior 'behavior-name', &block
25
+ end
26
+
27
+ it "should work" do
28
+ new_behavior = @site.behavior 'behavior-name' do
29
+ set :key, 'value'
30
+ set :enable, true
31
+ end
32
+ new_behavior.key.should == 'value'
33
+ new_behavior.enable.should == true
34
+ end
35
+ end
36
+
37
+ describe "creating rewards" do
38
+ it "adds a reward to the site" do
39
+ expect{ @site.reward 'reward-name' }.to change{ @site.rewards.count }.by(1)
40
+ end
41
+ end
42
+
43
+ describe "creating missions" do
44
+ it "adds a mission to the site" do
45
+ expect{ @site.mission 'mission-name' }.to change{ @site.missions.count }.by(1)
46
+ end
47
+ end
48
+
49
+ describe "creating tracks" do
50
+ it "adds a track to the site" do
51
+ expect{ @site.track 'track-name' }.to change{ @site.tracks.count }.by(1)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier::DSL do
4
+
5
+ class Whatever
6
+ include Gamifier::DSL
7
+ end
8
+
9
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier::Engine do
4
+
5
+ def site
6
+ {
7
+ "name" => "ibadgedyou",
8
+ "created_at" => "2012-05-14T01:50:49-07:00",
9
+ "url" => "ibadgedyou.dev",
10
+ "id" => "4fb0c76949f83818c20000eb",
11
+ "_id" => "4fb0c76949f83818c20000eb"
12
+ }
13
+ end
14
+
15
+ before do
16
+ @logger = Logger.new(STDERR)
17
+ Gamifier.stub!(:config) {
18
+ {:uri => "http://somewhere.ltd/path/", :key => "1234"}
19
+ }
20
+ Gamifier.stub!(:logger).and_return @logger
21
+
22
+ @engine = Gamifier::Engine.new
23
+ end
24
+
25
+ describe "#uri_to" do
26
+ it "should correctly concatenate URIs" do
27
+ @engine.uri_to.to_s.should == "http://somewhere.ltd/path/1234/"
28
+ @engine.uri_to("resource").to_s.should == "http://somewhere.ltd/path/1234/resource"
29
+ @engine.uri_to("/resource").to_s.should == "http://somewhere.ltd/resource"
30
+ @engine.uri_to("../resource").to_s.should == "http://somewhere.ltd/path/resource"
31
+ end
32
+ end
33
+
34
+ describe "configuration" do
35
+ before do
36
+ @engine = Gamifier::Engine.new(:key => "new-key")
37
+ end
38
+ it "should overwrite the global configuration with the given parameters at intialization time" do
39
+ @engine.config.should == {:uri => "http://somewhere.ltd/path/", :key => "new-key"}
40
+ Gamifier.config.should == {:uri => "http://somewhere.ltd/path/", :key => "1234"}
41
+ end
42
+
43
+ describe "#ok?" do
44
+ it "should be true" do
45
+ @engine.should be_ok
46
+ end
47
+
48
+ it "should be false if the key is missing" do
49
+ @engine.config[:key] = ""
50
+ @engine.should_not be_ok
51
+ end
52
+
53
+ it "should be false if the uri is missing" do
54
+ @engine.config[:uri] = ""
55
+ @engine.should_not be_ok
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ it "should have initialized a connection" do
62
+ @engine.connection.should be_a(Object)
63
+ end
64
+
65
+ describe "#transmit" do
66
+ it "transmits POST requests to the API" do
67
+ stub_request(:post, "http://somewhere.ltd/path/1234/resource.json").
68
+ with(:body => Gamifier::Engine.encode_www_form({:a => "b", :c => ["d", "e"]}), :headers => { 'Content-Type' => 'application/x-www-form-urlencoded' })
69
+ @engine.transmit :post, "resource", :body => {"a" => "b", :c => ["d", "e"]}
70
+ end
71
+ it "transmits GET requests to the API" do
72
+ stub_request(:get, "http://somewhere.ltd/path/1234/resource.json").
73
+ with(:query => {"limit" => "5"})
74
+ @engine.transmit :get, "resource", :query => {"limit" => "5"}
75
+ end
76
+ it "should return true if the response is 204" do
77
+ stub_request(:get, "http://somewhere.ltd/resource.json").
78
+ to_return(:status => 204)
79
+ @engine.transmit(:get, "/resource").should be_true
80
+ end
81
+ it "should return the parsed response if the status is in the 2xx range (except 204)" do
82
+ stub_request(:get, "http://somewhere.ltd/resource.json").
83
+ to_return(:body => JSON.dump(site), :headers => {'Content-Type' => 'application/json;charset=utf-8'})
84
+ @engine.transmit(:get, "/resource").should == site
85
+ end
86
+ [:get, :head].each do |method|
87
+ it "should return nil if the response status is 422 (Unprocessable Entity) and method is #{method}" do
88
+ stub_request(method, "http://somewhere.ltd/resource.json").
89
+ to_return(:status => 422)
90
+ @engine.transmit(method, "/resource").should be_nil
91
+ end
92
+ end
93
+ [:delete, :post, :put].each do |method|
94
+ it "should return false if the response status is 422 (Unprocessable Entity) and method is #{method}" do
95
+ stub_request(method, "http://somewhere.ltd/resource.json").
96
+ to_return(:status => 422)
97
+ @engine.transmit(method, "/resource").should be_false
98
+ end
99
+ end
100
+ it "should return nil if the response status is 404" do
101
+ stub_request(:get, "http://somewhere.ltd/resource.json").
102
+ to_return(:status => 404)
103
+ @engine.transmit(:get, "/resource").should be_nil
104
+ end
105
+ it "should raise an HTTPServerError for a 5xx code" do
106
+ stub_request(:get, "http://somewhere.ltd/resource.json").
107
+ to_return(:status => 500)
108
+ expect {
109
+ @engine.transmit(:get, "/resource")
110
+ }.to raise_error Gamifier::HTTPServerError
111
+ end
112
+ it "should raise an HTTPClientError for a 4xx code" do
113
+ stub_request(:get, "http://somewhere.ltd/resource.json").
114
+ to_return(:status => 400)
115
+ expect {
116
+ @engine.transmit(:get, "/resource")
117
+ }.to raise_error Gamifier::HTTPClientError
118
+ end
119
+ end
120
+
121
+ describe "collections" do
122
+ %w{activities activity_definitions groups players rewards reward_definitions sites units users}.each do |collection|
123
+ it "should respond ##{collection}" do
124
+ @engine.should respond_to(collection.to_sym)
125
+ end
126
+ end
127
+ it "should instantiate a new collection object with the correct model" do
128
+ collection = @engine.players
129
+ collection.should be_a(Gamifier::Collection)
130
+ collection.engine.should == @engine
131
+ collection.model.should == Gamifier::Player
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamifier do
4
+
5
+ before do
6
+ Gamifier.reset!
7
+ end
8
+
9
+ it "should allow to set the API URI" do
10
+ Gamifier.set :uri, "http://somwhere.ltd"
11
+ Gamifier.config[:uri].should == "http://somwhere.ltd"
12
+ end
13
+
14
+ it "should allow to set the API KEY" do
15
+ Gamifier.set :key, "1234"
16
+ Gamifier.config[:key].should == "1234"
17
+ end
18
+
19
+ it "should allow to set multiple configuration options at once" do
20
+ Gamifier.set :uri => "http://somewhere.ltd", "key" => "1234"
21
+ Gamifier.config[:uri].should == "http://somewhere.ltd"
22
+ Gamifier.config[:key].should == "1234"
23
+ end
24
+
25
+ it "should allow to reset the state" do
26
+ Gamifier.set :key, "http://somewhere.ltd"
27
+ Gamifier.reset!
28
+ Gamifier.config.should be_empty
29
+ end
30
+
31
+ it "should accept a logger object" do
32
+ logger = mock("logger")
33
+ Gamifier.logger = logger
34
+ Gamifier.logger.should == logger
35
+ end
36
+
37
+ describe "with proper configuration" do
38
+ before do
39
+ Gamifier.set :uri => "http://somewhere.ltd/path/", :key => "1234"
40
+ end
41
+
42
+ it "should return a singleton object when calling Gamifier#engine" do
43
+ engine = Gamifier.engine
44
+ engine.should be_a(Object)
45
+ Gamifier.engine.should be_equal(engine)
46
+ end
47
+
48
+ it "should return an instance of Gamifier::Engine" do
49
+ Gamifier.engine.should be_instance_of(Gamifier::Engine)
50
+ end
51
+
52
+ it "should create a new engine object after reset" do
53
+ engine = Gamifier.engine
54
+ Gamifier.reset!
55
+ Gamifier.set :uri => "http://somewhere.ltd/path/", :key => "1234"
56
+ Gamifier.engine.should_not be_equal(engine)
57
+ end
58
+ end
59
+
60
+ end