rails-graphql 0.2.1 → 1.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. checksums.yaml +4 -4
  2. data/ext/console.rb +18 -0
  3. data/ext/extconf.h +3 -0
  4. data/ext/extconf.rb +1 -54
  5. data/ext/gql_parser.c +646 -0
  6. data/ext/shared.c +482 -0
  7. data/ext/shared.h +177 -0
  8. data/lib/gql_parser.so +0 -0
  9. data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
  10. data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
  11. data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
  12. data/lib/rails/graphql/alternative/field_set.rb +36 -0
  13. data/lib/rails/graphql/alternative/mutation.rb +17 -0
  14. data/lib/rails/graphql/alternative/query.rb +93 -0
  15. data/lib/rails/graphql/alternative/subscription.rb +17 -0
  16. data/lib/rails/graphql/alternative.rb +20 -0
  17. data/lib/rails/graphql/argument.rb +21 -24
  18. data/lib/rails/graphql/callback.rb +24 -9
  19. data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
  20. data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
  21. data/lib/rails/graphql/collectors/json_collector.rb +22 -17
  22. data/lib/rails/graphql/collectors.rb +4 -4
  23. data/lib/rails/graphql/config.rb +130 -15
  24. data/lib/rails/graphql/directive/cached_directive.rb +33 -0
  25. data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
  26. data/lib/rails/graphql/directive/include_directive.rb +5 -4
  27. data/lib/rails/graphql/directive/skip_directive.rb +5 -4
  28. data/lib/rails/graphql/directive.rb +118 -63
  29. data/lib/rails/graphql/errors.rb +33 -4
  30. data/lib/rails/graphql/event.rb +16 -5
  31. data/lib/rails/graphql/field/authorized_field.rb +19 -3
  32. data/lib/rails/graphql/field/input_field.rb +11 -10
  33. data/lib/rails/graphql/field/mutation_field.rb +42 -7
  34. data/lib/rails/graphql/field/output_field.rb +102 -13
  35. data/lib/rails/graphql/field/proxied_field.rb +31 -22
  36. data/lib/rails/graphql/field/resolved_field.rb +26 -24
  37. data/lib/rails/graphql/field/scoped_config.rb +10 -4
  38. data/lib/rails/graphql/field/subscription_field.rb +140 -0
  39. data/lib/rails/graphql/field/typed_field.rb +43 -22
  40. data/lib/rails/graphql/field.rb +70 -56
  41. data/lib/rails/graphql/global_id.rb +85 -0
  42. data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
  43. data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
  44. data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
  45. data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -0
  46. data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
  47. data/lib/rails/graphql/helpers/instantiable.rb +15 -0
  48. data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
  49. data/lib/rails/graphql/helpers/registerable.rb +44 -62
  50. data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
  51. data/lib/rails/graphql/helpers/with_arguments.rb +31 -27
  52. data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
  53. data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
  54. data/lib/rails/graphql/helpers/with_description.rb +71 -0
  55. data/lib/rails/graphql/helpers/with_directives.rb +54 -30
  56. data/lib/rails/graphql/helpers/with_events.rb +21 -23
  57. data/lib/rails/graphql/helpers/with_fields.rb +76 -22
  58. data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
  59. data/lib/rails/graphql/helpers/with_name.rb +43 -0
  60. data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
  61. data/lib/rails/graphql/helpers/with_owner.rb +8 -7
  62. data/lib/rails/graphql/helpers/with_schema_fields.rb +137 -55
  63. data/lib/rails/graphql/helpers/with_validator.rb +9 -9
  64. data/lib/rails/graphql/helpers.rb +10 -3
  65. data/lib/rails/graphql/introspection.rb +43 -36
  66. data/lib/rails/graphql/railtie.rb +88 -33
  67. data/lib/rails/graphql/railties/base_generator.rb +3 -9
  68. data/lib/rails/graphql/railties/channel.rb +157 -0
  69. data/lib/rails/graphql/railties/controller.rb +62 -17
  70. data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
  71. data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
  72. data/lib/rails/graphql/request/arguments.rb +24 -49
  73. data/lib/rails/graphql/request/backtrace.rb +191 -0
  74. data/lib/rails/graphql/request/component/field.rb +86 -65
  75. data/lib/rails/graphql/request/component/fragment.rb +72 -24
  76. data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
  77. data/lib/rails/graphql/request/component/operation.rb +63 -31
  78. data/lib/rails/graphql/request/component/spread.rb +68 -25
  79. data/lib/rails/graphql/request/component/typename.rb +27 -12
  80. data/lib/rails/graphql/request/component.rb +75 -36
  81. data/lib/rails/graphql/request/context.rb +18 -8
  82. data/lib/rails/graphql/request/errors.rb +16 -6
  83. data/lib/rails/graphql/request/event.rb +19 -8
  84. data/lib/rails/graphql/request/helpers/directives.rb +68 -27
  85. data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
  86. data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
  87. data/lib/rails/graphql/request/prepared_data.rb +98 -0
  88. data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
  89. data/lib/rails/graphql/request/steps/organizable.rb +110 -48
  90. data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
  91. data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
  92. data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
  93. data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
  94. data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
  95. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
  96. data/lib/rails/graphql/request/strategy.rb +131 -75
  97. data/lib/rails/graphql/request/subscription.rb +80 -0
  98. data/lib/rails/graphql/request.rb +305 -86
  99. data/lib/rails/graphql/schema.rb +240 -48
  100. data/lib/rails/graphql/shortcuts.rb +22 -3
  101. data/lib/rails/graphql/source/active_record/builders.rb +49 -35
  102. data/lib/rails/graphql/source/active_record_source.rb +70 -54
  103. data/lib/rails/graphql/source/base.rb +111 -0
  104. data/lib/rails/graphql/source/builder.rb +128 -0
  105. data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
  106. data/lib/rails/graphql/source.rb +89 -213
  107. data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
  108. data/lib/rails/graphql/subscription/provider/base.rb +191 -0
  109. data/lib/rails/graphql/subscription/provider.rb +18 -0
  110. data/lib/rails/graphql/subscription/store/base.rb +145 -0
  111. data/lib/rails/graphql/subscription/store/memory.rb +127 -0
  112. data/lib/rails/graphql/subscription/store.rb +19 -0
  113. data/lib/rails/graphql/subscription.rb +17 -0
  114. data/lib/rails/graphql/to_gql.rb +29 -32
  115. data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
  116. data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
  117. data/lib/rails/graphql/type/enum.rb +34 -48
  118. data/lib/rails/graphql/type/input.rb +74 -23
  119. data/lib/rails/graphql/type/interface.rb +16 -26
  120. data/lib/rails/graphql/type/object/directive_object.rb +4 -4
  121. data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
  122. data/lib/rails/graphql/type/object/field_object.rb +24 -6
  123. data/lib/rails/graphql/type/object/input_value_object.rb +3 -3
  124. data/lib/rails/graphql/type/object/schema_object.rb +5 -8
  125. data/lib/rails/graphql/type/object/type_object.rb +29 -19
  126. data/lib/rails/graphql/type/object.rb +26 -23
  127. data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
  128. data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
  129. data/lib/rails/graphql/type/scalar/binary_scalar.rb +3 -3
  130. data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
  131. data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
  132. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
  133. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -3
  134. data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
  135. data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
  136. data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
  137. data/lib/rails/graphql/type/scalar/json_scalar.rb +39 -0
  138. data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
  139. data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
  140. data/lib/rails/graphql/type/scalar.rb +25 -22
  141. data/lib/rails/graphql/type/union.rb +14 -16
  142. data/lib/rails/graphql/type.rb +34 -25
  143. data/lib/rails/graphql/type_map.rb +256 -164
  144. data/lib/rails/graphql/uri.rb +166 -0
  145. data/lib/rails/graphql/version.rb +15 -3
  146. data/lib/rails/graphql.rake +3 -0
  147. data/lib/rails/graphql.rb +85 -52
  148. data/lib/rails-graphql.rb +1 -1
  149. data/test/assets/en.yml +29 -0
  150. data/test/assets/introspection-mem.txt +1 -1
  151. data/test/assets/mem.gql +18 -45
  152. data/test/assets/mysql.gql +392 -0
  153. data/test/assets/sqlite.gql +21 -12
  154. data/test/assets/translate.gql +335 -0
  155. data/test/config.rb +18 -8
  156. data/test/graphql/schema_test.rb +12 -19
  157. data/test/graphql/source_test.rb +8 -75
  158. data/test/graphql/type/enum_test.rb +207 -203
  159. data/test/graphql/type/input_test.rb +14 -9
  160. data/test/graphql/type/interface_test.rb +4 -4
  161. data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
  162. data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
  163. data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
  164. data/test/graphql/type_map_test.rb +51 -66
  165. data/test/graphql/type_test.rb +0 -19
  166. data/test/graphql_test.rb +1 -1
  167. data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
  168. data/test/integration/config.rb +36 -3
  169. data/test/integration/customization_test.rb +39 -0
  170. data/test/integration/global_id_test.rb +99 -0
  171. data/test/integration/memory/star_wars_introspection_test.rb +24 -16
  172. data/test/integration/memory/star_wars_query_test.rb +54 -3
  173. data/test/integration/memory/star_wars_validation_test.rb +1 -1
  174. data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
  175. data/test/integration/persisted_query_test.rb +87 -0
  176. data/test/integration/resolver_precedence_test.rb +154 -0
  177. data/test/integration/schemas/memory.rb +22 -7
  178. data/test/integration/schemas/mysql.rb +62 -0
  179. data/test/integration/schemas/sqlite.rb +21 -12
  180. data/test/integration/sqlite/star_wars_global_id_test.rb +83 -0
  181. data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
  182. data/test/integration/sqlite/star_wars_query_test.rb +14 -1
  183. data/test/integration/translate_test.rb +61 -0
  184. data/test/test_ext.rb +16 -13
  185. metadata +108 -157
  186. data/ext/depend +0 -3
  187. data/ext/graphqlparser/Ast.cpp +0 -346
  188. data/ext/graphqlparser/Ast.h +0 -1214
  189. data/ext/graphqlparser/AstNode.h +0 -36
  190. data/ext/graphqlparser/AstVisitor.h +0 -137
  191. data/ext/graphqlparser/GraphQLParser.cpp +0 -76
  192. data/ext/graphqlparser/GraphQLParser.h +0 -55
  193. data/ext/graphqlparser/JsonVisitor.cpp +0 -161
  194. data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
  195. data/ext/graphqlparser/JsonVisitor.h +0 -121
  196. data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
  197. data/ext/graphqlparser/VERSION +0 -1
  198. data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
  199. data/ext/graphqlparser/c/GraphQLAst.h +0 -180
  200. data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
  201. data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
  202. data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
  203. data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  204. data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
  205. data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  206. data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
  207. data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
  208. data/ext/graphqlparser/c/GraphQLParser.h +0 -54
  209. data/ext/graphqlparser/dump_json_ast.cpp +0 -48
  210. data/ext/graphqlparser/lexer.lpp +0 -324
  211. data/ext/graphqlparser/parser.ypp +0 -693
  212. data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
  213. data/ext/graphqlparser/parsergen/lexer.h +0 -528
  214. data/ext/graphqlparser/parsergen/location.hh +0 -189
  215. data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
  216. data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
  217. data/ext/graphqlparser/parsergen/position.hh +0 -179
  218. data/ext/graphqlparser/parsergen/stack.hh +0 -156
  219. data/ext/graphqlparser/syntaxdefs.h +0 -19
  220. data/ext/libgraphqlparser/AstNode.h +0 -36
  221. data/ext/libgraphqlparser/CMakeLists.txt +0 -148
  222. data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
  223. data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
  224. data/ext/libgraphqlparser/GraphQLParser.h +0 -55
  225. data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
  226. data/ext/libgraphqlparser/JsonVisitor.h +0 -121
  227. data/ext/libgraphqlparser/LICENSE +0 -22
  228. data/ext/libgraphqlparser/README.clang-tidy +0 -7
  229. data/ext/libgraphqlparser/README.md +0 -84
  230. data/ext/libgraphqlparser/ast/ast.ast +0 -203
  231. data/ext/libgraphqlparser/ast/ast.py +0 -61
  232. data/ext/libgraphqlparser/ast/c.py +0 -100
  233. data/ext/libgraphqlparser/ast/c.pyc +0 -0
  234. data/ext/libgraphqlparser/ast/c_impl.py +0 -61
  235. data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
  236. data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
  237. data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
  238. data/ext/libgraphqlparser/ast/casing.py +0 -26
  239. data/ext/libgraphqlparser/ast/casing.pyc +0 -0
  240. data/ext/libgraphqlparser/ast/cxx.py +0 -197
  241. data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
  242. data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
  243. data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
  244. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
  245. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
  246. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
  247. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
  248. data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
  249. data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
  250. data/ext/libgraphqlparser/ast/js.py +0 -65
  251. data/ext/libgraphqlparser/ast/license.py +0 -10
  252. data/ext/libgraphqlparser/ast/license.pyc +0 -0
  253. data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
  254. data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
  255. data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  256. data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
  257. data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  258. data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
  259. data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
  260. data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
  261. data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
  262. data/ext/libgraphqlparser/cmake/version.cmake +0 -16
  263. data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
  264. data/ext/libgraphqlparser/go/README.md +0 -20
  265. data/ext/libgraphqlparser/go/callbacks.go +0 -18
  266. data/ext/libgraphqlparser/go/gotest.go +0 -64
  267. data/ext/libgraphqlparser/lexer.lpp +0 -324
  268. data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
  269. data/ext/libgraphqlparser/parser.ypp +0 -693
  270. data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
  271. data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
  272. data/ext/libgraphqlparser/parsergen/location.hh +0 -189
  273. data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
  274. data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
  275. data/ext/libgraphqlparser/parsergen/position.hh +0 -179
  276. data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
  277. data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
  278. data/ext/libgraphqlparser/python/README.md +0 -5
  279. data/ext/libgraphqlparser/python/example.py +0 -31
  280. data/ext/libgraphqlparser/syntaxdefs.h +0 -19
  281. data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
  282. data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
  283. data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
  284. data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
  285. data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
  286. data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
  287. data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
  288. data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
  289. data/ext/libgraphqlparser/test/valgrind.supp +0 -33
  290. data/ext/version.cpp +0 -21
  291. data/lib/graphqlparser.so +0 -0
  292. data/lib/rails/graphql/native/functions.rb +0 -38
  293. data/lib/rails/graphql/native/location.rb +0 -41
  294. data/lib/rails/graphql/native/pointers.rb +0 -23
  295. data/lib/rails/graphql/native/visitor.rb +0 -349
  296. data/lib/rails/graphql/native.rb +0 -56
  297. data/test/integration/schemas/authorization.rb +0 -12
