graphql 2.0.31 → 2.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  4. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  5. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  6. data/lib/generators/graphql/install_generator.rb +49 -0
  7. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  8. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  9. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  10. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  11. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  12. data/lib/generators/graphql/templates/base_field.erb +2 -0
  13. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  15. data/lib/generators/graphql/templates/base_object.erb +2 -0
  16. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  17. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  18. data/lib/generators/graphql/templates/base_union.erb +2 -0
  19. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +5 -0
  26. data/lib/generators/graphql/type_generator.rb +1 -1
  27. data/lib/graphql/analysis/analyzer.rb +90 -0
  28. data/lib/graphql/analysis/field_usage.rb +82 -0
  29. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  30. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  31. data/lib/graphql/analysis/query_complexity.rb +263 -0
  32. data/lib/graphql/analysis/query_depth.rb +58 -0
  33. data/lib/graphql/analysis/visitor.rb +280 -0
  34. data/lib/graphql/analysis.rb +102 -1
  35. data/lib/graphql/autoload.rb +38 -0
  36. data/lib/graphql/backtrace/table.rb +118 -55
  37. data/lib/graphql/backtrace.rb +1 -19
  38. data/lib/graphql/coercion_error.rb +1 -9
  39. data/lib/graphql/current.rb +57 -0
  40. data/lib/graphql/dashboard/application_controller.rb +41 -0
  41. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  42. data/lib/graphql/dashboard/installable.rb +22 -0
  43. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  44. data/lib/graphql/dashboard/limiters.rb +93 -0
  45. data/lib/graphql/dashboard/operation_store.rb +199 -0
  46. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  47. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  48. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  49. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  50. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  51. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  52. data/lib/graphql/dashboard/statics/icon.png +0 -0
  53. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  54. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  65. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  66. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  67. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  68. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  69. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  70. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  71. data/lib/graphql/dashboard.rb +96 -0
  72. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  73. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  74. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  75. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  76. data/lib/graphql/dataloader/request.rb +5 -0
  77. data/lib/graphql/dataloader/source.rb +35 -12
  78. data/lib/graphql/dataloader.rb +224 -149
  79. data/lib/graphql/date_encoding_error.rb +1 -1
  80. data/lib/graphql/dig.rb +2 -1
  81. data/lib/graphql/duration_encoding_error.rb +16 -0
  82. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  83. data/lib/graphql/execution/finalize.rb +217 -0
  84. data/lib/graphql/execution/input_values.rb +261 -0
  85. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  86. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  87. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  88. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  89. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  90. data/lib/graphql/execution/interpreter/runtime.rb +365 -435
  91. data/lib/graphql/execution/interpreter.rb +87 -163
  92. data/lib/graphql/execution/lazy.rb +1 -1
  93. data/lib/graphql/execution/load_argument_step.rb +64 -0
  94. data/lib/graphql/execution/lookahead.rb +105 -31
  95. data/lib/graphql/execution/multiplex.rb +7 -6
  96. data/lib/graphql/execution/next.rb +90 -0
  97. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  98. data/lib/graphql/execution/runner.rb +410 -0
  99. data/lib/graphql/execution/selections_step.rb +91 -0
  100. data/lib/graphql/execution.rb +8 -4
  101. data/lib/graphql/execution_error.rb +17 -10
  102. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  103. data/lib/graphql/introspection/directive_type.rb +7 -3
  104. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  105. data/lib/graphql/introspection/entry_points.rb +20 -6
  106. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  107. data/lib/graphql/introspection/field_type.rb +13 -5
  108. data/lib/graphql/introspection/input_value_type.rb +21 -13
  109. data/lib/graphql/introspection/schema_type.rb +8 -11
  110. data/lib/graphql/introspection/type_type.rb +64 -28
  111. data/lib/graphql/invalid_name_error.rb +1 -1
  112. data/lib/graphql/invalid_null_error.rb +26 -17
  113. data/lib/graphql/language/block_string.rb +34 -18
  114. data/lib/graphql/language/cache.rb +13 -0
  115. data/lib/graphql/language/comment.rb +18 -0
  116. data/lib/graphql/language/definition_slice.rb +1 -1
  117. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  118. data/lib/graphql/language/lexer.rb +323 -193
  119. data/lib/graphql/language/nodes.rb +139 -77
  120. data/lib/graphql/language/parser.rb +807 -1985
  121. data/lib/graphql/language/printer.rb +324 -151
  122. data/lib/graphql/language/sanitized_printer.rb +21 -23
  123. data/lib/graphql/language/static_visitor.rb +171 -0
  124. data/lib/graphql/language/visitor.rb +62 -119
  125. data/lib/graphql/language.rb +71 -1
  126. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  127. data/lib/graphql/pagination/array_connection.rb +6 -6
  128. data/lib/graphql/pagination/connection.rb +30 -1
  129. data/lib/graphql/pagination/connections.rb +32 -0
  130. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  131. data/lib/graphql/query/context/scoped_context.rb +101 -0
  132. data/lib/graphql/query/context.rb +88 -144
  133. data/lib/graphql/query/null_context.rb +15 -18
  134. data/lib/graphql/query/partial.rb +194 -0
  135. data/lib/graphql/query/validation_pipeline.rb +4 -4
  136. data/lib/graphql/query/variable_validation_error.rb +1 -1
  137. data/lib/graphql/query/variables.rb +3 -3
  138. data/lib/graphql/query.rb +135 -81
  139. data/lib/graphql/railtie.rb +16 -6
  140. data/lib/graphql/rake_task.rb +3 -12
  141. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  142. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  143. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  144. data/lib/graphql/rubocop.rb +2 -0
  145. data/lib/graphql/runtime_error.rb +6 -0
  146. data/lib/graphql/schema/addition.rb +26 -13
  147. data/lib/graphql/schema/always_visible.rb +7 -2
  148. data/lib/graphql/schema/argument.rb +78 -14
  149. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  150. data/lib/graphql/schema/build_from_definition.rb +140 -66
  151. data/lib/graphql/schema/directive/flagged.rb +4 -2
  152. data/lib/graphql/schema/directive/one_of.rb +12 -0
  153. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  154. data/lib/graphql/schema/directive.rb +78 -12
  155. data/lib/graphql/schema/enum.rb +110 -27
  156. data/lib/graphql/schema/enum_value.rb +11 -3
  157. data/lib/graphql/schema/field/connection_extension.rb +4 -51
  158. data/lib/graphql/schema/field/scope_extension.rb +19 -7
  159. data/lib/graphql/schema/field.rb +245 -119
  160. data/lib/graphql/schema/field_extension.rb +12 -9
  161. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  162. data/lib/graphql/schema/input_object.rb +123 -65
  163. data/lib/graphql/schema/interface.rb +60 -16
  164. data/lib/graphql/schema/introspection_system.rb +8 -17
  165. data/lib/graphql/schema/late_bound_type.rb +4 -0
  166. data/lib/graphql/schema/list.rb +8 -4
  167. data/lib/graphql/schema/loader.rb +3 -4
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -12
  169. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  170. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  171. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  173. data/lib/graphql/schema/member/has_directives.rb +5 -5
  174. data/lib/graphql/schema/member/has_fields.rb +121 -17
  175. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  177. data/lib/graphql/schema/member/has_validators.rb +1 -1
  178. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  179. data/lib/graphql/schema/member/scoped.rb +19 -0
  180. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  181. data/lib/graphql/schema/member/validates_input.rb +3 -3
  182. data/lib/graphql/schema/member.rb +6 -0
  183. data/lib/graphql/schema/mutation.rb +7 -0
  184. data/lib/graphql/schema/non_null.rb +1 -1
  185. data/lib/graphql/schema/object.rb +34 -8
  186. data/lib/graphql/schema/printer.rb +9 -7
  187. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  188. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  189. data/lib/graphql/schema/resolver.rb +128 -32
  190. data/lib/graphql/schema/scalar.rb +4 -9
  191. data/lib/graphql/schema/subscription.rb +63 -12
  192. data/lib/graphql/schema/timeout.rb +19 -2
  193. data/lib/graphql/schema/type_expression.rb +2 -2
  194. data/lib/graphql/schema/union.rb +2 -2
  195. data/lib/graphql/schema/unique_within_type.rb +1 -1
  196. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  197. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  198. data/lib/graphql/schema/validator.rb +3 -1
  199. data/lib/graphql/schema/visibility/migration.rb +188 -0
  200. data/lib/graphql/schema/visibility/profile.rb +464 -0
  201. data/lib/graphql/schema/visibility/visit.rb +190 -0
  202. data/lib/graphql/schema/visibility.rb +311 -0
  203. data/lib/graphql/schema/warden.rb +275 -103
  204. data/lib/graphql/schema/wrapper.rb +7 -1
  205. data/lib/graphql/schema.rb +954 -212
  206. data/lib/graphql/static_validation/all_rules.rb +3 -3
  207. data/lib/graphql/static_validation/base_visitor.rb +96 -71
  208. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  209. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +2 -2
  210. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  211. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +6 -2
  212. data/lib/graphql/static_validation/rules/directives_are_defined.rb +6 -3
  213. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  214. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +14 -3
  215. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +59 -15
  216. data/lib/graphql/static_validation/rules/fields_will_merge.rb +391 -262
  217. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  218. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +6 -6
  219. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +15 -2
  220. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  221. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  222. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  223. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  224. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  225. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +28 -8
  226. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  227. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  228. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  229. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  230. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  231. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  232. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +14 -2
  233. data/lib/graphql/static_validation/validation_context.rb +22 -6
  234. data/lib/graphql/static_validation/validator.rb +9 -1
  235. data/lib/graphql/static_validation.rb +0 -1
  236. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  237. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  238. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +45 -19
  239. data/lib/graphql/subscriptions/event.rb +22 -4
  240. data/lib/graphql/subscriptions/serialize.rb +3 -1
  241. data/lib/graphql/subscriptions.rb +56 -17
  242. data/lib/graphql/testing/helpers.rb +161 -0
  243. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  244. data/lib/graphql/testing.rb +3 -0
  245. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  246. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  247. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  248. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  249. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  250. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  251. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  252. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  253. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  254. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  255. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  256. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  257. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  258. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  259. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  260. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  261. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  262. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  263. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  264. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  265. data/lib/graphql/tracing/null_trace.rb +9 -0
  266. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  267. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  268. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  269. data/lib/graphql/tracing/platform_trace.rb +5 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  272. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  273. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  274. data/lib/graphql/tracing/scout_trace.rb +32 -55
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +118 -1
  280. data/lib/graphql/tracing.rb +31 -28
  281. data/lib/graphql/type_kinds.rb +2 -1
  282. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  283. data/lib/graphql/types/relay/connection_behaviors.rb +45 -3
  284. data/lib/graphql/types/relay/edge_behaviors.rb +19 -1
  285. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  286. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  287. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  288. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  289. data/lib/graphql/types.rb +18 -10
  290. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  291. data/lib/graphql/unauthorized_error.rb +9 -1
  292. data/lib/graphql/version.rb +1 -1
  293. data/lib/graphql.rb +69 -54
  294. data/readme.md +12 -2
  295. metadata +236 -40
  296. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  297. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  298. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  299. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  300. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  301. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  302. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  303. data/lib/graphql/analysis/ast.rb +0 -81
  304. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  305. data/lib/graphql/backtrace/trace.rb +0 -96
  306. data/lib/graphql/backtrace/tracer.rb +0 -80
  307. data/lib/graphql/deprecation.rb +0 -9
  308. data/lib/graphql/filter.rb +0 -59
  309. data/lib/graphql/language/parser.y +0 -560
  310. data/lib/graphql/language/token.rb +0 -34
  311. data/lib/graphql/schema/base_64_bp.rb +0 -26
  312. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  313. data/lib/graphql/schema/null_mask.rb +0 -11
  314. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  315. data/lib/graphql/static_validation/type_stack.rb +0 -216
  316. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -40,7 +40,7 @@ module GraphQL
