riaction 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Overview #
2
2
 
3
- riaction provides both a ruby wrapper for IActionable's restful API and an "acts-as" style interface for a Rails application's ActiveRecord models to associate them with IActionable profiles and have them drive the logging of game events.
3
+ riaction provides both a ruby wrapper for IActionable's restful API and an "acts-as" style interface for the models in your Rails application. This document assumes knowledge of IActionable's API.
4
4
 
5
5
  # How To Use #
6
6
 
@@ -17,7 +17,7 @@ The wrapper for IActionable's API is used internally by the rest of the gem, but
17
17
  :version => 3 )
18
18
  @api = IActionable::Api.new
19
19
 
20
- IActionable's API speaks in JSON, and here those responses are wrapped in simple objects where nesting and variable names are determined by [IActionable's documentation](http://www.http://iactionable.com/api/). For example, here the wrapper is making a call to load a profile summary:
20
+ IActionable's API speaks in JSON, and here those responses are wrapped in simple objects where nesting and variable names are determined by [IActionable's documentation](http://www.iactionable.com/api/). For example, here the wrapper is making a call to load a profile summary:
21
21
 
22
22
  profile_summary = @api.get_profile_summary("user", "username", "zortnac", 10)
23
23
  profile_summary.display_name # => "Chris Eberz"
@@ -51,11 +51,11 @@ Models in your application may declare themselves as profiles that exist on IAct
51
51
  # id :integer(4)
52
52
  # nickname :string(255)
53
53
 
54
- Here, the class User declares itself as a profile of type "player", identifiable by two of IActionable's supported ID types, username and custom, the values of which are the fields (or any symbol that an instance of the class responds to) nickname and id, respectively. When a class declares itself as an riaction profile, an after_create callback will be added to register that model on IActionable as a profile as the type, and with the identifiers, described in the class.
54
+ Here, the class User declares itself as a profile of type "player", identifiable by two of IActionable's supported identifier types, username and custom. The values of these identifiers are the fields nickname and id, respectively, and can be any method that an instance of the class responds to. When a class declares itself as a riaction profile, an after_create callback will be added to create the profile on IActionable with the identifiers declared in the class.
55
55
 
56
56
  #### Profile Instance Methods ####
57
57
 
58
- Classes that declare themselves as IActionable profiles are given instance methods that tie in to the IActionable API, as many uses of the API take a profile as an argument.
58
+ Classes that declare themselves as IActionable profiles are given instance methods that tie in to the IActionable API, as many uses of the API treat the profile as a top-level resource.
59
59
 
60
60
  @api.get_profile_summary("player", "username", "zortnac", 10)
61
61
  # is equivalent to the following...
@@ -71,14 +71,14 @@ Classes that declare themselves as IActionable profiles are given instance metho
71
71
 
72
72
  ### Declaring Events ###
73
73
 
74
- Models in your application may declare any number of events that they log through IActionable. For each event that is declared the important elements are:
74
+ Models in your application may declare any number of events that they wish to log through IActionable. For each event that is declared the important elements are:
75
75
 
76
- 1. The event's name (or key)
77
- 2. The type of trigger that causes the event to be logged
78
- 3. The profile under which the event is logged
79
- 4. Any optional parameters (key-value pairs) that you want to pass
76
+ * The event's name (or key).
77
+ * The type of trigger that causes the event to be logged.
78
+ * The profile under which the event is logged.
79
+ * Any optional parameters (key-value pairs) that you want to pass.
80
80
 
81
- ` `
81
+ <!-- end list -->
82
82
 
83
83
  class Comment
84
84
  belongs_to :user
@@ -99,17 +99,17 @@ Here, the name of the event is `make_a_comment`. The trigger for the event, in
99
99
 
100
100
  _Note: If the trigger is one of :create, :update, or :destroy, then the appropriate ActiveRecord callback will log the event. If the trigger is anything else, then an instance method is provided to log the event by hand. For example, an argument of `:trigger => :foo` will provide an instance method `trigger_foo!`_
101
101
 
102
- The profile that this event will be logged under can be any object whose class declares itself as a profile. Here, the profile is the object returned by the ActiveRecord association `:user`, and we assume is an instance of the User class from above. Lastly, the optional params passed along with the event is the key-value pair `{:post => :post_id}`, where `:post_id` is an ActiveRecord table column.
102
+ The profile that this event will be logged under can be any object whose class declares itself as a profile. Here, the profile is the object returned by the ActiveRecord association `:user` (for this example we assume this is an instance of the User class from above). Lastly, the optional params passed along with the event is the key-value pair `{:post => :post_id}`, where `:post_id` is an ActiveRecord table column.
103
103
 
104
104
  Putting this all together, whenever an instance of the Comment class is created, an event is logged for which the equivalent call to the API might look like this:
105
105
 
106
106
  @api.log_event("player", "username", "zortnac", "make_a_comment", {:post => 33})
107
107
 
108
- _Note: If a class both declares itself as a profile and declares one or more events, and wants to refer to itself as the profile for any of those events, use `:profile => :self`_
108
+ _Note: If a class declares itself as a profile and also declares one or more events, but wants to refer to itself as the profile for any of those events, use `:profile => :self` in the event's declaration_
109
109
 
110
110
  ### Rails Rake Tasks ###
111
111
 
112
- There are 3 rakes tasks included for summarizing all of your models' declarations as well as a way to initialize profiles on IActionable. To see a report of all the events declared across your application, run the following:
112
+ There are 3 rake tasks included for summarizing all of your models' declarations as well as a way to initialize profiles on IActionable. To see a report of all the events declared across your application, run the following:
113
113
 
114
114
  rake riaction:rails:list:events
115
115
 
@@ -19,10 +19,14 @@ module Riaction
19
19
  Set.new [:create, :update, :destroy]
20
20
  end
21
21
 
22
+ def self.retry_attempts_for_internal_error
23
+ 3
24
+ end
25
+
22
26
  class ProfileCreator
23
27
  @queue = :riaction_profile_creator
24
28
 
25
- def self.perform(klass_name, id)
29
+ def self.perform(klass_name, id, attempt=0)
26
30
  if klass_name.constantize.riaction_profile?
27
31
  iactionable_api = IActionable::Api.new
28
32
  profile_object = klass_name.constantize.find_by_id!(id)
@@ -36,13 +40,21 @@ module Riaction
36
40
  rescue IActionable::Error::BadRequest => e
37
41
  # This should only be thrown if the profile type names specified in the model don't match what's on IActionable's dashboard
38
42
  raise e
43
+ rescue IActionable::Error::Internal => e
44
+ # upon an intenal error from IActionable, retry some set number of times by requeueing the task through Resque
45
+ # after the max number of attempts, re-raise
46
+ if attempt < Riaction.retry_attempts_for_internal_error
47
+ Resque.enqueue(Riaction::ProfileCreator, klass_name, id, attempt+1)
48
+ else
49
+ raise e
50
+ end
39
51
  end
40
52
  end
41
53
 
42
54
  class EventPerformer
43
55
  @queue = :riaction_event_logger
44
56
 
45
- def self.perform(event_name, klass_name, id)
57
+ def self.perform(event_name, klass_name, id, attempt=0)
46
58
  iactionable_api = IActionable::Api.new
47
59
 
48
60
  event_object = klass_name.constantize.find_by_id!(id)
@@ -66,6 +78,14 @@ module Riaction
66
78
  # Log event should never throw this as of IActionable API v3
67
79
  rescue NoMethodError => e
68
80
  raise NoEventDefined.new
81
+ rescue IActionable::Error::Internal => e
82
+ # upon an intenal error from IActionable, retry some set number of times by requeueing the task through Resque
83
+ # after the max number of attempts, re-raise
84
+ if attempt < Riaction.retry_attempts_for_internal_error
85
+ Resque.enqueue(Riaction::EventPerformer, event_name, klass_name, id, attempt+1)
86
+ else
87
+ raise e
88
+ end
69
89
  end
70
90
  end
71
91
 
@@ -1,3 +1,3 @@
1
1
  module Riaction
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -86,6 +86,24 @@ describe Riaction do
86
86
  lambda { Riaction::EventPerformer.perform(:create_profile, "BadClass", @bad_instance.id) }.should raise_error(Riaction::NoEventDefined)
87
87
  end
88
88
  end
89
+
90
+ describe "when the API raises an IActionable internal error" do
91
+ before do
92
+ @api.stub!(:get_profile_summary)
93
+ @api.stub!(:create_profile)
94
+ @api.stub!(:log_event).and_raise(IActionable::Error::Internal.new(nil))
95
+ Resque.stub!(:enqueue)
96
+ end
97
+
98
+ it "should re-schedule the task some defined number of times before re-raising again on the last attempt" do
99
+ Resque.should_receive(:enqueue).exactly(Riaction.retry_attempts_for_internal_error).times.with(Riaction::EventPerformer, :create_profile, "MyClass", @instance.id, instance_of(Fixnum))
100
+
101
+ (Riaction.retry_attempts_for_internal_error).times do |i|
102
+ lambda { Riaction::EventPerformer.perform(:create_profile, "MyClass", @bad_instance.id, i) }.should_not raise_error
103
+ end
104
+ lambda { Riaction::EventPerformer.perform(:create_profile, "MyClass", @bad_instance.id, Riaction.retry_attempts_for_internal_error) }.should raise_error(IActionable::Error::Internal)
105
+ end
106
+ end
89
107
  end
90
108
 
91
109
  describe "profile generation" do
@@ -119,6 +137,24 @@ describe Riaction do
119
137
  lambda { Riaction::ProfileCreator.perform("BadClass", @instance.id) }.should raise_error(Riaction::NoProfileDefined)
120
138
  end
121
139
  end
140
+
141
+ describe "when the API raises an IActionable internal error" do
142
+ before do
143
+ @instance = MyClass.new
144
+ MyClass.stub!(:find_by_id!).and_return(@instance)
145
+ @api.stub!(:create_profile).and_raise(IActionable::Error::Internal.new(nil))
146
+ Resque.stub!(:enqueue)
147
+ end
148
+
149
+ it "should re-schedule the task some defined number of times before re-raising again on the last attempt" do
150
+ Resque.should_receive(:enqueue).exactly(Riaction.retry_attempts_for_internal_error).times.with(Riaction::ProfileCreator, "MyClass", @instance.id, instance_of(Fixnum))
151
+
152
+ (Riaction.retry_attempts_for_internal_error).times do |i|
153
+ lambda { Riaction::ProfileCreator.perform("MyClass", @instance.id, i) }.should_not raise_error
154
+ end
155
+ lambda { Riaction::ProfileCreator.perform("MyClass", @instance.id, Riaction.retry_attempts_for_internal_error) }.should raise_error(IActionable::Error::Internal)
156
+ end
157
+ end
122
158
  end
123
159
 
124
160
  describe "event triggering" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riaction
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-02 00:00:00.000000000Z
12
+ date: 2011-11-11 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2153398980 !ruby/object:Gem::Requirement
16
+ requirement: &2156451960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.6'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2153398980
24
+ version_requirements: *2156451960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2153398280 !ruby/object:Gem::Requirement
27
+ requirement: &2156451540 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2153398280
35
+ version_requirements: *2156451540
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: faraday
38
- requirement: &2153397820 !ruby/object:Gem::Requirement
38
+ requirement: &2156447860 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2153397820
46
+ version_requirements: *2156447860
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: faraday-stack
49
- requirement: &2153397260 !ruby/object:Gem::Requirement
49
+ requirement: &2156447440 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *2153397260
57
+ version_requirements: *2156447440
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: activemodel
60
- requirement: &2153396640 !ruby/object:Gem::Requirement
60
+ requirement: &2156447000 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *2153396640
68
+ version_requirements: *2156447000
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: activerecord
71
- requirement: &2153386700 !ruby/object:Gem::Requirement
71
+ requirement: &2156446380 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *2153386700
79
+ version_requirements: *2156446380
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: resque
82
- requirement: &2153386200 !ruby/object:Gem::Requirement
82
+ requirement: &2156445720 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *2153386200
90
+ version_requirements: *2156445720
91
91
  description: Wrapper for IActionable's restful API and an "acts-as" style interface
92
92
  for models to behave as profiles and drive game events.
93
93
  email: