-
Notifications
You must be signed in to change notification settings - Fork 78
/
achievement.rb
114 lines (90 loc) · 3.88 KB
/
achievement.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# frozen_string_literal: true
class Course::Condition::Achievement < ApplicationRecord
acts_as_condition
include DuplicationStateTrackingConcern
# Trigger for evaluating the satisfiability of conditionals for a course user
Course::UserAchievement.after_save do |achievement|
Course::Condition::Achievement.on_dependent_status_change(achievement)
end
Course::UserAchievement.after_destroy do |achievement|
Course::Condition::Achievement.on_dependent_status_change(achievement)
end
validate :validate_achievement_condition, if: :achievement_id_changed?
validates :achievement, presence: true
belongs_to :achievement, class_name: 'Course::Achievement', inverse_of: :achievement_conditions
default_scope { includes(:achievement) }
delegate :title, to: :achievement
alias_method :dependent_object, :achievement
# Checks if the user has the required achievement.
#
# @param [CourseUser] course_user The user that the achievement condition is being checked on. The
# user must respond to `achievements` and returns an ActiveRecord::Association that
# contains all achievements the subject has obtained.
# @return [Boolean] true if the user has the required achievement and false otherwise.
def satisfied_by?(course_user)
# Unpublished achievements are considered not satisfied.
return false unless achievement.published?
course_user.achievements.exists?(achievement.id)
end
# Class that the condition depends on.
def self.dependent_class
Course::Achievement.name
end
def self.on_dependent_status_change(achievement)
return unless achievement.saved_changes.any? || achievement.destroyed?
achievement.execute_after_commit { evaluate_conditional_for(achievement.course_user) }
end
def initialize_duplicate(duplicator, other)
self.achievement = duplicator.duplicate(other.achievement)
self.conditional_type = other.conditional_type # this is a simple string
self.conditional = duplicator.duplicate(other.conditional)
case duplicator.mode
when :course
self.course = duplicator.duplicate(other.course)
when :object
self.course = duplicator.options[:destination_course]
end
set_duplication_flag
end
private
# Given a conditional object, returns all achievements that it requires.
#
# @param [#conditions] conditional The object that is declared as acts_as_conditional and for
# which returned achievements are required.
# @return [Array<Course::Achievement>]
def required_achievements_for(conditional)
# Course::Condition::Achievement.
# joins { condition.conditional(Course::Achievement) }.
# where.has { condition.conditional.id == achievement.id }.
# map(&:achievement)
# Workaround, pending the squeel bugfix (activerecord-hackery/squeel#390) that will allow
# allow the above query to work without #reload
Course::Achievement.joins(<<-SQL)
INNER JOIN
(SELECT cca.achievement_id
FROM course_condition_achievements cca INNER JOIN course_conditions cc
ON cc.actable_type = 'Course::Condition::Achievement' AND cc.actable_id = cca.id
WHERE cc.conditional_id = #{conditional.id}
AND cc.conditional_type = #{ActiveRecord::Base.connection.quote(conditional.class.name)}
) ids
ON ids.achievement_id = course_achievements.id
SQL
end
def validate_achievement_condition
validate_references_self
validate_unique_dependency unless duplicating?
validate_acyclic_dependency
end
def validate_references_self
return unless achievement == conditional
errors.add(:achievement, :references_self)
end
def validate_unique_dependency
return unless required_achievements_for(conditional).include?(achievement)
errors.add(:achievement, :unique_dependency)
end
def validate_acyclic_dependency
return unless cyclic?
errors.add(:achievement, :cyclic_dependency)
end
end