40
40
  case node
41
41
  when FalseClass, Float, Integer, String, TrueClass
42
42
  if @current_argument && redact_argument_value?(@current_argument, node)
43
- redacted_argument_value(@current_argument)
43
+ print_string(redacted_argument_value(@current_argument))
44
44
  else
45
45
  super
46
46
  end
@@ -51,9 +51,8 @@ module GraphQL
51
51
  @current_input_type = @current_input_type.of_type if @current_input_type.non_null?
52
52
  end
53
53
 
54
- res = super
54
+ super
55
55
  @current_input_type = old_input_type
56
- res
57
56
  else
58
57
  super
59
58
  end
@@ -89,11 +88,12 @@ module GraphQL
89
88
  else
90
89
  argument.value
91
90
  end
92
- res = "#{argument.name}: #{print_node(argument_value)}".dup
91
+
92
+ print_string("#{argument.name}: ")
93
+ print_node(argument_value)
93
94
 
94
95
  @current_input_type = old_input_type
95
96
  @current_argument = old_current_argument
96
- res
97
97
  end
98
98
 
99
99
  def coerce_argument_value_to_list?(type, value)
@@ -113,12 +113,11 @@ module GraphQL
113
113
  end
114
114
 
115
115
  def print_field(field, indent: "")
116
- @current_field = query.get_field(@current_type, field.name)
116
+ @current_field = query.types.field(@current_type, field.name)
117
117
  old_type = @current_type
