graphql 1.7.6 → 1.8.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 (289) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/function_generator.rb +1 -1
  3. data/lib/generators/graphql/install_generator.rb +14 -8
  4. data/lib/generators/graphql/loader_generator.rb +1 -1
  5. data/lib/generators/graphql/mutation_generator.rb +6 -1
  6. data/lib/generators/graphql/templates/function.erb +2 -2
  7. data/lib/generators/graphql/templates/loader.erb +2 -2
  8. data/lib/generators/graphql/templates/schema.erb +1 -1
  9. data/lib/graphql/argument.rb +25 -19
  10. data/lib/graphql/backtrace/tracer.rb +16 -22
  11. data/lib/graphql/backtrace.rb +1 -1
  12. data/lib/graphql/backwards_compatibility.rb +2 -3
  13. data/lib/graphql/base_type.rb +31 -31
  14. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +14 -0
  15. data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
  16. data/lib/graphql/define/assign_object_field.rb +5 -12
  17. data/lib/graphql/deprecated_dsl.rb +42 -0
  18. data/lib/graphql/directive.rb +1 -0
  19. data/lib/graphql/enum_type.rb +3 -1
  20. data/lib/graphql/execution/execute.rb +21 -13
  21. data/lib/graphql/execution/instrumentation.rb +82 -0
  22. data/lib/graphql/execution/lazy/lazy_method_map.rb +1 -1
  23. data/lib/graphql/execution/lazy/resolve.rb +1 -3
  24. data/lib/graphql/execution/multiplex.rb +12 -29
  25. data/lib/graphql/execution.rb +1 -0
  26. data/lib/graphql/field.rb +21 -4
  27. data/lib/graphql/function.rb +14 -0
  28. data/lib/graphql/input_object_type.rb +3 -1
  29. data/lib/graphql/interface_type.rb +5 -3
  30. data/lib/graphql/internal_representation/node.rb +26 -14
  31. data/lib/graphql/internal_representation/visit.rb +3 -6
  32. data/lib/graphql/introspection/base_object.rb +16 -0
  33. data/lib/graphql/introspection/directive_location_enum.rb +11 -7
  34. data/lib/graphql/introspection/directive_type.rb +23 -16
  35. data/lib/graphql/introspection/dynamic_fields.rb +11 -0
  36. data/lib/graphql/introspection/entry_points.rb +29 -0
  37. data/lib/graphql/introspection/enum_value_type.rb +16 -11
  38. data/lib/graphql/introspection/field_type.rb +21 -12
  39. data/lib/graphql/introspection/input_value_type.rb +26 -23
  40. data/lib/graphql/introspection/schema_field.rb +7 -2
  41. data/lib/graphql/introspection/schema_type.rb +36 -22
  42. data/lib/graphql/introspection/type_by_name_field.rb +10 -2
  43. data/lib/graphql/introspection/type_kind_enum.rb +10 -6
  44. data/lib/graphql/introspection/type_type.rb +85 -23
  45. data/lib/graphql/introspection/typename_field.rb +1 -0
  46. data/lib/graphql/introspection.rb +3 -10
  47. data/lib/graphql/language/block_string.rb +47 -0
  48. data/lib/graphql/language/document_from_schema_definition.rb +280 -0
  49. data/lib/graphql/language/generation.rb +3 -182
  50. data/lib/graphql/language/lexer.rb +144 -69
  51. data/lib/graphql/language/lexer.rl +15 -4
  52. data/lib/graphql/language/nodes.rb +141 -78
  53. data/lib/graphql/language/parser.rb +677 -630
  54. data/lib/graphql/language/parser.y +18 -12
  55. data/lib/graphql/language/printer.rb +361 -0
  56. data/lib/graphql/language/token.rb +10 -3
  57. data/lib/graphql/language.rb +3 -0
  58. data/lib/graphql/non_null_type.rb +1 -1
  59. data/lib/graphql/object_type.rb +1 -6
  60. data/lib/graphql/query/arguments.rb +63 -32
  61. data/lib/graphql/query/context.rb +32 -2
  62. data/lib/graphql/query/literal_input.rb +4 -1
  63. data/lib/graphql/query/null_context.rb +1 -1
  64. data/lib/graphql/query/result.rb +1 -1
  65. data/lib/graphql/query/variables.rb +21 -3
  66. data/lib/graphql/query.rb +19 -6
  67. data/lib/graphql/railtie.rb +109 -0
  68. data/lib/graphql/relay/connection_resolve.rb +3 -0
  69. data/lib/graphql/relay/connection_type.rb +5 -3
  70. data/lib/graphql/relay/edge_type.rb +2 -1
  71. data/lib/graphql/relay/global_id_resolve.rb +5 -1
  72. data/lib/graphql/relay/mongo_relation_connection.rb +40 -0
  73. data/lib/graphql/relay/mutation/instrumentation.rb +1 -1
  74. data/lib/graphql/relay/mutation/resolve.rb +5 -1
  75. data/lib/graphql/relay/relation_connection.rb +14 -19
  76. data/lib/graphql/relay/type_extensions.rb +30 -0
  77. data/lib/graphql/relay.rb +2 -0
  78. data/lib/graphql/scalar_type.rb +14 -2
  79. data/lib/graphql/schema/argument.rb +92 -0
  80. data/lib/graphql/schema/build_from_definition.rb +64 -18
  81. data/lib/graphql/schema/enum.rb +85 -0
  82. data/lib/graphql/schema/enum_value.rb +74 -0
  83. data/lib/graphql/schema/field.rb +372 -0
  84. data/lib/graphql/schema/finder.rb +153 -0
  85. data/lib/graphql/schema/input_object.rb +87 -0
  86. data/lib/graphql/schema/interface.rb +105 -0
  87. data/lib/graphql/schema/introspection_system.rb +93 -0
  88. data/lib/graphql/schema/late_bound_type.rb +32 -0
  89. data/lib/graphql/schema/list.rb +32 -0
  90. data/lib/graphql/schema/loader.rb +2 -2
  91. data/lib/graphql/schema/member/accepts_definition.rb +152 -0
  92. data/lib/graphql/schema/member/base_dsl_methods.rb +100 -0
  93. data/lib/graphql/schema/member/build_type.rb +137 -0
  94. data/lib/graphql/schema/member/cached_graphql_definition.rb +26 -0
  95. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  96. data/lib/graphql/schema/member/has_arguments.rb +50 -0
  97. data/lib/graphql/schema/member/has_fields.rb +130 -0
  98. data/lib/graphql/schema/member/instrumentation.rb +115 -0
  99. data/lib/graphql/schema/member/type_system_helpers.rb +34 -0
  100. data/lib/graphql/schema/member.rb +28 -0
  101. data/lib/graphql/schema/middleware_chain.rb +5 -1
  102. data/lib/graphql/schema/mutation.rb +138 -0
  103. data/lib/graphql/schema/non_null.rb +38 -0
  104. data/lib/graphql/schema/object.rb +81 -0
  105. data/lib/graphql/schema/printer.rb +33 -266
  106. data/lib/graphql/schema/relay_classic_mutation.rb +87 -0
  107. data/lib/graphql/schema/rescue_middleware.rb +8 -7
  108. data/lib/graphql/schema/resolver.rb +122 -0
  109. data/lib/graphql/schema/scalar.rb +35 -0
  110. data/lib/graphql/schema/traversal.rb +102 -22
  111. data/lib/graphql/schema/union.rb +36 -0
  112. data/lib/graphql/schema/validation.rb +3 -2
  113. data/lib/graphql/schema.rb +381 -12
  114. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  115. data/lib/graphql/static_validation/literal_validator.rb +16 -4
  116. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +6 -6
  117. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -1
  118. data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
  119. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +11 -1
  120. data/lib/graphql/static_validation/validation_context.rb +1 -1
  121. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -5
  122. data/lib/graphql/subscriptions/instrumentation.rb +5 -1
  123. data/lib/graphql/subscriptions/serialize.rb +2 -0
  124. data/lib/graphql/subscriptions.rb +90 -16
  125. data/lib/graphql/tracing/data_dog_tracing.rb +49 -0
  126. data/lib/graphql/tracing/new_relic_tracing.rb +26 -0
  127. data/lib/graphql/tracing/platform_tracing.rb +20 -7
  128. data/lib/graphql/tracing/scout_tracing.rb +2 -2
  129. data/lib/graphql/tracing.rb +1 -0
  130. data/lib/graphql/unresolved_type_error.rb +3 -2
  131. data/lib/graphql/upgrader/member.rb +894 -0
  132. data/lib/graphql/upgrader/schema.rb +37 -0
  133. data/lib/graphql/version.rb +1 -1
  134. data/lib/graphql.rb +5 -25
  135. data/readme.md +2 -2
  136. data/spec/dummy/app/channels/graphql_channel.rb +23 -2
  137. data/spec/dummy/log/development.log +239 -0
  138. data/spec/dummy/log/test.log +410 -0
  139. data/spec/dummy/test/system/action_cable_subscription_test.rb +4 -0
  140. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-x/-xYZjAnuuzgR79fcznLTQtSdh6AARxu8FcQ_J6p7L3U.cache +0 -0
  141. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/13/13HiV12xyoQvT-1L39ZzLwMZxjyaGMiENmfw7f-QTIc.cache +0 -0
  142. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3W/3Wtf5pCWdqq0AB-iB0Y9uUNrTkruRxIEf1XFn_BETU0.cache +1 -0
  143. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5i/5iguGafb4hOn8262Kn8Q37ogNN9MxxQKGKNzHAzUcvI.cache +1 -0
  144. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8m/8mj2T6yy847Mc2Z7k3Xzh8O91hhVJt3NrPe8ASNDlIA.cache +1 -0
  145. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DT/DTQyMpr4ABZYQetsdRJ5A7S4jf1r3ie4FGOR7GZBNSs.cache +3 -0
  146. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dq/DqJ5_yJPrP5iLlOQyTQsjAVI5FE5LCVDkED0f7GgsSo.cache +3 -0
  147. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -0
  148. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -0
  149. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rw/RwDuCV-XpnCtjNkvhpJfBuxXMk0b5AD3L9eR6M-wcy0.cache +3 -0
  150. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/UL/ULdjhhb0bRuqmaG7XSZlFYzGYCXTDnqZuJBTWRlzqgw.cache +0 -0
  151. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Up/UpPNgh0yYoUsyMDh5zWqe_U6qJIyTC6-dxMMAs1vvlM.cache +1 -0
  152. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wg/Wguh-szFGTI1gaL6npYwPekMXflugRei7F_mOyRucXg.cache +0 -0
  153. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X-/X-khLYMA9mqFRPg3zAi86mREDxpKl4bdKYp3uF6WHos.cache +0 -0
  154. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bi/BIkdhfxsezxM4q-HZ4oCNTq97WEJTigcq0tpX2cDvbY.cache +0 -0
  155. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ff/FfxmA4CMHQZT7exx0G7NS1Wpcnny0vzp-Jhc2H36bp8.cache +1 -0
  156. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gE/gEiiG4GZNy_djEjK2pHm_NgA-gyhLZhdQvo0Yt96GqE.cache +0 -0
  157. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gn/gnA9ZSqpjccNL2m8pe_jBvY6SinXlCzXDWyop83Od8s.cache +1 -0
  158. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lO/lOAan3cMwCE_Hli6gsDML88xFNfn0nxPmvrSkW7eEOw.cache +1 -0
  159. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m1/M1pv8MJEPLXGLvS8QxVh3DSO9cI4mRt5FHFWdrvUj6o.cache +2 -0
  160. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m7/m77qH7ZqH0_0SmwJbiKGDd-aLau1Dav847DC6ge46zY.cache +1 -0
  161. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sj/sjRjnjRB37lH2vrgtkdJ8Cz84__IJ978IuKTM7HcztI.cache +0 -0
  162. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/um/um1JrirR4hJhK-1rE-HywlyCi5ibgxHVrReiujZBWJM.cache +1 -0
  163. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v4/v4fwVytD7ITcE0_GDbslZEYud8a5Okm85fV1o7SDl6g.cache +0 -0
  164. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v_/v_0PAQt0iipQjFP5zjgkkk9Stnpf4VzvnMv67d1Keuw.cache +1 -0
  165. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdT9U4MKxe1PyqNjVuCKMpCl3dxGCIRJIlwUTfh2DQU.cache +1 -0
  166. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xI/xIaxut_fEIhKBDqljTNwYaADK9kj3gG0ESrfHs-5_og.cache +3 -0
  167. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/y0/y0SJOqIx2fn1SKqOkAihsQow0trRJrSIyAswufVuoA8.cache +0 -0
  168. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zg/zgpzeaX-KZErHyGJ1aBH3ZusweNXMneVZule88XsIJI.cache +1 -0
  169. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zy/zYFltDy-8VC-uKq2BVEiJJyYXNFvVzAKuMlR3ZIYZsk.cache +0 -0
  170. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  171. data/spec/fixtures/upgrader/account.original.rb +19 -0
  172. data/spec/fixtures/upgrader/account.transformed.rb +20 -0
  173. data/spec/fixtures/upgrader/blame_range.original.rb +43 -0
  174. data/spec/fixtures/upgrader/blame_range.transformed.rb +30 -0
  175. data/spec/fixtures/upgrader/date_time.original.rb +24 -0
  176. data/spec/fixtures/upgrader/date_time.transformed.rb +23 -0
  177. data/spec/fixtures/upgrader/delete_project.original.rb +28 -0
  178. data/spec/fixtures/upgrader/delete_project.transformed.rb +27 -0
  179. data/spec/fixtures/upgrader/gist_order_field.original.rb +14 -0
  180. data/spec/fixtures/upgrader/gist_order_field.transformed.rb +13 -0
  181. data/spec/fixtures/upgrader/increment_count.original.rb +59 -0
  182. data/spec/fixtures/upgrader/increment_count.transformed.rb +50 -0
  183. data/spec/fixtures/upgrader/photo.original.rb +10 -0
  184. data/spec/fixtures/upgrader/photo.transformed.rb +12 -0
  185. data/spec/fixtures/upgrader/release_order.original.rb +15 -0
  186. data/spec/fixtures/upgrader/release_order.transformed.rb +14 -0
  187. data/spec/fixtures/upgrader/starrable.original.rb +49 -0
  188. data/spec/fixtures/upgrader/starrable.transformed.rb +46 -0
  189. data/spec/fixtures/upgrader/subscribable.original.rb +55 -0
  190. data/spec/fixtures/upgrader/subscribable.transformed.rb +51 -0
  191. data/spec/fixtures/upgrader/type_x.original.rb +65 -0
  192. data/spec/fixtures/upgrader/type_x.transformed.rb +56 -0
  193. data/spec/generators/graphql/function_generator_spec.rb +26 -0
  194. data/spec/generators/graphql/install_generator_spec.rb +1 -1
  195. data/spec/generators/graphql/loader_generator_spec.rb +24 -0
  196. data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
  197. data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
  198. data/spec/graphql/argument_spec.rb +21 -0
  199. data/spec/graphql/backtrace_spec.rb +10 -0
  200. data/spec/graphql/base_type_spec.rb +42 -0
  201. data/spec/graphql/boolean_type_spec.rb +3 -3
  202. data/spec/graphql/directive_spec.rb +3 -1
  203. data/spec/graphql/enum_type_spec.rb +18 -5
  204. data/spec/graphql/execution/execute_spec.rb +4 -4
  205. data/spec/graphql/execution/instrumentation_spec.rb +165 -0
  206. data/spec/graphql/execution/multiplex_spec.rb +2 -2
  207. data/spec/graphql/execution_error_spec.rb +18 -0
  208. data/spec/graphql/float_type_spec.rb +2 -2
  209. data/spec/graphql/id_type_spec.rb +1 -1
  210. data/spec/graphql/input_object_type_spec.rb +15 -2
  211. data/spec/graphql/int_type_spec.rb +2 -2
  212. data/spec/graphql/interface_type_spec.rb +12 -0
  213. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
  214. data/spec/graphql/introspection/schema_type_spec.rb +2 -0
  215. data/spec/graphql/language/block_string_spec.rb +70 -0
  216. data/spec/graphql/language/document_from_schema_definition_spec.rb +770 -0
  217. data/spec/graphql/language/generation_spec.rb +21 -186
  218. data/spec/graphql/language/lexer_spec.rb +21 -1
  219. data/spec/graphql/language/nodes_spec.rb +21 -12
  220. data/spec/graphql/language/parser_spec.rb +1 -1
  221. data/spec/graphql/language/printer_spec.rb +203 -0
  222. data/spec/graphql/object_type_spec.rb +22 -0
  223. data/spec/graphql/query/arguments_spec.rb +25 -15
  224. data/spec/graphql/query/context_spec.rb +18 -0
  225. data/spec/graphql/query/executor_spec.rb +2 -1
  226. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -8
  227. data/spec/graphql/query/variables_spec.rb +42 -1
  228. data/spec/graphql/query_spec.rb +31 -5
  229. data/spec/graphql/rake_task_spec.rb +3 -1
  230. data/spec/graphql/relay/base_connection_spec.rb +1 -1
  231. data/spec/graphql/relay/connection_instrumentation_spec.rb +2 -2
  232. data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
  233. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  234. data/spec/graphql/relay/mongo_relation_connection_spec.rb +474 -0
  235. data/spec/graphql/relay/mutation_spec.rb +9 -7
  236. data/spec/graphql/relay/range_add_spec.rb +5 -1
  237. data/spec/graphql/relay/relation_connection_spec.rb +65 -1
  238. data/spec/graphql/schema/argument_spec.rb +87 -0
  239. data/spec/graphql/schema/build_from_definition_spec.rb +89 -5
  240. data/spec/graphql/schema/enum_spec.rb +74 -0
  241. data/spec/graphql/schema/field_spec.rb +225 -0
  242. data/spec/graphql/schema/finder_spec.rb +135 -0
  243. data/spec/graphql/schema/input_object_spec.rb +111 -0
  244. data/spec/graphql/schema/instrumentation_spec.rb +40 -0
  245. data/spec/graphql/schema/interface_spec.rb +185 -0
  246. data/spec/graphql/schema/introspection_system_spec.rb +39 -0
  247. data/spec/graphql/schema/member/accepts_definition_spec.rb +111 -0
  248. data/spec/graphql/schema/member/build_type_spec.rb +17 -0
  249. data/spec/graphql/schema/member/has_fields_spec.rb +129 -0
  250. data/spec/graphql/schema/member/type_system_helpers_spec.rb +63 -0
  251. data/spec/graphql/schema/mutation_spec.rb +148 -0
  252. data/spec/graphql/schema/object_spec.rb +175 -0
  253. data/spec/graphql/schema/printer_spec.rb +111 -15
  254. data/spec/graphql/schema/relay_classic_mutation_spec.rb +38 -0
  255. data/spec/graphql/schema/rescue_middleware_spec.rb +11 -0
  256. data/spec/graphql/schema/resolver_spec.rb +131 -0
  257. data/spec/graphql/schema/scalar_spec.rb +95 -0
  258. data/spec/graphql/schema/traversal_spec.rb +31 -0
  259. data/spec/graphql/schema/union_spec.rb +65 -0
  260. data/spec/graphql/schema/validation_spec.rb +1 -1
  261. data/spec/graphql/schema/warden_spec.rb +11 -11
  262. data/spec/graphql/schema_spec.rb +55 -12
  263. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -2
  264. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  265. data/spec/graphql/string_type_spec.rb +3 -3
  266. data/spec/graphql/subscriptions_spec.rb +273 -184
  267. data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +1 -1
  268. data/spec/graphql/tracing/new_relic_tracing_spec.rb +47 -0
  269. data/spec/graphql/tracing/platform_tracing_spec.rb +60 -1
  270. data/spec/graphql/union_type_spec.rb +1 -1
  271. data/spec/graphql/upgrader/member_spec.rb +516 -0
  272. data/spec/graphql/upgrader/schema_spec.rb +82 -0
  273. data/spec/spec_helper.rb +8 -0
  274. data/spec/support/dummy/schema.rb +53 -24
  275. data/spec/support/jazz.rb +544 -0
  276. data/spec/support/lazy_helpers.rb +21 -23
  277. data/spec/support/new_relic.rb +24 -0
  278. data/spec/support/star_trek/data.rb +109 -0
  279. data/spec/support/star_trek/schema.rb +388 -0
  280. data/spec/support/star_wars/data.rb +6 -7
  281. data/spec/support/star_wars/schema.rb +127 -171
  282. metadata +233 -11
  283. data/lib/graphql/introspection/arguments_field.rb +0 -7
  284. data/lib/graphql/introspection/enum_values_field.rb +0 -18
  285. data/lib/graphql/introspection/fields_field.rb +0 -13
  286. data/lib/graphql/introspection/input_fields_field.rb +0 -12
  287. data/lib/graphql/introspection/interfaces_field.rb +0 -11
  288. data/lib/graphql/introspection/of_type_field.rb +0 -6
  289. data/lib/graphql/introspection/possible_types_field.rb +0 -11