@@ -1,27 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- module Helpers # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ module Helpers
6
6
  # Helper module that allows other objects to hold schema fields (query,
7
7
  # mutation, and subscription fields). Works very similar to fields, but
8
8
  # they are placed in different places regarding their type.
9
9
  module WithSchemaFields
10
- SCHEMA_FIELD_TYPES = %i[query mutation subscription].map do |key|
11
- [key, "_#{key.to_s.classify}"]
12
- end.to_h.freeze
13
-
14
10
  TYPE_FIELD_CLASS = {
15
11
  query: 'OutputField',
16
12
  mutation: 'MutationField',
17
- subscription: 'OutputField',
13
+ subscription: 'SubscriptionField',
18
14
  }.freeze
19
15
 
20
- module ClassMethods # :nodoc: all
16
+ module ClassMethods
21
17
  def inherited(subclass)
22
18
  super if defined? super
23
19
 
24
- SCHEMA_FIELD_TYPES.each_key do |kind|
20
+ TYPE_FIELD_CLASS.each_key do |kind|
25
21
  fields = instance_variable_defined?("@#{kind}_fields")
26
22
  fields = fields ? instance_variable_get("@#{kind}_fields") : {}
27
23
  fields.each_value { |field| subclass.add_proxy_field(kind, field) }