118
118
  @current_type = @current_field.type.unwrap
119
- res = super
119
+ super
120
120
  @current_type = old_type
121
- res
122
121
  end
123
122
 
124
123
  def print_inline_fragment(inline_fragment, indent: "")
@@ -128,31 +127,26 @@ module GraphQL
128
127
  @current_type = query.get_type(inline_fragment.type.name)
129
128
  end
130
129
 
131
- res = super
130
+ super
132
131
 
133
132
  @current_type = old_type
134
-
135
- res
136
133
  end
137
134
 
138
135
  def print_fragment_definition(fragment_def, indent: "")
139
136
  old_type = @current_type
140
137
  @current_type = query.get_type(fragment_def.type.name)
141
138
 
142
- res = super
139
+ super
143
140
 
144
141
  @current_type = old_type
145
-
146
- res
147
142
  end
148
143
 
149
144
  def print_directive(directive)
150
145
  @current_directive = query.schema.directives[directive.name]
151
146
 
152
- res = super
147
+ super
153
148
 
154
149
  @current_directive = nil
155
- res
156
150
  end
157
151
 
158
152
  # Print the operation definition but do not include the variable
@@ -162,16 +156,15 @@ module GraphQL
162
156
  @current_type = query.schema.public_send(operation_definition.operation_type)
