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,35 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # = GraphQL Output Field
6
6
  #
7
7
  # Most of the fields in a GraphQL operation are output fields or similar or
8
8
  # proxies of it. They can express both leaf and branch data. They can also
9
9
  # be the entry point of a GraphQL request.
10
+ #
11
+ # ==== Options
12
+ #
13
+ # * <tt>:method_name</tt> - The name of the method used to fetch the field data
14
+ # (defaults to nil).
15
+ # * <tt>:deprecated</tt> - A shortcut to adding a deprecated directive to the field
16
+ # (defaults to nil).
10
17
  class Field::OutputField < Field
18
+ # Do not change this order because it can affect how events work. Callback
19
+ # must always come after events
11
20
  include Helpers::WithArguments
12
- include Helpers::WithValidator
13
21
  include Helpers::WithEvents
14
22
  include Helpers::WithCallbacks
15
23
 
24
+ include Helpers::WithGlobalID
25
+ include Helpers::WithValidator
26
+
16
27
  include Field::AuthorizedField
17
28
  include Field::ResolvedField
18
29
  include Field::TypedField
19
30
 
20
31
  module Proxied # :nodoc: all
21
- def initialize(*args, **xargs, &block)
22
- @method_name = xargs.delete(:method_name) if xargs.key?(:method_name)
23
- super(*args, **xargs, &block)
24
- end
25
-
26
32
  def all_arguments
27
- field.arguments.merge(super)
33
+ inherited = field.all_arguments
34
+ return inherited unless defined?(@arguments)
35
+ inherited.blank? ? super : inherited + super
28
36
  end
29
37
 
30
38
  def has_argument?(name)
31
39
  super || field.has_argument?(name)
32
40
  end
41
+
42
+ def arguments?
43
+ super || field.arguments?
44
+ end
45
+
46
+ def all_events
47
+ if (inherited = super).nil?
48
+ field.all_events
49
+ elsif (proxied = field.all_events).nil?
50
+ inherited
51
+ else
52
+ Helpers.merge_hash_array(proxied, inherited)
53
+ end
54
+ end
55
+
56
+ def events?
57
+ super || field.events?
58
+ end
59
+
60
+ def all_listeners
61
+ if (inherited = super).nil?
62
+ field.all_listeners
63
+ elsif (proxied = field.all_listeners).nil?
64
+ inherited
65
+ else
66
+ proxied + inherited
67
+ end
68
+ end
69
+
70
+ def listeners?
71
+ super || field.listeners?
72
+ end
33
73
  end
34
74
 
35
75
  redefine_singleton_method(:output_type?) { true }
@@ -37,9 +77,10 @@ module Rails # :nodoc:
37
77
 
38
78
  def initialize(*args, method_name: nil, deprecated: false, **xargs, &block)
39
79
  @method_name = method_name.to_s.underscore.to_sym unless method_name.nil?
80
+ @broadcastable = xargs.delete(:broadcastable) if xargs.key?(:broadcastable)
40
81
 
41
82
  if deprecated.present?
42
- xargs[:directives] = Array.wrap(xargs[:directives])
83
+ xargs[:directives] = ::Array.wrap(xargs[:directives])
43
84
  xargs[:directives] << Directive::DeprecatedDirective.new(
44
85
  reason: (deprecated.is_a?(String) ? deprecated : nil),
45
86
  )
@@ -48,12 +89,23 @@ module Rails # :nodoc:
48
89
  super(*args, **xargs, &block)
49
90
  end
50
91
 
92
+ # Accept changes to the method name through the +apply_changes+
93
+ def apply_changes(**xargs, &block)
94
+ @method_name = xargs.delete(:method_name) if xargs.key?(:method_name)
95
+ super
96
+ end
97
+
98
+ # By default, output fields that belongs to a schema is a query field
99
+ def schema_type
100
+ :query
101
+ end
102
+
51
103
  # Check if the arguments are also equivalent
52
104
  def =~(other)