@@ -29,7 +25,8 @@ module Rails # :nodoc:
29
25
  end
30
26
  end
31
27
 
32
- ScopedConfig = Struct.new(:source, :type) do # :nodoc: all
28
+ # Helper class to be used as the +self+ in configuration blocks
29
+ ScopedConfig = Struct.new(:source, :type) do
33
30
  def arg(*args, **xargs, &block)
34
31
  xargs[:owner] ||= source
35
32
  GraphQL::Argument.new(*args, **xargs, &block)
@@ -54,27 +51,42 @@ module Rails # :nodoc:
54
51
  typed_methods = typed_methods.zip(typed_methods).to_h
55
52
  typed_methods.merge(
56
53
  fields: :fields_for,
54
+ fields?: :fields_for?,
57
55
  safe_field: :safe_add_field,
58
56
  field: :add_field,
59
57
  proxy_field: :add_proxy_field,
60
58
  field?: :has_field?,
59
+ import: :import_into,
60
+ import_all: :import_all_into,
61
61
  )
62
62
  end
63
63
  end
64
64
  end
65
65
 
66
- def self.extended(other) # :nodoc:
66
+ def self.extended(other)
67
67
  other.extend(WithSchemaFields::ClassMethods)
68
68
  end
69
69
 
70
70
  # A little helper for getting the list of fields of a given type