163
157
 
164
158
  if @inline_variables
165
- out = "#{indent}#{operation_definition.operation_type}".dup
166
- out << " #{operation_definition.name}" if operation_definition.name
167
- out << print_directives(operation_definition.directives)
168
- out << print_selections(operation_definition.selections, indent: indent)
159
+ print_string("#{indent}#{operation_definition.operation_type}")
160
+ print_string(" #{operation_definition.name}") if operation_definition.name
161
+ print_directives(operation_definition.directives)
162
+ print_selections(operation_definition.selections, indent: indent)
169
163
  else
170
- out = super
164
+ super
171
165
  end
172
166
 
173
167
  @current_type = old_type
174
- out
175
168
  end
176
169
 
177
170
  private
@@ -210,7 +203,12 @@ module GraphQL
210
203
  [value].map { |v| value_to_ast(v, type.of_type) }
211
204
  end
212
205
  when "ENUM"
213
- GraphQL::Language::Nodes::Enum.new(name: value)
206
+ if value.is_a?(GraphQL::Language::Nodes::Enum)
207
+ # if it was a default value, it's already wrapped
208
+ value
209
+ else
210
+ GraphQL::Language::Nodes::Enum.new(name: value)
211
+ end
214
212
  else
215
213
  value
216
214
  end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Language
