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,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 Schema
6
6
  #
7
7
  # This is a pure representation of a GraphQL schema.
@@ -11,7 +11,7 @@ module Rails # :nodoc:
11
11
  # namespaces, where each schema is associated with one and only one
12
12
  # namespace, guiding requests and types searching.
13
13
  #
14
- # This class works similary to the {TypeMap}[rdoc-ref:Rails::base_classMap]
14
+ # This class works similarly to the {TypeMap}[rdoc-ref:Rails::base_classMap]
15
15
  # class, where its purpose is to know which QueryFields, Mutations, and
16
16
  # Subscriptions are available. The main difference is that it doesn't hold
17
17
  # namespace-based objects, since each schema is associated to a single
@@ -19,17 +19,13 @@ module Rails # :nodoc:
19
19
  class Schema
20
20
  extend Helpers::WithSchemaFields
21
21
  extend Helpers::WithDirectives
22
+ extend Helpers::WithGlobalID
22
23
  extend Helpers::Registerable
23
24
  extend GraphQL::Introspection
24
25
 
25
26
  include ActiveSupport::Configurable
26
27
  include ActiveSupport::Rescuable
27
-
28
- # The purpose of instantiating an schema is to have access to its
29
- # public methods. It then runs from the strategy perspective, pointing
30
- # out any other methods to the manually set event
31
- delegate_missing_to :event
32
- attr_reader :event
28
+ include Helpers::Instantiable
33
29
 
34
30
  self.abstract = true
35
31
  self.spec_object = true
@@ -37,20 +33,26 @@ module Rails # :nodoc:
37
33
 
38
34
  # Imports schema specific configurations
39
35
  configure do |config|
40
- %i[enable_string_collector request_strategies].each do |name|
41
- config.send("#{name}=", GraphQL.config.send(name))
36
+ inherited_keys = %i[
37
+ enable_introspection request_strategies
38
+ enable_string_collector default_response_format
39
+ schema_type_names cache
40
+ default_subscription_provider default_subscription_broadcastable
41
+ ].to_set
42
+
43
+ config.default_proc = proc do |hash, key|
44
+ hash[key] = GraphQL.config.send(key) if inherited_keys.include?(key)
42
45
  end
43
46
  end
44
47
 
48
+ rescue_from(PersistedQueryNotFound) do |error|
49
+ response = { errors: [{ message: +'PersistedQueryNotFound' }] }
50
+ error.request.force_response(response, error)
51
+ end
52
+
45
53
  class << self
46
54
  delegate :type_map, :logger, to: '::Rails::GraphQL'
47
-
48
- # Mark the given class to be pending of registration
49
- def inherited(subclass)
50
- subclass.spec_object = false
51
- subclass.abstract = false
52
- super if defined? super
53
- end
55
+ delegate :version, to: :type_map
54
56
 
55
57
  # :singleton-method:
56
58
  # Since there are only one schema per namespace, the name is constant
@@ -63,7 +65,7 @@ module Rails # :nodoc:
63
65
  # :singleton-method:
64
66
  # Since there is only one schema per namespace, then both kind and
65
67
  # to_sym, which is used to register, are the same
66
- def kind # :nodoc:
68
+ def kind
67
69
  :schema
68
70
  end
69
71
 
@@ -90,9 +92,18 @@ module Rails # :nodoc:
90
92
  end
91
93
 
92
94
  # :singleton-method:
93
- # For campatibility with type map
94
- def eager_load!
95
- TypeMap.loaded! :Schema
95
+ # The base class of all schemas is always +Schema+
96
+ def gid_base_class
97
+ Schema
98
+ end
99
+
100
+ # :singleton-method:
101
+ # Return the schema
102
+ def find_by_gid(gid)
103
+ result = find!(gid.namespace.to_sym)
104
+ return result if gid.name.nil?
105
+
106
+ result.find_field!(gid.scope, gid.name)
96
107
  end
97
108
 
98
109
  # Find all types that are available for the current schema