53
105
  super && match_arguments?(other)
54
106
  end
55
107
 
56
- # Checks if a given unserialized value is valid for this field
108
+ # Checks if a given raw value is valid for this field
57
109
  def valid_output?(value, deep: true)
58
110
  return false unless super
59
111
  return null? if value.nil?
@@ -74,11 +126,43 @@ module Rails # :nodoc:
74
126
  def validate!(*)
75
127
  super if defined? super
76
128
 
77
- raise ArgumentError, <<~MSG.squish unless type_klass.output_type?
129
+ raise ArgumentError, (+<<~MSG).squish unless type_klass.output_type?
78
130
  The "#{type_klass.gql_name}" is not a valid output type.
79
131
  MSG
80
132
  end
81
133
 
134
+ def all_events
135
+ if !defined?(@events) || !(local = @events).present?
136
+ super
137
+ elsif (inherited = super).nil?
138
+ local
139
+ else
140
+ Helpers.merge_hash_array(inherited, local)
141
+ end
142
+ end
143
+
144
+ def events?
145
+ super || defined?(@events) && @events.present?
146
+ end
147
+
148
+ def all_listeners
149
+ if !defined?(@listeners) || !(local = @listeners).present?
150
+ super
151
+ elsif (inherited = super).nil?
152
+ local
153
+ else
154
+ inherited + local
155
+ end
156
+ end
157
+
158
+ def listeners?
159
+ super || defined?(@listeners) && @listeners.present?
160
+ end
161
+
162
+ def broadcastable?
163
+ defined?(@broadcastable) && @broadcastable
164
+ end
165
+
82
166
  protected
83
167
 
84
168
  # Check if the given +value+ is a valid array as output
@@ -91,7 +175,12 @@ module Rails # :nodoc:
91
175
  end
92
176
  end
93
177
 
94
- def proxied # :nodoc:
178
+ # Properly display the owner section when the field is owned by a Schema
179
+ def inspect_owner
180
+ owner.is_a?(Helpers::WithSchemaFields) ? +"#{owner.name}[:#{schema_type}]" : super
181
+ end
182
+
183
+ def proxied
95
184
  super if defined? super
96
185
  extend Field::OutputField::Proxied
97
186
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # = GraphQL Proxied Field
6
6
  #
7
7
  # Proxied fields are a soft way to copy a real field. The good part is that
@@ -26,7 +26,7 @@ module Rails # :nodoc:
26
26
  module Field::ProxiedField
27
27
  delegate_missing_to :field
28
28
  delegate :leaf_type?, :array?, :internal?, :valid_input?, :valid_output?,
29
- :to_json, :as_json, :deserialize, :valid?, to: :field
29
+ :to_json, :as_json, :deserialize, :valid?, :proxied_owner, to: :field
30
30
 
31
31
  Field.proxyable_methods %w[name gql_name method_name resolver description
32
32
  null? nullable? enabled?], klass: self
@@ -43,15 +43,22 @@ module Rails # :nodoc:
43
43
  true
44
44
  end
45
45
 
46
+ # Make sure to check the original field as well
47
+ # TODO: Maybe not use the +==+ method, but so far so good
48
+ def ==(other)
49
+ super || field == other
50
+ end
51
+
46
52
  # Allow chaging most of the general kind-independent initialize settings
47
53
  def apply_changes(**xargs, &block)
48
54
  if (deprecated = xargs[:deprecated])
49
- xargs[:directives] = Array.wrap(xargs[:directives])
55
+ xargs[:directives] = ::Array.wrap(xargs[:directives])
50
56
  xargs[:directives] << Directive::DeprecatedDirective.new(
51
57
  reason: (deprecated.is_a?(String) ? deprecated : nil),
52
58
  )
53
59
  end
54
60
 
61
+ # TODO: Replace by a proper method to build and set @directives
55
62
  @directives = GraphQL.directives_to_set(xargs[:directives], source: self) \
56
63
  if xargs.key?(:directives)
57
64
 
