hyper-mesh 1.0.0.lap27 → 1.0.0.lap28
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -2
- data/Gemfile +0 -1
- data/Rakefile +2 -2
- data/hyper-mesh.gemspec +1 -1
- data/lib/active_record_base.rb +39 -27
- data/lib/hyper-mesh.rb +6 -1
- data/lib/hypermesh/version.rb +1 -1
- data/lib/object/tap.rb +7 -0
- data/lib/reactive_record/active_record/associations.rb +14 -3
- data/lib/reactive_record/active_record/base.rb +1 -2
- data/lib/reactive_record/active_record/class_methods.rb +120 -67
- data/lib/reactive_record/active_record/error.rb +17 -12
- data/lib/reactive_record/active_record/errors.rb +374 -0
- data/lib/reactive_record/active_record/instance_methods.rb +58 -67
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +1 -4
- data/lib/reactive_record/active_record/reactive_record/base.rb +129 -234
- data/lib/reactive_record/active_record/reactive_record/collection.rb +51 -18
- data/lib/reactive_record/active_record/reactive_record/column_types.rb +5 -3
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +6 -4
- data/lib/reactive_record/active_record/reactive_record/getters.rb +133 -0
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +99 -87
- data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +54 -0
- data/lib/reactive_record/active_record/reactive_record/operations.rb +2 -1
- data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +1 -1
- data/lib/reactive_record/active_record/reactive_record/setters.rb +194 -0
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +4 -5
- data/lib/reactive_record/active_record_error.rb +4 -0
- data/lib/reactive_record/broadcast.rb +55 -18
- data/lib/reactive_record/permissions.rb +5 -4
- data/lib/reactive_record/scope_description.rb +14 -6
- data/lib/reactive_record/server_data_cache.rb +119 -70
- metadata +16 -13
- data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +0 -189
@@ -0,0 +1,54 @@
|
|
1
|
+
module ReactiveRecord
|
2
|
+
module LookupTables
|
3
|
+
def initialize_lookup_tables
|
4
|
+
@records = Hash.new { |hash, key| hash[key] = [] }
|
5
|
+
@records_by_id = `{}`
|
6
|
+
@records_by_vector = `{}`
|
7
|
+
@records_by_object_id = `{}`
|
8
|
+
@class_scopes = Hash.new { |hash, key| hash[key] = {} }
|
9
|
+
@waiting_for_save = Hash.new { |hash, key| hash[key] = [] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def class_scopes(model)
|
13
|
+
@class_scopes[model.base_class]
|
14
|
+
end
|
15
|
+
|
16
|
+
def waiting_for_save(model)
|
17
|
+
@waiting_for_save[model]
|
18
|
+
end
|
19
|
+
|
20
|
+
def wait_for_save(model, &block)
|
21
|
+
@waiting_for_save[model] << block
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear_waiting_for_save(model)
|
25
|
+
@waiting_for_save[model] = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def lookup_by_object_id(object_id)
|
29
|
+
`#{@records_by_object_id}[#{object_id}]`.ar_instance
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_object_id_lookup(record)
|
33
|
+
`#{@records_by_object_id}[#{record.object_id}] = #{record}`
|
34
|
+
end
|
35
|
+
|
36
|
+
def lookup_by_id(*args) # model and id
|
37
|
+
`#{@records_by_id}[#{args}]` || nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_id_lookup(record)
|
41
|
+
`#{@records_by_id}[#{[record.model, record.id]}] = #{record}`
|
42
|
+
end
|
43
|
+
|
44
|
+
def lookup_by_vector(vector)
|
45
|
+
`#{@records_by_vector}[#{vector}]` || nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_vector_lookup(record, vector)
|
49
|
+
record.vector = vector
|
50
|
+
`delete #{@records_by_vector}[#{record.vector}]`
|
51
|
+
`#{@records_by_vector}[#{vector}] = record`
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -74,6 +74,7 @@ module ReactiveRecord
|
|
74
74
|
param :acting_user, nils: true
|
75
75
|
param models: []
|
76
76
|
param associations: []
|
77
|
+
param :save, type: :boolean
|
77
78
|
param :validate, type: :boolean
|
78
79
|
|
79
80
|
step do
|
@@ -82,7 +83,7 @@ module ReactiveRecord
|
|
82
83
|
params.associations.map(&:with_indifferent_access),
|
83
84
|
params.acting_user,
|
84
85
|
params.validate,
|
85
|
-
|
86
|
+
params.save
|
86
87
|
)
|
87
88
|
end
|
88
89
|
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module ReactiveRecord
|
2
|
+
module Setters
|
3
|
+
def set_attr_value(attr, raw_value)
|
4
|
+
set_common(attr, raw_value) { |value| update_simple_attribute(attr, value) }
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_ar_aggregate(aggr, raw_value)
|
8
|
+
set_common(aggr.attribute, raw_value) do |value, attr|
|
9
|
+
@attributes[attr] ||= aggr.klass.new if new?
|
10
|
+
abr = @attributes[attr].backing_record
|
11
|
+
abr.virgin = false
|
12
|
+
map = value.attributes if value
|
13
|
+
aggr.mapped_attributes.each do |mapped_attr|
|
14
|
+
abr.update_aggregate_attribute mapped_attr, map && map[mapped_attr]
|
15
|
+
end
|
16
|
+
return @attributes[attr]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_non_ar_aggregate(aggregation, raw_value)
|
21
|
+
set_common(aggregation.attribute, raw_value) do |value, attr|
|
22
|
+
if data_loading?
|
23
|
+
@synced_attributes[attr] = aggregation.deserialize(aggregation.serialize(value))
|
24
|
+
else
|
25
|
+
changed = !@synced_attributes.key?(attr) || @synced_attributes[attr] != value
|
26
|
+
end
|
27
|
+
set_attribute_change_status_and_notify attr, changed, value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_has_many(assoc, raw_value)
|
32
|
+
set_common(assoc.attribute, raw_value) do |value, attr|
|
33
|
+
# create a new collection to hold value, shove it in, and return the new collection
|
34
|
+
# the replace method will take care of updating the inverse belongs_to links as
|
35
|
+
# the collection is overwritten
|
36
|
+
collection = Collection.new(assoc.klass, @ar_instance, assoc)
|
37
|
+
collection.replace(value || [])
|
38
|
+
@synced_attributes[attr] = value if data_loading?
|
39
|
+
set_attribute_change_status_and_notify attr, value != @synced_attributes[attr], collection
|
40
|
+
return collection
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_belongs_to(assoc, raw_value)
|
45
|
+
set_common(assoc.attribute, raw_value) do |value, attr|
|
46
|
+
if assoc.inverse.collection?
|
47
|
+
update_has_many_through_associations assoc, value
|
48
|
+
update_inverse_collections assoc, value
|
49
|
+
else
|
50
|
+
update_inverse_attribute assoc, value
|
51
|
+
end
|
52
|
+
# itself will just reactively read the value (a model instance) by doing a .id
|
53
|
+
update_belongs_to attr, value.itself
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def sync_has_many(attr)
|
58
|
+
set_change_status_and_notify_only attr, @attributes[attr] != @synced_attributes[attr]
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_simple_attribute(attr, value)
|
62
|
+
if data_loading?
|
63
|
+
@synced_attributes[attr] = value
|
64
|
+
else
|
65
|
+
changed = !@synced_attributes.key?(attr) || @synced_attributes[attr] != value
|
66
|
+
end
|
67
|
+
set_attribute_change_status_and_notify attr, changed, value
|
68
|
+
end
|
69
|
+
|
70
|
+
alias update_belongs_to update_simple_attribute
|
71
|
+
alias update_aggregate_attribute update_simple_attribute
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def set_common(attr, value)
|
76
|
+
value = convert(attr, value)
|
77
|
+
@virgin = false unless data_loading?
|
78
|
+
if !@destroyed && (
|
79
|
+
!@attributes.key?(attr) ||
|
80
|
+
@attributes[attr].is_a?(Base::DummyValue) ||
|
81
|
+
@attributes[attr] != value)
|
82
|
+
yield value, attr
|
83
|
+
end
|
84
|
+
value
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_attribute_change_status_and_notify(attr, changed, new_value)
|
88
|
+
if @virgin
|
89
|
+
@attributes[attr] = new_value
|
90
|
+
else
|
91
|
+
change_status_and_notify_helper(attr, changed) do |had_key, current_value|
|
92
|
+
@attributes[attr] = new_value
|
93
|
+
if !data_loading? ||
|
94
|
+
(on_opal_client? && had_key && current_value.loaded? && current_value != new_value)
|
95
|
+
React::State.set_state(self, attr, new_value, data_loading?)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def set_change_status_and_notify_only(attr, changed)
|
102
|
+
return if @virgin
|
103
|
+
change_status_and_notify_helper(attr, changed) do
|
104
|
+
React::State.set_state(self, attr, nil) unless data_loading?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def change_status_and_notify_helper(attr, changed)
|
109
|
+
empty_before = changed_attributes.empty?
|
110
|
+
# TODO: confirm this works:
|
111
|
+
# || data_loading? added so that model.new can be wrapped in a ReactiveRecord.load_data
|
112
|
+
if !changed || data_loading?
|
113
|
+
changed_attributes.delete(attr)
|
114
|
+
elsif !changed_attributes.include?(attr)
|
115
|
+
changed_attributes << attr
|
116
|
+
end
|
117
|
+
yield @attributes.key?(attr), @attributes[attr]
|
118
|
+
return unless empty_before != changed_attributes.empty?
|
119
|
+
if on_opal_client? && !data_loading?
|
120
|
+
React::State.set_state(self, '!CHANGED!', !changed_attributes.empty?, true)
|
121
|
+
end
|
122
|
+
return unless aggregate_owner
|
123
|
+
aggregate_owner.set_change_status_and_notify_only(
|
124
|
+
attr, !@attributes[attr].backing_record.changed_attributes.empty?
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_inverse_attribute(association, value)
|
129
|
+
# when updating the inverse attribute of a belongs_to that is itself a belongs_to
|
130
|
+
# (i.e. 1-1 relationship) we clear the existing inverse value and then
|
131
|
+
# write the current record to the new value
|
132
|
+
current_value = @attributes[association.attribute]
|
133
|
+
inverse_attr = association.inverse.attribute
|
134
|
+
current_value.attributes[inverse_attr] = nil unless current_value.nil?
|
135
|
+
return if value.nil?
|
136
|
+
value.attributes[inverse_attr] = @ar_instance
|
137
|
+
return if data_loading?
|
138
|
+
React::State.set_state(value.backing_record, inverse_attr, @ar_instance)
|
139
|
+
end
|
140
|
+
|
141
|
+
def update_inverse_collections(association, value)
|
142
|
+
# when updating an inverse attribute of a belongs_to that is a has_many (i.e. a collection)
|
143
|
+
# we need to first remove the current associated value (if non-nil), then add the new
|
144
|
+
# value to the collection. If the inverse collection is not yet initialized we do it here.
|
145
|
+
current_value = @attributes[association.attribute]
|
146
|
+
inverse_attr = association.inverse.attribute
|
147
|
+
if value.nil?
|
148
|
+
current_value.attributes[inverse_attr].delete(@ar_instance) unless current_value.nil?
|
149
|
+
else
|
150
|
+
value.backing_record.push_onto_collection(@model, association.inverse, @ar_instance)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def push_onto_collection(model, association, ar_instance)
|
155
|
+
@attributes[association.attribute] ||= Collection.new(model, @ar_instance, association)
|
156
|
+
@attributes[association.attribute] << ar_instance
|
157
|
+
end
|
158
|
+
|
159
|
+
def update_has_many_through_associations(association, value)
|
160
|
+
association.through_associations.each { |ta| update_through_association(ta, value) }
|
161
|
+
association.source_associations.each { |sa| update_source_association(sa, value) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def update_through_association(ta, new_belongs_to_value)
|
165
|
+
# appointment.doctor = doctor_new_value (i.e. through association is changing)
|
166
|
+
# means appointment.doctor_new_value.patients << appointment.patient
|
167
|
+
# and we have to appointment.doctor_current_value.patients.delete(appointment.patient)
|
168
|
+
source_value = @attributes[ta.source]
|
169
|
+
current_belongs_to_value = @attributes[ta.inverse.attribute]
|
170
|
+
return unless source_value
|
171
|
+
unless current_belongs_to_value.nil? || current_belongs_to_value.attributes[ta.attribute].nil?
|
172
|
+
current_belongs_to_value.attributes[ta.attribute].delete(source_value)
|
173
|
+
end
|
174
|
+
return unless new_belongs_to_value
|
175
|
+
new_belongs_to_value.attributes[ta.attribute] ||= Collection.new(ta.klass, new_belongs_to_value, ta)
|
176
|
+
new_belongs_to_value.attributes[ta.attribute] << source_value
|
177
|
+
end
|
178
|
+
|
179
|
+
def update_source_association(sa, new_source_value)
|
180
|
+
# appointment.patient = patient_value (i.e. source is changing)
|
181
|
+
# means appointment.doctor.patients.delete(appointment.patient)
|
182
|
+
# means appointment.doctor.patients << patient_value
|
183
|
+
belongs_to_value = @attributes[sa.inverse.attribute]
|
184
|
+
current_source_value = @attributes[sa.source]
|
185
|
+
return unless belongs_to_value
|
186
|
+
unless belongs_to_value.attributes[sa.attribute].nil? || current_source_value.nil?
|
187
|
+
belongs_to_value.attributes[sa.attribute].delete(current_source_value)
|
188
|
+
end
|
189
|
+
return unless new_source_value
|
190
|
+
belongs_to_value.attributes[sa.attribute] ||= Collection.new(sa.klass, belongs_to_value, sa)
|
191
|
+
belongs_to_value.attributes[sa.attribute] << new_source_value
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -17,7 +17,7 @@ module ReactiveRecord
|
|
17
17
|
result = block.call.itself
|
18
18
|
if @loads_pending
|
19
19
|
@blocks_to_load ||= []
|
20
|
-
@blocks_to_load << [Base.
|
20
|
+
@blocks_to_load << [Base.current_fetch_id, promise, block]
|
21
21
|
else
|
22
22
|
promise.resolve result
|
23
23
|
end
|
@@ -36,7 +36,7 @@ module ReactiveRecord
|
|
36
36
|
if Base.pending_fetches.count > 0
|
37
37
|
true
|
38
38
|
else # this happens when for example loading foo.x results in somebody looking at foo.y while foo.y is still being loaded
|
39
|
-
ReactiveRecord::WhileLoading.loaded_at Base.
|
39
|
+
ReactiveRecord::WhileLoading.loaded_at Base.current_fetch_id
|
40
40
|
ReactiveRecord::WhileLoading.quiet!
|
41
41
|
false
|
42
42
|
end
|
@@ -53,8 +53,8 @@ module ReactiveRecord
|
|
53
53
|
@load_stack << @loads_pending
|
54
54
|
@loads_pending = nil
|
55
55
|
result = block.call(failure)
|
56
|
-
if check_loads_pending
|
57
|
-
@blocks_to_load << [Base.
|
56
|
+
if check_loads_pending && !failure
|
57
|
+
@blocks_to_load << [Base.current_fetch_id, promise, block]
|
58
58
|
else
|
59
59
|
promise.resolve result
|
60
60
|
end
|
@@ -297,7 +297,6 @@ if RUBY_ENGINE == 'opal'
|
|
297
297
|
def reactive_record_link_set_while_loading_container_class
|
298
298
|
node = dom_node
|
299
299
|
loading = (waiting_on_resources ? `true` : `false`)
|
300
|
-
#puts "******* reactive_record_link_set_while_loading_container_class #{self} #{node} #{loading}"
|
301
300
|
%x{
|
302
301
|
if (typeof node === "undefined" || node === null) return;
|
303
302
|
var while_loading_container_id = node.getAttribute('data-reactive_record_while_loading_container_id');
|
@@ -23,7 +23,7 @@ module ReactiveRecord
|
|
23
23
|
operation: operation,
|
24
24
|
salt: salt,
|
25
25
|
authorization: authorization
|
26
|
-
)
|
26
|
+
).tap { |p| raise p.error if p.rejected? }
|
27
27
|
end unless RUBY_ENGINE == 'opal'
|
28
28
|
|
29
29
|
class SendPacket < Hyperloop::ServerOp
|
@@ -54,9 +54,7 @@ module ReactiveRecord
|
|
54
54
|
if params.operation == :destroy
|
55
55
|
ReactiveRecord::Collection.sync_scopes broadcast
|
56
56
|
else
|
57
|
-
ReactiveRecord::
|
58
|
-
ReactiveRecord::Collection.sync_scopes broadcast
|
59
|
-
end
|
57
|
+
ReactiveRecord::Collection.sync_scopes broadcast.process_previous_changes
|
60
58
|
end
|
61
59
|
end
|
62
60
|
end
|
@@ -137,47 +135,86 @@ module ReactiveRecord
|
|
137
135
|
@klass = record.class.name
|
138
136
|
@record = data
|
139
137
|
record.backing_record.destroyed = false
|
140
|
-
@record
|
138
|
+
@record[:id] = record.id if record.id
|
141
139
|
record.backing_record.destroyed = @destroyed
|
142
140
|
@backing_record = record.backing_record
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
141
|
+
@previous_changes = record.changes
|
142
|
+
# attributes = record.attributes
|
143
|
+
# data.each do |k, v|
|
144
|
+
# next if klass.reflect_on_association(k) || attributes[k] == v
|
145
|
+
# @previous_changes[k] = [attributes[k], v]
|
146
|
+
# end
|
148
147
|
self
|
149
148
|
end
|
150
149
|
|
151
150
|
def receive(params)
|
152
151
|
@destroyed = params.operation == :destroy
|
153
|
-
@is_new = params.operation == :create
|
154
152
|
@channels ||= Hyperloop::IncomingBroadcast.open_channels.intersection params.channels
|
155
|
-
#raise 'synchromesh security violation' unless @channels.include? params.channels
|
156
153
|
@received << params.channel
|
157
154
|
@klass ||= params.klass
|
158
155
|
@record.merge! params.record
|
159
156
|
@previous_changes.merge! params.previous_changes
|
160
|
-
|
161
|
-
|
157
|
+
ReactiveRecord::Base.when_not_saving(klass) do
|
158
|
+
@backing_record = ReactiveRecord::Base.exists?(klass, params.record[:id])
|
159
|
+
@is_new = params.operation == :create && !@backing_record
|
160
|
+
yield complete! if @channels == @received
|
161
|
+
end
|
162
162
|
end
|
163
163
|
|
164
164
|
def complete!
|
165
165
|
self.class.in_transit.delete @id
|
166
166
|
end
|
167
167
|
|
168
|
+
def value_changed?(attr, value)
|
169
|
+
attrs = @backing_record.synced_attributes
|
170
|
+
return true if attr == @backing_record.primary_key
|
171
|
+
return attrs[attr] != @backing_record.convert(attr, value) if attrs.key?(attr)
|
172
|
+
|
173
|
+
assoc = klass.reflect_on_association_by_foreign_key attr
|
174
|
+
|
175
|
+
return value unless assoc
|
176
|
+
child = attrs[assoc.attribute]
|
177
|
+
return value != child.id if child
|
178
|
+
value
|
179
|
+
end
|
180
|
+
|
181
|
+
def integrity_check
|
182
|
+
@previous_changes.each do |attr, value|
|
183
|
+
next if @record.key?(attr) && @record[attr] == value.last
|
184
|
+
React::IsomorphicHelpers.log "Broadcast contained change to #{attr} -> #{value.last} "\
|
185
|
+
"without corresponding value in attributes (#{@record}).\n",
|
186
|
+
:error
|
187
|
+
raise "Broadcast Integrity Error"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def process_previous_changes
|
192
|
+
return self unless @backing_record
|
193
|
+
integrity_check
|
194
|
+
return self if destroyed?
|
195
|
+
@record.dup.each do |attr, value|
|
196
|
+
next if value_changed?(attr, value)
|
197
|
+
@record.delete(attr)
|
198
|
+
@previous_changes.delete(attr)
|
199
|
+
end
|
200
|
+
self
|
201
|
+
end
|
202
|
+
|
168
203
|
def merge_current_values(br)
|
169
204
|
current_values = Hash[*@previous_changes.collect do |attr, values|
|
170
205
|
value = attr == :id ? record[:id] : values.first
|
171
206
|
if br.attributes.key?(attr) &&
|
172
207
|
br.attributes[attr] != br.convert(attr, value) &&
|
173
208
|
br.attributes[attr] != br.convert(attr, values.last)
|
174
|
-
|
175
|
-
"local value: #{br.attributes[attr]} remote value: #{br.convert(attr, value)}->#{br.convert(attr, values.last)}"
|
209
|
+
React::IsomorphicHelpers.log "warning #{attr} has changed locally - will force a reload.\n"\
|
210
|
+
"local value: #{br.attributes[attr]} remote value: #{br.convert(attr, value)}->#{br.convert(attr, values.last)}",
|
211
|
+
:warning
|
176
212
|
return nil
|
177
213
|
end
|
178
214
|
[attr, value]
|
179
|
-
end.compact.flatten]
|
180
|
-
|
215
|
+
end.compact.flatten]
|
216
|
+
# TODO: verify - it used to be current_values.merge(br.attributes)
|
217
|
+
klass._react_param_conversion(br.attributes.merge(current_values))
|
181
218
|
end
|
182
219
|
end
|
183
220
|
end
|
@@ -88,10 +88,11 @@ class ActiveRecord::Base
|
|
88
88
|
alias belongs_to_without_reactive_record_add_is_method belongs_to
|
89
89
|
|
90
90
|
def belongs_to(attr_name, scope = nil, options = {})
|
91
|
-
|
92
|
-
|
91
|
+
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options).tap do
|
92
|
+
define_method "#{attr_name}_is?".to_sym do |model|
|
93
|
+
self.class.reflections[attr_name].foreign_key == model.id
|
94
|
+
end
|
93
95
|
end
|
94
|
-
belongs_to_without_reactive_record_add_is_method(attr_name, scope, options)
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
@@ -103,7 +104,7 @@ class ActiveRecord::Base
|
|
103
104
|
self.acting_user = old
|
104
105
|
self
|
105
106
|
else
|
106
|
-
raise
|
107
|
+
raise Hyperloop::AccessViolation, "for #{permission}(#{args})"
|
107
108
|
end
|
108
109
|
end
|
109
110
|
|