rails-graphql 0.1.0

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 (266) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +19 -0
  4. data/Rakefile +31 -0
  5. data/ext/depend +3 -0
  6. data/ext/extconf.rb +57 -0
  7. data/ext/graphqlparser/Ast.cpp +346 -0
  8. data/ext/graphqlparser/Ast.h +1214 -0
  9. data/ext/graphqlparser/AstNode.h +36 -0
  10. data/ext/graphqlparser/AstVisitor.h +137 -0
  11. data/ext/graphqlparser/GraphQLParser.cpp +76 -0
  12. data/ext/graphqlparser/GraphQLParser.h +55 -0
  13. data/ext/graphqlparser/JsonVisitor.cpp +161 -0
  14. data/ext/graphqlparser/JsonVisitor.cpp.inc +456 -0
  15. data/ext/graphqlparser/JsonVisitor.h +121 -0
  16. data/ext/graphqlparser/JsonVisitor.h.inc +110 -0
  17. data/ext/graphqlparser/VERSION +1 -0
  18. data/ext/graphqlparser/c/GraphQLAst.cpp +324 -0
  19. data/ext/graphqlparser/c/GraphQLAst.h +180 -0
  20. data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +44 -0
  21. data/ext/graphqlparser/c/GraphQLAstNode.cpp +25 -0
  22. data/ext/graphqlparser/c/GraphQLAstNode.h +33 -0
  23. data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +21 -0
  24. data/ext/graphqlparser/c/GraphQLAstToJSON.h +24 -0
  25. data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +55 -0
  26. data/ext/graphqlparser/c/GraphQLAstVisitor.h +53 -0
  27. data/ext/graphqlparser/c/GraphQLParser.cpp +35 -0
  28. data/ext/graphqlparser/c/GraphQLParser.h +54 -0
  29. data/ext/graphqlparser/dump_json_ast.cpp +48 -0
  30. data/ext/graphqlparser/lexer.lpp +324 -0
  31. data/ext/graphqlparser/parser.ypp +693 -0
  32. data/ext/graphqlparser/parsergen/lexer.cpp +2633 -0
  33. data/ext/graphqlparser/parsergen/lexer.h +528 -0
  34. data/ext/graphqlparser/parsergen/location.hh +189 -0
  35. data/ext/graphqlparser/parsergen/parser.tab.cpp +3300 -0
  36. data/ext/graphqlparser/parsergen/parser.tab.hpp +646 -0
  37. data/ext/graphqlparser/parsergen/position.hh +179 -0
  38. data/ext/graphqlparser/parsergen/stack.hh +156 -0
  39. data/ext/graphqlparser/syntaxdefs.h +19 -0
  40. data/ext/libgraphqlparser/AstNode.h +36 -0
  41. data/ext/libgraphqlparser/CMakeLists.txt +148 -0
  42. data/ext/libgraphqlparser/CONTRIBUTING.md +23 -0
  43. data/ext/libgraphqlparser/GraphQLParser.cpp +76 -0
  44. data/ext/libgraphqlparser/GraphQLParser.h +55 -0
  45. data/ext/libgraphqlparser/JsonVisitor.cpp +161 -0
  46. data/ext/libgraphqlparser/JsonVisitor.h +121 -0
  47. data/ext/libgraphqlparser/LICENSE +22 -0
  48. data/ext/libgraphqlparser/README.clang-tidy +7 -0
  49. data/ext/libgraphqlparser/README.md +84 -0
  50. data/ext/libgraphqlparser/ast/ast.ast +203 -0
  51. data/ext/libgraphqlparser/ast/ast.py +61 -0
  52. data/ext/libgraphqlparser/ast/c.py +100 -0
  53. data/ext/libgraphqlparser/ast/c.pyc +0 -0
  54. data/ext/libgraphqlparser/ast/c_impl.py +61 -0
  55. data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
  56. data/ext/libgraphqlparser/ast/c_visitor_impl.py +39 -0
  57. data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
  58. data/ext/libgraphqlparser/ast/casing.py +26 -0
  59. data/ext/libgraphqlparser/ast/casing.pyc +0 -0
  60. data/ext/libgraphqlparser/ast/cxx.py +197 -0
  61. data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
  62. data/ext/libgraphqlparser/ast/cxx_impl.py +61 -0
  63. data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
  64. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +42 -0
  65. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
  66. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +80 -0
  67. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
  68. data/ext/libgraphqlparser/ast/cxx_visitor.py +64 -0
  69. data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
  70. data/ext/libgraphqlparser/ast/js.py +65 -0
  71. data/ext/libgraphqlparser/ast/license.py +10 -0
  72. data/ext/libgraphqlparser/ast/license.pyc +0 -0
  73. data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +25 -0
  74. data/ext/libgraphqlparser/c/GraphQLAstNode.h +33 -0
  75. data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +21 -0
  76. data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +24 -0
  77. data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +55 -0
  78. data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +53 -0
  79. data/ext/libgraphqlparser/c/GraphQLParser.cpp +35 -0
  80. data/ext/libgraphqlparser/c/GraphQLParser.h +54 -0
  81. data/ext/libgraphqlparser/clang-tidy-all.sh +3 -0
  82. data/ext/libgraphqlparser/cmake/version.cmake +16 -0
  83. data/ext/libgraphqlparser/dump_json_ast.cpp +48 -0
  84. data/ext/libgraphqlparser/go/README.md +20 -0
  85. data/ext/libgraphqlparser/go/callbacks.go +18 -0
  86. data/ext/libgraphqlparser/go/gotest.go +64 -0
  87. data/ext/libgraphqlparser/lexer.lpp +324 -0
  88. data/ext/libgraphqlparser/libgraphqlparser.pc.in +11 -0
  89. data/ext/libgraphqlparser/parser.ypp +693 -0
  90. data/ext/libgraphqlparser/parsergen/lexer.cpp +2633 -0
  91. data/ext/libgraphqlparser/parsergen/lexer.h +528 -0
  92. data/ext/libgraphqlparser/parsergen/location.hh +189 -0
  93. data/ext/libgraphqlparser/parsergen/parser.tab.cpp +3300 -0
  94. data/ext/libgraphqlparser/parsergen/parser.tab.hpp +646 -0
  95. data/ext/libgraphqlparser/parsergen/position.hh +179 -0
  96. data/ext/libgraphqlparser/parsergen/stack.hh +156 -0
  97. data/ext/libgraphqlparser/python/CMakeLists.txt +14 -0
  98. data/ext/libgraphqlparser/python/README.md +5 -0
  99. data/ext/libgraphqlparser/python/example.py +31 -0
  100. data/ext/libgraphqlparser/syntaxdefs.h +19 -0
  101. data/ext/libgraphqlparser/test/BuildCAPI.c +5 -0
  102. data/ext/libgraphqlparser/test/CMakeLists.txt +25 -0
  103. data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +28 -0
  104. data/ext/libgraphqlparser/test/ParserTests.cpp +352 -0
  105. data/ext/libgraphqlparser/test/kitchen-sink.graphql +59 -0
  106. data/ext/libgraphqlparser/test/kitchen-sink.json +1 -0
  107. data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +78 -0
  108. data/ext/libgraphqlparser/test/schema-kitchen-sink.json +1 -0
  109. data/ext/libgraphqlparser/test/valgrind.supp +33 -0
  110. data/ext/version.cpp +21 -0
  111. data/lib/generators/graphql/controller_generator.rb +22 -0
  112. data/lib/generators/graphql/schema_generator.rb +22 -0
  113. data/lib/generators/graphql/templates/controller.erb +5 -0
  114. data/lib/generators/graphql/templates/schema.erb +6 -0
  115. data/lib/graphqlparser.so +0 -0
  116. data/lib/rails-graphql.rb +2 -0
  117. data/lib/rails/graphql.rake +1 -0
  118. data/lib/rails/graphql.rb +185 -0
  119. data/lib/rails/graphql/adapters/mysql_adapter.rb +0 -0
  120. data/lib/rails/graphql/adapters/pg_adapter.rb +50 -0
  121. data/lib/rails/graphql/adapters/sqlite_adapter.rb +39 -0
  122. data/lib/rails/graphql/argument.rb +220 -0
  123. data/lib/rails/graphql/callback.rb +124 -0
  124. data/lib/rails/graphql/collectors.rb +14 -0
  125. data/lib/rails/graphql/collectors/hash_collector.rb +83 -0
  126. data/lib/rails/graphql/collectors/idented_collector.rb +73 -0
  127. data/lib/rails/graphql/collectors/json_collector.rb +114 -0
  128. data/lib/rails/graphql/config.rb +61 -0
  129. data/lib/rails/graphql/directive.rb +203 -0
  130. data/lib/rails/graphql/directive/deprecated_directive.rb +59 -0
  131. data/lib/rails/graphql/directive/include_directive.rb +24 -0
  132. data/lib/rails/graphql/directive/skip_directive.rb +24 -0
  133. data/lib/rails/graphql/errors.rb +42 -0
  134. data/lib/rails/graphql/event.rb +141 -0
  135. data/lib/rails/graphql/field.rb +318 -0
  136. data/lib/rails/graphql/field/input_field.rb +92 -0
  137. data/lib/rails/graphql/field/mutation_field.rb +52 -0
  138. data/lib/rails/graphql/field/output_field.rb +96 -0
  139. data/lib/rails/graphql/field/proxied_field.rb +131 -0
  140. data/lib/rails/graphql/field/resolved_field.rb +96 -0
  141. data/lib/rails/graphql/field/scoped_config.rb +22 -0
  142. data/lib/rails/graphql/field/typed_field.rb +104 -0
  143. data/lib/rails/graphql/helpers.rb +40 -0
  144. data/lib/rails/graphql/helpers/attribute_delegator.rb +39 -0
  145. data/lib/rails/graphql/helpers/inherited_collection.rb +152 -0
  146. data/lib/rails/graphql/helpers/leaf_from_ar.rb +141 -0
  147. data/lib/rails/graphql/helpers/registerable.rb +103 -0
  148. data/lib/rails/graphql/helpers/with_arguments.rb +125 -0
  149. data/lib/rails/graphql/helpers/with_assignment.rb +113 -0
  150. data/lib/rails/graphql/helpers/with_callbacks.rb +55 -0
  151. data/lib/rails/graphql/helpers/with_directives.rb +126 -0
  152. data/lib/rails/graphql/helpers/with_events.rb +81 -0
  153. data/lib/rails/graphql/helpers/with_fields.rb +141 -0
  154. data/lib/rails/graphql/helpers/with_namespace.rb +40 -0
  155. data/lib/rails/graphql/helpers/with_owner.rb +35 -0
  156. data/lib/rails/graphql/helpers/with_schema_fields.rb +230 -0
  157. data/lib/rails/graphql/helpers/with_validator.rb +52 -0
  158. data/lib/rails/graphql/introspection.rb +53 -0
  159. data/lib/rails/graphql/native.rb +56 -0
  160. data/lib/rails/graphql/native/functions.rb +38 -0
  161. data/lib/rails/graphql/native/location.rb +41 -0
  162. data/lib/rails/graphql/native/pointers.rb +23 -0
  163. data/lib/rails/graphql/native/visitor.rb +349 -0
  164. data/lib/rails/graphql/railtie.rb +85 -0
  165. data/lib/rails/graphql/railties/base_generator.rb +35 -0
  166. data/lib/rails/graphql/railties/controller.rb +101 -0
  167. data/lib/rails/graphql/railties/controller_runtime.rb +40 -0
  168. data/lib/rails/graphql/railties/log_subscriber.rb +62 -0
  169. data/lib/rails/graphql/request.rb +343 -0
  170. data/lib/rails/graphql/request/arguments.rb +93 -0
  171. data/lib/rails/graphql/request/component.rb +100 -0
  172. data/lib/rails/graphql/request/component/field.rb +225 -0
  173. data/lib/rails/graphql/request/component/fragment.rb +118 -0
  174. data/lib/rails/graphql/request/component/operation.rb +178 -0
  175. data/lib/rails/graphql/request/component/operation/subscription.rb +16 -0
  176. data/lib/rails/graphql/request/component/spread.rb +119 -0
  177. data/lib/rails/graphql/request/component/typename.rb +82 -0
  178. data/lib/rails/graphql/request/context.rb +51 -0
  179. data/lib/rails/graphql/request/errors.rb +54 -0
  180. data/lib/rails/graphql/request/event.rb +112 -0
  181. data/lib/rails/graphql/request/helpers/directives.rb +64 -0
  182. data/lib/rails/graphql/request/helpers/selection_set.rb +87 -0
  183. data/lib/rails/graphql/request/helpers/value_writers.rb +115 -0
  184. data/lib/rails/graphql/request/steps/organizable.rb +146 -0
  185. data/lib/rails/graphql/request/steps/prepareable.rb +33 -0
  186. data/lib/rails/graphql/request/steps/resolveable.rb +32 -0
  187. data/lib/rails/graphql/request/strategy.rb +249 -0
  188. data/lib/rails/graphql/request/strategy/dynamic_instance.rb +41 -0
  189. data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +36 -0
  190. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +28 -0
  191. data/lib/rails/graphql/schema.rb +272 -0
  192. data/lib/rails/graphql/shortcuts.rb +77 -0
  193. data/lib/rails/graphql/source.rb +371 -0
  194. data/lib/rails/graphql/source/active_record/builders.rb +154 -0
  195. data/lib/rails/graphql/source/active_record_source.rb +231 -0
  196. data/lib/rails/graphql/source/scoped_arguments.rb +87 -0
  197. data/lib/rails/graphql/to_gql.rb +368 -0
  198. data/lib/rails/graphql/type.rb +138 -0
  199. data/lib/rails/graphql/type/enum.rb +206 -0
  200. data/lib/rails/graphql/type/enum/directive_location_enum.rb +30 -0
  201. data/lib/rails/graphql/type/enum/type_kind_enum.rb +57 -0
  202. data/lib/rails/graphql/type/input.rb +134 -0
  203. data/lib/rails/graphql/type/interface.rb +82 -0
  204. data/lib/rails/graphql/type/object.rb +111 -0
  205. data/lib/rails/graphql/type/object/directive_object.rb +34 -0
  206. data/lib/rails/graphql/type/object/enum_value_object.rb +25 -0
  207. data/lib/rails/graphql/type/object/field_object.rb +54 -0
  208. data/lib/rails/graphql/type/object/input_value_object.rb +49 -0
  209. data/lib/rails/graphql/type/object/schema_object.rb +40 -0
  210. data/lib/rails/graphql/type/object/type_object.rb +136 -0
  211. data/lib/rails/graphql/type/scalar.rb +71 -0
  212. data/lib/rails/graphql/type/scalar/bigint_scalar.rb +34 -0
  213. data/lib/rails/graphql/type/scalar/binary_scalar.rb +30 -0
  214. data/lib/rails/graphql/type/scalar/boolean_scalar.rb +37 -0
  215. data/lib/rails/graphql/type/scalar/date_scalar.rb +34 -0
  216. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +32 -0
  217. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +35 -0
  218. data/lib/rails/graphql/type/scalar/float_scalar.rb +32 -0
  219. data/lib/rails/graphql/type/scalar/id_scalar.rb +39 -0
  220. data/lib/rails/graphql/type/scalar/int_scalar.rb +36 -0
  221. data/lib/rails/graphql/type/scalar/string_scalar.rb +28 -0
  222. data/lib/rails/graphql/type/scalar/time_scalar.rb +40 -0
  223. data/lib/rails/graphql/type/union.rb +87 -0
  224. data/lib/rails/graphql/type_map.rb +347 -0
  225. data/lib/rails/graphql/version.rb +7 -0
  226. data/test/assets/introspection-db.json +0 -0
  227. data/test/assets/introspection-mem.txt +1 -0
  228. data/test/assets/introspection.gql +91 -0
  229. data/test/assets/luke.jpg +0 -0
  230. data/test/assets/mem.gql +428 -0
  231. data/test/assets/sqlite.gql +423 -0
  232. data/test/config.rb +80 -0
  233. data/test/graphql/request/context_test.rb +70 -0
  234. data/test/graphql/schema_test.rb +190 -0
  235. data/test/graphql/source_test.rb +237 -0
  236. data/test/graphql/type/enum_test.rb +203 -0
  237. data/test/graphql/type/input_test.rb +138 -0
  238. data/test/graphql/type/interface_test.rb +72 -0
  239. data/test/graphql/type/object_test.rb +104 -0
  240. data/test/graphql/type/scalar/bigint_scalar_test.rb +42 -0
  241. data/test/graphql/type/scalar/binary_scalar_test.rb +17 -0
  242. data/test/graphql/type/scalar/boolean_scalar_test.rb +40 -0
  243. data/test/graphql/type/scalar/date_scalar_test.rb +29 -0
  244. data/test/graphql/type/scalar/date_time_scalar_test.rb +29 -0
  245. data/test/graphql/type/scalar/decimal_scalar_test.rb +28 -0
  246. data/test/graphql/type/scalar/float_scalar_test.rb +22 -0
  247. data/test/graphql/type/scalar/id_scalar_test.rb +26 -0
  248. data/test/graphql/type/scalar/int_scalar_test.rb +26 -0
  249. data/test/graphql/type/scalar/string_scalar_test.rb +17 -0
  250. data/test/graphql/type/scalar/time_scalar_test.rb +36 -0
  251. data/test/graphql/type/scalar_test.rb +45 -0
  252. data/test/graphql/type/union_test.rb +82 -0
  253. data/test/graphql/type_map_test.rb +362 -0
  254. data/test/graphql/type_test.rb +68 -0
  255. data/test/graphql_test.rb +55 -0
  256. data/test/integration/config.rb +56 -0
  257. data/test/integration/memory/star_wars_introspection_test.rb +144 -0
  258. data/test/integration/memory/star_wars_query_test.rb +184 -0
  259. data/test/integration/memory/star_wars_validation_test.rb +99 -0
  260. data/test/integration/schemas/memory.rb +232 -0
  261. data/test/integration/schemas/sqlite.rb +82 -0
  262. data/test/integration/sqlite/star_wars_introspection_test.rb +15 -0
  263. data/test/integration/sqlite/star_wars_mutation_test.rb +82 -0
  264. data/test/integration/sqlite/star_wars_query_test.rb +71 -0
  265. data/test/test_ext.rb +48 -0
  266. metadata +509 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ module Helpers # :nodoc:
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :AttributeDelegator
9
+ autoload :InheritedCollection
10
+ autoload :LeafFromAr
11
+ autoload :Registerable
12
+
13
+ autoload :WithArguments
14
+ autoload :WithAssignment
15
+ autoload :WithCallbacks
16
+ autoload :WithDirectives
17
+ autoload :WithEvents
18
+ autoload :WithFields
19
+ autoload :WithNamespace
20
+ autoload :WithOwner
21
+ autoload :WithSchemaFields
22
+ autoload :WithValidator
23
+
24
+ # Easy way to duplicate objects and set a new owner
25
+ def self.dup_all_with_owner(enumerator, owner)
26
+ enumerator.map { |item| dup_with_owner(item, owner) }.presence
27
+ end
28
+
29
+ # Easy way to duplicate a object and set a new owner
30
+ def self.dup_with_owner(item, owner)
31
+ item.dup.tap { |x| x.instance_variable_set(:@owner, owner) }
32
+ end
33
+
34
+ # Global helper that merge a hash that contains values as arrays
35
+ def self.merge_hash_array(one, other)
36
+ one.merge(other) { |_, lval, rval| lval + rval }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ module Helpers # :nodoc:
6
+ # This is an extra magic on top of the delegator class from the standard
7
+ # lib that allows fetching a specific property of the delegated object
8
+ class AttributeDelegator < ActiveSupport::ProxyObject
9
+ def initialize(obj = nil, attribute = nil, cache: true, &block)
10
+ @delegate_sd_attr = attribute
11
+ @delegate_sd_obj = block.presence || obj
12
+ @delegate_cache = cache
13
+ end
14
+
15
+ private
16
+
17
+ def respond_to_missing?(method_name, include_private = false) # :nodoc:
18
+ __getobj__.respond_to?(method_name, include_private) || super
19
+ end
20
+
21
+ def method_missing(method_name, *args, **xargs, &block) # :nodoc:
22
+ return super unless __getobj__.respond_to?(method_name)
23
+ __getobj__.public_send(method_name, *args, **xargs, &block)
24
+ end
25
+
26
+ def __getobj__
27
+ @delegate_cache ? (@delegate_ch_obj ||= __buildobj__) : __buildobj__
28
+ end
29
+
30
+ def __buildobj__
31
+ result = @delegate_sd_obj
32
+ result = result.call if result.respond_to?(:call)
33
+ result = result&.public_send(@delegate_sd_attr) if @delegate_sd_attr
34
+ result
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ module Helpers # :nodoc:
6
+ module InheritedCollection # :nodoc:
7
+ DEFAULT_TYPES = {
8
+ array: '[]',
9
+ set: 'Set.new',
10
+ hash: '{}',
11
+ hash_array: 'Hash.new { |h, k| h[k] = [] }',
12
+ hash_set: 'Hash.new { |h, k| h[k] = Set.new }',
13
+ }.freeze
14
+
15
+ # Declare a class-level attribute whose value is both isolated and also
16
+ # inherited from parent classes. Subclasses can change their own value
17
+ # and it will not impact parent class.
18
+ #
19
+ # Inspired by +class_attribute+ from ActiveSupport.
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * <tt>:instance_reader</tt> - Sets the instance reader method (defaults to true).
24
+ # * <tt>:instance_predicate</tt> - Sets a predicate method (defaults to true).
25
+ # * <tt>:type</tt> - Defines the type of the values stored (defaults to :set).
26
+ #
27
+ # ==== Examples
28
+ #
29
+ # class Base
30
+ # inherited_collection :settings
31
+ # end
32
+ #
33
+ # class Subclass < Base
34
+ # end
35
+ #
36
+ # Base.settings << :a
37
+ # Subclass.settings # => []
38
+ # Subclass.all_settings # => [:a]
39
+ # Subclass.settings << :b
40
+ # Subclass.settings # => [:b]
41
+ # Subclass.all_settings # => [:a, :b]
42
+ # Base.settings # => [:a]
43
+ # Base.all_settings # => [:a]
44
+ #
45
+ # For convenience, an instance predicate method is defined as well,
46
+ # which checks for the +all_+ method. To skip it, pass
47
+ # <tt>instance_predicate: false</tt>.
48
+ #
49
+ # Subclass.settings? # => false
50
+ #
51
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
52
+ #
53
+ # object.settings # => NoMethodError
54
+ # object.settings? # => NoMethodError
55
+ #
56
+ def inherited_collection(
57
+ *attrs,
58
+ instance_reader: true,
59
+ instance_predicate: true,
60
+ type: :set
61
+ )
62
+ attrs.each do |name|
63
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
64
+ def self.all_#{name}
65
+ ::Rails::GraphQL::Helpers::AttributeDelegator.new do
66
+ fetch_inherited_#{type}('@#{name}')
67
+ end
68
+ end
69
+
70
+ def self.#{name}
71
+ @#{name} ||= #{DEFAULT_TYPES[type]}
72
+ end
73
+ RUBY
74
+
75
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1) if instance_predicate
76
+ def self.#{name}?
77
+ (defined?(@#{name}) && @#{name}.present?) || superclass.try(:#{name}?)
78
+ end
79
+ RUBY
80
+
81
+ if instance_reader
82
+ delegate(name.to_sym, :"all_#{name}", to: :class)
83
+ delegate(:"#{name}?", to: :class) if instance_predicate
84
+ end
85
+ end
86
+ end
87
+
88
+ protected
89
+
90
+ # Combine an inherited list of arrays
91
+ def fetch_inherited_array(ivar)
92
+ inherited_ancestors.each_with_object([]) do |klass, result|
93
+ next result unless klass.instance_variable_defined?(ivar)
94
+ val = klass.instance_variable_get(ivar)
95
+ result.merge(val) unless val.blank?
96
+ end
97
+ end
98
+
99
+ # Combine an inherited list of set objects
100
+ def fetch_inherited_set(ivar)
101
+ inherited_ancestors.each_with_object(Set.new) do |klass, result|
102
+ next result unless klass.instance_variable_defined?(ivar)
103
+ val = klass.instance_variable_get(ivar)
104
+ result.merge(val) unless val.blank?
105
+ end
106
+ end
107
+
108
+ # Combine an inherited list of hashes but keeping only the most recent
109
+ # value, which means that keys might be replaced
110
+ def fetch_inherited_hash(ivar)
111
+ inherited_ancestors.each_with_object({}) do |klass, result|
112
+ next result unless klass.instance_variable_defined?(ivar)
113
+ val = klass.instance_variable_get(ivar)
114
+ result.merge!(val) unless val.blank?
115
+ end
116
+ end
117
+
118
+ # Right now we can't use Hash with default proc for equivalency due to
119
+ # a bug on Ruby https://bugs.ruby-lang.org/issues/17181
120
+
121
+ # Combine an inherited list of hashes, which also will combine arrays,
122
+ # ensuring that same key items will be combined
123
+ def fetch_inherited_hash_array(ivar)
124
+ inherited_ancestors.inject({}) do |result, klass|
125
+ next result unless klass.instance_variable_defined?(ivar)
126
+ val = klass.instance_variable_get(ivar)
127
+ Helpers.merge_hash_array(result, val)
128
+ end
129
+ end
130
+
131
+ # Combine an inherited list of hashes, which also will combine arrays,
132
+ # ensuring that same key items will be combined
133
+ def fetch_inherited_hash_set(ivar)
134
+ inherited_ancestors.inject({}) do |result, klass|
135
+ next result unless klass.instance_variable_defined?(ivar)
136
+ val = klass.instance_variable_get(ivar)
137
+ Helpers.merge_hash_array(result, val)
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ # Return a list of all the ancestor classes up until object
144
+ def inherited_ancestors
145
+ [self].tap do |list|
146
+ list.unshift(list.first.superclass) until list.first.superclass === Object
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ module Helpers # :nodoc:
6
+ # Helper module allowing leaf values to be collected direct from
7
+ # ActiveRecord. It also helps AR Adapters to define the necessary
8
+ # methods and settings to operate with this extractor.
9
+ #
10
+ # TODO: Implement ActiveRecord serialization
11
+ module LeafFromAr
12
+ def self.extended(other)
13
+ # Defines which type exactly represents the scalar type on the
14
+ # ActiveRecord adapter for casting purposes
15
+ other.class_attribute :ar_adapter_type, instance_writer: false, default: {}
16
+
17
+ # A list of ActiveRecord aliases per adapter to skip casting
18
+ other.class_attribute :ar_adapter_aliases, instance_writer: false,
19
+ default: (Hash.new { |h, k| h[k] = Set.new })
20
+ end
21
+
22
+ # Identifies the ActiveRecord type (actually it uses the
23
+ # ActiveModel::Type#type method, but ActiveRecord uses the same
24
+ # reference) of this object. When mismatching, the query must cast the
25
+ # value.
26
+ def ar_type
27
+ :string
28
+ end
29
+
30
+ # If a class extend this module, we assume that it can serialize
31
+ # attributes direct from the query point of view
32
+ def from_ar?(ar_object, attribute)
33
+ ar_object&.has_attribute?(attribute)
34
+ end
35
+
36
+ # Returns an Arel object that represents how this object is serialized
37
+ # direct from the query
38
+ #
39
+ # This happens in 3 parts
40
+ #
41
+ # 1. It finds a method to get the arel representation of the
42
+ # accessor of the given attribute
43
+ # 2. If the attribute type mismatch the ar type or any of its aliases,
44
+ # then invoke a adapter-specific cast
45
+ # 3. If necessary, adapters can describe a specific way to serialize
46
+ # the arel attribute to ensure equivalency
47
+ #
48
+ # ==== Example
49
+ #
50
+ # Lets imagine a scenario where the adapter is the +PostgreSQL+, the
51
+ # attribute is a +data+ field with +enum+ type from a +sample+ table and
52
+ # the result must be a binary base64 data:
53
+ #
54
+ # 1. Sample.arel_attribute(:data)
55
+ # # => "samples"."data"
56
+ # 2. Arel::Nodes::NamedFunction.new('CAST', [arel_object, Arel.sql('text')])
57
+ # # => CAST("samples"."data" AS text)
58
+ # 3. Arel::Nodes::NamedFunction.new('ENCODE', [arel_object, Arel.sql("'base64")])
59
+ # # => ENCODE(CAST("samples"."data" AS text), 'base64')
60
+ def from_ar(ar_object, attribute)
61
+ key = adapter_key(ar_object)
62
+ method_name = "from_#{key}_adapter"
63
+ method_name = 'from_abstract_adapter' unless respond_to?(method_name, true)
64
+
65
+ arel_object = send(method_name, ar_object, attribute)
66
+ return if arel_object.nil?
67
+
68
+ arel_object = try("cast_#{key}_attribute", arel_object, ar_adapter_type[key]) \
69
+ unless match_ar_type?(ar_object, attribute, key)
70
+ return if arel_object.nil?
71
+
72
+ method_name = "#{key}_serialize"
73
+ respond_to?(method_name, true) ? try(method_name, arel_object) : arel_object
74
+ end
75
+
76
+ # Helper method that should be used for ActiveRecord adapters in order
77
+ # to provide the correct methods and settings for retrieving the given
78
+ # value direct from a query.
79
+ #
80
+ # ==== Options
81
+ #
82
+ # * <tt>:fetch</tt> - A specific function to build the arel for fetching the attribute
83
+ # * <tt>:cast</tt> - A function to cast an attribute to the correct value
84
+ # * <tt>:type</tt> - A symbol that represents the exactly database type that matches
85
+ # the ActiveRecord type (ie. :varchar for :string)
86
+ # * <tt>:aliases</tt> - An array of AR aliases that for the adapter they are
87
+ # equivalent
88
+ def define_for(adapter, **settings)
89
+ adapter = ar_adapters[adapter] if ar_adapters.key?(adapter)
90
+ raise ArgumentError, <<~MSG.squish unless ar_adapters.values.include?(adapter)
91
+ The given #{adapter.inspect} adapter is not a valid option.
92
+ The valid options are: #{ar_adapters.to_a.flatten.map(&:inspect).to_sentence}.
93
+ MSG
94
+
95
+ define_singleton_method("from_#{adapter}_adapter", &settings[:fetch]) \
96
+ if settings.key?(:fetch)
97
+
98
+ define_singleton_method("cast_#{adapter}_attribute", &settings[:cast]) \
99
+ if settings.key?(:cast)
100
+
101
+ ar_adapter_type[adapter] = settings[:type] if settings.key?(:type)
102
+ ar_adapter_aliases[adapter] += Array.wrap(settings[:aliases]) \
103
+ if settings.key?(:aliases)
104
+ end
105
+
106
+ protected
107
+
108
+ # A pretty abstract way to access an attribute from an ActiveRecord
109
+ # object using arel
110
+ def from_abstract_adapter(ar_object, attribute)
111
+ ar_object.arel_attribute(attribute)
112
+ end
113
+
114
+ # Change the ActiveRecord type of the given object
115
+ def set_ar_type!(type)
116
+ redefine_singleton_method(:ar_type) { type }
117
+ end
118
+
119
+ private
120
+
121
+ # Return the list of defined ActiveRecord Adapters
122
+ def ar_adapters
123
+ GraphQL.config.ar_adapters
124
+ end
125
+
126
+ # Given the ActiveRecord Object, find the key to compound the method
127
+ # name for the specific attribute accessor
128
+ def adapter_key(ar_object)
129
+ ar_adapters[ar_object.connection.adapter_name]
130
+ end
131
+
132
+ # Check if the GraphQL ar type of this object matches the
133
+ # ActiveRecord type or any alias for the specific adapter
134
+ def match_ar_type?(ar_object, attribute, adapter_key)
135
+ attr_type = ar_object.columns_hash[attribute.to_s].type
136
+ ar_type.eql?(attr_type) || ar_adapter_aliases[adapter_key].include?(attr_type)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ module Helpers # :nodoc:
6
+ # Helper module responsible for name stuff and also responsible for
7
+ # registering the objects to the type map, which also checks for the
8
+ # uniqueness of the name of things.
9
+ module Registerable
10
+ NAME_EXP = /GraphQL::(?:Type::\w+::|Directive::)?([:\w]+?)([A-Z][a-z]+)?\z/.freeze
11
+
12
+ # Here we define a couple of attributes used by registration
13
+ def self.extended(other)
14
+ other.extend(Registerable::ClassMethods)
15
+ other.extend(Helpers::WithNamespace)
16
+
17
+ # If a type is marked as abstract, it's then used as a base and it
18
+ # won't appear in the introspection
19
+ other.class_attribute :abstract, instance_writer: false, default: false
20
+
21
+ # Marks if the object is one of those defined on the spec, which
22
+ # marks the object as part of the introspection system
23
+ other.class_attribute :spec_object, instance_writer: false, default: false
24
+
25
+ # The given description of the type
26
+ other.class_attribute :description, instance_writer: false
27
+ end
28
+
29
+ module ClassMethods # :nodoc: all
30
+ # Here we wait for the class to be fully loaded so that we can
31
+ # correctly trigger the registration
32
+ def inherited(subclass)
33
+ super if defined? super
34
+ return if subclass.try(:abstract)
35
+ GraphQL.type_map.postpone_registration(subclass)
36
+ end
37
+
38
+ # Check if the class is already registered in the typemap
39
+ def registered?
40
+ GraphQL.type_map.object_exist?(self, exclusive: true)
41
+ end
42
+
43
+ # The process to register a class and it's name on the index
44
+ def register!
45
+ return if abstract? || gql_name.blank?
46
+
47
+ raise DuplicatedError, <<~MSG.squish if registered?
48
+ The "#{gql_name}" is already defined, the only way to change its
49
+ definition is by using extensions.
50
+ MSG
51
+
52
+ invalid_internal = !spec_object && gql_name.start_with?('__')
53
+ raise NameError, <<~MSG.squish if invalid_internal
54
+ The name "#{gql_name}" is invalid. Only internal objects from the
55
+ spec can have a name starting with "__".
56
+ MSG
57
+
58
+ super if defined? super
59
+ GraphQL.type_map.register(self).method(:validate!)
60
+ end
61
+ end
62
+
63
+ # Return the name of the object as a GraphQL name
64
+ def gql_name
65
+ @gql_name ||= begin
66
+ name.match(NAME_EXP).try(:[], 1)&.tr(':', '')
67
+ end unless anonymous?
68
+ end
69
+
70
+ alias graphql_name gql_name
71
+
72
+ # Return the name of the object as a symbol
73
+ def to_sym
74
+ @gql_key ||= gql_name&.underscore&.to_sym
75
+ end
76
+
77
+ # Get or set a list of aliases for this object
78
+ def aliases(*list)
79
+ if list.empty?
80
+ defined?(@aliases) ? @aliases : Set.new
81
+ else
82
+ (@aliases ||= Set.new).merge list.flatten.map do |item|
83
+ item.to_s.underscore.to_sym
84
+ end
85
+ end
86
+ end
87
+
88
+ protected
89
+
90
+ # An alias for +description = value.strip_heredoc.chomp+ that can be
91
+ # used as method
92
+ def desc(value)
93
+ self.description = value.strip_heredoc.chomp
94
+ end
95
+
96
+ # Change the gql name of the object
97
+ def rename!(name)
98
+ @gql_name = name
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end