@@ -63,11 +70,6 @@ module Rails # :nodoc:
63
70
  super
64
71
  end
65
72
 
66
- # Return the original owner from +field+
67
- def proxied_owner
68
- field.owner
69
- end
70
-
71
73
  # Override this to include proxied owners
72
74
  def all_owners
73
75
  super + proxied_owner.all_owners
@@ -78,34 +80,41 @@ module Rails # :nodoc:
78
80
  @field
79
81
  end
80
82
 
81
- def disable! # :nodoc:
83
+ # Just ensure that when the field is proxied to an interface it does not
84
+ # allow disabling
85
+ def disable!
82
86
  super unless non_interface_proxy!('disable')
83
87
  end
84
88
 
85
- def enable! # :nodoc:
89
+ # Just ensure that when the field is proxied to an interface it does not
90
+ # allow enabling
91
+ def enable!
86
92
  super unless non_interface_proxy!('enable')
87
93
  end
88
94
 
89
- def all_directives # :nodoc:
90
- field.all_directives + super
95
+ # Prepend the proxy directives and then the source directives
96
+ def all_directives
97
+ inherited = field.all_directives
98
+ return inherited unless defined?(@directives)
99
+ inherited.present? ? inherited + super : super
100
+ end
101
+
102
+ # Check if the field has directives locally or in the proxied field
103
+ def directives?
104
+ super || field.directives?
91
105
  end
92
106
 
93
- # It is important to ensure that the proxied field is also valid. If the
94
- # proxied owner is registered, then it is safe to assume that it is valid
107
+ # It is important to ensure that the proxied field is also valid
95
108
  def validate!(*)
96
109
  super if defined? super
97
- field.validate! unless GraphQL.type_map.object_exist?(
98
- proxied_owner,
99
- namespaces: namespaces,
100
- exclusive: true,
101
- )
110
+ field.validate!
102
111
  end
103
112
 
104
113
  protected
105
114
 
106
115
  alias field proxied_field
107
116
 
108
- def normalize_name(value) # :nodoc:
117
+ def normalize_name(value)
109
118
  super unless value.blank? || non_interface_proxy!('rename')
110
119
  end
111
120
 
@@ -124,7 +133,7 @@ module Rails # :nodoc:
124
133
 
125
134
  # Display the source of the proxy for inspection
126
135
  def inspect_source
127
- "@source=#{proxied_owner.name}[:#{field.name}] [proxied]"
136
+ +"@source=#{field.owner.name}[:#{field.name}] [proxied]"
128
137
  end
129
138
 
130
139
  # This is trigerred when the field is proxied
@@ -1,23 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # This provides ways for fields to be resolved manually, by adding callbacks
6
6
  # and additional configurations in order to resolve a field value during a
7
7
  # request
8
8
  module Field::ResolvedField
9
9
  module Proxied # :nodoc: all
10
- def all_listeners
11
- field.all_listeners + super
12
- end
13
-
14
- def all_events
15
- events = defined?(@events) ? @events : {}
16
- Helpers.merge_hash_array(field.all_events, events).transform_values do |arr|
17
- arr.sort_by { |cb| cb.try(:target).is_a?(GraphQL::Field) ? 0 : 1 }
18
- end
19
- end
20
-
21
10
  def resolver
22
11
  super || field.resolver
23
12
  end
@@ -65,26 +54,39 @@ module Rails # :nodoc:
65
54
 
66
55
  # Store this result for performance purposes
67
56
  @dynamic_resolver = dynamic_resolver?
68
- return unless defined? @events
57
+ return unless defined?(@events)
69
58
 
70
- invalid = @events.each_value.reject do |callback|
71
- callback.block.is_a?(Proc) || callable?(callback.block)
72
- end
59
+ # TODO: Change how events are validated. Relying on the +source_location
60
+ # uses inspect, which is not a good approach
61
+
62
+ # invalid = @events.each_pair.each_with_object({}) do |(key, events), hash|
63
+ # events.each do |event|
64
+ # _, method_name = event.source_location
65
+ # next if method_name.nil? || callable?(method_name)
66
+
67
+ # (hash[key] ||= []).push(method_name)
68
+ # end
69
+ # end
70
+
71
+ # return if invalid.empty?
73
72
 
