2020-10-27 00:46:40 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2014-03-04 03:55:14 +08:00
|
|
|
#
|
2017-04-28 10:30:08 +08:00
|
|
|
# Copyright (C) 2014 - present Instructure, Inc.
|
2014-03-04 03:55:14 +08:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
class CustomData < ActiveRecord::Base
|
2021-11-06 06:02:13 +08:00
|
|
|
class WriteConflict < RuntimeError
|
2014-03-04 03:55:14 +08:00
|
|
|
attr_accessor :conflict_scope, :type_at_conflict, :value_at_conflict
|
|
|
|
|
|
|
|
def initialize(opts = {})
|
|
|
|
opts.each do |k, v|
|
2023-12-19 00:42:37 +08:00
|
|
|
instance_variable_set(:"@#{k}", v)
|
2014-03-04 03:55:14 +08:00
|
|
|
end
|
2023-12-02 05:46:58 +08:00
|
|
|
super("write conflict for custom_data hash")
|
2014-03-04 03:55:14 +08:00
|
|
|
end
|
2017-03-08 13:33:13 +08:00
|
|
|
|
|
|
|
def as_json
|
|
|
|
{
|
2023-06-02 06:06:09 +08:00
|
|
|
conflict_scope:,
|
|
|
|
type_at_conflict:,
|
|
|
|
value_at_conflict:
|
2017-03-08 13:33:13 +08:00
|
|
|
}
|
|
|
|
end
|
2014-03-04 03:55:14 +08:00
|
|
|
end
|
|
|
|
|
2014-07-24 01:14:22 +08:00
|
|
|
self.table_name = "custom_data"
|
2014-03-04 03:55:14 +08:00
|
|
|
|
|
|
|
belongs_to :user
|
|
|
|
|
2024-02-01 02:54:30 +08:00
|
|
|
serialize :data, type: Hash
|
2014-03-04 03:55:14 +08:00
|
|
|
|
2021-11-16 07:03:48 +08:00
|
|
|
validates :user, :namespace, presence: true
|
2014-03-04 03:55:14 +08:00
|
|
|
|
|
|
|
def get_data(scope)
|
|
|
|
hash_data_from_scope(data_frd, "d/#{scope}")
|
|
|
|
end
|
|
|
|
|
2019-08-09 04:16:29 +08:00
|
|
|
def lock_and_save
|
|
|
|
transaction do
|
|
|
|
lock!
|
|
|
|
yield
|
|
|
|
destroyed? || save
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-04 03:55:14 +08:00
|
|
|
def set_data(scope, val)
|
|
|
|
set_hash_data_from_scope(data_frd, "d/#{scope}", val)
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_data(scope)
|
|
|
|
delete_hash_data_from_scope(data_frd, "d/#{scope}")
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def hash_data_from_scope(hash, scope)
|
|
|
|
keys = scope.split("/")
|
2021-11-04 05:36:34 +08:00
|
|
|
keys.inject(hash) do |h, k|
|
|
|
|
raise ArgumentError, "invalid scope for hash" unless h.is_a?(Hash)
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2021-11-04 05:36:34 +08:00
|
|
|
h[k]
|
2014-03-04 03:55:14 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_hash_data_from_scope(hash, scope, data)
|
|
|
|
keys = scope.split("/")
|
|
|
|
last = keys.pop
|
|
|
|
|
|
|
|
traverse = lambda do |hsh, key_idx|
|
|
|
|
return hsh if key_idx == keys.length
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2014-03-04 03:55:14 +08:00
|
|
|
k = keys[key_idx]
|
|
|
|
h = hsh[k]
|
|
|
|
if h.nil?
|
|
|
|
hsh[k] = {}
|
|
|
|
elsif !h.is_a? Hash
|
|
|
|
raise WriteConflict.new({
|
|
|
|
conflict_scope: keys.slice(1..key_idx).join("/"),
|
|
|
|
type_at_conflict: h.class,
|
|
|
|
value_at_conflict: h
|
|
|
|
})
|
|
|
|
end
|
|
|
|
traverse.call(hsh[k], key_idx + 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
h = traverse.call(hash, 0)
|
|
|
|
overwrite = !h[last].nil?
|
|
|
|
h[last] = data
|
|
|
|
overwrite
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_hash_data_from_scope(hash, scope)
|
|
|
|
keys = scope.split("/")
|
2021-11-04 05:36:34 +08:00
|
|
|
del_frd = lambda do |hash2|
|
2014-03-04 03:55:14 +08:00
|
|
|
k = keys.shift
|
|
|
|
if keys.empty?
|
2021-11-04 05:36:34 +08:00
|
|
|
raise ArgumentError, "invalid scope for hash" unless hash2.key?(k)
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2021-11-04 05:36:34 +08:00
|
|
|
hash2.delete k
|
2014-03-04 03:55:14 +08:00
|
|
|
else
|
2021-11-04 05:36:34 +08:00
|
|
|
hash3 = hash2[k]
|
|
|
|
raise ArgumentError, "invalid scope for hash" if hash3.nil?
|
2021-09-23 00:20:17 +08:00
|
|
|
|
2021-11-04 05:36:34 +08:00
|
|
|
ret = del_frd.call(hash3)
|
|
|
|
hash2.delete k if hash3.empty?
|
2014-03-04 03:55:14 +08:00
|
|
|
ret
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ret = del_frd.call(hash)
|
|
|
|
destroy if hash.empty?
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
|
|
|
|
def data_frd
|
2015-12-29 03:01:30 +08:00
|
|
|
read_or_initialize_attribute(:data, {})
|
2014-03-04 03:55:14 +08:00
|
|
|
end
|
|
|
|
end
|