@@ -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