74
- raise ArgumentError, <<~MSG.squish if invalid.present?
75
- The "#{owner.name}" class does not define the following methods needed
76
- for performing hooks: #{invalid.map(&:block).to_sentence}.
77
- MSG
73
+ # invalid = invalid.map { |key, list| (+"#{key} => [#{list.join(', ')}]") }
74
+ # raise ArgumentError, (+<<~MSG).squish if invalid.present?
75
+ # The "#{owner.name}" class does not define the following methods needed
76
+ # for performing callbacks: #{invalid.join(', ')}.
77
+ # MSG
78
78
  end
79
79
 
80
80
  protected
81
81
 
82
- # Chedck if a given +method_name+ is callable from the owner perspective
82
+ # Check if the method is defined and does not belong to a method defined
83
+ # by the gem itself
83
84
  def callable?(method_name)
84
- owner.is_a?(Class) && owner.try(:gql_resolver?, method_name)
85
+ owner.is_a?(Class) && owner.public_method_defined?(method_name) &&
86
+ !owner.public_instance_method(method_name).owner.try(:abstract?)
85
87
  end
86
88
 
87
- def proxied # :nodoc:
89
+ def proxied
88
90
  super if defined? super
89
91
  extend Field::ResolvedField::Proxied
90
92
  end
@@ -1,22 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # Helper class to be used while configuring a field using a block. An
6
6
  # instance of this class works as proxy for changes to the actual field.
7
- Field::ScopedConfig = Struct.new(:field, :receiver) do
7
+ class Field::ScopedConfig < Struct.new(:field, :receiver)
8
8
  delegate :argument, :ref_argument, :id_argument, :use, :internal?, :disabled?,
9
9
  :enabled?, :disable!, :enable!, :authorize, to: :field
10
10
 
11
11
  delegate_missing_to :receiver
12
12
 
13
+ def rename!(name)
14
+ field.instance_variable_set(:@gql_name, name.to_s)
15
+ end
16
+
13
17
  def method_name(value)
14
18
  field.instance_variable_set(:@method_name, value.to_sym)
15
19
  end
16
20
 
17
21
  def desc(value)
18
- field.instance_variable_set(:@desc, value.strip_heredoc.chomp)
22
+ field.description = value
19
23
  end
24
+
25
+ alias description desc
20
26
  end
21
27
  end
22
28
  end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module GraphQL