@@ -46,32 +46,30 @@ module LazyHelpers
46
46
  end
47
47
  end
48
48
 
49
- LazySum = GraphQL::ObjectType.define do
50
- name "LazySum"
51
- field :value, types.Int do
52
- resolve ->(o, a, c) { o == 13 ? nil : o }
49
+ class LazySum < GraphQL::Schema::Object
50
+ field :value, Integer, null: true, resolve: ->(o, a, c) { o == 13 ? nil : o }
51
+ field :nestedSum, LazySum, null: false do
52
+ argument :value, Integer, required: true
53
53
  end
54
- field :nestedSum, !LazySum do
55
- argument :value, !types.Int
56
- resolve ->(o, args, c) {
57
- if args[:value] == 13
58
- Wrapper.new(nil)
59
- else
60
- SumAll.new(c, o + args[:value])
61
- end
62
- }
54
+
55
+ def nested_sum(value:)
56
+ if value == 13
57
+ Wrapper.new(nil)
58
+ else
59
+ SumAll.new(@context, @object + value)
60
+ end
63
61
  end
64
62
 
65
- field :nullableNestedSum, LazySum do
66
- argument :value, types.Int
67
- resolve ->(o, args, c) {
68
- if args[:value] == 13
69
- Wrapper.new(nil)
70
- else
71
- SumAll.new(c, o + args[:value])
72
- end
73
- }
63
+ field :nullableNestedSum, LazySum, null: true do
64
+ argument :value, Integer, required: true
74
65
  end