@@ -100,17 +111,66 @@ module Rails # :nodoc:
100
111
  type_map.each_from(namespace, base_class: base_class, &block)
101
112
  end
102
113
 
103
- # Schemas are assigned to a single namespace
104
- def set_namespace(*list)
105
- super(list.first)
114
+ # Schemas are assigned to a single namespace. You can provide a module
115
+ # as the second argument to associate that module to the same namespace
116
+ def set_namespace(ns, mod = nil)
117
+ @namespace = normalize_namespaces([ns]).first
118
+ type_map.associate(@namespace, mod) if mod.is_a?(Module)
106
119
  end
107
120
 
121
+ alias set_namespaces set_namespace
122
+
108
123
  # Schemas are assigned to a single namespace and not inherited
109
- def namespace(*list)
110
- list.blank? ? (namespaces.first || :base) : set_namespace(*list)
124
+ def namespace(*args)
125
+ if args.present?
126
+ set_namespace(*args)
127
+ elsif defined?(@namespace) && !@namespace.nil?
128
+ @namespace
129
+ else
130
+ :base
131
+ end
132
+ end
133
+
134
+ # Add compatibility to the list of namespaces
135
+ def namespaces
136
+ namespace
137
+ end
138
+
139
+ # Return the subscription provider for the current schema
140
+ def subscription_provider
141
+ if !defined?(@subscription_provider)
142
+ @subscription_provider = config.default_subscription_provider
143
+ subscription_provider
144
+ elsif @subscription_provider.is_a?(String)
145
+ provider = (name = @subscription_provider).safe_constantize
146
+ return @subscription_provider = provider.new(logger: logger) unless provider.nil?
147
+
148
+ raise ::NameError, +"uninitialized constant #{name}"
149
+ else
150
+ @subscription_provider
151
+ end
111
152
  end
112
153
 
113
- # Check if the class is already registered in the typemap
154
+ # Check if the schema is valid
155
+ def valid?
156
+ defined?(@validated) && @validated
157
+ end
158
+
159
+ # Only run the validated process if it has not yet been validated
160
+ def validate
161
+ validate! unless valid?
162
+ rescue StandardError
163
+ GraphQL.logger.warn(+"\e[1m\e[31mSchema #{name} is invalid!\e[0m")
164
+ raise
165
+ end
166
+
167
+ # Run validations and then mark itself as validated
168
+ def validate!(*)
169
+ super if defined? super
170
+ @validated = true
171
+ end
172
+
173
+ # Check if the class is already registered in the type map
114
174
  def registered?
115
175
  type_map.object_exist?(self, exclusive: true)
116
176
  end
@@ -118,11 +178,7 @@ module Rails # :nodoc:
118
178
  # The process to register a class and it's name on the index
119
179
  def register!
120
180
  return if self == GraphQL::Schema
121
-
122
- unless registered?
123
- super if defined? super
124
- return type_map.register(self).method(:validate!)
125
- end
181
+ return super unless registered?
126
182
 
127
183
  current = type_map.fetch(:schema,
128
184
  namespaces: namespace,
@@ -130,15 +186,16 @@ module Rails # :nodoc:
130
186
  exclusive: true,
131
187
  )
132
188
 
133
- raise ArgumentError, <<~MSG.squish
189
+ raise ArgumentError, (+<<~MSG).squish
134
190
  The #{namespace.inspect} namespace is already assigned to "#{current.name}".
135
191
  Please change the namespace for "#{klass.name}" class.
136
192
  MSG
137
193
  end
138
194
 
139
- # Checks if a given method can act as resolver
140
- def gql_resolver?(method_name)
141
- (instance_methods - GraphQL::Schema.instance_methods).include?(method_name)
195
+ # Hook into the unregister process to reset the subscription provider
196
+ def unregister!
197
+ restart_subscriptions
198
+ super
142
199
  end
143
200
 
144
201
  # Find a given +type+ associated with the schema
@@ -164,6 +221,65 @@ module Rails # :nodoc:
164
221
  type_map.fetch!(directive, **xargs)
165
222
  end
166
223
 
