forked from sfu/canvas-lms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sticky_sis_fields.rb
148 lines (131 loc) · 5.17 KB
/
sticky_sis_fields.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'skip_callback'
module StickySisFields
module InstanceMethods
# this method is set as a before_update callback
def set_sis_stickiness
self.class.sis_stickiness_options ||= {}
currently_stuck_sis_fields = if self.class.sis_stickiness_options[:clear_sis_stickiness]
[].to_set
else
calculate_currently_stuck_sis_fields
end
if load_stuck_sis_fields_cache != currently_stuck_sis_fields
write_attribute(:stuck_sis_fields, currently_stuck_sis_fields.map(&:to_s).sort.join(','))
end
@stuck_sis_fields_cache = currently_stuck_sis_fields
@sis_fields_to_stick = [].to_set
@sis_fields_to_unstick = [].to_set
end
# this method is what you want to use to determine which fields are currently stuck
def stuck_sis_fields
self.class.sis_stickiness_options ||= {}
if self.class.sis_stickiness_options[:override_sis_stickiness]
return [].to_set
else
return calculate_currently_stuck_sis_fields
end
end
def stuck_sis_fields=(fields)
fields = [fields] if (fields.is_a? String)
clear_sis_stickiness(*(stuck_sis_fields.to_a))
add_sis_stickiness(*(fields.map(&:to_sym).to_set))
end
# clear stickiness on a set of fields
def clear_sis_stickiness(*fields)
@sis_fields_to_stick ||= [].to_set
@sis_fields_to_unstick ||= [].to_set
fields.map(&:to_sym).each do |field|
@sis_fields_to_stick.delete field
@sis_fields_to_unstick.add field
end
end
# make some fields sticky
def add_sis_stickiness(*fields)
@sis_fields_to_stick ||= [].to_set
@sis_fields_to_unstick ||= [].to_set
fields.map(&:to_sym).each do |field|
@sis_fields_to_stick.add field
@sis_fields_to_unstick.delete field
end
end
private
def load_stuck_sis_fields_cache
@stuck_sis_fields_cache ||= (read_attribute(:stuck_sis_fields) || '').split(',').map(&:to_sym).to_set
end
def calculate_currently_stuck_sis_fields
@sis_fields_to_stick ||= [].to_set
@sis_fields_to_unstick ||= [].to_set
changed_sis_fields = self.class.sticky_sis_fields & (self.changed.map(&:to_sym).to_set | @sis_fields_to_stick)
return (load_stuck_sis_fields_cache | changed_sis_fields) - @sis_fields_to_unstick
end
def reload_with_sis_stickiness(*a)
@stuck_sis_fields_cache = @sis_fields_to_stick = @sis_fields_to_unstick = nil
reload_without_sis_stickiness(*a)
end
end
module ClassMethods
# specify which fields are able to be stuck
def are_sis_sticky(*fields)
self.sticky_sis_fields = fields.map(&:to_sym).to_set
end
# takes a block and runs it with the following options:
# override_sis_stickiness: default false,
# if true, all code inside the block will be run as if the class
# mixing in this module has no stuck sis fields
# add_sis_stickiness: default false,
# unless add_sis_stickiness (or clear_sis_stickiness) is true, the
# set_sis_stickiness callback is skipped, so no sis stickiness is
# modified. if true, the set_sis_stickiness is enabled like normal,
# such that everything is processed like non-sis. it doesn't really
# make tons of sense to use this feature without
# override_sis_stickiness.
# clear_sis_stickiness: default false,
# if true, the set_sis_stickiness callback is enabled and configured
# to write out an empty stickiness list on every save.
def process_as_sis(opts={})
self.sis_stickiness_options ||= {}
old_options = self.sis_stickiness_options.clone
[:override_sis_stickiness, :clear_sis_stickiness].each do |option|
self.sis_stickiness_options[option] = opts[option]
end
begin
if opts[:add_sis_stickiness] || opts[:clear_sis_stickiness]
yield
else
self.skip_callback(:set_sis_stickiness) do
yield
end
end
ensure
self.sis_stickiness_options = old_options
end
end
end
def self.included(klass)
if klass < ActiveRecord::Base
klass.send :extend, ClassMethods
klass.send :include, InstanceMethods
klass.cattr_accessor :sticky_sis_fields
klass.cattr_accessor :sis_stickiness_options
klass.before_update :set_sis_stickiness
klass.alias_method_chain :reload, :sis_stickiness
end
end
end