From 9b0333be632cf77c4d4846a3434aa20993a61095 Mon Sep 17 00:00:00 2001 From: Greg MacWilliam Date: Fri, 20 Jun 2025 07:55:36 -0400 Subject: [PATCH] remove depth executor prototype. --- README.md | 6 +- lib/graphql/cardinal.rb | 1 - lib/graphql/cardinal/depth_executor.rb | 95 ------------------- lib/graphql/cardinal/executor.rb | 2 - test/graphql/cardinal/depth_executor_test.rb | 9 -- .../graphql/cardinal/executor/loaders_test.rb | 6 +- ...adth_executor_test.rb => executor_test.rb} | 0 test/test_helper.rb | 7 +- 8 files changed, 7 insertions(+), 119 deletions(-) delete mode 100644 lib/graphql/cardinal/depth_executor.rb delete mode 100644 test/graphql/cardinal/depth_executor_test.rb rename test/graphql/cardinal/{breadth_executor_test.rb => executor_test.rb} (100%) diff --git a/README.md b/README.md index 9b8579b..8957c9a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **An (experimental) breadth-first GraphQL executor for Ruby** -Depth-first execution resolves every object field descending down a response tree, while breadth-first visits every _selection position_ once with an aggregated set of objects. The breadth-first approach tends to be much faster due to fewer resolver calls and intermediary promises. +Depth-first execution resolves every object field descending down a response tree, while breadth-first visits every _selection position_ once with an aggregated set of objects. The breadth-first approach is much faster due to fewer resolver calls and intermediary promises. ```shell graphql-ruby: 140002 resolvers @@ -43,5 +43,5 @@ Breadth-first then runs a single resolver per document selection, and coalesces While bigger responses will always take longer to process, the workload is your own business logic with very little GraphQL execution overhead. Other advantages: -* Eliminates the need for DataLoader promises, because resolvers are inherently batched. -* Executes via flat queuing without deep recursion and huge call stacks. +* Eliminates boilerplate need for DataLoader promises, because resolvers are inherently batched. +* Executes via flat queuing without deep recursion and large call stacks. diff --git a/lib/graphql/cardinal.rb b/lib/graphql/cardinal.rb index a91c887..7828ad1 100644 --- a/lib/graphql/cardinal.rb +++ b/lib/graphql/cardinal.rb @@ -15,5 +15,4 @@ module Cardinal require_relative "cardinal/loader" require_relative "cardinal/field_resolvers" require_relative "cardinal/executor" -require_relative "cardinal/depth_executor" require_relative "cardinal/version" diff --git a/lib/graphql/cardinal/depth_executor.rb b/lib/graphql/cardinal/depth_executor.rb deleted file mode 100644 index 2956ffe..0000000 --- a/lib/graphql/cardinal/depth_executor.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -require_relative "./executor/hot_paths" - -module GraphQL - module Cardinal - class DepthExecutor - include GraphQL::Cardinal::Executor::HotPaths - - attr_reader :exec_count - - def initialize(schema, resolvers, document, root_object) - @schema = schema - @resolvers = resolvers - @document = document - @root_object = root_object - @exec_count = 0 - end - - def perform - @query = GraphQL::Query.new(@schema, document: @document) # << for schema reference - operation = @query.selected_operation - parent_type = @query.root_type_for_operation(operation.operation_type) - { - "data" => exec_object_scope(parent_type, operation.selections, @root_object, path: []), - } - end - - private - - def exec_object_scope(parent_type, selections, source, path:, response: nil) - response ||= {} - selections.each do |node| - case node - when GraphQL::Language::Nodes::Field - node_type = @query.get_field(parent_type, node.name).type - named_type = node_type.unwrap - field_name = node.alias || node.name - path.push(field_name) - - resolved_source = @resolvers.dig(parent_type.graphql_name, node.name).call(source) - @exec_count += 1 - - response[field_name] = if node_type.list? - node_type = node_type.of_type while node_type.non_null? - exec_list_scope(node_type, node.selections, resolved_source, path: path) - elsif named_type.kind.leaf? - if !resolved_source.nil? && named_type.kind.scalar? - coerce_scalar_value(named_type, resolved_source) - else - resolved_source - end - else - exec_object_scope(named_type, node.selections, resolved_source, path: path) - end - path.pop - - when GraphQL::Language::Nodes::InlineFragment - fragment_type = node.type ? @query.get_type(node.type.name) : parent_type - exec_object_scope(fragment_type, node.selections, source, path: path, response: response) - - when GraphQL::Language::Nodes::FragmentSpread - fragment = @query.fragments[node.name] - fragment_type = @query.get_type(fragment.type.name) - exec_object_scope(fragment_type, node.selections, source, path: path, response: response) - - else - raise DocumentError.new("selection node type") - end - end - - response - end - - def exec_list_scope(parent_type, selections, sources, path:) - parent_type = parent_type.of_type while parent_type.non_null? - next_node_type = parent_type.of_type - named_type = parent_type.unwrap - - sources.map.with_index do |src, i| - path.push(i) - result = if next_node_type.list? - resolve_list_scope(next_node_type, selections, src, path: path) - elsif named_type.kind.leaf? - src - else - exec_object_scope(named_type, selections, src, path: path) - end - path.pop - result - end - end - end - end -end diff --git a/lib/graphql/cardinal/executor.rb b/lib/graphql/cardinal/executor.rb index 37b399d..fa04a27 100644 --- a/lib/graphql/cardinal/executor.rb +++ b/lib/graphql/cardinal/executor.rb @@ -294,7 +294,5 @@ def report_exception(message, path: @path.dup) # todo: hook up some kind of error reporting... end end - - class BreadthExecutor < Executor; end end end diff --git a/test/graphql/cardinal/depth_executor_test.rb b/test/graphql/cardinal/depth_executor_test.rb deleted file mode 100644 index b683f07..0000000 --- a/test/graphql/cardinal/depth_executor_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require "test_helper" - -class GraphQL::Cardinal::DepthExecutorTest < Minitest::Test - def test_runs - assert_equal BASIC_SOURCE, depth_exec(BASIC_DOCUMENT, BASIC_SOURCE).dig("data") - end -end diff --git a/test/graphql/cardinal/executor/loaders_test.rb b/test/graphql/cardinal/executor/loaders_test.rb index 7dd0546..60f62b7 100644 --- a/test/graphql/cardinal/executor/loaders_test.rb +++ b/test/graphql/cardinal/executor/loaders_test.rb @@ -95,7 +95,7 @@ def test_splits_loaders_by_group_across_fields } } - executor = GraphQL::Cardinal::BreadthExecutor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) + executor = GraphQL::Cardinal::Executor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) assert_equal expected, executor.perform assert_equal [["Apple", "Banana"], ["Coconut"]], FancyLoader.perform_keys end @@ -129,7 +129,7 @@ def test_maintains_ordered_selections_around_object_fields } } - executor = GraphQL::Cardinal::BreadthExecutor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) + executor = GraphQL::Cardinal::Executor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) result = executor.perform assert_equal expected, result assert_equal result.dig("data", "widget").keys, expected.dig("data", "widget").keys @@ -164,7 +164,7 @@ def test_maintains_ordered_selections_around_leaf_fields } } - executor = GraphQL::Cardinal::BreadthExecutor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) + executor = GraphQL::Cardinal::Executor.new(LOADER_SCHEMA, LOADER_RESOLVERS, document, source) result = executor.perform assert_equal expected, result assert_equal result.dig("data", "widget").keys, expected.dig("data", "widget").keys diff --git a/test/graphql/cardinal/breadth_executor_test.rb b/test/graphql/cardinal/executor_test.rb similarity index 100% rename from test/graphql/cardinal/breadth_executor_test.rb rename to test/graphql/cardinal/executor_test.rb diff --git a/test/test_helper.rb b/test/test_helper.rb index db0154e..487bc42 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -19,11 +19,6 @@ require_relative './fixtures' def breadth_exec(query, source, variables: {}, context: {}) - executor = GraphQL::Cardinal::BreadthExecutor.new(SCHEMA, BREADTH_RESOLVERS, GraphQL.parse(query), source) - executor.perform -end - -def depth_exec(query, source, variables: {}, context: {}) - executor = GraphQL::Cardinal::DepthExecutor.new(SCHEMA, DEPTH_RESOLVERS, GraphQL.parse(query), source) + executor = GraphQL::Cardinal::Executor.new(SCHEMA, BREADTH_RESOLVERS, GraphQL.parse(query), source) executor.perform end