riaction 0.0.1

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 (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