graphql 1.11.3 → 1.11.4
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +8 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/generators/graphql/templates/union.erb +2 -0
- data/lib/graphql/execution/interpreter/runtime.rb +24 -25
- data/lib/graphql/query/context.rb +20 -1
- data/lib/graphql/query/fingerprint.rb +2 -0
- data/lib/graphql/query/validation_pipeline.rb +3 -0
- data/lib/graphql/schema.rb +4 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/subscription.rb +2 -12
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/subscriptions.rb +31 -19
- data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
- data/lib/graphql/types/iso_8601_date_time.rb +2 -1
- data/lib/graphql/types/relay/base_connection.rb +6 -5
- data/lib/graphql/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5bf42de25927dab863bd600879d33d204c54bdf124b2b4cc5b27c4c16d4f28b
|
4
|
+
data.tar.gz: 58b21a4dd5ba80ec0f6c3362013a6adb07c2f1af243e5a851d4c3f08554fe09d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f199d24a190910e44b189340b6a3d2a3c121ab56ba10ec6cf44d8801107db8d73541f24788b00b13ee7dd357f7bfe6ecff7001f053fe417c777bca59db5851db
|
7
|
+
data.tar.gz: fd0955c7f67b20370ea376d6a286fdf0ac7272ac5b243d6c309d2159a780cd0b8f92fd0e06ca312a69a16b7c8ad0ebd7bb4587feac9dc77b27da507c9a44cd8f
|
@@ -1,3 +1,4 @@
|
|
1
|
+
<% module_namespacing_when_supported do -%>
|
1
2
|
class GraphqlController < ApplicationController
|
2
3
|
# If accessing from outside this domain, nullify the session
|
3
4
|
# This allows for outside API access while preventing CSRF attacks,
|
@@ -48,3 +49,4 @@ class GraphqlController < ApplicationController
|
|
48
49
|
render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
|
49
50
|
end
|
50
51
|
end
|
52
|
+
<% end -%>
|
@@ -1,6 +1,8 @@
|
|
1
|
+
<% module_namespacing_when_supported do -%>
|
1
2
|
module Types
|
2
3
|
class <%= type_ruby_name.split('::')[-1] %> < Types::BaseObject
|
3
4
|
<% if options.node %> implements GraphQL::Relay::Node.interface
|
4
5
|
<% end %><% normalized_fields.each do |f| %> <%= f.to_ruby %>
|
5
6
|
<% end %> end
|
6
7
|
end
|
8
|
+
<% end -%>
|
@@ -170,13 +170,13 @@ module GraphQL
|
|
170
170
|
begin
|
171
171
|
kwarg_arguments = arguments(object, field_defn, ast_node)
|
172
172
|
rescue GraphQL::ExecutionError => e
|
173
|
-
continue_value(next_path, e, field_defn, return_type.non_null?, ast_node)
|
173
|
+
continue_value(next_path, e, owner_type, field_defn, return_type.non_null?, ast_node)
|
174
174
|
next
|
175
175
|
end
|
176
176
|
|
177
177
|
after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
|
178
178
|
if resolved_arguments.is_a? GraphQL::ExecutionError
|
179
|
-
continue_value(next_path, resolved_arguments, field_defn, return_type.non_null?, ast_node)
|
179
|
+
continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node)
|
180
180
|
next
|
181
181
|
end
|
182
182
|
|
@@ -228,12 +228,12 @@ module GraphQL
|
|
228
228
|
err
|
229
229
|
end
|
230
230
|
after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |inner_result|
|
231
|
-
continue_value = continue_value(next_path, inner_result, field_defn, return_type.non_null?, ast_node)
|
231
|
+
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node)
|
232
232
|
if RawValue === continue_value
|
233
233
|
# Write raw value directly to the response without resolving nested objects
|
234
234
|
write_in_response(next_path, continue_value.resolve)
|
235
235
|
elsif HALT != continue_value
|
236
|
-
continue_field(next_path, continue_value, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
|
236
|
+
continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
|
237
237
|
end
|
238
238
|
end
|
239
239
|
end
|
@@ -251,10 +251,9 @@ module GraphQL
|
|
251
251
|
end
|
252
252
|
|
253
253
|
HALT = Object.new
|
254
|
-
def continue_value(path, value, field, is_non_null, ast_node)
|
254
|
+
def continue_value(path, value, parent_type, field, is_non_null, ast_node)
|
255
255
|
if value.nil?
|
256
256
|
if is_non_null
|
257
|
-
parent_type = field.owner_type
|
258
257
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
259
258
|
write_invalid_null_in_response(path, err)
|
260
259
|
else
|
@@ -282,7 +281,7 @@ module GraphQL
|
|
282
281
|
err
|
283
282
|
end
|
284
283
|
|
285
|
-
continue_value(path, next_value, field, is_non_null, ast_node)
|
284
|
+
continue_value(path, next_value, parent_type, field, is_non_null, ast_node)
|
286
285
|
elsif GraphQL::Execution::Execute::SKIP == value
|
287
286
|
HALT
|
288
287
|
else
|
@@ -298,49 +297,49 @@ module GraphQL
|
|
298
297
|
# Location information from `path` and `ast_node`.
|
299
298
|
#
|
300
299
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
301
|
-
def continue_field(path, value, field,
|
302
|
-
case
|
300
|
+
def continue_field(path, value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments) # rubocop:disable Metrics/ParameterLists
|
301
|
+
case current_type.kind.name
|
303
302
|
when "SCALAR", "ENUM"
|
304
|
-
r =
|
303
|
+
r = current_type.coerce_result(value, context)
|
305
304
|
write_in_response(path, r)
|
306
305
|
r
|
307
306
|
when "UNION", "INTERFACE"
|
308
|
-
resolved_type_or_lazy, resolved_value = resolve_type(
|
307
|
+
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
|
309
308
|
resolved_value ||= value
|
310
309
|
|
311
|
-
after_lazy(resolved_type_or_lazy, owner:
|
312
|
-
possible_types = query.possible_types(
|
310
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
311
|
+
possible_types = query.possible_types(current_type)
|
313
312
|
|
314
313
|
if !possible_types.include?(resolved_type)
|
315
314
|
parent_type = field.owner_type
|
316
|
-
err_class =
|
315
|
+
err_class = current_type::UnresolvedTypeError
|
317
316
|
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
318
317
|
schema.type_error(type_error, context)
|
319
318
|
write_in_response(path, nil)
|
320
319
|
nil
|
321
320
|
else
|
322
|
-
continue_field(path, resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
|
321
|
+
continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
|
323
322
|
end
|
324
323
|
end
|
325
324
|
when "OBJECT"
|
326
325
|
object_proxy = begin
|
327
|
-
authorized_new(
|
326
|
+
authorized_new(current_type, value, context, path)
|
328
327
|
rescue GraphQL::ExecutionError => err
|
329
328
|
err
|
330
329
|
end
|
331
|
-
after_lazy(object_proxy, owner:
|
332
|
-
continue_value = continue_value(path, inner_object, field, is_non_null, ast_node)
|
330
|
+
after_lazy(object_proxy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
331
|
+
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
|
333
332
|
if HALT != continue_value
|
334
333
|
response_hash = {}
|
335
334
|
write_in_response(path, response_hash)
|
336
|
-
evaluate_selections(path, context.scoped_context, continue_value,
|
335
|
+
evaluate_selections(path, context.scoped_context, continue_value, current_type, next_selections)
|
337
336
|
response_hash
|
338
337
|
end
|
339
338
|
end
|
340
339
|
when "LIST"
|
341
340
|
response_list = []
|
342
341
|
write_in_response(path, response_list)
|
343
|
-
inner_type =
|
342
|
+
inner_type = current_type.of_type
|
344
343
|
idx = 0
|
345
344
|
scoped_context = context.scoped_context
|
346
345
|
begin
|
@@ -352,9 +351,9 @@ module GraphQL
|
|
352
351
|
set_type_at_path(next_path, inner_type)
|
353
352
|
# This will update `response_list` with the lazy
|
354
353
|
after_lazy(inner_value, owner: inner_type, path: next_path, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
|
355
|
-
continue_value = continue_value(next_path, inner_inner_value, field, inner_type.non_null?, ast_node)
|
354
|
+
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
|
356
355
|
if HALT != continue_value
|
357
|
-
continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
356
|
+
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
358
357
|
end
|
359
358
|
end
|
360
359
|
end
|
@@ -371,12 +370,12 @@ module GraphQL
|
|
371
370
|
|
372
371
|
response_list
|
373
372
|
when "NON_NULL"
|
374
|
-
inner_type =
|
373
|
+
inner_type = current_type.of_type
|
375
374
|
# Don't `set_type_at_path` because we want the static type,
|
376
375
|
# we're going to use that to determine whether a `nil` should be propagated or not.
|
377
|
-
continue_field(path, value, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
|
376
|
+
continue_field(path, value, owner_type, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
|
378
377
|
else
|
379
|
-
raise "Invariant: Unhandled type kind #{
|
378
|
+
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
380
379
|
end
|
381
380
|
end
|
382
381
|
|
@@ -168,7 +168,6 @@ module GraphQL
|
|
168
168
|
attr_accessor :scoped_context
|
169
169
|
|
170
170
|
def_delegators :@provided_values, :[]=
|
171
|
-
def_delegators :to_h, :fetch, :dig
|
172
171
|
def_delegators :@query, :trace, :interpreter?
|
173
172
|
|
174
173
|
# @!method []=(key, value)
|
@@ -180,6 +179,26 @@ module GraphQL
|
|
180
179
|
@provided_values[key]
|
181
180
|
end
|
182
181
|
|
182
|
+
UNSPECIFIED_FETCH_DEFAULT = Object.new
|
183
|
+
|
184
|
+
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
185
|
+
if @scoped_context.key?(key)
|
186
|
+
@scoped_context[key]
|
187
|
+
elsif @provided_values.key?(key)
|
188
|
+
@provided_values[key]
|
189
|
+
elsif default != UNSPECIFIED_FETCH_DEFAULT
|
190
|
+
default
|
191
|
+
elsif block_given?
|
192
|
+
yield(self, key)
|
193
|
+
else
|
194
|
+
raise KeyError.new(key: key)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def dig(key, *other_keys)
|
199
|
+
@scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
|
200
|
+
end
|
201
|
+
|
183
202
|
def to_h
|
184
203
|
@provided_values.merge(@scoped_context)
|
185
204
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -44,7 +44,7 @@ module GraphQL
|
|
44
44
|
if field.has_max_page_size? && !value.has_max_page_size_override?
|
45
45
|
value.max_page_size = field.max_page_size
|
46
46
|
end
|
47
|
-
if (custom_t = context.schema.connections.edge_class_for_field(@field))
|
47
|
+
if context.schema.new_connections? && (custom_t = context.schema.connections.edge_class_for_field(@field))
|
48
48
|
value.edge_class = custom_t
|
49
49
|
end
|
50
50
|
value
|
@@ -12,16 +12,6 @@ module GraphQL
|
|
12
12
|
#
|
13
13
|
# Also, `#unsubscribe` terminates the subscription.
|
14
14
|
class Subscription < GraphQL::Schema::Resolver
|
15
|
-
class EarlyTerminationError < StandardError
|
16
|
-
end
|
17
|
-
|
18
|
-
# Raised when `unsubscribe` is called; caught by `subscriptions.rb`
|
19
|
-
class UnsubscribedError < EarlyTerminationError
|
20
|
-
end
|
21
|
-
|
22
|
-
# Raised when `no_update` is returned; caught by `subscriptions.rb`
|
23
|
-
class NoUpdateError < EarlyTerminationError
|
24
|
-
end
|
25
15
|
extend GraphQL::Schema::Resolver::HasPayloadType
|
26
16
|
extend GraphQL::Schema::Member::HasFields
|
27
17
|
|
@@ -65,7 +55,7 @@ module GraphQL
|
|
65
55
|
def resolve_update(**args)
|
66
56
|
ret_val = args.any? ? update(**args) : update
|
67
57
|
if ret_val == :no_update
|
68
|
-
|
58
|
+
throw :graphql_no_subscription_update
|
69
59
|
else
|
70
60
|
ret_val
|
71
61
|
end
|
@@ -90,7 +80,7 @@ module GraphQL
|
|
90
80
|
|
91
81
|
# Call this to halt execution and remove this subscription from the system
|
92
82
|
def unsubscribe
|
93
|
-
|
83
|
+
throw :graphql_subscription_unsubscribed
|
94
84
|
end
|
95
85
|
|
96
86
|
READING_SCOPE = ::Object.new
|
@@ -40,7 +40,6 @@ module GraphQL
|
|
40
40
|
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
41
41
|
# @param context [GraphQL::Query::Context]
|
42
42
|
# @param schema [GraphQL::Schema]
|
43
|
-
# @param deep_check [Boolean]
|
44
43
|
def initialize(filter, context:, schema:)
|
45
44
|
@schema = schema.interpreter? ? schema : schema.graphql_definition
|
46
45
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
@@ -51,7 +50,7 @@ module GraphQL
|
|
51
50
|
@visibility_cache = read_through { |m| filter.call(m, context) }
|
52
51
|
end
|
53
52
|
|
54
|
-
# @return [
|
53
|
+
# @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
|
55
54
|
def types
|
56
55
|
@types ||= begin
|
57
56
|
vis_types = {}
|
@@ -199,7 +198,7 @@ module GraphQL
|
|
199
198
|
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
|
200
199
|
any_interface_has_field = true
|
201
200
|
|
202
|
-
if
|
201
|
+
if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
|
203
202
|
any_interface_has_visible_field = true
|
204
203
|
end
|
205
204
|
end
|
@@ -100,31 +100,43 @@ module GraphQL
|
|
100
100
|
# Lookup the saved data for this subscription
|
101
101
|
query_data = read_subscription(subscription_id)
|
102
102
|
if query_data.nil?
|
103
|
-
|
104
|
-
|
103
|
+
delete_subscription(subscription_id)
|
104
|
+
return nil
|
105
105
|
end
|
106
|
+
|
106
107
|
# Fetch the required keys from the saved data
|
107
108
|
query_string = query_data.fetch(:query_string)
|
108
109
|
variables = query_data.fetch(:variables)
|
109
110
|
context = query_data.fetch(:context)
|
110
111
|
operation_name = query_data.fetch(:operation_name)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
112
|
+
result = nil
|
113
|
+
# this will be set to `false` unless `.execute` is terminated
|
114
|
+
# with a `throw :graphql_subscription_unsubscribed`
|
115
|
+
unsubscribed = true
|
116
|
+
catch(:graphql_subscription_unsubscribed) do
|
117
|
+
catch(:graphql_no_subscription_update) do
|
118
|
+
# Re-evaluate the saved query,
|
119
|
+
# but if it terminates early with a `throw`,
|
120
|
+
# it will stay `nil`
|
121
|
+
result = @schema.execute(
|
122
|
+
query: query_string,
|
123
|
+
context: context,
|
124
|
+
subscription_topic: event.topic,
|
125
|
+
operation_name: operation_name,
|
126
|
+
variables: variables,
|
127
|
+
root_value: object,
|
128
|
+
)
|
129
|
+
end
|
130
|
+
unsubscribed = false
|
131
|
+
end
|
132
|
+
|
133
|
+
if unsubscribed
|
134
|
+
# `unsubscribe` was called, clean up on our side
|
135
|
+
# TODO also send `{more: false}` to client?
|
136
|
+
delete_subscription(subscription_id)
|
137
|
+
end
|
138
|
+
|
139
|
+
result
|
128
140
|
end
|
129
141
|
|
130
142
|
# Run the update query for this subscription and deliver it
|
@@ -55,7 +55,15 @@ module GraphQL
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def platform_field_key(type, field)
|
58
|
-
"graphql.#{type.
|
58
|
+
"graphql.#{type.graphql_name}.#{field.graphql_name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def platform_authorized_key(type)
|
62
|
+
"graphql.authorized.#{type.graphql_name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def platform_resolve_type_key(type)
|
66
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
59
67
|
end
|
60
68
|
|
61
69
|
private
|
@@ -107,7 +115,7 @@ module GraphQL
|
|
107
115
|
else
|
108
116
|
[key, data[key]]
|
109
117
|
end
|
110
|
-
end.flatten.each_slice(2).to_h.merge(Spec: 'graphql')
|
118
|
+
end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
|
111
119
|
end
|
112
120
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
113
121
|
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
# It's called when you subclass this base connection, trying to use the
|
49
49
|
# class name to set defaults. You can call it again in the class definition
|
50
50
|
# to override the default (or provide a value, if the default lookup failed).
|
51
|
-
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: true)
|
51
|
+
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: true, node_nullable: true)
|
52
52
|
# Set this connection's graphql name
|
53
53
|
node_type_name = node_type.graphql_name
|
54
54
|
|
@@ -61,7 +61,7 @@ module GraphQL
|
|
61
61
|
description: "A list of edges.",
|
62
62
|
edge_class: edge_class
|
63
63
|
|
64
|
-
define_nodes_field if nodes_field
|
64
|
+
define_nodes_field(node_nullable) if nodes_field
|
65
65
|
|
66
66
|
description("The connection type for #{node_type_name}.")
|
67
67
|
end
|
@@ -90,9 +90,10 @@ module GraphQL
|
|
90
90
|
|
91
91
|
private
|
92
92
|
|
93
|
-
def define_nodes_field
|
94
|
-
|
95
|
-
|
93
|
+
def define_nodes_field(nullable = true)
|
94
|
+
type = nullable ? [@node_type, null: true] : [@node_type]
|
95
|
+
field :nodes, type,
|
96
|
+
null: nullable,
|
96
97
|
description: "A list of nodes."
|
97
98
|
end
|
98
99
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.11.
|
4
|
+
version: 1.11.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|