66
+ alias :nullable_nested_sum :nested_sum
67
+ end
68
+
69
+ using GraphQL::DeprecatedDSL
70
+ if RUBY_ENGINE == "jruby"
71
+ # JRuby doesn't support refinements, so the `using` above won't work
72
+ GraphQL::DeprecatedDSL.activate
75
73
  end
76
74
 
77
75
  LazyQuery = GraphQL::ObjectType.define do
@@ -136,7 +134,7 @@ module LazyHelpers
136
134
  end
137
135
  end
138
136
 
139
- LazySchema = GraphQL::Schema.define do
137
+ class LazySchema < GraphQL::Schema
140
138
  query(LazyQuery)
141
139
  mutation(LazyQuery)
142
140
  lazy_resolve(Wrapper, :item)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # A stub for the NewRelic agent, so we can make assertions about how it is used
3
+ if defined?(NewRelic)
4
+ raise "Expected NewRelic to be undefined, so that we could define a stub for it."
5
+ end
6
+
7
+ module NewRelic
8
+ TRANSACTION_NAMES = []
9
+ # Reset state between tests
10
+ def self.clear_all
11
+ TRANSACTION_NAMES.clear
12
+ end
13
+ module Agent
14
+ def self.set_transaction_name(name)
15
+ TRANSACTION_NAMES << name
16
+ end
17
+
18
+ module MethodTracerHelpers
19
+ def self.trace_execution_scoped(trace_name)
20
+ yield
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+ require 'ostruct'
3
+
4
+ module StarTrek
5
+ names = [
6
+ 'USS Enterprise',
7
+ 'USS Excelsior',
8
+ 'USS Reliant',
9
+ 'IKS Koraga',
10
+ 'IKS Kronos One',
11
+ 'IRW Khazara',
12
+ 'IRW Praetus',
13
+ ]
14
+
15
+ MONGOID_CONFIG = {
16
+ clients: {
17
+ default: {
18
+ database: 'graphql_ruby_test',
19
+ hosts: ['localhost:27017']
20
+ }
21
+ },
22
+ sessions: {
23
+ default: {
24
+ database: 'graphql_ruby_test',
25
+ hosts: ['localhost:27017']
26
+ }
27
+ }
28
+ }.freeze
29
+
30
+ def db_name
31
+ MONGOID_CONFIG[:clients][:default][:database]
32
+ end
33
+ module_function :db_name
34
+
35
+ # Set up "Bases" in MongoDB
36
+ Mongoid.load_configuration(MONGOID_CONFIG)
37
+
38
+ class Base
39
+ include Mongoid::Document
40
+ field :name, type: String
41
+ field :sector, type: String
42
+ field :faction_id, type: Integer
43
+ end
44
+
45
+ Base.collection.drop
46
+ Base.create!(name: "Deep Space Station K-7", sector: "Mempa", faction_id: 1)
47
+ Base.create!(name: "Regula I", sector: "Mutara", faction_id: 1)
48
+ Base.create!(name: "Deep Space Nine", sector: "Bajoran", faction_id: 1)
49
+ Base.create!(name: "Firebase P'ok", sector: nil, faction_id: 2)
50
+ Base.create!(name: "Ganalda Space Station", sector: "Archanis", faction_id: 2)
51
+ Base.create!(name: "Rh'Ihho Station", sector: "Rator", faction_id: 3)
52
+
53
+ class FactionRecord
54
+ attr_reader :id, :name, :ships, :bases, :bases_clone
55
+ def initialize(id:, name:, ships:, bases:, bases_clone:)
56
+ @id = id
57
+ @name = name
58
+ @ships = ships
59
+ @bases = bases
60
+ @bases_clone = bases_clone
61
+ end
62
+ end
63
+
64
+ federation = FactionRecord.new({
65
+ id: '1',
66
+ name: 'United Federation of Planets',
67
+ ships: ['1', '2', '3'],
68
+ bases: Base.where(faction_id: 1),
69
+ bases_clone: Base.where(faction_id: 1),
70
+ })
71
+
72
+ klingon = FactionRecord.new({
73
+ id: '2',
74
+ name: 'Klingon Empire',
75
+ ships: ['4', '5'],
76
+ bases: Base.where(faction_id: 2),
77
+ bases_clone: Base.where(faction_id: 2),
78
+ })
79
+
80
+ romulan = FactionRecord.new({
81
+ id: '2',
82
+ name: 'Romulan Star Empire',
83
+ ships: ['6', '7'],
84
+ bases: Base.where(faction_id: 3),
85
+ bases_clone: Base.where(faction_id: 3),
86
+ })
87
+
88
+ DATA = {
89
+ "Faction" => {
90
+ "1" => federation,
91
+ "2" => klingon,
92
+ "3" => romulan,
93
+ },
94
+ "Ship" => names.each_with_index.reduce({}) do |memo, (name, idx)|
95
+ id = (idx + 1).to_s
96
+ memo[id] = OpenStruct.new(name: name, id: id)
97
+ memo
98
+ end,
99
+ "Base" => Hash.new { |h, k| h[k] = Base.find(k) }
100
+ }
101
+
102
+ def DATA.create_ship(name, faction_id)
103
+ new_id = (self["Ship"].keys.map(&:to_i).max + 1).to_s
104
+ new_ship = OpenStruct.new(id: new_id, name: name)
105
+ self["Ship"][new_id] = new_ship
106
+ self["Faction"][faction_id].ships << new_id
107
+ new_ship
108
+ end
109
+ end
@@ -0,0 +1,388 @@
1
+ # frozen_string_literal: true
2
+ module StarTrek
3
+ # Adapted from graphql-relay-js
4
+ # https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/StarTrekSchema.js
5
+
6
+ class Ship < GraphQL::Schema::Object
7
+ implements GraphQL::Relay::Node.interface
8
+ global_id_field :id
9
+ field :name, String, null: true
10
+ # Test cyclical connection types:
11
+ field :ships, Ship.connection_type, null: false
12
+ end
13
+
14
+ class BaseType < GraphQL::Schema::Object
15
+ graphql_name "Base"
16
+ implements GraphQL::Relay::Node.interface
17
+ global_id_field :id
18
+ field :name, String, null: false, resolve: ->(obj, args, ctx) {
19
+ LazyWrapper.new {
20
+ if obj.id.nil?
21
+ raise GraphQL::ExecutionError, "Boom!"
22
+ else
23
+ obj.name
24
+ end
25
+ }
26
+ }
27
+ field :sector, String, null: true
28
+ end
29
+
30
+ # Use an optional block to add fields to the connection type:
31
+ BaseConnectionWithTotalCountType = BaseType.define_connection(nodes_field: true) do
32
+ name "BasesConnectionWithTotalCount"
33
+ field :totalCount do
34
+ type types.Int
35
+ resolve ->(obj, args, ctx) { obj.nodes.count }
36
+ end
37
+ end
38
+
39
+ class CustomBaseEdge < GraphQL::Relay::Edge
40
+ def upcased_name
41
+ node.name.upcase
42
+ end
43
+
44
+ def upcased_parent_name
45
+ parent.name.upcase
46
+ end
47
+ end
48
+
49
+ CustomBaseEdgeType = BaseType.define_edge do
50
+ name "CustomBaseEdge"
51
+ field :upcasedName, types.String, property: :upcased_name
52
+ field :upcasedParentName, types.String, property: :upcased_parent_name
53
+ field :edgeClassName, types.String do
54
+ resolve ->(obj, args, ctx) { obj.class.name }
55
+ end
56
+ end
57
+
58
+ CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBaseEdge, edge_type: CustomBaseEdgeType, nodes_field: true) do
59
+ name "CustomEdgeBaseConnection"
60
+
61
+ field :totalCountTimes100 do
62
+ type types.Int
63
+ resolve ->(obj, args, ctx) { obj.nodes.count * 100 }
64
+ end
65
+
66
+ field :fieldName, types.String, resolve: ->(obj, args, ctx) { obj.field.name }
67
+ end
68
+
69
+ # Example of GraphQL::Function used with the connection helper:
70
+ class ShipsWithMaxPageSize < GraphQL::Function
71
+ argument :nameIncludes, GraphQL::STRING_TYPE
72
+ def call(obj, args, ctx)
73
+ all_ships = obj.ships.map { |ship_id| StarTrek::DATA["Ship"][ship_id] }
74
+ if args[:nameIncludes]
75
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
76
+ end
77
+ all_ships
78
+ end
79
+
80
+ type Ship.connection_type
81
+ end
82
+
83
+ ShipConnectionWithParentType = Ship.define_connection do
84
+ name "ShipConnectionWithParent"
85
+ field :parentClassName, !types.String do
86
+ resolve ->(o, a, c) { o.parent.class.name }
87
+ end
88
+ end
89
+
90
+ class Faction < GraphQL::Schema::Object
91
+ implements GraphQL::Relay::Node.interface
92
+
93
+ field :id, ID, null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
94
+ field :name, String, null: true
95
+ field :ships, ShipConnectionWithParentType, connection: true, max_page_size: 1000, null: true, resolve: ->(obj, args, ctx) {
96
+ all_ships = obj.ships.map {|ship_id| StarTrek::DATA["Ship"][ship_id] }
97
+ if args[:nameIncludes]
98
+ case args[:nameIncludes]
99
+ when "error"
100
+ all_ships = GraphQL::ExecutionError.new("error from within connection")
101
+ when "raisedError"
102
+ raise GraphQL::ExecutionError.new("error raised from within connection")
103
+ when "lazyError"
104
+ all_ships = LazyWrapper.new { GraphQL::ExecutionError.new("lazy error from within connection") }
105
+ when "lazyRaisedError"
106
+ all_ships = LazyWrapper.new { raise GraphQL::ExecutionError.new("lazy raised error from within connection") }
107
+ when "null"
108
+ all_ships = nil
109
+ when "lazyObject"
110
+ prev_all_ships = all_ships
111
+ all_ships = LazyWrapper.new { prev_all_ships }
112
+ else
113
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
114
+ end
115
+ end
116
+ all_ships
117
+ } do
118
+ # You can define arguments here and use them in the connection
119
+ argument :nameIncludes, String, required: false
120
+ end
121
+
122
+ field :shipsWithMaxPageSize, "Ships with max page size", max_page_size: 2, function: ShipsWithMaxPageSize.new
123
+
124
+ field :bases, BaseConnectionWithTotalCountType, null: true, connection: true, resolve: ->(obj, args, ctx) {
125
+ all_bases = obj.bases
126
+ if args[:nameIncludes]
127
+ all_bases = all_bases.where(name: Regexp.new(args[:nameIncludes]))
128
+ end
129
+ all_bases
130
+ } do
131
+ argument :nameIncludes, String, required: false
132
+ end
133
+
134
+ field :basesClone, BaseType.connection_type, null: true
135
+ field :basesByName, BaseType.connection_type, null: true do
136
+ argument :order, String, default_value: "name", required: false
137
+ end
138
+ def bases_by_name(order: nil)
139
+ if order.present?
140
+ @object.bases.order_by(name: order)
141
+ else
142
+ @object.bases
143
+ end
144
+ end
145
+
146
+ field :basesWithMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all}
147
+ field :basesWithMaxLimitArray, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all.to_a }
148
+ field :basesWithDefaultMaxLimitRelation, BaseType.connection_type, null: true, resolve: Proc.new { Base.all }
149
+ field :basesWithDefaultMaxLimitArray, BaseType.connection_type, null: true, resolve: Proc.new { Base.all.to_a }
150
+ field :basesWithLargeMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 1000, resolve: Proc.new { Base.all }
151
+
152
+ field :basesWithCustomEdge, CustomEdgeBaseConnectionType, null: true, connection: true, resolve: ->(o, a, c) { LazyNodesWrapper.new(o.bases) }
153
+ end
154
+
155
+ class IntroduceShipMutation < GraphQL::Schema::RelayClassicMutation
156
+ description "Add a ship to this faction"
157
+
158
+ # Nested under `input` in the query:
159
+ argument :ship_name, String, required: false
160
+ argument :faction_id, ID, required: true
161
+
162
+ # Result may have access to these fields:
163
+ field :ship_edge, Ship.edge_type, null: true
164
+ field :faction, Faction, null: true
165
+ field :aliased_faction, Faction, hash_key: :aliased_faction, null: true
166
+
167
+ def resolve(ship_name: nil, faction_id:)
168
+ IntroduceShipFunction.new.call(object, {ship_name: ship_name, faction_id: faction_id}, context)
169
+ end
170
+ end
171
+
172
+ class IntroduceShipFunction < GraphQL::Function
173
+ description "Add a ship to this faction"
174
+
175
+ argument :shipName, GraphQL::STRING_TYPE
176
+ argument :factionId, !GraphQL::ID_TYPE
177
+
178
+ type(GraphQL::ObjectType.define do
179
+ name "IntroduceShipFunctionPayload"
180
+ field :shipEdge, Ship.edge_type, hash_key: :shipEdge
181
+ field :faction, Faction, hash_key: :shipEdge
182
+ end)
183
+
184
+ def call(obj, args, ctx)
185
+ # support old and new args
186
+ ship_name = args["shipName"] || args[:ship_name]
187
+ faction_id = args["factionId"] || args[:faction_id]
188
+ if ship_name == 'USS Voyager'
189
+ GraphQL::ExecutionError.new("Sorry, USS Voyager ship is reserved")
190
+ elsif ship_name == 'IKS Korinar'
191
+ raise GraphQL::ExecutionError.new("🔥")
192
+ elsif ship_name == 'Scimitar'
193
+ LazyWrapper.new { raise GraphQL::ExecutionError.new("💥")}
194
+ else
195
+ ship = DATA.create_ship(ship_name, faction_id)
196
+ faction = DATA["Faction"][faction_id]
197
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
198
+ ships_connection = connection_class.new(faction.ships, args)
199
+ ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
200
+ result = {
201
+ shipEdge: ship_edge,
202
+ ship_edge: ship_edge, # support new-style, too
203
+ faction: faction,
204
+ aliased_faction: faction,
205
+ }
206
+ if args["shipName"] == "Slave II"
207
+ LazyWrapper.new(result)
208
+ else
209
+ result
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ IntroduceShipFunctionMutation = GraphQL::Relay::Mutation.define do
216
+ # Used as the root for derived types:
217
+ name "IntroduceShipFunction"
218
+ function IntroduceShipFunction.new
219
+ end
220
+
221
+ # GraphQL-Batch knockoff
222
+ class LazyLoader
223
+ def self.defer(ctx, model, id)
224
+ ids = ctx.namespace(:loading)[model] ||= []
225
+ ids << id
226
+ self.new(model: model, id: id, context: ctx)
227
+ end
228
+
229
+ def initialize(model:, id:, context:)
230
+ @model = model
231
+ @id = id
232
+ @context = context
233
+ end
234
+
235
+ def value
236
+ loaded = @context.namespace(:loaded)[@model] ||= {}
237
+ if loaded.empty?
238
+ ids = @context.namespace(:loading)[@model]
239
+ # Example custom tracing
240
+ @context.trace("lazy_loader", { ids: ids, model: @model}) do
241
+ records = @model.where(id: ids)
242
+ records.each do |record|
243
+ loaded[record.id.to_s] = record
244
+ end
245
+ end
246
+ end
247
+
248
+ loaded[@id]
249
+ end
250
+ end
251
+
252
+ class LazyWrapper
253
+ def initialize(value = nil, &block)
254
+ if block_given?
255
+ @lazy_value = block
256
+ else
257
+ @value = value
258
+ end
259
+ end
260
+
261
+ def value
262
+ @resolved_value = @value || @lazy_value.call
263
+ end
264
+ end
265
+
266
+ LazyNodesWrapper = Struct.new(:relation)
267
+ class LazyNodesRelationConnection < GraphQL::Relay::RelationConnection
268
+ def initialize(wrapper, *args)
269
+ super(wrapper.relation, *args)
270
+ end
271
+
272
+ def edge_nodes
273
+ LazyWrapper.new { super }
274
+ end
275
+ end
276
+
277
+ GraphQL::Relay::BaseConnection.register_connection_implementation(LazyNodesWrapper, LazyNodesRelationConnection)
278
+
279
+ class QueryType < GraphQL::Schema::Object
280
+ graphql_name "Query"
281
+
282
+ field :federation, Faction, null: true, resolve: ->(obj, args, ctx) { StarTrek::DATA["Faction"]["1"]}
283
+
284
+ field :klingons, Faction, null: true, resolve: ->(obj, args, ctx) { StarTrek::DATA["Faction"]["2"]}
285
+
286
+ field :romulans, Faction, null: true, resolve: ->(obj, args, ctx) { StarTrek::DATA["Faction"]["3"]}
287
+
288
+ field :largestBase, BaseType, null: true, resolve: ->(obj, args, ctx) { Base.find(3) }
289
+
290
+ field :newestBasesGroupedByFaction, BaseType.connection_type, null: true, resolve: ->(obj, args, ctx) {
291
+ agg = Base.collection.aggregate([{
292
+ "$group" => {
293
+ "_id" => "$faction_id",
294
+ "baseId" => { "$max" => "$_id" }
295
+ }
296
+ }])
297
+ Base.
298
+ in(id: agg.map { |doc| doc['baseId'] }).
299
+ order_by(faction_id: -1)
300
+ }
301
+
302
+ field :basesWithNullName, BaseType.connection_type, null: false, resolve: ->(obj, args, ctx) {
303
+ [OpenStruct.new(id: nil)]
304
+ }
305
+
306
+ field :node, field: GraphQL::Relay::Node.field
307
+
308
+ custom_node_field = GraphQL::Relay::Node.field do
309
+ resolve ->(_, _, _) { StarTrek::DATA["Faction"]["1"] }
310
+ end
311
+ field :nodeWithCustomResolver, field: custom_node_field
312
+
313
+ field :nodes, field: GraphQL::Relay::Node.plural_field
314
+ field :nodesWithCustomResolver, field: GraphQL::Relay::Node.plural_field(
315
+ resolve: ->(_, _, _) { [StarTrek::DATA["Faction"]["1"], StarTrek::DATA["Faction"]["2"]] }
316
+ )
317
+
318
+ field :batchedBase, BaseType, null: true do
319
+ argument :id, ID, required: true
320
+ end
321
+
322
+ def batched_base(id:)
323
+ LazyLoader.defer(@context, Base, id)
324
+ end
325
+ end
326
+
327
+ class MutationType < GraphQL::Schema::Object
328
+ graphql_name "Mutation"
329
+ field :introduceShip, mutation: IntroduceShipMutation
330
+ # To hook up a Relay::Mutation
331
+ field :introduceShipFunction, field: IntroduceShipFunctionMutation.field
332
+ end
333
+
334
+ class ClassNameRecorder
335
+ def initialize(context_key)
336
+ @context_key = context_key
337
+ end
338
+
339
+ def instrument(type, field)
340
+ inner_resolve = field.resolve_proc
341
+ key = @context_key
342
+ field.redefine {
343
+ resolve ->(o, a, c) {
344
+ res = inner_resolve.call(o, a, c)
345
+ if c[key]
346
+ c[key] << res.class.name
347
+ end
348
+ res
349
+ }
350
+ }
351
+ end
352
+ end
353
+
354
+ class Schema < GraphQL::Schema
355
+ query(QueryType)
356
+ mutation(MutationType)
357
+ default_max_page_size 3
358
+
359
+ def self.resolve_type(type, object, ctx)
360
+ if object == :test_error
361
+ :not_a_type
362
+ elsif object.is_a?(Base)
363
+ BaseType
364
+ elsif DATA["Faction"].values.include?(object)
365
+ Faction
366
+ elsif DATA["Ship"].values.include?(object)
367
+ Ship
368
+ else
369
+ nil
370
+ end
371
+ end
372
+
373
+ def self.object_from_id(node_id, ctx)
374
+ type_name, id = GraphQL::Schema::UniqueWithinType.decode(node_id)
375
+ StarTrek::DATA[type_name][id]
376
+ end
377
+
378
+ def self.id_from_object(object, type, ctx)
379
+ GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
380
+ end
381
+
382
+ lazy_resolve(LazyWrapper, :value)
383
+ lazy_resolve(LazyLoader, :value)
384
+
385
+ instrument(:field, ClassNameRecorder.new(:before_built_ins))
386
+ instrument(:field, ClassNameRecorder.new(:after_built_ins), after_built_ins: true)
387
+ end
388
+ end
@@ -18,13 +18,12 @@ module StarWars
18
18
  'Executor',