71
- def fields_for(type)
72
- public_send("#{type}_fields")
71
+ def fields_for(type, initialize = nil)
72
+ if instance_variable_defined?(ivar = :"@#{type}_fields")
73
+ instance_variable_get(ivar)
74
+ elsif initialize
75
+ instance_variable_set(ivar, Concurrent::Map.new)
76
+ end
73
77
  end
74
78
 
79
+ # Check if there are fields set fot he given type
80
+ def fields_for?(type)
81
+ public_send("#{type}_fields?")
82
+ end
83
+
84
+ alias [] :fields_for
85
+
75
86
  # Return the object name for a given +type+ of list of fields
76
87
  def type_name_for(type)
77
- SCHEMA_FIELD_TYPES[type]
88
+ method_name = :"#{type}_type_name"
89
+ public_send(method_name) if respond_to?(method_name)
78
90
  end
79
91
 
80
92
  # Only add the field if it is not already defined
@@ -88,36 +100,39 @@ module Rails # :nodoc:
88
100
  # Add a new field of the give +type+
89
101
  # See {OutputField}[rdoc-ref:Rails::GraphQL::OutputField] class.
90
102
  def add_field(type, *args, **xargs, &block)
91
- xargs[:owner] = self
92
103
  klass = Field.const_get(TYPE_FIELD_CLASS[type])
93
- object = klass.new(*args, **xargs, &block)
104
+ object = klass.new(*args, **xargs, owner: self, &block)
94
105
 
95
- raise DuplicatedError, <<~MSG.squish if has_field?(type, object.name)
106
+ raise DuplicatedError, (+<<~MSG).squish if has_field?(type, object.name)
96
107
  The "#{object.name}" field is already defined on #{type} fields and
97
108
  cannot be redefined.
98
109
  MSG
99
110
 
100
- fields_for(type)[object.name] = object
111
+ fields_for(type, true)[object.name] = object
101
112
  rescue DefinitionError => e
102
- raise e.class, e.message + "\n Defined at: #{caller(2)[0]}"
113
+ raise e.class, +"#{e.message}\n Defined at: #{caller(2)[0]}"
103
114
  end
104
115
 
105
116
  # Add a new field to the list but use a proxy instead of a hard copy of
106
117
  # a given +field+
107
118
  def add_proxy_field(type, field, *args, **xargs, &block)
119
+ raise ArgumentError, (+<<~MSG).squish if field.schema_type != type
120
+ A #{field.schema_type} field cannot be added as a #{type} field.
121
+ MSG
122
+
108
123
  klass = Field.const_get(TYPE_FIELD_CLASS[type])
109
- raise ArgumentError, <<~MSG.squish unless field.is_a?(klass)
124
+ raise ArgumentError, (+<<~MSG).squish unless field.is_a?(klass)
110
125
  The #{field.class.name} is not a valid field for #{type} fields.
111
126
  MSG
112
127
 
113
128
  xargs[:owner] = self
114
129
  object = field.to_proxy(*args, **xargs, &block)
115
- raise DuplicatedError, <<~MSG.squish if has_field?(type, object.name)
130
+ raise DuplicatedError, (+<<~MSG).squish if has_field?(type, object.name)
116
131
  The #{field.name.inspect} field is already defined on #{type} fields
