riaction 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +59 -0
- data/README +110 -0
- data/Rakefile +1 -0
- data/lib/riaction.rb +3 -0
- data/lib/riaction/iactionable/api.rb +209 -0
- data/lib/riaction/iactionable/connection.rb +114 -0
- data/lib/riaction/iactionable/error.rb +17 -0
- data/lib/riaction/iactionable/objects.rb +6 -0
- data/lib/riaction/iactionable/objects/achievement.rb +19 -0
- data/lib/riaction/iactionable/objects/awardable.rb +44 -0
- data/lib/riaction/iactionable/objects/challenge.rb +18 -0
- data/lib/riaction/iactionable/objects/goal.rb +19 -0
- data/lib/riaction/iactionable/objects/i_actionable_object.rb +40 -0
- data/lib/riaction/iactionable/objects/identifier.rb +11 -0
- data/lib/riaction/iactionable/objects/leaderboard.rb +10 -0
- data/lib/riaction/iactionable/objects/leaderboard_report.rb +23 -0
- data/lib/riaction/iactionable/objects/level.rb +18 -0
- data/lib/riaction/iactionable/objects/level_type.rb +10 -0
- data/lib/riaction/iactionable/objects/point_type.rb +10 -0
- data/lib/riaction/iactionable/objects/profile_level.rb +16 -0
- data/lib/riaction/iactionable/objects/profile_points.rb +21 -0
- data/lib/riaction/iactionable/objects/profile_summary.rb +24 -0
- data/lib/riaction/iactionable/objects/progress.rb +37 -0
- data/lib/riaction/iactionable/settings.rb +30 -0
- data/lib/riaction/riaction.rb +368 -0
- data/lib/riaction/version.rb +3 -0
- data/lib/tasks/riaction.rake +101 -0
- data/riaction.gemspec +32 -0
- data/spec/api_spec.rb +288 -0
- data/spec/connection_spec.rb +111 -0
- data/spec/riaction_spec.rb +253 -0
- data/spec/settings_spec.rb +52 -0
- data/spec/spec_helper.rb +1 -0
- 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,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,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,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
|