4
+ # Like `GraphQL::Language::Visitor` except it doesn't support
5
+ # making changes to the document -- only visiting it as-is.
6
+ class StaticVisitor
7
+ def initialize(document)
8
+ @document = document
9
+ end
10
+
11
+ # Visit `document` and all children
12
+ # @return [void]
13
+ def visit
14
+ # `@document` may be any kind of node:
15
+ visit_method = @document.visit_method
16
+ result = public_send(visit_method, @document, nil)
17
+ @result = if result.is_a?(Array)
18
+ result.first
19
+ else
20
+ # The node wasn't modified
21
+ @document
22
+ end
23
+ end
24
+
25
+ def on_document_children(document_node)
26
+ document_node.children.each do |child_node|
27
+ visit_method = child_node.visit_method
28
+ public_send(visit_method, child_node, document_node)
29
+ end
30
+ end
31
+
32
+ def on_field_children(new_node)
33
+ new_node.arguments.each do |arg_node| # rubocop:disable Development/ContextIsPassedCop
34
+ on_argument(arg_node, new_node)
35
+ end
36
+ visit_directives(new_node)
37
+ visit_selections(new_node)
38
+ end
39
+
40
+ def visit_directives(new_node)
41
+ new_node.directives.each do |dir_node|
42
+ on_directive(dir_node, new_node)
43
+ end
44
+ end
45
+
46
+ def visit_selections(new_node)
47
+ new_node.selections.each do |selection|
48
+ case selection
49
+ when GraphQL::Language::Nodes::Field
50
+ on_field(selection, new_node)
51
+ when GraphQL::Language::Nodes::InlineFragment
52
+ on_inline_fragment(selection, new_node)
53
+ when GraphQL::Language::Nodes::FragmentSpread
54
+ on_fragment_spread(selection, new_node)
55
+ else
56
+ raise ArgumentError, "Invariant: unexpected field selection #{selection.class} (#{selection.inspect})"
57
+ end
58
+ end
59
+ end
60
+
61
+ def on_fragment_definition_children(new_node)
62
+ visit_directives(new_node)
63
+ visit_selections(new_node)
64
+ end
65
+
66
+ alias :on_inline_fragment_children :on_fragment_definition_children
67
+
68
+ def on_operation_definition_children(new_node)
69
+ new_node.variables.each do |arg_node|
70
+ on_variable_definition(arg_node, new_node)
71
+ end
72
+ visit_directives(new_node)
73
+ visit_selections(new_node)
74
+ end
75
+
76
+ def on_argument_children(new_node)
77
+ new_node.children.each do |value_node|
78
+ case value_node
79
+ when Language::Nodes::VariableIdentifier
80
+ on_variable_identifier(value_node, new_node)
81
+ when Language::Nodes::InputObject
82
+ on_input_object(value_node, new_node)
83
+ when Language::Nodes::Enum
84
+ on_enum(value_node, new_node)
85
+ when Language::Nodes::NullValue
86
+ on_null_value(value_node, new_node)
87
+ else
88
+ raise ArgumentError, "Invariant: unexpected argument value node #{value_node.class} (#{value_node.inspect})"
89
+ end
90
+ end
91
+ end
92
+
93
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
94
+
95
+ # We don't use `alias` here because it breaks `super`
96
+ def self.make_visit_methods(ast_node_class)
97
+ node_method = ast_node_class.visit_method
98
+ children_of_type = ast_node_class.children_of_type
99
+ child_visit_method = :"#{node_method}_children"
100
+
101
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
102
+ # The default implementation for visiting an AST node.
103
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
104
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
105
+ # in your subclasses.
106
+ #
107
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
108
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
109
+ # @return [void]
110
+ def #{node_method}(node, parent)
111
+ #{
112
+ if method_defined?(child_visit_method)
113
+ "#{child_visit_method}(node)"
114
+ elsif children_of_type
115
+ children_of_type.map do |child_accessor, child_class|
116
+ "node.#{child_accessor}.each do |child_node|
117
+ #{child_class.visit_method}(child_node, node)
118
+ end"
119
+ end.join("\n")
120
+ else
121
+ ""
122
+ end
123
+ }
124
+ end
125
+ RUBY
126
+ end
127
+
128
+ [
129
+ Language::Nodes::Argument,
130
+ Language::Nodes::Directive,
131
+ Language::Nodes::DirectiveDefinition,
132
+ Language::Nodes::DirectiveLocation,
133
+ Language::Nodes::Document,
134
+ Language::Nodes::Enum,
135
+ Language::Nodes::EnumTypeDefinition,
136
+ Language::Nodes::EnumTypeExtension,
137
+ Language::Nodes::EnumValueDefinition,
138
+ Language::Nodes::Field,
139
+ Language::Nodes::FieldDefinition,
140
+ Language::Nodes::FragmentDefinition,
141
+ Language::Nodes::FragmentSpread,
142
+ Language::Nodes::InlineFragment,
143
+ Language::Nodes::InputObject,
144
+ Language::Nodes::InputObjectTypeDefinition,
145
+ Language::Nodes::InputObjectTypeExtension,
146
+ Language::Nodes::InputValueDefinition,
147
+ Language::Nodes::InterfaceTypeDefinition,
148
+ Language::Nodes::InterfaceTypeExtension,
149
+ Language::Nodes::ListType,
150
+ Language::Nodes::NonNullType,
151
+ Language::Nodes::NullValue,
152
+ Language::Nodes::ObjectTypeDefinition,
153
+ Language::Nodes::ObjectTypeExtension,
154
+ Language::Nodes::OperationDefinition,
155
+ Language::Nodes::ScalarTypeDefinition,
156
+ Language::Nodes::ScalarTypeExtension,
157
+ Language::Nodes::SchemaDefinition,
158
+ Language::Nodes::SchemaExtension,
159
+ Language::Nodes::TypeName,
160
+ Language::Nodes::UnionTypeDefinition,
161
+ Language::Nodes::UnionTypeExtension,
162
+ Language::Nodes::VariableDefinition,
163
+ Language::Nodes::VariableIdentifier,
164
+ ].each do |ast_node_class|
165
+ make_visit_methods(ast_node_class)
166
+ end
167
+
168
+ # rubocop:disable Development/NoEvalCop
169
+ end
170
+ end
171
+ end
@@ -30,12 +30,9 @@ module GraphQL
30
30
  # # Check the result
31
31
  # visitor.count
32
32
  # # => 3
