Skip to content

Instantly share code, notes, and snippets.

Ryan Foster theorygeek

Block or report user

Report or block theorygeek

Hide content and notifications from this user.

Learn more about blocking users

Contact Support about this user’s behavior.

Learn more about reporting abuse

Report abuse
View GitHub Profile
@theorygeek
theorygeek / async_graph_resolution.rb
Last active Jun 28, 2019
Better GraphQL Promise Handling
View async_graph_resolution.rb
module AsyncGraphResolution
extend ActiveSupport::Concern
class_methods do
def async(method_name)
unbound_method = instance_method(method_name)
define_method(method_name) do |*args|
FiberResolver.new(unbound_method.bind(self), context, args, method_name, unbound_method.owner.name)
end
@theorygeek
theorygeek / graphql.rb
Created Mar 27, 2019
Guard Against Recursive Calls
View graphql.rb
GraphQL::Define::AssignObjectField.instance_eval do
alias original_call call
def call(*args, &block)
if !built_in_object(args[0]) && detect_proc_needed(args[2])
raise "You must wrap class-based types inside of a proc before attaching them to legacy-style types. Check #{args[0].name}.#{args[1]}"
end
original_call(*args, &block)
end
View error_instrumentation.rb
# frozen_string_literal: true
class ErrorInstrumentation
def instrument(type, field)
old_resolver = field.resolve_proc
new_resolver = -> (obj, args, ctx) do
begin # rubocop:disable Style/RedundantBegin
result = old_resolver.call(obj, args, ctx)
if result.is_a?(StandardError)
set_error_context(result, type, obj, field, args, ctx)
View initializers_graphql.rb
# Monkey Patch: Add a count field to all connections by default.
# Note, I tried to make this nicer, where it would just call the original method and then add a count field,
# but the challenge outlasted my patience. So I just replaced the whole method. TODO: better way to do this
module GraphQL
module Relay
module ConnectionType
def self.create_type(wrapped_type, edge_type: wrapped_type.edge_type, edge_class: GraphQL::Relay::Edge, nodes_field: ConnectionType.default_nodes_field, &block)
custom_edge_class = edge_class
# Any call that would trigger `wrapped_type.ensure_defined`
View caching_instrumentation.rb
# Define an instrumentation that performs the caching
class CachingInstrumentation
def instrument(_type, field)
return field unless field.metadata.include?(:cache_proc)
old_resolver = field.resolve_proc
new_resolver = -> (obj, args, ctx) do
# Get the caching key
cache_key = field.metadata[:cache_proc].call(obj, args, ctx)
@theorygeek
theorygeek / association_loader.rb
Last active Jul 5, 2019
Preloading Associations with graphql-batch
View association_loader.rb
# frozen_string_literal: true
class AssociationLoader < GraphQL::Batch::Loader
attr_reader :klass, :association
def initialize(klass, association)
raise ArgumentError, "association to load must be a symbol (got #{association.inspect})" unless association.is_a?(Symbol)
raise ArgumentError, "cannot load associations for class #{klass.name}" unless klass < ActiveRecord::Base
raise TypeError, "association #{association} does not exist on #{klass.name}" unless klass.reflect_on_association(association)
@klass = klass
@theorygeek
theorygeek / merged_query.rb
Created Nov 19, 2016
GraphQL Merged Queries
View merged_query.rb
module GraphQL
module Language
class MergedQuery
attr_accessor :original_queries, :original_variables
def initialize(queries, variables)
@original_queries = queries
@original_variables = variables
@fragment_counter = 0
end
@theorygeek
theorygeek / detect_bad_keys.rb
Created Jul 5, 2016
Finds keys in a hash that are not strings
View detect_bad_keys.rb
def detect_bad_keys(obj, parent_path)
result = []
case obj
when Hash
obj.each do |key, value|
if key.is_a?(String)
result.concat(detect_bad_keys(value, [*parent_path, key]))
else
result.push([*parent_path, key])
end
You can’t perform that action at this time.