lib/arel/middleware/chain.rb in arel_toolkit-0.4.2 vs lib/arel/middleware/chain.rb in arel_toolkit-0.4.3

- old
+ new

@@ -1,93 +1,102 @@ +require_relative './no_op_cache' +require_relative './cache_accessor' + module Arel module Middleware class Chain - attr_reader :executing_middleware + attr_reader :executing_middleware_depth attr_reader :executor + attr_reader :cache + MAX_RECURSION_DEPTH = 10 + def initialize( internal_middleware = [], internal_context = {}, - executor_class = Arel::Middleware::DatabaseExecutor + executor_class = Arel::Middleware::DatabaseExecutor, + cache: nil ) @internal_middleware = internal_middleware @internal_context = internal_context @executor = executor_class.new(internal_middleware) - @executing_middleware = false + @executing_middleware_depth = 0 + @cache = cache || NoOpCache end + def cache_accessor + @cache_accessor ||= CacheAccessor.new @cache + end + def execute(sql, binds = [], &execute_sql) return execute_sql.call(sql, binds).to_casted_result if internal_middleware.length.zero? - check_middleware_recursion(sql) + if (cached_sql = cache_accessor.read(sql)) + return execute_sql.call(cached_sql, binds).to_casted_result + end - updated_context = context.merge(original_sql: sql) - enhanced_arel = Arel.enhance(Arel.sql_to_arel(sql, binds: binds)) - - result = executor.run(enhanced_arel, updated_context, execute_sql) - - result.to_casted_result + execute_with_middleware(sql, binds, execute_sql).to_casted_result rescue ::PgQuery::ParseError execute_sql.call(sql, binds) ensure - @executing_middleware = false + @executing_middleware_depth -= 1 end def current internal_middleware.dup end - def apply(middleware, &block) + def apply(middleware, cache: @cache, &block) new_middleware = Array.wrap(middleware) - continue_chain(new_middleware, internal_context, &block) + continue_chain(new_middleware, internal_context, cache: cache, &block) end alias only apply def none(&block) - continue_chain([], internal_context, &block) + continue_chain([], internal_context, cache: cache, &block) end - def except(without_middleware, &block) + def except(without_middleware, cache: @cache, &block) without_middleware = Array.wrap(without_middleware) new_middleware = internal_middleware - without_middleware - continue_chain(new_middleware, internal_context, &block) + continue_chain(new_middleware, internal_context, cache: cache, &block) end - def insert_before(new_middleware, existing_middleware, &block) + def insert_before(new_middleware, existing_middleware, cache: @cache, &block) new_middleware = Array.wrap(new_middleware) index = internal_middleware.index(existing_middleware) updated_middleware = internal_middleware.insert(index, *new_middleware) - continue_chain(updated_middleware, internal_context, &block) + continue_chain(updated_middleware, internal_context, cache: cache, &block) end - def prepend(new_middleware, &block) + def prepend(new_middleware, cache: @cache, &block) new_middleware = Array.wrap(new_middleware) updated_middleware = new_middleware + internal_middleware - continue_chain(updated_middleware, internal_context, &block) + continue_chain(updated_middleware, internal_context, cache: cache, &block) end - def insert_after(new_middleware, existing_middleware, &block) + def insert_after(new_middleware, existing_middleware, cache: @cache, &block) new_middleware = Array.wrap(new_middleware) index = internal_middleware.index(existing_middleware) updated_middleware = internal_middleware.insert(index + 1, *new_middleware) - continue_chain(updated_middleware, internal_context, &block) + continue_chain(updated_middleware, internal_context, cache: cache, &block) end - def append(new_middleware, &block) + def append(new_middleware, cache: @cache, &block) new_middleware = Array.wrap(new_middleware) updated_middleware = internal_middleware + new_middleware - continue_chain(updated_middleware, internal_context, &block) + continue_chain(updated_middleware, internal_context, cache: cache, &block) end def context(new_context = nil, &block) if new_context.nil? && !block.nil? raise 'You cannot do a block statement while calling context without arguments' end return internal_context if new_context.nil? - continue_chain(internal_middleware, new_context, &block) + continue_chain(internal_middleware, new_context, cache: @cache, &block) end def to_sql(type, &block) middleware = Arel::Middleware::ToSqlMiddleware.new(type) @@ -107,12 +116,27 @@ attr_reader :internal_middleware attr_reader :internal_context private - def continue_chain(middleware, context, &block) - new_chain = Arel::Middleware::Chain.new(middleware, context) + def execute_with_middleware(sql, binds, execute_sql) + check_middleware_recursion(sql) + + updated_context = context.merge( + original_sql: sql, + original_binds: binds, + cache_accessor: cache_accessor, + ) + + arel = Arel.sql_to_arel(sql, binds: binds) + enhanced_arel = Arel.enhance(arel) + + executor.run(enhanced_arel, updated_context, execute_sql) + end + + def continue_chain(middleware, context, cache:, &block) + new_chain = Arel::Middleware::Chain.new(middleware, context, cache: cache) maybe_execute_block(new_chain, &block) end def maybe_execute_block(new_chain, &block) return new_chain if block.nil? @@ -123,11 +147,11 @@ ensure Arel::Middleware.current_chain = previous_chain end def check_middleware_recursion(sql) - if executing_middleware + if executing_middleware_depth > MAX_RECURSION_DEPTH message = <<~ERROR Middleware is being called from within middleware, aborting execution to prevent endless recursion. You can do the following if you want to execute SQL inside middleware: @@ -138,10 +162,10 @@ #{sql} ERROR raise message else - @executing_middleware = true + @executing_middleware_depth += 1 end end end end end