19
19
  ]
20
20
 
21
- # ActiveRecord::Base.logger = Logger.new(STDOUT)
22
21
  `rm -f ./_test_.db`
23
22
  # Set up "Bases" in ActiveRecord
24
23
 
25
24
  if jruby?
26
25
  ActiveRecord::Base.establish_connection(adapter: "jdbcsqlite3", database: "./_test_.db")
27
- Sequel.connect('jdbc:sqlite:./_test_.db')
26
+ DB = Sequel.connect('jdbc:sqlite:./_test_.db')
28
27
  elsif ENV['DATABASE'] == 'POSTGRESQL'
29
28
  ActiveRecord::Base.establish_connection(
30
29
  adapter: "postgresql",
@@ -60,13 +59,13 @@ module StarWars
60
59
  end
61
60
 
62
61
  class FactionRecord
63
- attr_reader :id, :name, :ships, :bases, :basesClone
64
- def initialize(id:, name:, ships:, bases:, basesClone:)
62
+ attr_reader :id, :name, :ships, :bases, :bases_clone
63
+ def initialize(id:, name:, ships:, bases:, bases_clone:)
65
64
  @id = id
66
65
  @name = name
67
66
  @ships = ships
68
67
  @bases = bases
69
- @basesClone = basesClone
68
+ @bases_clone = bases_clone
70
69
  end
71
70
  end
72
71
 
@@ -75,7 +74,7 @@ module StarWars
75
74
  name: 'Alliance to Restore the Republic',
76
75
  ships: ['1', '2', '3', '4', '5'],
77
76
  bases: Base.where(faction_id: 1),
78
- basesClone: Base.where(faction_id: 1),
77
+ bases_clone: Base.where(faction_id: 1),
79
78
  })
80
79
 
81
80
 
@@ -84,7 +83,7 @@ module StarWars
84
83
  name: 'Galactic Empire',
85
84
  ships: ['6', '7', '8'],
86
85
  bases: Base.where(faction_id: 2),
87
- basesClone: Base.where(faction_id: 2),
86
+ bases_clone: Base.where(faction_id: 2),
88
87
  })
89
88
 
90
89
  DATA = {