5
+ # = GraphQL Subscription Field
6
+ #
7
+ # TODO: Finish and add description
8
+ class Field::SubscriptionField < Field::OutputField
9
+ redefine_singleton_method(:subscription?) { true }
10
+ event_types(:subscribed, append: true)
11
+
12
+ attr_reader :prepare_context
13
+
14
+ module Proxied # :nodoc: all
15
+ def full_scope
16
+ field.full_scope + super
17
+ end
18
+
19
+ def prepare_context
20
+ super || field.prepare_context
21
+ end
22
+ end
23
+
24
+ # Change the schema type of the field
25
+ def schema_type
26
+ :subscription
27
+ end
28
+
29
+ # A kind of alias to the subscribe event available
30
+ def subscribed(*args, **xargs, &block)
31
+ on(:subscribed, *args, **xargs, &block)
32
+ self
33
+ end
34
+
35
+ # Set the parts of the scope of the subscription
36
+ def scope(*parts)
37
+ (defined?(@scope) ? (@scope += parts) : (@scope = parts)).freeze
38
+ end
39
+
40
+ # Get the full scope of the field
41
+ def full_scope
42
+ return EMPTY_ARRAY unless defined?(@scope)
43
+ end
44
+
45
+ # Checks if the scope is correctly defined
46
+ def validate!(*)
47
+ super if defined? super
48
+ return unless defined?(@scope)
49
+
50
+ invalid = @scope.reject { |item| item.is_a?(Symbol) || item.is_a?(Proc) }
51
+ raise ArgumentError, (+<<~MSG).squish if invalid.any?
52
+ The "#{type_klass.gql_name}" has invalid values set for its scope: #{invalid.inspect}.
53
+ MSG
54
+ end
55
+
56
+ # A shortcut for trigger when everything is related to the provided object
57
+ # TODO: Maybe add support for object as an array of things
58
+ # TODO: Add support for +data_for+. The only problem right now is that
59
+ # providers run asynchronously, so passing data is a bit more complicated
60
+ # and maybe dangerous (size speaking)
61
+ def trigger_for(object, **xargs)
62
+ match_valid_object!(object)
63
+ xargs[:args] ||= extract_args_from(object)
64
+ trigger(**xargs)
65
+ end
66
+
67
+ # A shortcut for unsubscribe when everything is related to the provided
68
+ # object
69
+ # TODO: Maybe add support for object as an array of things
70
+ def unsubscribe_from(object, **xargs)
71
+ match_valid_object!(object)
72
+ xargs[:args] ||= extract_args_from(object)
73
+ unsubscribe(**xargs)
74
+ end
75
+
76
+ # Trigger an update to the subscription
77
+ def trigger(args: nil, scope: nil)
78
+ provider = owner.subscription_provider
79
+ provider.search_and_update(field: self, args: args, scope: scope)
80
+ end
81
+
82
+ # Force matching subscriptions to be removed
83
+ def unsubscribe(args: nil, scope: nil)
84
+ provider = owner.subscription_provider
85
+ provider.search_and_remove(field: self, args: args, scope: scope)
86
+ end
87
+
88
+ protected
89
+
90
+ # Match any argument with properties from the given +object+ so it
91
+ # produces all the possibilities of an update
92
+ def extract_args_from(object)
93
+ return unless arguments?
94
+
95
+ # Prepare all the possibilities
96
+ keys = []
97
+ hash_like = object.respond_to?(:[])
98
+ possibilities = all_arguments.each_value.with_object([]) do |arg, list|
99
+ keys << arg.name
100
+ list << []
101
+
102
+ value =
103
+ if object.respond_to?(arg.name)
104
+ object.public_send(arg.name)
105
+ elsif hash_like && (object.key?(arg.name) || object.key?(arg.name.to_s))
106
+ object[arg.name] || arg.name[arg.name.to_s]
107
+ end
108
+
109
+ list.last << value unless value.nil?
110
+ list.last << nil if arg.null?
111
+ end
112
+
113
+ # Now turn them into actual possible args
114
+ possibilities.reduce(:product).flatten.each_slice(keys.size).map do |items|
115
+ keys.zip(items).to_h
116
+ end
117
+ end
118
+
119
+ # Check if the provided +object+ is a match for the type that this field
120
+ # is associated with
121
+ def match_valid_object!(object)
122
+ raise ::ArgumentError, (+<<~MSG).squish unless type_klass&.object?
123
+ Cannot trigger with an object when the field is not associated to
124
+ an object-like result.
125
+ MSG
126
+
127
+ assignable = !object.is_a?(Module) && type_klass.valid_member?(object)
128
+ raise ::ArgumentError, (+<<~MSG).squish unless assignable
129
+ The provided object "#{object.inspect}" is not a valid member of
130
+ #{type_klass.inspect} for the :#{name} field.
131
+ MSG
132
+ end
133
+
134
+ def proxied
135
+ super if defined? super
136
+ extend Field::SubscriptionField::Proxied
137
+ end
138
+ end
139
+ end
140
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # This is a helper module that basically works with fields that have an
6
6
  # assigned type value
7
7
  module Field::TypedField
@@ -14,13 +14,7 @@ module Rails # :nodoc:
14
14
  delegate :input_type?, :output_type?, :leaf_type?, :kind, to: :type_klass