33
+ #
34
+ # @see GraphQL::Language::StaticVisitor for a faster visitor that doesn't support modifying the document
33
35
  class Visitor
34
- # If any hook returns this value, the {Visitor} stops visiting this
35
- # node right away
36
- # @deprecated Use `super` to continue the visit; or don't call it to halt.
37
- SKIP = :_skip
38
-
39
36
  class DeleteNode; end
40
37
 
41
38
  # When this is returned from a visitor method,
@@ -44,25 +41,13 @@ module GraphQL
44
41
 
45
42
  def initialize(document)
46
43
  @document = document
47
- @visitors = {}
48
44
  @result = nil
49
45
  end
50
46
 
51
47
  # @return [GraphQL::Language::Nodes::Document] The document with any modifications applied
52
48
  attr_reader :result
53
49
 
54
- # Get a {NodeVisitor} for `node_class`
55
- # @param node_class [Class] The node class that you want to listen to
56
- # @return [NodeVisitor]
57
- #
58
- # @example Run a hook whenever you enter a new Field
59
- # visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) { p "Here's a field" }
60
- # @deprecated see `on_` methods, like {#on_field}
61
- def [](node_class)
62
- @visitors[node_class] ||= NodeVisitor.new
63
- end
64
-
65
- # Visit `document` and all children, applying hooks as you go
50
+ # Visit `document` and all children
66
51
  # @return [void]
67
52
  def visit
68
53
  # `@document` may be any kind of node:
@@ -76,67 +61,6 @@ module GraphQL
76
61
  end
77
62
  end
78
63
 
79
- # We don't use `alias` here because it breaks `super`
80
- def self.make_visit_methods(ast_node_class)
81
- node_method = ast_node_class.visit_method
82
- children_of_type = ast_node_class.children_of_type
83
- child_visit_method = :"#{node_method}_children"
84
-
85
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
86
- # The default implementation for visiting an AST node.
87
- # It doesn't _do_ anything, but it continues to visiting the node's children.
88
- # To customize this hook, override one of its make_visit_methods (or the base method?)
89
- # in your subclasses.
90
- #
91
- # For compatibility, it calls hook procs, too.
92
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
93
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
94
- # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
95
- def #{node_method}(node, parent)
96
- if node.equal?(DELETE_NODE)
97
- # This might be passed to `super(DELETE_NODE, ...)`
98
- # by a user hook, don't want to keep visiting in that case.
99
- [node, parent]
100
- else
101
- # Run hooks if there are any
102
- new_node = node
103
- no_hooks = !@visitors.key?(node.class)
104
- if no_hooks || begin_visit(new_node, parent)
105
- #{
106
- if method_defined?(child_visit_method)
107
- "new_node = #{child_visit_method}(new_node)"
108
- elsif children_of_type
109
- children_of_type.map do |child_accessor, child_class|
110
- "node.#{child_accessor}.each do |child_node|
111
- new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
112
- # Reassign `node` in case the child hook makes a modification
113
- if new_child_and_node.is_a?(Array)
114
- new_node = new_child_and_node[1]
115
- end
116
- end"
117
- end.join("\n")
118
- else
119
- ""
120
- end
121
- }
122
- end
123
- end_visit(new_node, parent) unless no_hooks
124
-
125
- if new_node.equal?(node)
126
- [node, parent]
127
- else
128
- [new_node, parent]
129
- end
130
- end
131
- end
132
-
133
- def #{node_method}_with_modifications(node, parent)
134
- new_node_and_new_parent = #{node_method}(node, parent)
135
- apply_modifications(node, parent, new_node_and_new_parent)
136
- end
137
- RUBY
138
- end
139
-
140
64
  def on_document_children(document_node)
141
65
  new_node = document_node
142
66
  document_node.children.each do |child_node|
@@ -237,6 +161,63 @@ module GraphQL
237
161
  new_node
238
162
  end
239
163
 
