riaction 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +59 -0
  3. data/README +110 -0
  4. data/Rakefile +1 -0
  5. data/lib/riaction.rb +3 -0
  6. data/lib/riaction/iactionable/api.rb +209 -0
  7. data/lib/riaction/iactionable/connection.rb +114 -0
  8. data/lib/riaction/iactionable/error.rb +17 -0
  9. data/lib/riaction/iactionable/objects.rb +6 -0
  10. data/lib/riaction/iactionable/objects/achievement.rb +19 -0
  11. data/lib/riaction/iactionable/objects/awardable.rb +44 -0
  12. data/lib/riaction/iactionable/objects/challenge.rb +18 -0
  13. data/lib/riaction/iactionable/objects/goal.rb +19 -0
  14. data/lib/riaction/iactionable/objects/i_actionable_object.rb +40 -0
  15. data/lib/riaction/iactionable/objects/identifier.rb +11 -0
  16. data/lib/riaction/iactionable/objects/leaderboard.rb +10 -0
  17. data/lib/riaction/iactionable/objects/leaderboard_report.rb +23 -0
  18. data/lib/riaction/iactionable/objects/level.rb +18 -0
  19. data/lib/riaction/iactionable/objects/level_type.rb +10 -0
  20. data/lib/riaction/iactionable/objects/point_type.rb +10 -0
  21. data/lib/riaction/iactionable/objects/profile_level.rb +16 -0
  22. data/lib/riaction/iactionable/objects/profile_points.rb +21 -0
  23. data/lib/riaction/iactionable/objects/profile_summary.rb +24 -0
  24. data/lib/riaction/iactionable/objects/progress.rb +37 -0
  25. data/lib/riaction/iactionable/settings.rb +30 -0
  26. data/lib/riaction/riaction.rb +368 -0
  27. data/lib/riaction/version.rb +3 -0
  28. data/lib/tasks/riaction.rake +101 -0
  29. data/riaction.gemspec +32 -0
  30. data/spec/api_spec.rb +288 -0
  31. data/spec/connection_spec.rb +111 -0
  32. data/spec/riaction_spec.rb +253 -0
  33. data/spec/settings_spec.rb +52 -0
  34. data/spec/spec_helper.rb +1 -0
  35. metadata +153 -0
