rails-graphql 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +31 -0
- data/ext/depend +3 -0
- data/ext/extconf.rb +57 -0
- data/ext/graphqlparser/Ast.cpp +346 -0
- data/ext/graphqlparser/Ast.h +1214 -0
- data/ext/graphqlparser/AstNode.h +36 -0
- data/ext/graphqlparser/AstVisitor.h +137 -0
- data/ext/graphqlparser/GraphQLParser.cpp +76 -0
- data/ext/graphqlparser/GraphQLParser.h +55 -0
- data/ext/graphqlparser/JsonVisitor.cpp +161 -0
- data/ext/graphqlparser/JsonVisitor.cpp.inc +456 -0
- data/ext/graphqlparser/JsonVisitor.h +121 -0
- data/ext/graphqlparser/JsonVisitor.h.inc +110 -0
- data/ext/graphqlparser/VERSION +1 -0
- data/ext/graphqlparser/c/GraphQLAst.cpp +324 -0
- data/ext/graphqlparser/c/GraphQLAst.h +180 -0
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +44 -0
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/graphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/graphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/graphqlparser/c/GraphQLParser.h +54 -0
- data/ext/graphqlparser/dump_json_ast.cpp +48 -0
- data/ext/graphqlparser/lexer.lpp +324 -0
- data/ext/graphqlparser/parser.ypp +693 -0
- data/ext/graphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/graphqlparser/parsergen/lexer.h +528 -0
- data/ext/graphqlparser/parsergen/location.hh +189 -0
- data/ext/graphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/graphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/graphqlparser/parsergen/position.hh +179 -0
- data/ext/graphqlparser/parsergen/stack.hh +156 -0
- data/ext/graphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/AstNode.h +36 -0
- data/ext/libgraphqlparser/CMakeLists.txt +148 -0
- data/ext/libgraphqlparser/CONTRIBUTING.md +23 -0
- data/ext/libgraphqlparser/GraphQLParser.cpp +76 -0
- data/ext/libgraphqlparser/GraphQLParser.h +55 -0
- data/ext/libgraphqlparser/JsonVisitor.cpp +161 -0
- data/ext/libgraphqlparser/JsonVisitor.h +121 -0
- data/ext/libgraphqlparser/LICENSE +22 -0
- data/ext/libgraphqlparser/README.clang-tidy +7 -0
- data/ext/libgraphqlparser/README.md +84 -0
- data/ext/libgraphqlparser/ast/ast.ast +203 -0
- data/ext/libgraphqlparser/ast/ast.py +61 -0
- data/ext/libgraphqlparser/ast/c.py +100 -0
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +61 -0
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +39 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +26 -0
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +197 -0
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +61 -0
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +42 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +80 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +64 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +65 -0
- data/ext/libgraphqlparser/ast/license.py +10 -0
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/libgraphqlparser/c/GraphQLParser.h +54 -0
- data/ext/libgraphqlparser/clang-tidy-all.sh +3 -0
- data/ext/libgraphqlparser/cmake/version.cmake +16 -0
- data/ext/libgraphqlparser/dump_json_ast.cpp +48 -0
- data/ext/libgraphqlparser/go/README.md +20 -0
- data/ext/libgraphqlparser/go/callbacks.go +18 -0
- data/ext/libgraphqlparser/go/gotest.go +64 -0
- data/ext/libgraphqlparser/lexer.lpp +324 -0
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +11 -0
- data/ext/libgraphqlparser/parser.ypp +693 -0
- data/ext/libgraphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/libgraphqlparser/parsergen/lexer.h +528 -0
- data/ext/libgraphqlparser/parsergen/location.hh +189 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/libgraphqlparser/parsergen/position.hh +179 -0
- data/ext/libgraphqlparser/parsergen/stack.hh +156 -0
- data/ext/libgraphqlparser/python/CMakeLists.txt +14 -0
- data/ext/libgraphqlparser/python/README.md +5 -0
- data/ext/libgraphqlparser/python/example.py +31 -0
- data/ext/libgraphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/test/BuildCAPI.c +5 -0
- data/ext/libgraphqlparser/test/CMakeLists.txt +25 -0
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +28 -0
- data/ext/libgraphqlparser/test/ParserTests.cpp +352 -0
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +59 -0
- data/ext/libgraphqlparser/test/kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +78 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/valgrind.supp +33 -0
- data/ext/version.cpp +21 -0
- data/lib/generators/graphql/controller_generator.rb +22 -0
- data/lib/generators/graphql/schema_generator.rb +22 -0
- data/lib/generators/graphql/templates/controller.erb +5 -0
- data/lib/generators/graphql/templates/schema.erb +6 -0
- data/lib/graphqlparser.so +0 -0
- data/lib/rails-graphql.rb +2 -0
- data/lib/rails/graphql.rake +1 -0
- data/lib/rails/graphql.rb +185 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +0 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +50 -0
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +39 -0
- data/lib/rails/graphql/argument.rb +220 -0
- data/lib/rails/graphql/callback.rb +124 -0
- data/lib/rails/graphql/collectors.rb +14 -0
- data/lib/rails/graphql/collectors/hash_collector.rb +83 -0
- data/lib/rails/graphql/collectors/idented_collector.rb +73 -0
- data/lib/rails/graphql/collectors/json_collector.rb +114 -0
- data/lib/rails/graphql/config.rb +61 -0
- data/lib/rails/graphql/directive.rb +203 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +59 -0
- data/lib/rails/graphql/directive/include_directive.rb +24 -0
- data/lib/rails/graphql/directive/skip_directive.rb +24 -0
- data/lib/rails/graphql/errors.rb +42 -0
- data/lib/rails/graphql/event.rb +141 -0
- data/lib/rails/graphql/field.rb +318 -0
- data/lib/rails/graphql/field/input_field.rb +92 -0
- data/lib/rails/graphql/field/mutation_field.rb +52 -0
- data/lib/rails/graphql/field/output_field.rb +96 -0
- data/lib/rails/graphql/field/proxied_field.rb +131 -0
- data/lib/rails/graphql/field/resolved_field.rb +96 -0
- data/lib/rails/graphql/field/scoped_config.rb +22 -0
- data/lib/rails/graphql/field/typed_field.rb +104 -0
- data/lib/rails/graphql/helpers.rb +40 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +39 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +152 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +141 -0
- data/lib/rails/graphql/helpers/registerable.rb +103 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +125 -0
- data/lib/rails/graphql/helpers/with_assignment.rb +113 -0
- data/lib/rails/graphql/helpers/with_callbacks.rb +55 -0
- data/lib/rails/graphql/helpers/with_directives.rb +126 -0
- data/lib/rails/graphql/helpers/with_events.rb +81 -0
- data/lib/rails/graphql/helpers/with_fields.rb +141 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +40 -0
- data/lib/rails/graphql/helpers/with_owner.rb +35 -0
- data/lib/rails/graphql/helpers/with_schema_fields.rb +230 -0
- data/lib/rails/graphql/helpers/with_validator.rb +52 -0
- data/lib/rails/graphql/introspection.rb +53 -0
- data/lib/rails/graphql/native.rb +56 -0
- data/lib/rails/graphql/native/functions.rb +38 -0
- data/lib/rails/graphql/native/location.rb +41 -0
- data/lib/rails/graphql/native/pointers.rb +23 -0
- data/lib/rails/graphql/native/visitor.rb +349 -0
- data/lib/rails/graphql/railtie.rb +85 -0
- data/lib/rails/graphql/railties/base_generator.rb +35 -0
- data/lib/rails/graphql/railties/controller.rb +101 -0
- data/lib/rails/graphql/railties/controller_runtime.rb +40 -0
- data/lib/rails/graphql/railties/log_subscriber.rb +62 -0
- data/lib/rails/graphql/request.rb +343 -0
- data/lib/rails/graphql/request/arguments.rb +93 -0
- data/lib/rails/graphql/request/component.rb +100 -0
- data/lib/rails/graphql/request/component/field.rb +225 -0
- data/lib/rails/graphql/request/component/fragment.rb +118 -0
- data/lib/rails/graphql/request/component/operation.rb +178 -0
- data/lib/rails/graphql/request/component/operation/subscription.rb +16 -0
- data/lib/rails/graphql/request/component/spread.rb +119 -0
- data/lib/rails/graphql/request/component/typename.rb +82 -0
- data/lib/rails/graphql/request/context.rb +51 -0
- data/lib/rails/graphql/request/errors.rb +54 -0
- data/lib/rails/graphql/request/event.rb +112 -0
- data/lib/rails/graphql/request/helpers/directives.rb +64 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +87 -0
- data/lib/rails/graphql/request/helpers/value_writers.rb +115 -0
- data/lib/rails/graphql/request/steps/organizable.rb +146 -0
- data/lib/rails/graphql/request/steps/prepareable.rb +33 -0
- data/lib/rails/graphql/request/steps/resolveable.rb +32 -0
- data/lib/rails/graphql/request/strategy.rb +249 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +41 -0
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +36 -0
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +28 -0
- data/lib/rails/graphql/schema.rb +272 -0
- data/lib/rails/graphql/shortcuts.rb +77 -0
- data/lib/rails/graphql/source.rb +371 -0
- data/lib/rails/graphql/source/active_record/builders.rb +154 -0
- data/lib/rails/graphql/source/active_record_source.rb +231 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +87 -0
- data/lib/rails/graphql/to_gql.rb +368 -0
- data/lib/rails/graphql/type.rb +138 -0
- data/lib/rails/graphql/type/enum.rb +206 -0
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +30 -0
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +57 -0
- data/lib/rails/graphql/type/input.rb +134 -0
- data/lib/rails/graphql/type/interface.rb +82 -0
- data/lib/rails/graphql/type/object.rb +111 -0
- data/lib/rails/graphql/type/object/directive_object.rb +34 -0
- data/lib/rails/graphql/type/object/enum_value_object.rb +25 -0
- data/lib/rails/graphql/type/object/field_object.rb +54 -0
- data/lib/rails/graphql/type/object/input_value_object.rb +49 -0
- data/lib/rails/graphql/type/object/schema_object.rb +40 -0
- data/lib/rails/graphql/type/object/type_object.rb +136 -0
- data/lib/rails/graphql/type/scalar.rb +71 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +37 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +35 -0
- data/lib/rails/graphql/type/scalar/float_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/id_scalar.rb +39 -0
- data/lib/rails/graphql/type/scalar/int_scalar.rb +36 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +28 -0
- data/lib/rails/graphql/type/scalar/time_scalar.rb +40 -0
- data/lib/rails/graphql/type/union.rb +87 -0
- data/lib/rails/graphql/type_map.rb +347 -0
- data/lib/rails/graphql/version.rb +7 -0
- data/test/assets/introspection-db.json +0 -0
- data/test/assets/introspection-mem.txt +1 -0
- data/test/assets/introspection.gql +91 -0
- data/test/assets/luke.jpg +0 -0
- data/test/assets/mem.gql +428 -0
- data/test/assets/sqlite.gql +423 -0
- data/test/config.rb +80 -0
- data/test/graphql/request/context_test.rb +70 -0
- data/test/graphql/schema_test.rb +190 -0
- data/test/graphql/source_test.rb +237 -0
- data/test/graphql/type/enum_test.rb +203 -0
- data/test/graphql/type/input_test.rb +138 -0
- data/test/graphql/type/interface_test.rb +72 -0
- data/test/graphql/type/object_test.rb +104 -0
- data/test/graphql/type/scalar/bigint_scalar_test.rb +42 -0
- data/test/graphql/type/scalar/binary_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +40 -0
- data/test/graphql/type/scalar/date_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/date_time_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/decimal_scalar_test.rb +28 -0
- data/test/graphql/type/scalar/float_scalar_test.rb +22 -0
- data/test/graphql/type/scalar/id_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/int_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/string_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/time_scalar_test.rb +36 -0
- data/test/graphql/type/scalar_test.rb +45 -0
- data/test/graphql/type/union_test.rb +82 -0
- data/test/graphql/type_map_test.rb +362 -0
- data/test/graphql/type_test.rb +68 -0
- data/test/graphql_test.rb +55 -0
- data/test/integration/config.rb +56 -0
- data/test/integration/memory/star_wars_introspection_test.rb +144 -0
- data/test/integration/memory/star_wars_query_test.rb +184 -0
- data/test/integration/memory/star_wars_validation_test.rb +99 -0
- data/test/integration/schemas/memory.rb +232 -0
- data/test/integration/schemas/sqlite.rb +82 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +15 -0
- data/test/integration/sqlite/star_wars_mutation_test.rb +82 -0
- data/test/integration/sqlite/star_wars_query_test.rb +71 -0
- data/test/test_ext.rb +48 -0
- metadata +509 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This exposed module allows some shortcuts while working outside of the gem
|
4
|
+
module GraphQL
|
5
|
+
# List of constant shortcuts, as string to not trigger autoload
|
6
|
+
CONST_SHORTCUTS = {
|
7
|
+
Controller: '::Rails::GraphQL::Controller',
|
8
|
+
Directive: '::Rails::GraphQL::Directive',
|
9
|
+
Field: '::Rails::GraphQL::Field',
|
10
|
+
Mutation: '::Rails::GraphQL::Mutation',
|
11
|
+
Request: '::Rails::GraphQL::Request',
|
12
|
+
Schema: '::Rails::GraphQL::Schema',
|
13
|
+
Source: '::Rails::GraphQL::Source',
|
14
|
+
|
15
|
+
Enum: '::Rails::GraphQL::Type::Enum',
|
16
|
+
Input: '::Rails::GraphQL::Type::Input',
|
17
|
+
Interface: '::Rails::GraphQL::Type::Interface',
|
18
|
+
Object: '::Rails::GraphQL::Type::Object',
|
19
|
+
Scalar: '::Rails::GraphQL::Type::Scalar',
|
20
|
+
Union: '::Rails::GraphQL::Type::Union',
|
21
|
+
|
22
|
+
ProxyField: '::Rails::GraphQL::Field::ProxyField',
|
23
|
+
AssociationField: '::Rails::GraphQL::Field::AssociationField',
|
24
|
+
|
25
|
+
ActiveRecordSource: '::Rails::GraphQL::Source::ActiveRecordSource',
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# List of directive shortcuts, which are basically the shortcut of another
|
29
|
+
# shortcut to instantiate a directive.
|
30
|
+
#
|
31
|
+
# ==== Examples
|
32
|
+
#
|
33
|
+
# GraphQL::DeprecatedDirective(...)
|
34
|
+
# # => Rails::GraphQL::Directive::DeprecatedDirective(...)
|
35
|
+
#
|
36
|
+
# Rails::GraphQL::Directive::DeprecatedDirective(...)
|
37
|
+
# # => Rails::GraphQL::Directive::DeprecatedDirective.new(...)
|
38
|
+
DIRECTIVE_SHORTCUTS = %i[DeprecatedDirective IncludeDirective SkipDirective].freeze
|
39
|
+
|
40
|
+
class << self
|
41
|
+
delegate(:to_gql, :to_graphql, :type_map, to: 'Rails::GraphQL')
|
42
|
+
delegate(*DIRECTIVE_SHORTCUTS, to: 'Rails::GraphQL::Directive')
|
43
|
+
|
44
|
+
# See {Request}[rdoc-ref:Rails::GraphQL::Request]
|
45
|
+
def request(*args, **xargs)
|
46
|
+
Rails::GraphQL::Request.new(*args, **xargs)
|
47
|
+
end
|
48
|
+
|
49
|
+
# See {Request}[rdoc-ref:Rails::GraphQL::Request]
|
50
|
+
def execute(*args, **xargs)
|
51
|
+
Rails::GraphQL::Request.execute(*args, **xargs)
|
52
|
+
end
|
53
|
+
|
54
|
+
alias perform execute
|
55
|
+
|
56
|
+
# See {CONST_SHORTCUTS}[rdoc-ref:GraphQL::CONST_SHORTCUTS]
|
57
|
+
def const_defined?(name, *)
|
58
|
+
name = :"ActiveRecord#{name[2..-1]}" if name[0..1] === 'AR'
|
59
|
+
CONST_SHORTCUTS.key?(name) || super
|
60
|
+
end
|
61
|
+
|
62
|
+
# See {CONST_SHORTCUTS}[rdoc-ref:GraphQL::CONST_SHORTCUTS]
|
63
|
+
def const_missing(name)
|
64
|
+
name = :"ActiveRecord#{name[2..-1]}" if name[0..1] === 'AR'
|
65
|
+
return resolved[name] if resolved.key?(name)
|
66
|
+
return super unless CONST_SHORTCUTS.key?(name)
|
67
|
+
resolved[name] = CONST_SHORTCUTS[name].constantize
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Stores resolved constants for increased performance
|
73
|
+
def resolved
|
74
|
+
@@resolved = {}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,371 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
# = GraphQL Source
|
6
|
+
#
|
7
|
+
# Source is an abstract object that can contains fields, objects, and
|
8
|
+
# informations that them are delivered to the relative schemas throughout
|
9
|
+
# proxies, ensuring that it still kepps the main ownership of the objects
|
10
|
+
class Source
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
|
13
|
+
extend Helpers::InheritedCollection
|
14
|
+
extend Helpers::WithSchemaFields
|
15
|
+
extend Helpers::WithAssignment
|
16
|
+
extend Helpers::WithNamespace
|
17
|
+
|
18
|
+
DEFAULT_NAMESPACES = %i[base].freeze
|
19
|
+
|
20
|
+
eager_autoload do
|
21
|
+
autoload :ScopedArguments
|
22
|
+
|
23
|
+
autoload :ActiveRecordSource
|
24
|
+
end
|
25
|
+
|
26
|
+
ScopedConfig = Struct.new(:receiver, :self_object) do # :nodoc: all
|
27
|
+
def respond_to_missing?(method_name, include_private = false)
|
28
|
+
self_object.respond_to?(method_name, include_private) ||
|
29
|
+
receiver.respond_to?(method_name, include_private)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(method_name, *args, **xargs, &block)
|
33
|
+
self_object.respond_to?(method_name, true) \
|
34
|
+
? self_object.send(method_name, *args, **xargs, &block) \
|
35
|
+
: receiver.send(method_name, *args, **xargs, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# If a source is marked as abstract, it means that it generates a new
|
40
|
+
# source describer and any non-abstract class inherited from it will be
|
41
|
+
# described by this new abstraction
|
42
|
+
class_attribute :abstract, instance_writer: false, default: false
|
43
|
+
|
44
|
+
# List of hook names used while describing a new source. This basically
|
45
|
+
# set the order of the execution of the hooks while validating the hooks
|
46
|
+
# callbacks using the +on+ method. Make sure to kepp the +finish+ hook
|
47
|
+
# always at the end of the list
|
48
|
+
class_attribute :hook_names, instance_writer: false,
|
49
|
+
default: %i[start object input query mutation finish].to_set
|
50
|
+
|
51
|
+
# The list of hooks defined in order to describe a source
|
52
|
+
inherited_collection :hooks, instance_reader: false, type: :hash_array
|
53
|
+
|
54
|
+
# The name of the class (or the class itself) to be used as superclass for
|
55
|
+
# the generate GraphQL object type of this source
|
56
|
+
class_attribute :object_class, instance_writer: false,
|
57
|
+
default: '::Rails::GraphQL::Type::Object'
|
58
|
+
|
59
|
+
# The name of the class (or the class itself) to be used as superclass for
|
60
|
+
# the generate GraphQL input type of this source
|
61
|
+
class_attribute :input_class, instance_writer: false,
|
62
|
+
default: '::Rails::GraphQL::Type::Input'
|
63
|
+
|
64
|
+
# Mark if the objects created from this source will build fields for
|
65
|
+
# associations associated to the object
|
66
|
+
class_attribute :with_associations, instance_writer: false, default: true
|
67
|
+
|
68
|
+
# A list of fields to skip when performing shared methods
|
69
|
+
inherited_collection :skip_fields, instance_reader: false
|
70
|
+
|
71
|
+
# A list of fields to skip but segmented by holder source
|
72
|
+
inherited_collection :segmented_skip_fields, instance_reader: false, type: :hash_set
|
73
|
+
|
74
|
+
# The purpose of instantiating a source is to have access to its public
|
75
|
+
# methods. It then runs from the strategy perspective, pointing out any
|
76
|
+
# other methods to the manually set event
|
77
|
+
delegate_missing_to :event
|
78
|
+
attr_reader :event
|
79
|
+
|
80
|
+
self.abstract = true
|
81
|
+
|
82
|
+
class << self
|
83
|
+
attr_reader :schemas
|
84
|
+
|
85
|
+
delegate :field, :proxy_field, :overwrite_field, :[], :field?,
|
86
|
+
:field_names, :gql_name, to: :object
|
87
|
+
|
88
|
+
def kind # :nodoc:
|
89
|
+
:source
|
90
|
+
end
|
91
|
+
|
92
|
+
# Sources are close related to objects, meaning that they are type based
|
93
|
+
def base_type_class
|
94
|
+
:Type
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get the main name of the source
|
98
|
+
def base_name
|
99
|
+
name.demodulize[0..-7] unless abstract?
|
100
|
+
end
|
101
|
+
|
102
|
+
# Wait the end of the class in order to create the objects
|
103
|
+
def inherited(subclass)
|
104
|
+
subclass.abstract = false
|
105
|
+
super if defined? super
|
106
|
+
|
107
|
+
pending[subclass] ||= caller(1).find do |item|
|
108
|
+
!item.end_with?("`inherited'")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find a source for a given object. If none is found, then raise an
|
113
|
+
# exception
|
114
|
+
def find_for!(object)
|
115
|
+
find_for(object) || raise(::ArgumentError, <<~MSG.squish)
|
116
|
+
Unable to find a source for "#{object.name}".
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
|
120
|
+
# Using the list of +base_sources+, find the first one that can handle
|
121
|
+
# the given +object+
|
122
|
+
def find_for(object)
|
123
|
+
object = object.constantize if object.is_a?(String)
|
124
|
+
base_sources.reverse_each.find { |source| object <= source.assigned_class }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return the GraphQL object type associated with the source. It will
|
128
|
+
# create one if it's not defined yet. The created class will be added
|
129
|
+
# to the +::GraphQL+ namespace with the addition of any namespace of the
|
130
|
+
# currect class
|
131
|
+
def object
|
132
|
+
@object ||= create_type(superclass: object_class)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return the GraphQL input type associated with the source. It will
|
136
|
+
# create one if it's not defined yet. The created class will be added
|
137
|
+
# to the +::GraphQL+ namespace with the addition of any namespace of the
|
138
|
+
# currect class
|
139
|
+
def input
|
140
|
+
@input ||= create_type(superclass: input_class)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Check if the object was already built
|
144
|
+
def built?
|
145
|
+
defined?(@built) && !!@built
|
146
|
+
end
|
147
|
+
|
148
|
+
# Attach all defined schema fields into the schemas using the namespaces
|
149
|
+
# configured for the source
|
150
|
+
def attach_fields!
|
151
|
+
refresh_schemas!
|
152
|
+
schemas.each_value do |schema|
|
153
|
+
Helpers::WithSchemaFields::SCHEMA_FIELD_TYPES.keys.each do |type|
|
154
|
+
list = public_send("#{type}_fields")
|
155
|
+
next if list.empty?
|
156
|
+
|
157
|
+
list.each_value do |field|
|
158
|
+
next if schema.has_field?(type, field)
|
159
|
+
schema.add_proxy_field(type, field)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Find all the schemas associated with the configured namespaces
|
166
|
+
def refresh_schemas!
|
167
|
+
@schemas = (namespaces.presence || DEFAULT_NAMESPACES).map do |ns|
|
168
|
+
(schema = Schema.find(ns)).present? ? [ns, schema] : nil
|
169
|
+
end.compact.to_h
|
170
|
+
end
|
171
|
+
|
172
|
+
def eager_load! # :nodoc:
|
173
|
+
super
|
174
|
+
|
175
|
+
build_pending!
|
176
|
+
end
|
177
|
+
|
178
|
+
protected
|
179
|
+
|
180
|
+
# Find a given +type+ on the same namespaces of the source. It will
|
181
|
+
# raise an exception if the +type+ can not be found
|
182
|
+
def find_type!(type, **xargs)
|
183
|
+
xargs[:base_class] = :Type
|
184
|
+
xargs[:namespaces] = namespaces
|
185
|
+
GraphQL.type_map.fetch!(type, **xargs)
|
186
|
+
end
|
187
|
+
|
188
|
+
# A little bypass to the actual type map after register method which
|
189
|
+
# just add the namesapace by default
|
190
|
+
# See {TypeMap#after_register}[rdoc-ref:Rails::GraphQL::TypeMap#after_register]
|
191
|
+
def type_map_after_register(*args, **xargs, &block)
|
192
|
+
xargs[:namespaces] ||= namespaces
|
193
|
+
GraphQL.type_map.after_register(*args, **xargs, &block)
|
194
|
+
end
|
195
|
+
|
196
|
+
# A helper method to create an enum type
|
197
|
+
def create_enum(enum_name, values, **xargs, &block)
|
198
|
+
enumerator = values.each_pair if values.respond_to?(:each_pair)
|
199
|
+
enumerator ||= values.each.with_index
|
200
|
+
|
201
|
+
xargs = xargs.reverse_merge(once: true)
|
202
|
+
create_type(:enum, as: enum_name.classify, **xargs) do
|
203
|
+
indexed! if enumerator.first.last.is_a?(Numeric)
|
204
|
+
enumerator.sort_by(&:last).map(&:first).each(&method(:add))
|
205
|
+
instance_exec(&block) if block.present?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Helper method to create a class based on the given type and allows
|
210
|
+
# several other settings to be executed on it
|
211
|
+
def create_type(type = nil, **xargs, &block)
|
212
|
+
name = "#{gql_module.name}::#{xargs.delete(:as) || base_name}"
|
213
|
+
superclass = xargs.delete(:superclass)
|
214
|
+
with_owner = xargs.delete(:with_owner)
|
215
|
+
|
216
|
+
if superclass.nil?
|
217
|
+
superclass = type.to_s.classify
|
218
|
+
elsif superclass.is_a?(String)
|
219
|
+
superclass = superclass.constantize
|
220
|
+
end
|
221
|
+
|
222
|
+
source = self
|
223
|
+
Schema.send(:create_type, name, superclass, **xargs) do
|
224
|
+
include Helpers::WithOwner if with_owner
|
225
|
+
set_namespaces(*source.namespaces)
|
226
|
+
|
227
|
+
self.owner = source if respond_to?(:owner=)
|
228
|
+
self.assigned_to = source.safe_assigned_class \
|
229
|
+
if source.assigned? && is_a?(Helpers::WithAssignment)
|
230
|
+
|
231
|
+
instance_exec(&block) if block.present?
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Add fields to be skipped on the given +source+ as the segment
|
236
|
+
def skip_on(source, *fields)
|
237
|
+
segmented_skip_fields[source] += fields.flatten.compact.map(&:to_sym).to_set
|
238
|
+
end
|
239
|
+
|
240
|
+
# Add a new description hook. You can use +throw :skip+ and skip
|
241
|
+
# parent hooks. If the class is already built, then execute the hook.
|
242
|
+
# Use the +unshift: true+ to add the hook at the beginning of the
|
243
|
+
# list, which will then be the last to run
|
244
|
+
def on(hook_name, unshift: false, &block)
|
245
|
+
raise ArgumentError, <<~MSG.squish unless hook_names.include?(hook_name.to_sym)
|
246
|
+
The #{hook_name.inspect} is not a valid hook method.
|
247
|
+
MSG
|
248
|
+
|
249
|
+
if built?
|
250
|
+
send("run_#{hook_name}_hooks", block)
|
251
|
+
else
|
252
|
+
hooks[hook_name.to_sym].public_send(unshift ? :unshift : :push, block)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Creates a hook that throws a done action, preventing any parent hooks
|
257
|
+
def skip(*names)
|
258
|
+
names.each do |hook_name|
|
259
|
+
hook_name = hook_name.to_s.singularize.to_sym
|
260
|
+
on(hook_name) { throw :skip }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# This is a shortcut to +skip hook_name+ and then
|
265
|
+
# +on hook_name do; end+
|
266
|
+
def override(hook_name, &block)
|
267
|
+
skip(hook_name)
|
268
|
+
on(hook_name, &block)
|
269
|
+
end
|
270
|
+
|
271
|
+
# It's an alternative to +self.hook_names -= %i[*names]+ which
|
272
|
+
# disables a specific hook
|
273
|
+
def disable(*names)
|
274
|
+
self.hook_names -= names.flatten.map do |hook_name|
|
275
|
+
hook_name.to_s.singularize.to_sym
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# It's an alternative to +self.hook_names += %i[*names]+ which
|
280
|
+
# enables additional hooks
|
281
|
+
def enable(*names)
|
282
|
+
self.hook_names += names.flatten.map do |hook_name|
|
283
|
+
hook_name.to_s.singularize.to_sym
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Return the module where the GraphQL types should be created at
|
288
|
+
def gql_module
|
289
|
+
name.starts_with?('GraphQL::') ? module_parent : ::GraphQL
|
290
|
+
end
|
291
|
+
|
292
|
+
# Get the list of fields to be skipped from the given +holder+ as the
|
293
|
+
# segment source
|
294
|
+
def skips_for(holder)
|
295
|
+
segment = holder.kind
|
296
|
+
segment = :input if segment.eql?(:input_object)
|
297
|
+
segmented = all_segmented_skip_fields[segment]
|
298
|
+
segmented.present? ? all_skip_fields + segmented : all_skip_fields
|
299
|
+
end
|
300
|
+
|
301
|
+
private
|
302
|
+
|
303
|
+
# The list of pending sources to be built asscoaited to where they
|
304
|
+
# were defined
|
305
|
+
def pending
|
306
|
+
@@pending ||= {}
|
307
|
+
end
|
308
|
+
|
309
|
+
# Check if there are pending sources to be built
|
310
|
+
def pending?
|
311
|
+
pending.any?
|
312
|
+
end
|
313
|
+
|
314
|
+
# Build the pending sources
|
315
|
+
def build_pending!
|
316
|
+
while (klass, = pending.shift)
|
317
|
+
klass.send(:build!) unless klass.abstract?
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Find all classes that inherits from source that are abstract,
|
322
|
+
# meaning that they are a base sources
|
323
|
+
def base_sources
|
324
|
+
@@base_sources ||= begin
|
325
|
+
eager_load!
|
326
|
+
descendants.select(&:abstract?).to_set
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Build all the objects associated with this source
|
331
|
+
def build!
|
332
|
+
return if built?
|
333
|
+
|
334
|
+
raise DefinitionError, <<~MSG.squish if abstract
|
335
|
+
Abstract source #{name} cannot be built.
|
336
|
+
MSG
|
337
|
+
|
338
|
+
@built = true
|
339
|
+
|
340
|
+
catch(:done) do
|
341
|
+
hook_names.each do |hook_name|
|
342
|
+
break if hook_name === :finish
|
343
|
+
catch(:skip) { send("run_#{hook_name}_hooks") }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
catch(:skip) { send(:run_finish_hooks) } if respond_to?(:run_finish_hooks, true)
|
348
|
+
end
|
349
|
+
|
350
|
+
{
|
351
|
+
start: 'self',
|
352
|
+
finish: 'self',
|
353
|
+
object: 'Helpers::AttributeDelegator.new(self, :object)',
|
354
|
+
input: 'Helpers::AttributeDelegator.new(self, :input)',
|
355
|
+
query: format('schema_scoped_config(self, %s)', ':query'),
|
356
|
+
mutation: format('schema_scoped_config(self, %s)', ':mutation'),
|
357
|
+
subscription: format('schema_scoped_config(self, %s)', ':subscription'),
|
358
|
+
}.each do |key, object|
|
359
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
360
|
+
def run_#{key}_hooks(list = nil)
|
361
|
+
source_config = Source::ScopedConfig.new(self, #{object})
|
362
|
+
Array.wrap(list.presence || all_hooks[:#{key}]).reverse_each do |block|
|
363
|
+
source_config.instance_exec(&block)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
RUBY
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
# All the helper methods for building the source
|
6
|
+
module Source::ActiveRecordSource::Builders
|
7
|
+
# Override the object class to identify interfaces due to STI
|
8
|
+
def object_class
|
9
|
+
sti_interface? ? interface_class : super
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get all unique attribute names that exists in the current model
|
13
|
+
def reflection_attributes(holder)
|
14
|
+
items = []
|
15
|
+
each_reflection(holder) do |item|
|
16
|
+
next unless item.belongs_to?
|
17
|
+
next items << item.foreign_key.to_s unless item.polymorphic?
|
18
|
+
items += [item.foreign_type, item.foreign_key]
|
19
|
+
end
|
20
|
+
|
21
|
+
items.compact.flatten.unshift(primary_key)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Iterate over all the attributes, except the primary key, from the model
|
25
|
+
# but already set to be imported to GraphQL fields
|
26
|
+
# TODO: Turn into an enumerator
|
27
|
+
def each_attribute(holder, skip_primary_key = true)
|
28
|
+
adapter_key = GraphQL.ar_adapter_key(adapter_name)
|
29
|
+
|
30
|
+
skip_fields = skips_for(holder).map(&:to_s)
|
31
|
+
skip_fields << model.inheritance_column
|
32
|
+
skip_fields << primary_key unless skip_primary_key
|
33
|
+
|
34
|
+
send("#{adapter_key}_attributes") do |attribute, *args|
|
35
|
+
yield attribute, *args unless skip_fields.include?(attribute)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Iterate over all the model reflections
|
40
|
+
# TODO: Turn into an enumerator
|
41
|
+
def each_reflection(holder)
|
42
|
+
skip_fields = skips_for(holder).map(&:to_s)
|
43
|
+
model._reflections.each_value do |reflection|
|
44
|
+
next if skip_fields.include?(reflection.name.to_s)
|
45
|
+
|
46
|
+
reflection = model._reflections[reflection.to_s] \
|
47
|
+
unless reflection.is_a?(abstract_reflection)
|
48
|
+
|
49
|
+
yield reflection unless reflection.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Check if the given model is consider an interface due to single table
|
56
|
+
# inheritance and the given model is the base class
|
57
|
+
def sti_interface?
|
58
|
+
@sti_interface ||= begin
|
59
|
+
model.has_attribute?(model.inheritance_column) && model.base_class == model
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Build all enums associated to the class, collecting them from the
|
64
|
+
# model setting
|
65
|
+
def build_enum_types
|
66
|
+
return remove_instance_variable(:@enums) if enums.blank?
|
67
|
+
|
68
|
+
@enums = enums.map do |attribute, setting|
|
69
|
+
[attribute.to_s, create_enum(attribute.to_s, setting, once: true)]
|
70
|
+
rescue DuplicatedError
|
71
|
+
next
|
72
|
+
end.compact.to_h.freeze
|
73
|
+
end
|
74
|
+
|
75
|
+
# Build all necessary attribute fields into the given +holder+
|
76
|
+
def build_attribute_fields(holder, **field_options)
|
77
|
+
attributes_as_ids = reflection_attributes(holder)
|
78
|
+
each_attribute(holder) do |key, type, **options|
|
79
|
+
next if skip.include?(key) || holder.field?(key)
|
80
|
+
|
81
|
+
str_key = key.to_s
|
82
|
+
type = (defined?(@enums) && @enums.key?(str_key) && @enums[str_key]) ||
|
83
|
+
(attributes_as_ids.include?(str_key) && :id) || type
|
84
|
+
|
85
|
+
options[:null] = !attr_required?(key) unless options.key?(:null)
|
86
|
+
holder.field(key, type, **options.merge(field_options[key] || {}))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Build all necessary reflection fields into the given +holder+
|
91
|
+
def build_reflection_fields(holder)
|
92
|
+
each_reflection(holder) do |item|
|
93
|
+
next if holder.field?(item.name)
|
94
|
+
type_map_after_register(item.klass.name) do |type|
|
95
|
+
next unless (type.object? && type.try(:assigned_to) != item.klass) ||
|
96
|
+
type.interface?
|
97
|
+
|
98
|
+
options = reflection_to_options(item)
|
99
|
+
|
100
|
+
if type <= Source::ActiveRecordSource
|
101
|
+
source_name = item.collection? ? type.plural : type.singular
|
102
|
+
proxy_options = options.merge(alias: reflection.name, of_type: :proxy)
|
103
|
+
|
104
|
+
if (source = type.query_fields[source_name]).present?
|
105
|
+
field = holder.safe_field(source, **proxy_options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
field ||= holder.field(item.name, type, **options)
|
110
|
+
field.before_resolve(:preload_association, item.name)
|
111
|
+
field.before_resolve(:build_association_scope, item.name)
|
112
|
+
field.resolve(:parent_owned_records, item.collection?)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Build all +accepts_nested_attributes_for+ inside the input object
|
118
|
+
def build_reflection_inputs(holder)
|
119
|
+
model.nested_attributes_options.each_key do |reflection_name|
|
120
|
+
next if (reflection = model._reflect_on_association(reflection_name)).nil?
|
121
|
+
|
122
|
+
expected_name = reflection.klass.name.tr(':', '')
|
123
|
+
expected_name += 'Input' unless expected_name.ends_with?('Input')
|
124
|
+
|
125
|
+
type_map_after_register(expected_name) do |input|
|
126
|
+
options = reflection_to_options(reflection).merge(null: true)
|
127
|
+
field_name = "#{reflection.name}_attributes"
|
128
|
+
holder.safe_field(field_name, input, **options)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Transform a replection into a field options
|
134
|
+
def reflection_to_options(reflection)
|
135
|
+
options = { array: reflection.collection? }
|
136
|
+
|
137
|
+
required = options[:array]
|
138
|
+
required ||= attr_required?(reflection.name)
|
139
|
+
required ||= attr_required?(reflection.association_foreign_key) \
|
140
|
+
if reflection.belongs_to?
|
141
|
+
|
142
|
+
options[:nullable] = !options[:array]
|
143
|
+
options[:null] = !required
|
144
|
+
options
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def abstract_reflection # :nodoc:
|
150
|
+
::ActiveRecord::Reflection::AbstractReflection
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|