164
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
165
+
166
+ # We don't use `alias` here because it breaks `super`
167
+ def self.make_visit_methods(ast_node_class)
168
+ node_method = ast_node_class.visit_method
169
+ children_of_type = ast_node_class.children_of_type
170
+ child_visit_method = :"#{node_method}_children"
171
+
172
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
173
+ # The default implementation for visiting an AST node.
174
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
175
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
176
+ # in your subclasses.
177
+ #
178
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
179
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
180
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
181
+ def #{node_method}(node, parent)
182
+ if node.equal?(DELETE_NODE)
183
+ # This might be passed to `super(DELETE_NODE, ...)`
184
+ # by a user hook, don't want to keep visiting in that case.
185
+ [node, parent]
186
+ else
187
+ new_node = node
188
+ #{
189
+ if method_defined?(child_visit_method)
190
+ "new_node = #{child_visit_method}(new_node)"
191
+ elsif children_of_type
192
+ children_of_type.map do |child_accessor, child_class|
193
+ "node.#{child_accessor}.each do |child_node|
194
+ new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
195
+ # Reassign `node` in case the child hook makes a modification
196
+ if new_child_and_node.is_a?(Array)
197
+ new_node = new_child_and_node[1]
198
+ end
199
+ end"
200
+ end.join("\n")
201
+ else
202
+ ""
203
+ end
204
+ }
205
+
206
+ if new_node.equal?(node)
207
+ [node, parent]
208
+ else
209
+ [new_node, parent]
210
+ end
211
+ end
212
+ end
213
+
214
+ def #{node_method}_with_modifications(node, parent)
215
+ new_node_and_new_parent = #{node_method}(node, parent)
216
+ apply_modifications(node, parent, new_node_and_new_parent)
217
+ end
218
+ RUBY
219
+ end
220
+
240
221
  [
241
222
  Language::Nodes::Argument,
242
223
  Language::Nodes::Directive,
@@ -277,6 +258,8 @@ module GraphQL
277
258
  make_visit_methods(ast_node_class)
278
259
  end
279
260
 
261
+ # rubocop:enable Development/NoEvalCop
262
+
280
263
  private
281
264
 
282
265
  def apply_modifications(node, parent, new_node_and_new_parent)
@@ -305,46 +288,6 @@ module GraphQL
305
288
  new_node_and_new_parent
306
289
  end
307
290
  end
308
-
309
- def begin_visit(node, parent)
310
- node_visitor = self[node.class]
311
- self.class.apply_hooks(node_visitor.enter, node, parent)
312
- end
313
-
314
- # Should global `leave` visitors come first or last?
315
- def end_visit(node, parent)
316
- node_visitor = self[node.class]
317
- self.class.apply_hooks(node_visitor.leave, node, parent)
318
- end
319
-
320
- # If one of the visitors returns SKIP, stop visiting this node
321
- def self.apply_hooks(hooks, node, parent)
322
- hooks.each do |proc|
323
- return false if proc.call(node, parent) == SKIP
324
- end
325
- true
326
- end
327
-
328
- # Collect `enter` and `leave` hooks for classes in {GraphQL::Language::Nodes}
329
- #
330
- # Access {NodeVisitor}s via {GraphQL::Language::Visitor#[]}
331
- class NodeVisitor
332
- # @return [Array<Proc>] Hooks to call when entering a node of this type
333
- attr_reader :enter
334
- # @return [Array<Proc>] Hooks to call when leaving a node of this type
335
- attr_reader :leave
336
-
337
- def initialize
338
- @enter = []
339
- @leave = []
340
- end
341
-
342
- # Shorthand to add a hook to the {#enter} array
343
- # @param hook [Proc] A hook to add
344
- def <<(hook)
345
- enter << hook
346
- end
347
- end
348
291
  end
349
292
  end
350
293
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/language/block_string"
3
+ require "graphql/language/comment"
3
4
  require "graphql/language/printer"
4
5
  require "graphql/language/sanitized_printer"
5
6
  require "graphql/language/document_from_schema_definition"
@@ -8,9 +9,10 @@ require "graphql/language/lexer"
8
9
  require "graphql/language/nodes"
9
10
  require "graphql/language/cache"
10
11
  require "graphql/language/parser"
11
- require "graphql/language/token"
12
+ require "graphql/language/static_visitor"
12
13
  require "graphql/language/visitor"
13
14
  require "graphql/language/definition_slice"
15
+ require "strscan"
14
16
 
15
17
  module GraphQL
16
18
  module Language
@@ -31,6 +33,74 @@ module GraphQL
31
33
  else
32
34
  JSON.generate(value, quirks_mode: true)
33
35
  end
36
+ rescue JSON::GeneratorError
37
+ if Float::INFINITY == value
38
+ "Infinity"
39
+ else
40
+ raise
41
+ end
42
+ end
43
+
44
+ # Returns a new string if any single-quoted newlines were escaped.
45
+ # Otherwise, returns `query_str` unchanged.
46
+ # @return [String]
47
+ def self.escape_single_quoted_newlines(query_str)
48
+ scanner = StringScanner.new(query_str)
49
+ inside_single_quoted_string = false
50
+ new_query_str = nil
51
+ while !scanner.eos?
52
+ if scanner.skip(/(?:\\"|[^"\n\r]|""")+/m)
53
+ new_query_str && (new_query_str << scanner.matched)
54
+ elsif scanner.skip('"')
55
+ new_query_str && (new_query_str << '"')
56
+ inside_single_quoted_string = !inside_single_quoted_string
57
+ elsif scanner.skip("\n")
58
+ if inside_single_quoted_string
59
+ new_query_str ||= query_str[0, scanner.pos - 1]
60
+ new_query_str << '\\n'
61
+ else
62
+ new_query_str && (new_query_str << "\n")
63
+ end
64
+ elsif scanner.skip("\r")
65
+ if inside_single_quoted_string
66
+ new_query_str ||= query_str[0, scanner.pos - 1]
67
+ new_query_str << '\\r'
68
+ else
69
+ new_query_str && (new_query_str << "\r")
70
+ end
71
+ elsif scanner.eos?
72
+ break
73
+ else
74
+ raise ArgumentError, "Unmatchable string scanner segment: #{scanner.rest.inspect}"
75
+ end
76
+ end
77
+ new_query_str || query_str
78
+ end
79
+
80
+ LEADING_REGEX = Regexp.union(" ", *Lexer::Punctuation.constants.map { |const| Lexer::Punctuation.const_get(const) })
81
+
82
+ # Optimized pattern using:
83
+ # - Possessive quantifiers (*+, ++) to prevent backtracking in number patterns
84
+ # - Atomic group (?>...) for IGNORE to prevent backtracking
85
+ # - Single unified number pattern instead of three alternatives
86
+ EFFICIENT_NUMBER_REGEXP = /-?(?:0|[1-9][0-9]*+)(?:\.[0-9]++)?(?:[eE][+-]?[0-9]++)?/
87
+ EFFICIENT_IGNORE_REGEXP = /(?>[, \r\n\t]+|\#[^\n]*$)*/
88
+
89
+ MAYBE_INVALID_NUMBER = /\d[_a-zA-Z]/
90
+
91
+ INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP = %r{
92
+ (?<leading>#{LEADING_REGEX})
93
+ (?<num>#{EFFICIENT_NUMBER_REGEXP})
94
+ (?<name>#{Lexer::IDENTIFIER_REGEXP})
95
+ #{EFFICIENT_IGNORE_REGEXP}
96
+ :
97
+ }x
98
+
99
+ def self.add_space_between_numbers_and_names(query_str)
100
+ # Fast check for digit followed by identifier char. If this doesn't match, skip the more expensive regexp entirely.
101
+ return query_str unless query_str.match?(MAYBE_INVALID_NUMBER)
102
+ return query_str unless query_str.match?(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP)
103
+ query_str.gsub(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP, "\\k<leading>\\k<num> \\k<name>:")
34
104
  end
35
105
  end
36
106
  end
@@ -12,10 +12,14 @@ module GraphQL
12
12
  attr_reader :id
13
13
  # @return [Object] The value found with this ID
14
14
  attr_reader :object
15
- def initialize(argument:, id:, object:)
15
+ # @return [GraphQL::Query::Context]
16
+ attr_reader :context
17
+
18
+ def initialize(argument:, id:, object:, context:)
16
19
  @id = id
17
20
  @argument = argument
18
21
  @object = object
22
+ @context = context
19
23
  super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
20
24
  end
21
25
  end
@@ -35,10 +35,10 @@ module GraphQL
35
35
  def load_nodes
36
36
  @nodes ||= begin
37
37
  sliced_nodes = if before && after
38
- end_idx = index_from_cursor(before)-1
38
+ end_idx = index_from_cursor(before) - 2
39
39
  end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || []
40
40
  elsif before
41
- end_idx = index_from_cursor(before)-2
41
+ end_idx = index_from_cursor(before) - 2
42
42
  end_idx < 0 ? [] : items[0..end_idx] || []
43
43
  elsif after
44
44
  items[index_from_cursor(after)..-1] || []
@@ -56,12 +56,12 @@ module GraphQL
56
56
  false
57
57
  end
58
58
 
59
- @has_next_page = if first
60
- # There are more items after these items
61
- sliced_nodes.count > first
62
- elsif before
59
+ @has_next_page = if before
63
60
  # The original array is longer than the `before` index
64
61
  index_from_cursor(before) < items.length + 1
62
+ elsif first
63
+ # There are more items after these items
64
+ sliced_nodes.count > first
65
65
  else
66
66
  false
67
67
  end