224
+ # Remove subscriptions by their provided +sids+
225
+ def remove_subscriptions(*sids)
226
+ subscription_provider&.remove(*sids)
227
+ end
228
+
229
+ # Add a new subscription using all the provided request subscription
230
+ # objects
231
+ def add_subscriptions(*subscriptions)
232
+ subscription_provider.add(*subscriptions)
233
+ end
234
+
235
+ # The the schema is unloaded, we need to make sure that the provider
236
+ # can smoothly shutdown itself
237
+ def restart_subscriptions
238
+ return unless defined?(@subscription_provider) && !@subscription_provider.nil?
239
+ subscription_provider.shutdown
240
+ end
241
+
242
+ # Checks if the given +operation+ can be subscribed to
243
+ def accepts_subscription?(operation)
244
+ subscription_provider.accepts?(operation)
245
+ end
246
+
247
+ # This receives a request subscription object and return an id for that.
248
+ # By default, it just produces a random uuid
249
+ def subscription_id_for(*)
250
+ SecureRandom.uuid
251
+ end
252
+
253
+ # Simple delegator to the cache store set on the schema config, mapped
254
+ # to +exist?+
255
+ def cached?(name, options = nil)
256
+ config.cache.exist?(expand_cache_key(name), options)
257
+ end
258
+
259
+ # Simple delegator to the cache store set on the schema config, mapped
260
+ # to +delete+
261
+ def delete_from_cache(name, options = nil)
262
+ config.cache.delete(expand_cache_key(name), options)
263
+ end
264
+
265
+ # Simple delegator to the cache store set on the schema config, mapped
266
+ # to +read+
267
+ def read_from_cache(name, options = nil)
268
+ config.cache.read(expand_cache_key(name), options)
269
+ end
270
+
271
+ # Simple delegator to the cache store set on the schema config, mapped
272
+ # to +write+
273
+ def write_on_cache(name, value, options = nil)
274
+ config.cache.write(expand_cache_key(name), value, options)
275
+ end
276
+
277
+ # Simple delegator to the cache store set on the schema config, mapped
278
+ # to +fetch+
279
+ def fetch_from_cache(name, options = nil)
280
+ config.cache.fetch(expand_cache_key(name), options)
281
+ end
282
+
167
283
  # Describe a schema as a GraphQL string
168
284
  def to_gql(**xargs)
169
285
  ToGQL.describe(self, **xargs)
@@ -171,12 +287,84 @@ module Rails # :nodoc:
171
287
 
172
288
  protected
173
289
 
174
- # TODO: Maybe provide an optional 'Any' scalar
290
+ attr_writer :subscription_provider
291
+
292
+ # Mark the given class to be pending of registration
293
+ def inherited(subclass)
294
+ subclass.spec_object = false
295
+ subclass.abstract = false
296
+ super if defined? super
297
+
298
+ # The only way to actually get the namespace into the cache prefix
299
+ subclass.config.define_singleton_method(:cache_prefix) do
300
+ self[:cache_prefix] ||= "#{GraphQL.config.cache_prefix}#{subclass.namespace}/"
301
+ end
302
+ end
303
+
304
+ # Indicate to type map that the current schema depends on all the
305
+ # files in the provided +path+ directory
306
+ def load_directory(dir = '.', recursive: true)
307
+ source = caller_locations(2, 1).first.path
308
+
309
+ absolute = dir.start_with?(File::SEPARATOR)
310
+ path = recursive ? File.join('**', '*.rb') : '*.rb'
311
+ dir = File.expand_path(dir, File.dirname(source)) unless absolute
312
+
313
+ list = Dir.glob(File.join(dir, path)).select do |file_name|
314
+ file_name != source
315
+ end
316
+
317
+ type_map.add_dependencies(list, to: namespace)
318
+ end
319
+
320
+ alias load_current_directory load_directory
321
+
322
+ # Load a list of known dependencies based on the given +type+
323
+ def load_dependencies(type, *list)
324
+ GraphQL.add_dependencies(type, *list, to: namespace)
325
+ end
326
+
327
+ # A syntax sugar for +load_dependencies(:directive, *list)+
328
+ def load_directives(*list)
329
+ load_dependencies(:directive, *list)
330
+ end
331
+
332
+ # A syntax sugar for +load_dependencies(:source, *list)+
333
+ def load_sources(*list, build: false)
334
+ load_dependencies(:source, *list)
335
+ build_all_sources if build
336
+ end
337
+
338
+ # Build all sources that has the belongs to the current namespace
339
+ def build_all_sources
340
+ GraphQL::Source.descendants.each do |klass|
341
+ next if klass.abstract?
342
+
343
+ ns = klass.namespaces
344
+ klass.build_all if (ns.blank? && namespace == :base) ||
345
+ ns == namespace || ns.try(:include?, namespace)
346
+ end
347
+ end
348
+
349
+ # Make sure to prefix the cache key
350
+ def expand_cache_key(name)
351
+ if name.is_a?(String)
352
+ name = +"#{config.cache_prefix}#{name}"
353
+ elsif name.respond_to?(:cache_key=)
354
+ name.cache_key = +"#{config.cache_prefix}#{name.cache_key}"
355
+ end
356
+
357
+ name
358
+ end
175
359
 