@@ -0,0 +1,17 @@
1
+ module IActionable
2
+ module Error
3
+ class Error < StandardError
4
+ attr_accessor :response
5
+
6
+ def initialize(response)
7
+ @response = response
8
+ super
9
+ end
10
+ end
11
+
12
+ # http://iactionable.com/api/response-codes/
13
+ class BadRequest < Error; end
14
+ class Unauthorized < Error; end
15
+ class Internal < Error; end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ module IActionable
2
+ module Objects
3
+ end
4
+ end
5
+
6
+ Dir.glob(File.dirname(__FILE__) + '/objects/*') {|file| require file}
@@ -0,0 +1,19 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Achievement < IActionableObject
6
+ attr_accessor :key
7
+ attr_accessor :description
8
+ attr_accessor :image_url
9
+ attr_accessor :name
10
+
11
+ def initialize(key_values={})
12
+ initialize_awardable(key_values)
13
+ super(key_values)
14
+ end
15
+
16
+ awardable
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ require 'riaction/iactionable/objects/progress.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ module Awardable
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def awardable
12
+ attr_accessor :award_date
13
+ attr_accessor :progress
14
+ include IActionable::Objects::Awardable::InstanceMethods
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def initialize_awardable(key_values={})
20
+ @progress = extract_many_as(key_values, "Progress", IActionable::Objects::Progress)
21
+ # convert the miliseconds within the date string to seconds (per ruby)
22
+ # "/Date(1275706032317-0600)/" => "1275706032-0600"
23
+ @award_date = key_values.delete("AwardDate")
24
+ @award_date = IActionableObject.timestamp_to_seconds(@award_date) unless @award_date.blank?
25
+ end
26
+
27
+ def complete?
28
+ @progress.any? && @progress.all?
29
+ end
30
+
31
+ def percent_complete
32
+ Integer(Float(@progress.select{|p| p.complete?}.size) / @progress.size * 100)
33
+ rescue TypeError => e
34
+ 0
35
+ end
36
+
37
+ def awarded_on
38
+ # bug in ruby 1.9.2 where Time.strptime does not support seconds-since-epoch format, but Date.strptime does, so we'll use that for now
39
+ Date.strptime(@award_date, "%s%z").to_time unless @award_date.blank?
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Challenge < IActionableObject
6
+ awardable
7
+
8
+ attr_accessor :key
9
+ attr_accessor :description
10
+ attr_accessor :name
11
+
12
+ def initialize(key_values={})
13
+ initialize_awardable(key_values)
14
+ super(key_values)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Goal < IActionableObject
6
+ awardable
7
+
8
+ attr_accessor :key
9
+ attr_accessor :description
10
+ attr_accessor :name
11
+ attr_accessor :interval
12
+
13
+ def initialize(key_values={})
14
+ initialize_awardable(key_values)
15
+ super(key_values)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module IActionable
2
+ module Objects
3
+ class IActionableObject
4
+ def initialize(key_values={})
5
+ key_values.each_pair do |key, value|
6
+ instance_variable_set "@#{key.camelize.underscore}".to_sym, value
7
+ end
8
+ end
9
+
10
+ def self.timestamp_regexp
11
+ /\/Date\((\d+)((\-|\+)\d{4})\)\//
12
+ end
13
+
14
+ def self.timestamp_to_seconds(timestamp)
15
+ milliseconds = timestamp[timestamp_regexp, 1]
16
+ tz = timestamp[timestamp_regexp, 2]
17
+ "#{Integer(milliseconds)/1000}#{tz}"
18
+ end
19
+
20
+ private
21
+
22
+ def extract_many_as(key_values, field, klass)
23
+ if key_values.fetch(field).respond_to?(:inject)
24
+ loaded = []
25
+ content = key_values.delete(field)
26
+ content.each do |c|
27
+ loaded << klass.new(c)
28
+ end
29
+ loaded
30
+ else
31
+ nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ require "riaction/iactionable/objects/awardable.rb"
39
+
40
+ IActionable::Objects::IActionableObject.send(:include, IActionable::Objects::Awardable)
@@ -0,0 +1,11 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Identifier < IActionableObject
6
+ attr_accessor :id
7
+ attr_accessor :id_hash
8
+ attr_accessor :id_type
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Leaderboard < IActionableObject
6
+ attr_accessor :key
7
+ attr_accessor :name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+ require 'riaction/iactionable/objects/leaderboard.rb'
3
+
4
+
5
+ module IActionable
6
+ module Objects
7
+ class LeaderboardReport < IActionableObject
8
+ attr_accessor :page_count
9
+ attr_accessor :page_number
10
+ attr_accessor :total_count
11
+ attr_accessor :leaderboard
12
+ attr_accessor :point_type
13
+ attr_accessor :profiles
14
+
15
+ def initialize(key_values={})
16
+ @leaderboard = IActionable::Objects::PointType.new(key_values.delete("Leaderboard"))
17
+ @point_type = IActionable::Objects::PointType.new(key_values.delete("PointType"))
18
+ @profiles = extract_many_as(key_values, "Profiles", IActionable::Objects::ProfileSummary)
19
+ super(key_values)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+ require 'riaction/iactionable/objects/level_type.rb'
3
+
4
+ module IActionable
5
+ module Objects
6
+ class Level < IActionableObject
7
+ attr_accessor :name
8
+ attr_accessor :number
9
+ attr_accessor :required_points
10
+ attr_accessor :level_type
11
+
12
+ def initialize(key_values={})
13
+ @level_type = IActionable::Objects::LevelType.new(key_values.delete("LevelType"))
14
+ super(key_values)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class LevelType < IActionableObject
6
+ attr_accessor :key
7
+ attr_accessor :name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class PointType < IActionableObject
6
+ attr_accessor :key
7
+ attr_accessor :name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+ require 'riaction/iactionable/objects/level.rb'
3
+
4
+ module IActionable
5
+ module Objects
6
+ class ProfileLevel < IActionableObject
7
+ attr_accessor :current
8
+ attr_accessor :next
9
+
10
+ def initialize(key_values={})
11
+ @current = IActionable::Objects::Level.new(key_values.delete("Current")) unless key_values["Current"].blank?
12
+ @next = IActionable::Objects::Level.new(key_values.delete("Next")) unless key_values["Next"].blank?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+ require 'riaction/iactionable/objects/profile_level.rb'
3
+ require 'riaction/iactionable/objects/point_type.rb'
4
+
5
+ module IActionable
6
+ module Objects
7
+ class ProfilePoints < IActionableObject
8
+ attr_accessor :level # not always present
9
+ attr_accessor :point_type
10
+ attr_accessor :points
11
+ attr_accessor :reason # not always present
12
+
13
+ def initialize(key_values={})
14
+ levels = key_values.delete("Level")
15
+ @level = IActionable::Objects::ProfileLevel.new(levels) unless levels.nil?
16
+ @point_type = IActionable::Objects::PointType.new(key_values.delete("PointType"))
17
+ super(key_values)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+ require 'riaction/iactionable/objects/identifier.rb'
3
+ require 'riaction/iactionable/objects/profile_points.rb'
4
+ require 'riaction/iactionable/objects/achievement.rb'
5
+
6
+ module IActionable
7
+ module Objects
8
+ class ProfileSummary < IActionableObject
9
+ attr_accessor :display_name
10
+ attr_accessor :identifiers
11
+ attr_accessor :points
12
+ attr_accessor :recent_achievements
13
+ attr_accessor :rank
14
+
15
+ def initialize(key_values={})
16
+ @identifiers = extract_many_as(key_values, "Identifiers", IActionable::Objects::Identifier)
17
+ @points = extract_many_as(key_values, "Points", IActionable::Objects::ProfilePoints)
18
+ @recent_achievements = extract_many_as(key_values, "RecentAchievements", IActionable::Objects::Achievement)
19
+
20
+ super(key_values)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'riaction/iactionable/objects/i_actionable_object.rb'
2
+
3
+ module IActionable
4
+ module Objects
5
+ class Progress < IActionableObject
6
+ attr_accessor :description
7
+ attr_accessor :condition_met_date
8
+ attr_accessor :current_value
9
+ attr_accessor :required_value
10
+ attr :complete
11
+
12
+ def initialize(key_values={})
13
+ super(key_values)
14
+ # convert the miliseconds within the date string to seconds (per ruby)
15
+ # "/Date(1275706032317-0600)/" => "1275706032-0600"
16
+ @condition_met_date = IActionableObject.timestamp_to_seconds(@condition_met_date) unless @condition_met_date.blank?
17
+ end
18
+
19
+ def complete?
20
+ Integer(@current_value) >= Integer(@required_value)
21
+ rescue TypeError => e
22
+ false
23
+ end
24
+
25
+ def percent_complete
26
+ Integer(Float(@current_value) / Integer(@required_value) * 100)
27
+ rescue TypeError => e
28
+ 0
29
+ end
30
+
31
+ def condition_met_date
32
+ # bug in ruby 1.9.2 where Time.strptime does not support seconds-since-epoch format, but Date.strptime does, so we'll use that for now
33
+ Date.strptime(@condition_met_date, "%s%z").to_time unless @condition_met_date.blank?
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ module IActionable
2
+ class ConfigError < StandardError
3
+ end
4
+
5
+ class Settings
6
+ attr :settings
7
+
8
+ def initialize(values)
9
+ @settings = {
10
+ :app_key => values.fetch(:app_key),
11
+ :api_key => values.fetch(:api_key),
12
+ :version => values.fetch(:version)
13
+ }
14
+ rescue NoMethodError, KeyError => e
15
+ raise ConfigError.new("IAction::Settings being initialized with invalid arguments")
16
+ end
17
+
18
+ def app_key
19
+ @settings.fetch(:app_key)
20
+ end
21
+
22
+ def api_key
23
+ @settings.fetch(:api_key)
24
+ end
25
+
26
+ def version
27
+ @settings.fetch(:version)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,368 @@
1
+ require 'riaction/iactionable/api.rb'
2
+
3
+ require "active_support"
4
+ require "active_record"
5
+ require "resque"
6
+
7
+ module Riaction
8
+ PROFILE_CLASSES = []
9
+ EVENT_LOGGING_CLASSES = []
10
+
11
+ class NoEventDefined < StandardError; end
12
+ class NoProfileDefined < StandardError; end
13
+
14
+ def self.included(base)
15
+ base.extend(ClassMethods)
16
+ end
17
+
18
+ def self.crud_actions
19
+ Set.new [:create, :update, :destroy]
20
+ end
21
+
22
+ class ProfileCreator
23
+ @queue = :riaction_profile_creator
24
+
25
+ def self.perform(klass_name, id)
26
+ if klass_name.constantize.riaction_profile?
27
+ iactionable_api = IActionable::Api.new
28
+ profile_object = klass_name.constantize.find_by_id!(id)
29
+ profile_keys = profile_object.riaction_profile_keys
30
+ iactionable_api.create_profile(profile_keys[:profile_type], profile_keys[:id_type], profile_keys[:id])
31
+ else
32
+ raise NoProfileDefined.new
33
+ end
34
+ rescue ActiveRecord::RecordNotFound => e
35
+ # event_object no longer exists; no means to recover
36
+ rescue IActionable::Error::BadRequest => e
37
+ # This should only be thrown if the profile type names specified in the model don't match what's on IActionable's dashboard
38
+ raise e
39
+ end
40
+ end
41
+
42
+ class EventPerformer
43
+ @queue = :riaction_event_logger
44
+
45
+ def self.perform(event_name, klass_name, id)
46
+ iactionable_api = IActionable::Api.new
47
+
48
+ event_object = klass_name.constantize.find_by_id!(id)
49
+ event_details = event_object.riaction_event(event_name)
50
+ profile_keys = event_details[:profile].riaction_profile_keys
51
+
52
+ # assert the profile exists, and if not, create it
53
+ unless begin
54
+ !!iactionable_api.get_profile_summary(profile_keys[:profile_type], profile_keys[:id_type], profile_keys[:id])
55
+ rescue IActionable::Error::BadRequest => e
56
+ false
57
+ end
58
+ iactionable_api.create_profile(profile_keys[:profile_type], profile_keys[:id_type], profile_keys[:id])
59
+ end
60
+
61
+ # Log the event
62
+ iactionable_api.log_event(profile_keys[:profile_type], profile_keys[:id_type], profile_keys[:id], event_details[:key], event_details[:params])
63
+ rescue ActiveRecord::RecordNotFound => e
64
+ # event_object no longer exists; no means to recover
65
+ rescue IActionable::Error::BadRequest => e
66
+ # Log event should never throw this as of IActionable API v3
67
+ rescue NoMethodError => e
68
+ raise NoEventDefined.new
69
+ end
70
+ end
71
+
72
+ class CrudEventCallback
73
+ def initialize(event_name)
74
+ @event_name = event_name
75
+ end
76
+
77
+ def after_create(record)
78
+ if record.riaction_log_event?(@event_name)
79
+ Resque.enqueue(Riaction::EventPerformer, @event_name, record.class.base_class.to_s, record.id)
80
+ end
81
+ end
82
+
83
+ def after_update(record)
84
+ if record.riaction_log_event?(@event_name)
85
+ Resque.enqueue(Riaction::EventPerformer, @event_name, record.class.base_class.to_s, record.id)
86
+ end
87
+ end
88
+
89
+ def after_destroy(record)
90
+ if record.riaction_log_event?(@event_name)
91
+ Resque.enqueue(Riaction::EventPerformer, @event_name, record.class.base_class.to_s, record.id)
92
+ end
93
+ end
94
+ end
95
+
96
+ class ProfileCreationCallback
97
+ def after_create(record)
98
+ Resque.enqueue(Riaction::ProfileCreator, record.class.base_class.to_s, record.id)
99
+ end
100
+ end
101
+
102
+ module ClassMethods
103
+ def riaction(object_type, opts)
104
+ if object_type == :profile
105
+ (PROFILE_CLASSES << self).uniq!
106
+ define_profile(opts.delete(:type), opts)
107
+ include Riaction::ProfileInstanceMethods unless instance_methods.include? :riaction_profile
108
+ send "after_create".to_sym, Riaction::ProfileCreationCallback.new
109
+ elsif object_type == :event
110
+ (EVENT_LOGGING_CLASSES << self).uniq!
111
+ define_event(opts[:name], opts[:trigger], opts[:profile], opts[:params], opts[:if])
112
+ include Riaction::EventInstanceMethods unless instance_methods.include? :riaction_event
113
+ end
114
+ end
115
+
116
+ def define_event(name, trigger, profile, params = {}, guard = nil)
117
+ class << self
118
+ def riaction_events
119
+ class_variable_defined?(:@@riaction_events) ? class_variable_get(:@@riaction_events) : class_variable_set(:@@riaction_events, {})
120
+ end
121
+ end
122
+
123
+ events = riaction_events
124
+ trigger = name unless trigger
125
+
126
+ # store the event
127
+ events[name] = {:trigger => trigger, :profile => profile, :params => params, :guard => guard}
128
+ class_variable_set(:@@riaction_events, events)
129
+
130
+ # Create the callback or the means to trigger it
131
+ if Riaction.crud_actions.include? trigger
132
+ send "after_#{trigger}".to_sym, Riaction::CrudEventCallback.new(name)
133
+
134
+ define_method("trigger_#{name}!") do
135
+ if self.riaction_log_event?(name)
136
+ Resque.enqueue(Riaction::EventPerformer, name, self.class.base_class.to_s, self.id)
137
+ end
138
+ end
139
+ else
140
+ define_method("trigger_#{trigger}!") do
141
+ if self.riaction_log_event?(name)
142
+ Resque.enqueue(Riaction::EventPerformer, name, self.class.base_class.to_s, self.id)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def define_profile(type, fields)
149
+ class << self
150
+ def riaction_profiles
151
+ class_variable_defined?(:@@riaction_profiles) ? class_variable_get(:@@riaction_profiles) : class_variable_set(:@@riaction_profiles, {})
152
+ end
153
+ end
154
+
155
+ profiles = riaction_profiles
156
+
157
+ # store the profile
158
+ profiles[type] = fields
159
+ class_variable_set(:@@riaction_profiles, profiles)
160
+
161
+ class << self
162
+ def riaction_profile?
163
+ true
164
+ end
165
+ end
166
+ end
167
+
168
+ def riaction_profile?
169
+ false
170
+ end
171
+ end
172
+
173
+ module EventInstanceMethods
174
+ def riaction_event(event_name)
175
+ events = self.class.class_variable_defined?(:@@riaction_events) ? self.class.class_variable_get(:@@riaction_events) : {}
176
+ event = events.fetch(event_name.to_sym)
177
+
178
+ profile = riaction_event_profile(event[:profile])
179
+ unless profile.class.respond_to?(:riaction_profile?) && profile.class.riaction_profile?
180
+ raise TypeError.new("Object defined for #{self.class} on event #{event_name} as a profile must itself declare riaction(:profile ...)")
181
+ end
182
+ params = riaction_event_params(event[:params])
183
+ raise TypeError.new("Params defined for #{self.class} on event #{event_name} must be a hash") unless params.kind_of? Hash
184
+
185
+ {
186
+ :key => event_name.to_sym,
187
+ :profile => profile,
188
+ :params => params
189
+ }
190
+ rescue KeyError => e
191
+ raise NoEventDefined.new("no such event #{event_name} defined on #{self.class}")
192
+ end
193
+
194
+ def riaction_log_event?(event_name)
195
+ events = self.class.class_variable_defined?(:@@riaction_events) ? self.class.class_variable_get(:@@riaction_events) : {}
196
+ event = events.fetch(event_name.to_sym)
197
+ guard = event[:guard]
198
+
199
+ case guard
200
+ when NilClass
201
+ true
202
+ when Symbol
203
+ self.send guard
204
+ when Proc
205
+ guard.call self
206
+ else
207
+ true
208
+ end
209
+ rescue KeyError => e
210
+ raise NoEventDefined.new("no such event #{event_name} defined on #{self.class}")
211
+ end
212
+
213
+ private
214
+
215
+ def riaction_event_profile(profile)
216
+ if profile == :self
217
+ self
218
+ elsif profile.kind_of? Symbol
219
+ self.send(profile)
220
+ elsif profile.kind_of? Proc
221
+ profile.call
222
+ else
223
+ nil
224
+ end
225
+ rescue Exception => e
226
+ raise
227
+ end
228
+
229
+ def riaction_event_params(params)
230
+ if params.kind_of? Symbol
231
+ self.send(params)
232
+ elsif params.kind_of? Proc
233
+ params.call
234
+ elsif params.kind_of? Hash
235
+ resolved_params = {}
236
+ params.each_pair do |key, value|
237
+ resolved_params[key] = self.respond_to?(value) ? self.send(value) : value
238
+ end
239
+ resolved_params
240
+ else
241
+ {}
242
+ end
243
+ rescue Exception => e
244
+ raise
245
+ end
246
+ end
247
+
248
+ module ProfileInstanceMethods
249
+ def riaction_profile_keys(profile_type=nil, id_type=nil)
250
+ profiles = self.class.class_variable_defined?(:@@riaction_profiles) ? self.class.class_variable_get(:@@riaction_profiles) : {}
251
+
252
+ if profiles.size > 0
253
+ if profile_type && profiles.has_key?(profile_type)
254
+ ids = profiles.fetch(profile_type)
255
+ else
256
+ profile_type = profiles.first[0]
257
+ ids = profiles.first[1]
258
+ end
259
+
260
+ if id_type && ids.has_key?(id_type)
261
+ {:profile_type => profile_type.to_s, :id_type => id_type.to_s, :id => self.send(ids.fetch(id_type)).to_s}
262
+ else
263
+ {:profile_type => profile_type.to_s, :id_type => ids.first[0].to_s, :id => self.send(ids.first[1]).to_s}
264
+ end
265
+ else
266
+ {}
267
+ end
268
+ rescue KeyError, NoMethodError => e
269
+ {}
270
+ end
271
+
272
+ #################
273
+ # API wrappers #
274
+ #################
275
+
276
+ def riaction_profile_summary(achievement_count=nil)
277
+ keys = riaction_profile_keys
278
+ unless keys.empty?
279
+ @iactionable_api ||= IActionable::Api.new
280
+ @iactionable_api.get_profile_summary(keys[:profile_type], keys[:id_type], keys[:id], achievement_count)
281
+ else
282
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
283
+ end
284
+ rescue IActionable::Error::BadRequest => e
285
+ nil
286
+ end
287
+
288
+ def riaction_create_profile
289
+ keys = riaction_profile_keys
290
+ unless keys.empty?
291
+ existing = riaction_profile_summary
292
+ unless existing
293
+ @iactionable_api ||= IActionable::Api.new
294
+ @iactionable_api.create_profile(keys[:profile_type], keys[:id_type], keys[:id])
295
+ else
296
+ existing
297
+ end
298
+ else
299
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
300
+ end
301
+ end
302
+
303
+ def riaction_update_profile(new_id_type)
304
+ old_keys = riaction_profile_keys
305
+ new_keys = riaction_profile_keys(old_keys[:profile_type], new_id_type)
306
+ unless old_keys.empty? || new_keys.empty?
307
+ @iactionable_api ||= IActionable::Api.new
308
+ @iactionable_api.add_profile_identifier(old_keys[:profile_type], old_keys[:id_type], old_keys[:id], new_keys[:id_type], new_keys[:id])
309
+ else
310
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
311
+ end
312
+ end
313
+
314
+ def riaction_profile_achievements(filter_type=nil)
315
+ keys = riaction_profile_keys
316
+ unless keys.empty?
317
+ @iactionable_api ||= IActionable::Api.new
318
+ @iactionable_api.get_profile_achievements(keys[:profile_type], keys[:id_type], keys[:id], filter_type)
319
+ else
320
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
321
+ end
322
+ rescue IActionable::Error::BadRequest => e
323
+ nil
324
+ end
325
+
326
+ def riaction_profile_challenges(filter_type=nil)
327
+ keys = riaction_profile_keys
328
+ unless keys.empty?
329
+ @iactionable_api ||= IActionable::Api.new
330
+ @iactionable_api.get_profile_challenges(keys[:profile_type], keys[:id_type], keys[:id], filter_type)
331
+ else
332
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
333
+ end
334
+ rescue IActionable::Error::BadRequest => e
335
+ nil
336
+ end
337
+
338
+ def riaction_profile_goals(filter_type=nil)
339
+ keys = riaction_profile_keys
340
+ unless keys.empty?
341
+ @iactionable_api ||= IActionable::Api.new
342
+ @iactionable_api.get_profile_goals(keys[:profile_type], keys[:id_type], keys[:id], filter_type)
343
+ else
344
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
345
+ end
346
+ rescue IActionable::Error::BadRequest => e
347
+ nil
348
+ end
349
+
350
+ def riaction_profile_notifications
351
+ keys = riaction_profile_keys
352
+ unless keys.empty?
353
+ @iactionable_api ||= IActionable::Api.new
354
+ @iactionable_api.get_profile_notifications(keys[:profile_type], keys[:id_type], keys[:id])
355
+ else
356
+ raise NoProfileDefined.new("Class #{self.class} does not adequately define itself as an IActionable profile")
357
+ end
358
+ rescue IActionable::Error::BadRequest => e
359
+ nil
360
+ end
361
+ end
362
+ end
363
+
364
+ begin
365
+ ActiveRecord::Base.send(:include, Riaction)
366
+ rescue NameError => e
367
+ #
368
+ end