117
132
  and cannot be replaced.
118
133
  MSG
119
134
 
120
- fields_for(type)[object.name] = object
135
+ fields_for(type, true)[object.name] = object
121
136
  end
122
137
 
123
138
  # Find a field and then change some flexible attributes of it
@@ -145,6 +160,7 @@ module Rails # :nodoc:
145
160
  # Check if a field of the given +type+ exists. The +object+ can be the
146
161
  # +gql_name+, +name+, or an actual field.
147
162
  def has_field?(type, object)
163
+ return false unless fields_for?(type)
148
164
  object = object.name if object.is_a?(GraphQL::Field)
149
165
  fields_for(type).key?(object.is_a?(String) ? object.underscore.to_sym : object)
150
166
  end
@@ -152,41 +168,103 @@ module Rails # :nodoc:
152
168
  # Find a specific field on the given +type+ list. The +object+ can be
153
169
  # the +gql_name+, +name+, or an actual field.
154
170
  def find_field(type, object)
171
+ return unless fields_for?(type)
155
172
  object = object.name if object.is_a?(GraphQL::Field)
156
173
  fields_for(type)[object.is_a?(String) ? object.underscore.to_sym : object]
157
174
  end
158
175
 
159
176
  # If the field is not found it will raise an exception
160
177
  def find_field!(type, object)
161
- find_field(type, object) || raise(::ArgumentError, <<~MSG.squish)
178
+ find_field(type, object) || raise(NotFoundError, (+<<~MSG).squish)
162
179
  The #{object.inspect} field on #{type} is not defined yet.
163
180
  MSG
164
181
  end
165
182
 
166
183
  # Get the list of GraphQL names of all the fields difined
167
184
  def field_names_for(type, enabled_only = true)
168
- (enabled_only ? fields_for(type).select(&:enabled?) : fields_for(type))
169
- .map(&:gql_name).compact
185
+ return unless fields_for?(type)
186
+ list = fields_for(type)
187
+ list = list.select(&:enabled?) if enabled_only
188
+ list.map(&:gql_name).compact
170
189
  end
171
190
 
172
191
  # Run a configuration block for the given +type+
173
192
  def configure_fields(type, &block)
174
- schema_scoped_config(self, type).instance_exec(&block)
193
+ WithSchemaFields::ScopedConfig.new(self, type).instance_exec(&block)
194
+ end
195
+
196
+ # Import a class of fields into the given section of schema fields
197
+ def import_into(type, source)
198
+ return if source.try(:abstract?)
199
+
200
+ # Import an alternative declaration of a field
201
+ if source.is_a?(Module) && source <= Alternative::Query
202
+ return add_proxy_field(type, source.field)
203
+ end
204
+
205
+ case source
206
+ when Array
207
+ # Import a list of fields
208
+ source.each { |field| add_proxy_field(type, field) }
209
+ when Hash, Concurrent::Map
210
+ # Import a keyed list of fields
211
+ source.each_value { |field| add_proxy_field(type, field) }
212
+ when Helpers::WithFields
213
+ # Import a set of fields
214
+ source.fields.each_value { |field| add_proxy_field(type, field) }
215
+ when Helpers::WithSchemaFields
216
+ # Import other schema fields
217
+ (type == :all ? TYPE_FIELD_CLASS.each_key : type.then).each do |import_type|
218
+ source.fields_for(import_type)&.each_value do |field|
219
+ add_proxy_field(import_type, field)
220
+ end
221
+ end
222
+ else
223
+ return if GraphQL.config.silence_import_warnings
224
+ GraphQL.logger.warn(+"Unable to import #{source.inspect} into #{self.name}.")
225
+ end
226
+ end
227
+
228
+ # Import a module containing several classes to be imported
229
+ # TODO: Maybe add deepness into the recursive value
230
+ def import_all_into(type, mod, recursive: false, **xargs)
231
+ mod.constants.each do |const_name|
232
+ object = mod.const_get(const_name)
233
+
234
+ import_into(type, object, **xargs) if object.is_a?(Class)
235
+ import_all_into(type, object, recursive: recursive, **xargs) if recursive
236
+ end
237
+ end
238
+
239
+ # Same as above, but if the name of the module being imported already
240
+ # dictates the type, skip specifying it
241
+ def import_all(mod, **xargs)
242
+ type = mod.name.demodulize.underscore.singularize
243
+ type = TYPE_FIELD_CLASS.each_key.find { |key| key.to_s == type }
244
+ return import_all_into(type, mod, **xargs) unless type.nil?
245
+
246
+ raise(::ArgumentError, (+<<~MSG).squish)
247
+ Unable to extract type from #{mod.name}.
248
+ Please use "import_all_into(_type_, #{mod.name}) instead."
249
+ MSG
175
250
  end
176
251
 
177
252
  # Validate all the fields to make sure the definition is valid
178
253
  def validate!(*)
179
254
  super if defined? super
180
255
 
181
- # TODO: Maybe find a way to freeze the fields, since after validation
182
- # the best thing to do is block changes
183
- SCHEMA_FIELD_TYPES.each_key do |kind|
184
- next unless instance_variable_defined?("@#{kind}_fields")
185
- instance_variable_get("@#{kind}_fields")&.each_value(&:validate!)
256
+ TYPE_FIELD_CLASS.each_key do |kind|
257
+ next unless public_send("#{kind}_fields?")
258
+ fields_for(kind).each_value(&:validate!)
186
259
  end