176
360
  # Generate the helper methods to easily create types within the
177
361
  # definition of the schema
178
362
  GraphQL::Type::KINDS.each do |kind|
179
363
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
364
+ def load_#{kind.underscore.pluralize}(*list)
365
+ load_dependencies(:#{kind.underscore}, *list)
366
+ end
367
+
180
368
  def #{kind.underscore}(name, **xargs, &block)
181
369
  create_type(name, :#{kind}, **xargs, &block)
182
370
  end
@@ -184,11 +372,13 @@ module Rails # :nodoc:
184
372
  end
185
373
 
186
374
  # Helper method to create a single source
187
- def source(object, superclass = nil, **xargs, &block)
375
+ def source(object, superclass = nil, build: true, **xargs, &block)
188
376
  superclass ||= GraphQL::Source.find_for!(object)
189
377
 
190
378
  xargs[:suffix] = 'Source'
379
+ create_and_build = build
191
380
  schema_namespace = namespace
381
+
192
382
  create_klass(object, superclass, GraphQL::Source, **xargs) do
193
383
  set_namespace schema_namespace
194
384
 
@@ -198,16 +388,16 @@ module Rails # :nodoc:
198
388
  end
199
389
 
200
390
  instance_exec(&block) if block.present?
201
- build!
391
+ build_all if create_and_build
202
392
  end
203
393
  end
204
394
 
205
395
  # Helper method to create multiple sources with the same type
206
- def sources(*list, of_type: nil, &block)
396
+ def sources(*list, of_type: nil, build: true, &block)
207
397
  list = list.flatten
208
398
 
209
399
  of_type ||= GraphQL::Source.find_for!(list.first)
210
- list.each { |object| source(object, of_type, &block) }
400
+ list.each { |object| source(object, of_type, build: build, &block) }
211
401
  end
212
402
 
213
403
  # A simpler way to create a new type object without having to create
@@ -235,20 +425,24 @@ module Rails # :nodoc:
235
425
 
236
426
  klass_name = name.classify.demodulize
237
427
  klass_name += xargs[:suffix] if xargs.key?(:suffix) &&
238
- !klass_name.ends_with?(xargs[:suffix])
428
+ !klass_name.end_with?(xargs[:suffix])
239
429
 
240
430
  if base_module.const_defined?(klass_name)
241
431
  klass = base_module.const_get(klass_name)
242
432
 
243
- raise DuplicatedError, <<~MSG.squish unless !xargs[:once] && klass < superclass
433
+ raise DuplicatedError, (+<<~MSG).squish unless !xargs[:once] && klass < superclass
244
434
  A constant named "#{klass_name}" already exists for the
245
435
  "#{base_module.name}" module.
246
436
  MSG
437
+
438
+ # This likely happened because the classes are being reloaded, so
439
+ # call inherited again as if the class has just been created
440
+ superclass.inherited(klass)
247
441
  else
248
442
  base_class ||= superclass.ancestors.find { |k| k.superclass === Class }
249
443
 
250
444
  valid = superclass.is_a?(Module) && superclass < base_class
251
- raise DefinitionError, <<~MSG.squish unless valid
445
+ raise DefinitionError, (+<<~MSG).squish unless valid
252
446
  The given "#{superclass}" superclass does not inherites from
253
447
  #{base_class.name} class.
254
448
  MSG
@@ -261,12 +455,10 @@ module Rails # :nodoc:
261
455
  klass.is_a?(Helpers::WithAssignment)
262
456
 
263
457
  klass.set_namespace(namespace)
264
- klass.instance_exec(&block) if block.present?
458
+ klass.module_exec(&block) if block.present?
265
459
  klass
266
460
  end
267
461
  end
268
462
  end
269
-
270
- ActiveSupport.run_load_hooks(:graphql, Schema)
271
463
  end
272
464
  end
@@ -4,13 +4,20 @@
4
4
  module GraphQL
5
5
  # List of constant shortcuts, as string to not trigger autoload
6
6
  CONST_SHORTCUTS = {
7
+ CacheKey: '::Rails::GraphQL::CacheKey',
8
+ Channel: '::Rails::GraphQL::Channel',
7
9
  Controller: '::Rails::GraphQL::Controller',
8
10
  Directive: '::Rails::GraphQL::Directive',
9
11
  Field: '::Rails::GraphQL::Field',
10
- Mutation: '::Rails::GraphQL::Mutation',
12
+ GlobalID: '::Rails::GraphQL::GlobalID',
11
13
  Request: '::Rails::GraphQL::Request',
12
14
  Schema: '::Rails::GraphQL::Schema',
13
15
  Source: '::Rails::GraphQL::Source',
16
+ Type: '::Rails::GraphQL::Type',
17
+
18
+ Query: '::Rails::GraphQL::Alternative::Query',
19
+ Mutation: '::Rails::GraphQL::Alternative::Mutation',
20
+ Subscription: '::Rails::GraphQL::Alternative::Subscription',
14
21
 
15
22
  Enum: '::Rails::GraphQL::Type::Enum',
16
23
  Input: '::Rails::GraphQL::Type::Input',
@@ -22,6 +29,7 @@ module GraphQL
22
29
  ProxyField: '::Rails::GraphQL::Field::ProxyField',
23
30
  AssociationField: '::Rails::GraphQL::Field::AssociationField',
24
31
 
32
+ BaseSource: '::Rails::GraphQL::Source::BaseSource',
25
33
  ActiveRecordSource: '::Rails::GraphQL::Source::ActiveRecordSource',
26
34
  }.freeze
27
35
 
@@ -38,8 +46,9 @@ module GraphQL
38
46
  DIRECTIVE_SHORTCUTS = %i[DeprecatedDirective IncludeDirective SkipDirective].freeze
39
47
 
40
48
  class << self
41
- delegate(:to_gql, :to_graphql, :type_map, to: 'Rails::GraphQL')
42
- delegate(*DIRECTIVE_SHORTCUTS, to: 'Rails::GraphQL::Directive')
49
+ delegate *DIRECTIVE_SHORTCUTS, to: 'Rails::GraphQL::Directive'
50
+ delegate :add_dependencies, :configure, :config, :to_gql, :to_graphql, :type_map,
51
+ to: 'Rails::GraphQL'
43
52
 
44
53
  # See {Request}[rdoc-ref:Rails::GraphQL::Request]
45
54
  def request(*args, **xargs)
@@ -53,6 +62,16 @@ module GraphQL
53
62
 
54
63
  alias perform execute
55
64
 
65
+ # See {Request}[rdoc-ref:Rails::GraphQL::Request]
66
+ def compile(*args, **xargs)
67
+ Rails::GraphQL::Request.compile(*args, **xargs)
68
+ end
69
+
70
+ # See {Request}[rdoc-ref:Rails::GraphQL::Request]
71
+ def valid?(*args, **xargs)
72
+ Rails::GraphQL::Request.valid?(*args, **xargs)
73
+ end
74
+
56
75
  # See {CONST_SHORTCUTS}[rdoc-ref:GraphQL::CONST_SHORTCUTS]
57
76
  def const_defined?(name, *)
58
77
  name = :"ActiveRecord#{name[2..-1]}" if name[0..1] === 'AR'
@@ -9,16 +9,17 @@ module Rails
9
9
  sti_interface? ? interface_class : super
10
10
  end
11
11
 
12
- # Get all unique attribute names that exists in the current model
13
- def reflection_attributes(holder)
14
- items = []
15
- each_reflection(holder) do |item|
16
- next unless item.belongs_to?
17
- next items << item.foreign_key.to_s unless item.polymorphic?
18
- items += [item.foreign_type, item.foreign_key]
12
+ # List of all columns that should be threated as IDs
13
+ # TODO: Add a exclusive cache for the build process
14
+ def id_columns
15
+ @id_columns ||= begin
16
+ result = Set.new(GraphQL.enumerate(primary_key))
17
+ each_reflection.each_with_object(result) do |item, arr|
18
+ next unless item.belongs_to?
19
+ arr << item.foreign_key.to_s
20
+ arr << item.foreign_key if item.polymorphic?
21
+ end
19
22
  end
20
-
21
- items.compact.flatten.unshift(primary_key)
22
23
  end
23
24
 
24
25
  # Iterate over all the attributes, except the primary key, from the model
@@ -27,26 +28,32 @@ module Rails
27
28
  def each_attribute(holder, skip_primary_key = true)
28
29
  adapter_key = GraphQL.ar_adapter_key(adapter_name)
29
30
 
30
- skip_fields = skips_for(holder).map(&:to_s)
31
+ skip_fields = Set.new
31
32
  skip_fields << model.inheritance_column
32
33
  skip_fields << primary_key unless skip_primary_key
33
34
 
34
- send("#{adapter_key}_attributes") do |attribute, *args|
35
- yield attribute, *args unless skip_fields.include?(attribute)
35
+ send(:"#{adapter_key}_attributes") do |attribute, *args, **xargs|
36
+ yield(attribute, *args, **xargs) unless skip_fields.include?(attribute)
36
37
  end
37
38
  end
38
39
 
39
40
  # Iterate over all the model reflections
40
- # TODO: Turn into an enumerator
41
- def each_reflection(holder)
42
- skip_fields = skips_for(holder).map(&:to_s)
43
- model._reflections.each_value do |reflection|
44
- next if skip_fields.include?(reflection.name.to_s)
45
-
41
+ def each_reflection(&block)
42
+ model._reflections.each_value.select do |reflection|
46
43
  reflection = model._reflections[reflection.to_s] \
47
44
  unless reflection.is_a?(abstract_reflection)
48
45
 
49
- yield reflection unless reflection.nil?
46
+ !reflection.nil?
47
+ end.each(&block)
48
+ end
49
+
50
+ # Build arguments that correctly reflect the primary key, as a single
51
+ # column or as an array of columns
52
+ def build_primary_key_arguments(holder)
53
+ if primary_key.is_a?(::Array)
54
+ primary_key.each { |key| holder.argument(key, :id, null: false) }
55
+ else
56
+ holder.argument(primary_key, :id, null: false)
50
57
  end
51
58
  end
52
59
 
@@ -65,22 +72,22 @@ module Rails
65
72
  def build_enum_types
66
73
  return remove_instance_variable(:@enums) if enums.blank?
67
74
 
68
- @enums = enums.map do |attribute, setting|
69
- [attribute.to_s, create_enum(attribute.to_s, setting, once: true)]
75
+ @enums = enums.each_with_object({}) do |(attribute, setting), hash|
76
+ class_name = base_name + attribute.to_s.classify
77
+ hash[attribute.to_s] = create_enum(class_name, setting, once: true)
70
78
  rescue DuplicatedError
71
79
  next
72
- end.compact.to_h.freeze
80
+ end.freeze
73
81
  end
74
82
 
75
83
  # Build all necessary attribute fields into the given +holder+
76
84
  def build_attribute_fields(holder, **field_options)
77
- attributes_as_ids = reflection_attributes(holder)
78
85
  each_attribute(holder) do |key, type, **options|
79
- next if skip.include?(key) || holder.field?(key)
86
+ next if holder.field?(key) || skip_field?(key, on: holder.kind)
80
87
 
81
88
  str_key = key.to_s
82
89
  type = (defined?(@enums) && @enums.key?(str_key) && @enums[str_key]) ||
83
- (attributes_as_ids.include?(str_key) && :id) || type
90
+ (id_columns.include?(str_key) && :id) || type
84
91
 
85
92
  options[:null] = !attr_required?(key) unless options.key?(:null)
86
93
  holder.field(key, type, **options.merge(field_options[key] || {}))
@@ -89,15 +96,19 @@ module Rails
89
96
 
90
97
  # Build all necessary reflection fields into the given +holder+
91
98
  def build_reflection_fields(holder)
92
- each_reflection(holder) do |item|
93
- next if holder.field?(item.name)
99
+ return unless with_associations?
100
+
101
+ each_reflection do |item|
102
+ next if holder.field?(item.name) || item.polymorphic? ||
103
+ skip_field?(item.name, on: holder.kind)
104
+
94
105
  type_map_after_register(item.klass) do |type|
95
106
  next unless (type.object? && type.try(:assigned_to) != item.klass) ||
96
107
  type.interface?
97
108
 
98
109
  options = reflection_to_options(item)
99
110
 
100
- if type <= Source::ActiveRecordSource
111
+ if type <= Source::Base
101
112
  source_name = item.collection? ? type.plural : type.singular
102
113
  proxy_options = options.merge(alias: reflection.name, of_type: :proxy)
103
114
 
@@ -106,21 +117,24 @@ module Rails
106
117
  end
107
118
  end
108
119
 
109
- field ||= holder.field(item.name, type, **options)
110
- field.before_resolve(:preload_association, item.name)
111
- field.before_resolve(:build_association_scope, item.name)
112
- field.resolve(:parent_owned_records, item.collection?)
120
+ if (field ||= holder.safe_field(item.name, type, **options))
121
+ field.before_resolve(:preload_association, item.name)
122
+ field.before_resolve(:build_association_scope, item.name)
123
+ field.resolve(:parent_owned_records, item.collection?)
124
+ end
113
125
  end
114
126
  end
115
127
  end
116
128
 
117
129
  # Build all +accepts_nested_attributes_for+ inside the input object
118
130
  def build_reflection_inputs(holder)
131
+ return unless with_associations?
132
+
119
133
  model.nested_attributes_options.each_key do |reflection_name|
120
134
  next if (reflection = model._reflect_on_association(reflection_name)).nil?
121
135
 
122
136
  expected_name = reflection.klass.name.tr(':', '')
123
- expected_name += 'Input' unless expected_name.ends_with?('Input')
137
+ expected_name += 'Input' unless expected_name.end_with?('Input')
124
138
 
125
139
  type_map_after_register(expected_name) do |input|
126
140
  options = reflection_to_options(reflection).merge(null: true)
@@ -130,14 +144,14 @@ module Rails
130
144
  end
131
145
  end
132
146
 
133
- # Transform a replection into a field options
147
+ # Transform a reflection into a field options
134
148
  def reflection_to_options(reflection)
135
149
  options = { array: reflection.collection? }
136
150
 
137
151
  required = options[:array]
138
152
  required ||= attr_required?(reflection.name)
139
153
  required ||= attr_required?(reflection.association_foreign_key) \
140
- if reflection.belongs_to?
154
+ if reflection.belongs_to? && !reflection.options[:optional]
141
155
 
142
156
  options[:nullable] = !options[:array]
143
157
  options[:null] = !required