15
15
 
16
16
  def initialize(name, type, *args, **xargs, &block)
17
- if type.is_a?(Module) && type < GraphQL::Type
18
- @type_klass = type
19
- @type = type.to_sym
20
- else
21
- @type = type.to_s.underscore.to_sym
22
- end
23
-
17
+ assign_type(type)
24
18
  super(name, *args, **xargs, &block)
25
19
  end
26
20
 
@@ -36,7 +30,7 @@ module Rails # :nodoc:
36
30
  end
37
31
 
38
32
  # Sometimes the owner does not designate this, but it is safe to assume it
39
- # will be associated to the object valyd types
33
+ # will be associated to the object valid types
40
34
  def valid_field_types
41
35
  owner.try(:valid_field_types) || Type::Object.valid_field_types
42
36
  end
@@ -57,27 +51,42 @@ module Rails # :nodoc:
57
51
 
58
52
  # Add the listeners from the associated type
59
53
  def all_listeners
60
- type_klass.all_listeners + super
54
+ inherited = super
55
+ return inherited unless type_klass.listeners?
56
+ inherited.present? ? inherited + type_klass.all_listeners : type_klass.all_listeners
57
+ end
58
+
59
+ # Make sure to check the associated type
60
+ def listeners?
61
+ super || type_klass.listeners?
61
62
  end
62
63
 
63
64
  # Add the events from the associated type
64
65
  def all_events
65
- Helpers.merge_hash_array(type_klass.all_events, super)
66
+ inherited = super
67
+ return inherited unless type_klass.events?
68
+ return type_klass.all_events if inherited.blank?
69
+ Helpers.merge_hash_array(inherited, type_klass.all_events)
70
+ end
71
+
72
+ # Make sure to check the associated type
73
+ def events?
74
+ super || type_klass.events?
66
75
  end
67
76
 
68
77
  # Checks if the type of the field is valid
69
78
  def validate!(*)
70
79
  super if defined? super
71
80
 
72
- raise ArgumentError, <<~MSG.squish unless type_klass.is_a?(Module)
73
- Unable to find the "#{type.inspect}" input type on GraphQL context.
81
+ raise ArgumentError, (+<<~MSG).squish unless type_klass.is_a?(Module)
82
+ Unable to find the "#{type.inspect}" data type on GraphQL context.
74
83
  MSG
75
84
 
76
85
  valid_type = valid_field_types.empty? || valid_field_types.any? do |base_type|
77
86
  type_klass < base_type
78
87
  end
79
88
 
80
- raise ArgumentError, <<~MSG.squish unless valid_type
89
+ raise ArgumentError, (+<<~MSG).squish unless valid_type
81
90
  The "#{type_klass.base_type}" is not accepted in this context.
82
91
  MSG
83
92
  end
@@ -86,16 +95,28 @@ module Rails # :nodoc:
86
95
 
87
96
  # Little helper that shows the type of the field
88
97
  def inspect_type
89
- result = ': '
90
- result += '[' if array?
91
- result += type_klass.gql_name
92
- result += '!' if array? && !nullable?
93
- result += ']' if array?
94
- result += '!' unless null?
98
+ result = +': '
99
+ result << '[' if array?
100
+ result << type_klass.gql_name
101
+ result << '!' if array? && !nullable?
102
+ result << ']' if array?
103
+ result << '!' unless null?
95
104
  result
96
105
  end
97
106
 
98
- def proxied # :nodoc:
107
+ # A little hidden helper to support forcing reassignment of type, which
108
+ # should only be done with caution
109
+ def assign_type(type)
110
+ if type.is_a?(Module) && type < GraphQL::Type
111
+ @type_klass = type
112
+ @type = type.to_sym
113
+ else
114
+ @type_klass = nil
115
+ @type = type
116
+ end
117
+ end
118
+
119
+ def proxied
99
120
  super if defined? super
100
121
  extend Field::TypedField::Proxied
101
122
  end