Skip to content

Commit 19fc204

Browse files
committed
1 parent 8f9ee60 commit 19fc204

1 file changed

Lines changed: 53 additions & 39 deletions

File tree

ruby/hyper-model/lib/reactive_record/server_data_cache.rb

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -260,56 +260,70 @@ def timing(tag, &block)
260260
def apply_method_to_cache(method)
261261
@db_cache.cache.inject(nil) do |representative, cache_item|
262262
if cache_item.vector == vector
263-
if method == "*"
264-
# apply_star does the security check if value is present
265-
cache_item.apply_star || representative
266-
elsif method == "*all"
267-
# if we secure the collection then we assume its okay to read the ids
268-
secured_value = cache_item.value.__secure_collection_check(cache_item)
269-
cache_item.build_new_cache_item(timing(:active_record) { secured_value.collect { |record| record.id } }, method, method)
270-
elsif method == "*count"
271-
secured_value = cache_item.value.__secure_collection_check(cache_item)
272-
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.__secure_collection_check(cache_item).count }, method, method)
273-
elsif preloaded_value = @preloaded_records[cache_item.absolute_vector + [method]]
274-
# no security check needed since we already evaluated this
275-
cache_item.build_new_cache_item(preloaded_value, method, method)
276-
elsif aggregation = cache_item.aggregation?(method)
277-
# aggregations are not protected
278-
cache_item.build_new_cache_item(aggregation.mapping.collect { |attribute, accessor| cache_item.value[attribute] }, method, method)
279-
else
280-
if !cache_item.value || cache_item.value.is_a?(Array)
281-
# seeing as we just returning representative, no check is needed (its already checked)
282-
representative
283-
elsif method == 'model_name'
284-
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.model_name }, method, method)
263+
begin
264+
# error_recovery_method holds the current method that we are attempting to apply
265+
# in case we throw an exception, and need to give the developer a meaningful message.
266+
if method == "*"
267+
# apply_star does the security check if value is present
268+
cache_item.apply_star || representative
269+
elsif method == "*all"
270+
# if we secure the collection then we assume its okay to read the ids
271+
error_recovery_method = [:all]
272+
secured_value = cache_item.value.__secure_collection_check(cache_item)
273+
cache_item.build_new_cache_item(timing(:active_record) { secured_value.collect { |record| record.id } }, method, method)
274+
elsif method == "*count"
275+
error_recovery_method = [:count]
276+
secured_value = cache_item.value.__secure_collection_check(cache_item)
277+
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.__secure_collection_check(cache_item).count }, method, method)
278+
elsif preloaded_value = @preloaded_records[cache_item.absolute_vector + [method]]
279+
# no security check needed since we already evaluated this
280+
cache_item.build_new_cache_item(preloaded_value, method, method)
281+
elsif aggregation = cache_item.aggregation?(method)
282+
# aggregations are not protected
283+
error_recovery_method = [method, :mapping, :all]
284+
cache_item.build_new_cache_item(aggregation.mapping.collect { |attribute, accessor| cache_item.value[attribute] }, method, method)
285285
else
286-
begin
287-
secured_method = "__secure_remote_access_to_#{[*method].first}"
288-
289-
# order is important. This check must be first since scopes can have same name as attributes!
290-
if cache_item.value.respond_to? secured_method
291-
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.send(secured_method, cache_item.value, @acting_user, *([*method][1..-1])) }, method, method)
292-
elsif (cache_item.value.class < ActiveRecord::Base) && cache_item.value.attributes.has_key?(method) # TODO: second check is not needed, its built into check_permmissions, check should be does class respond to check_permissions...
293-
cache_item.value.check_permission_with_acting_user(@acting_user, :view_permitted?, method)
294-
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.send(*method) }, method, method)
295-
else
296-
raise "Method missing while fetching data: \`#{cache_item.value}##{[*method].first}\` "\
297-
'should either be an attribute or a method defined using the server_method of finder_method macros.'
286+
if !cache_item.value || cache_item.value.is_a?(Array)
287+
# seeing as we just returning representative, no check is needed (its already checked)
288+
representative
289+
elsif method == 'model_name'
290+
error_recovery_method = [:model_name]
291+
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.model_name }, method, method)
292+
else
293+
begin
294+
secured_method = "__secure_remote_access_to_#{[*method].first}"
295+
error_recovery_method = [*method]
296+
# order is important. This check must be first since scopes can have same name as attributes!
297+
if cache_item.value.respond_to? secured_method
298+
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.send(secured_method, cache_item.value, @acting_user, *([*method][1..-1])) }, method, method)
299+
elsif (cache_item.value.class < ActiveRecord::Base) && cache_item.value.attributes.has_key?(method) # TODO: second check is not needed, its built into check_permmissions, check should be does class respond to check_permissions...
300+
cache_item.value.check_permission_with_acting_user(@acting_user, :view_permitted?, method)
301+
cache_item.build_new_cache_item(timing(:active_record) { cache_item.value.send(*method) }, method, method)
302+
else
303+
raise "Method missing while fetching data: \`#{[*method].first}\` "\
304+
'was expected to be an attribute or a method defined using the server_method of finder_method macros.'
305+
end
298306
end
299-
# rescue Exception => e # this check may no longer be needed as we are quite explicit now on which methods we apply
300-
# binding.pry
301-
# # ReactiveRecord::Pry::rescued(e)
302-
# #::Rails.logger.debug "\033[0;31;1mERROR: HyperModel exception caught when applying #{method} to db object #{cache_item.value}: #{e}\033[0;30;21m"
303-
# raise e, "HyperModel fetching records failed, exception caught when applying #{method} to db object #{cache_item.value}: #{e}", e.backtrace
304307
end
305308
end
309+
rescue StandardError => e
310+
raise e.class, form_error_message(e, cache_item.vector + error_recovery_method), e.backtrace
306311
end
307312
else
308313
representative
309314
end
310315
end
311316
end
312317

318+
def form_error_message(original_error, vector)
319+
expression = vector.collect do |exp|
320+
next exp unless exp.is_a? Array
321+
next exp.first if exp.length == 1
322+
"#{exp.first}(#{exp[1..-1].join(', ')})"
323+
end.join('.')
324+
"raised when evaluating #{expression}\n#{original_error}"
325+
end
326+
313327
def aggregation?(method)
314328
if method.is_a?(String) && @value.class.respond_to?(:reflect_on_aggregation)
315329
aggregation = @value.class.reflect_on_aggregation(method.to_sym)

0 commit comments

Comments
 (0)