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