187
260
  end
188
261
 
189
- SCHEMA_FIELD_TYPES.each do |kind, type_name|
262
+ # Find a specific field using its id as +gql_name.type+
263
+ def find_by_gid(gid)
264
+ find_field!(gid.scope, gid.name)
265
+ end
266
+
267
+ TYPE_FIELD_CLASS.each_key do |kind|
190
268
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
191
269
  def #{kind}_field?(name)
192
270
  has_field?(:#{kind}, name)
@@ -196,38 +274,42 @@ module Rails # :nodoc:
196
274
  find_field(:#{kind}, name)
197
275
  end
198
276
 
277
+ def add_#{kind}_field(*args, **xargs, &block)
278
+ add_field(:#{kind}, *args, **xargs, &block)
279
+ end
280
+
281
+ def #{kind}_fields?
282
+ defined?(@#{kind}_fields) && @#{kind}_fields.present?
283
+ end
284
+
199
285
  def #{kind}_fields(&block)
200
- return @#{kind}_fields ||= Concurrent::Map.new if block.nil?
201
- configure_fields(:#{kind}, &block)
202
- @#{kind}_fields
286
+ configure_fields(:#{kind}, &block) if block.present?
287
+ @#{kind}_fields if defined?(@#{kind}_fields)
203
288
  end
204
289
 
205
290
  def #{kind}_type_name
206
- '#{type_name}'
291
+ source = (respond_to?(:config) ? config : GraphQL.config)
292
+ source.schema_type_names[:#{kind}]
207
293
  end
208
294
 
209
295
  def #{kind}_type
210
- OpenStruct.new(
211
- kind: :object,
212
- object?: true,
213
- kind_enum: 'OBJECT',
214
- fields: defined?(@#{kind}_fields) ? @#{kind}_fields : nil,
215
- gql_name: '#{type_name}',
216
- interfaces: nil,
217
- description: nil,
218
- interfaces?: false,
219
- internal?: false,
220
- ).freeze
296
+ if defined?(@#{kind}_fields) && @#{kind}_fields.present?
297
+ OpenStruct.new(
298
+ name: "\#{name}[:#{kind}]",
299
+ kind: :object,
300
+ object?: true,
301
+ kind_enum: 'OBJECT',
302
+ fields: @#{kind}_fields,
303
+ gql_name: #{kind}_type_name,
304
+ interfaces: nil,
305
+ description: nil,
306
+ interfaces?: false,
307
+ internal?: false,
308
+ ).freeze
309
+ end
221
310
  end
222
311
  RUBY
223
312
  end
224
-
225
- protected
226
-
227
- # Create a new instace of the +ScopedConfig+ class
228
- def schema_scoped_config(*args)
229
- WithSchemaFields::ScopedConfig.new(*args)
230
- end
231
313
  end
232
314
  end
233
315
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- module Helpers # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ module Helpers
6
6
  # Helper that contains the main exceptions and validation process for a
7
7
  # value against a type
8
8
  module WithValidator
@@ -22,15 +22,15 @@ module Rails # :nodoc:
22
22
  message, idx = result
23
23
 
24
24
  base_error = idx.present? \
25
- ? "#{ordinalize(idx + 1)} value of the #{gql_name} #{type}" \
26
- : "#{gql_name} #{type} value"
25
+ ? +"#{ordinalize(idx + 1)} value of the #{gql_name} #{type}" \
26
+ : +"#{gql_name} #{type} value"
27
27
 
28
- raise InvalidValueError, "The #{base_error} #{message}."
28
+ raise InvalidValueError, +"The #{base_error} #{message}."
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def validate_array(value) # :nodoc:
33
+ def validate_array(value)
34
34
  return 'is not an array' unless value.is_a?(Enumerable)
35
35
 
36
36
  value.each_with_index do |val, idx|
@@ -39,11 +39,11 @@ module Rails # :nodoc:
39
39
  end
40
40
  end
41
41
 
42
- def validate_null(value, checker = :null?) # :nodoc:
42
+ def validate_null(value, checker = :null?)
43
43
  'can not be null' if value.nil? && !send(checker)
44
44
  end
45
45
 
46
- def validate_type(value) # :nodoc:
46
+ def validate_type(value)
47
47
  'is invalid' if leaf_type? && !type_klass.valid_output?(value)
48
48
  end
49
49
  end
@@ -1,21 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- module Helpers # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ # All helpers that allow this gem to be flexible and extendable to any other
6
+ # sources of objects and other gems as well
7
+ module Helpers
6
8
  extend ActiveSupport::Autoload
7
9
 
8
10
  autoload :AttributeDelegator
9
11
  autoload :InheritedCollection
12
+ autoload :Instantiable
10
13
  autoload :LeafFromAr
14
+ autoload :Unregisterable
11
15
  autoload :Registerable
12
16
 
13
17
  autoload :WithArguments
14
18
  autoload :WithAssignment
15
19
  autoload :WithCallbacks
16
20
  autoload :WithDirectives
21
+ autoload :WithDescription
17
22
  autoload :WithEvents
18
23
  autoload :WithFields
24
+ autoload :WithGlobalID
25
+ autoload :WithName
19
26
  autoload :WithNamespace
20
27
  autoload :WithOwner
21
28
  autoload :WithSchemaFields
@@ -1,53 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # Module related to some methods regarding the introspection of a schema
6
6
  module Introspection
7
- module ClassMethods # :nodoc: all
8
- def inherited(subclass)
9
- super if defined? super
10
-
11
- subclass.query_fields do
12
- field(:__schema, '__Schema', null: false) do
13
- resolve { schema }
14
- end
15
-
16
- field(:__type, '__Type') do
17
- argument(:name, :string, null: false)
18
- resolve { schema.find_type(argument(:name)) }
19
- end
20
- end
21
- end
22
- end
23
-
24
- def self.extended(other) # :nodoc:
25
- other.extend(Introspection::ClassMethods)
26
- end
27
-
28
- # When register is called, add introspection fields?
7
+ # When register is called, add introspection related elements
29
8
  def register!(*)
30
9
  super if defined? super
31
- return unless introspection?
32
-
33
- Helpers::WithSchemaFields::SCHEMA_FIELD_TYPES.each do |type, name|
34
- GraphQL.type_map.register_alias(name, namespace: namespace) do
35
- result = public_send("#{type}_type")
36
- type.eql?(:query) || result.fields.present? ? result : nil
10
+ enable_introspection! if !introspection? && config.enable_introspection
11
+
12
+ # Although this is not necessary besides for introspection, there is no
13
+ # real disadvantage on adding it
14
+ Helpers::WithSchemaFields::TYPE_FIELD_CLASS.each_key do |type|
15
+ GraphQL.type_map.register_alias(type_name_for(type), namespace: namespace) do
16
+ result = public_send(:"#{type}_type")
17
+ type.eql?(:query) || result.present? ? result : nil
37
18
  end
38
19
  end
39
20
  end
40
21
 
41
22
  # Check if the schema has introspection enabled
42
23
  def introspection?
43
- true
24
+ false
44
25
  end
45
26
 
46
- # Remove introspection fields and disable introspection
47
- def disable_introspection!
48
- disable_fields(:query, :__schema, :__type)
49
- redefine_singleton_method(:introspection?) { false }
50
- end
27
+ protected
28
+
29
+ # Enaqble introspection fields
30
+ def enable_introspection!
31
+ redefine_singleton_method(:introspection?) { true }
32
+ introspection_dependencies!
33
+
34
+ safe_add_field(:query, :__schema, '__Schema', null: false) do
35
+ resolve { schema }
36
+ end
37
+
38
+ safe_add_field(:query, :__type, '__Type') do
39
+ argument(:name, :string, null: false)
40
+ resolve { schema.find_type(argument(:name)) }
41
+ end
42
+ end
43
+
44
+ # Add the introspection dependencies, but only when necessary
45
+ def introspection_dependencies!
46
+ GraphQL.type_map.add_dependencies([
47
+ "#{__dir__}/type/enum/directive_location_enum",
48
+ "#{__dir__}/type/enum/type_kind_enum",
49
+
50
+ "#{__dir__}/type/object/directive_object",
51
+ "#{__dir__}/type/object/enum_value_object",
52
+ "#{__dir__}/type/object/field_object",
53
+ "#{__dir__}/type/object/input_value_object",
54
+ "#{__dir__}/type/object/schema_object",
55
+ "#{__dir__}/type/object/type_object",
56
+ ], to: :base)
57
+ end
51
58
  end
52
59
  end
53
60
  end
@@ -4,8 +4,8 @@ require 'rails/railtie'
4
4
  require 'action_controller'
5
5
  require 'action_controller/railtie'
6
6
 
7
- module Rails # :nodoc:
8
- module GraphQL # :nodoc:
7
+ module Rails
8
+ module GraphQL
9
9
  # = Rails GraphQL Railtie
10
10
  #
11
11
  # Rails integration and configuration
@@ -21,19 +21,43 @@ module Rails # :nodoc:
21
21
  require_relative './schema'
22
22
  end
23
23
 
24
+ console do
25
+ config.graphql.enable_string_collector = false
26
+ config.graphql.default_response_format = :hash
27
+ end
28
+
24
29
  # Ensure a valid logger
25
- initializer 'graphql.logger' do
30
+ initializer 'graphql.logger' do |app|
26
31
  ActiveSupport.on_load(:graphql) do
27
32
  return if config.logger.present?
28
- if ::Rails.logger.respond_to?(:tagged)
29
- config.logger = ::Rails.logger
33
+
34
+ logger = ::Rails.logger
35
+ if logger.respond_to?(:tagged)
36
+ config.logger = logger
30
37
  else
31
- config.logger = ActiveSupport::TaggedLogging.new(::Rails.logger)
38
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
39
+ end
40
+ end
41
+ end
42
+
43
+ initializer 'graphql.active_record_backtrace_print' do
44
+ if defined?(ActiveRecord)
45
+ base = Module.new
46
+ base.send(:define_method, :to_gql_backtrace) do
47
+ +"#<#{self.class.name} id: #{id}>"
32
48
  end
49
+
50
+ relation = Module.new
51
+ relation.send(:define_method, :to_gql_backtrace) do
52
+ +"[#<#{model.name}>](#{size})"
53
+ end
54
+
55
+ ActiveRecord::Base.include(base)
56
+ ActiveRecord::Relation.include(relation)
33
57
  end
34
58
  end
35
59
 
36
- # Expose database runtime to controller for logging.
60
+ # Expose GraphQL runtime to controller for logging
37
61
  initializer 'graphql.log_runtime' do
38
62
  require_relative './railties/controller_runtime'
39
63
  ActiveSupport.on_load(:action_controller) do
@@ -41,45 +65,76 @@ module Rails # :nodoc:
41
65
  end
42
66
  end
43
67
 
68
+ # Clean up GraphQL params from logger
69
+ initializer 'graphql.params_cleanup' do
70
+ key = 'start_processing.action_controller'
71
+ ActiveSupport::Notifications.subscribe(key) do |*, payload|
72
+ payload[:params].except!(*config.graphql.omit_parameters) \
73
+ if payload[:headers]['action_controller.instance'].is_a?(GraphQL::Controller)
74
+ end
75
+ end
76
+
77
+ # Copy filter params when they are not exclusively set for GraphQL
78
+ initializer 'graphql.params_filter' do |app|
79
+ config.after_initialize do
80
+ config.graphql.filter_parameters ||= app.config.filter_parameters
81
+ end
82
+ end
83
+
44
84
  # Backtrace cleaner for removing gem paths
45
85
  initializer 'graphql.backtrace_cleaner' do
46
86
  require_relative './railties/log_subscriber'
47
87
  ActiveSupport.on_load(:graphql) do
48
- GraphQL::LogSubscriber.backtrace_cleaner = ::Rails.backtrace_cleaner
88
+ LogSubscriber.backtrace_cleaner = ::Rails.backtrace_cleaner
49
89
  end
50
90
  end
51
91
 
52
- # Add reloader ability for files under 'app/graphql'
53
- # TODO: Maybe improve to use rails auto loader
54
- initializer 'graphql.reloader' do
55
- Rails::GraphQL.eager_load!
56
- ActiveSupport::Reloader.to_prepare do
57
- Rails::GraphQL.type_map.use_checkpoint!
58
- Rails::GraphQL.reload_ar_adapters!
59
-
60
- Object.send(:remove_const, :GraphQL) if Object.const_defined?(:GraphQL)
92
+ # Add the GraphQL Global ID serializer to active job serializers
93
+ initializer 'graphql.global_id' do
94
+ ActiveSupport.on_load(:active_job) do
95
+ ActiveJob::Serializers.add_serializers(Rails::GraphQL::GlobalID::Serializer)
96
+ end
97
+ end
61
98
 
62
- load "#{__dir__}/shortcuts.rb"
99
+ # Simply switch to hash output if rails is running on test mode
100
+ initializer 'graphql.tests' do
101
+ if Rails.env.test?
102
+ config.graphql.enable_string_collector = false
103
+ config.graphql.default_response_format = :hash
104
+ end
105
+ end
63
106
 
64
- $LOAD_PATH.each do |path|
65
- next unless path =~ %r{\/app\/graphql$}
66
- Dir.glob("#{path}/**/*.rb").sort.each(&method(:load))
107
+ # Set GraphQL cache store same as rails default cache store, unless the
108
+ # default value is a Null Cache, then use the fallback instead
109
+ initializer 'graphql.cache', after: :initialize_cache do
110
+ config.graphql.cache ||= begin
111
+ if !::Rails.cache.is_a?(::ActiveSupport::Cache::NullStore)
112
+ ::Rails.cache
113
+ elsif config.graphql.cache_fallback.is_a?(Proc)
114
+ config.graphql.cache_fallback.call
115
+ else
116
+ config.graphql.cache_fallback
67
117
  end
68
-
69
- GraphQL::Source.send(:build_pending!)
70
118
  end
71
119
  end
72
120
 
73
- # initializer "active_record.set_reloader_hooks" do
74
- # ActiveSupport.on_load(:active_record) do
75
- # ActiveSupport::Reloader.before_class_unload do
76
- # if ActiveRecord::Base.connected?
77
- # ActiveRecord::Base.clear_cache!
78
- # ActiveRecord::Base.clear_reloadable_connections!
79
- # end
80
- # end
81
- # end
82
- # end
121
+ # Properly setup how GraphQL reload itself
122
+ # TODO: Check proper support for Rails engines
123
+ initializer 'graphql.reloader', before: :load_config_initializers do |app|
124
+ next unless (path = app.root.join('app', 'graphql')).exist?
125
+
126
+ children = config.graphql.paths.join(',')
127
+ autoloader = app.autoloaders.main
128
+
129
+ ActiveSupport::Dependencies.autoload_paths.delete(path.to_s)
130
+ autoloader.collapse(path.glob("**/{#{children}}").select(&:directory?))
131
+ autoloader.push_dir(path, namespace: ::GraphQL)
132
+ config.watchable_dirs[path.to_s] = [:rb]
133
+
134
+ autoloader.on_unload do |_, value, _|
135
+ value.unregister! if value.is_a?(Helpers::Unregisterable)
136
+ end
137
+ end
83
138
  end
84
139
  end
85
140
  end