graphiti 2.0.0.beta.9 → 2.0.0.beta.11
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/.github/workflows/release.yml +2 -0
- data/.tool-versions +1 -1
- data/CHANGELOG.md +46 -0
- data/Rakefile +17 -12
- data/lib/generators/graphiti/generator_mixin.rb +24 -0
- data/lib/generators/graphiti/templates/application_resource.rb.erb +9 -16
- data/lib/graphiti/adapters/abstract.rb +4 -3
- data/lib/graphiti/configuration.rb +55 -24
- data/lib/graphiti/delegates/pagination.rb +2 -2
- data/lib/graphiti/errors.rb +35 -3
- data/lib/graphiti/query.rb +61 -13
- data/lib/graphiti/rails/controller.rb +26 -6
- data/lib/graphiti/rails/rake_helpers.rb +17 -0
- data/lib/graphiti/renderer.rb +4 -1
- data/lib/graphiti/request_validators/update_validator.rb +1 -1
- data/lib/graphiti/request_validators/validator.rb +9 -0
- data/lib/graphiti/resource/configuration.rb +139 -40
- data/lib/graphiti/resource/dsl.rb +21 -2
- data/lib/graphiti/resource/interface.rb +1 -1
- data/lib/graphiti/resource/links.rb +39 -5
- data/lib/graphiti/resource/remote.rb +2 -1
- data/lib/graphiti/resource/sideloading.rb +2 -2
- data/lib/graphiti/schema.rb +2 -2
- data/lib/graphiti/scope.rb +100 -26
- data/lib/graphiti/scoping/filter.rb +4 -4
- data/lib/graphiti/scoping/paginate.rb +3 -3
- data/lib/graphiti/serializer.rb +30 -8
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +9 -9
- data/lib/graphiti/sideload.rb +50 -33
- data/lib/graphiti/spec_helpers/matchers.rb +2 -2
- data/lib/graphiti/spec_helpers/rspec.rb +6 -3
- data/lib/graphiti/util/link.rb +1 -1
- data/lib/graphiti/util/serializer_attributes.rb +5 -3
- data/lib/graphiti/util/serializer_relationships.rb +16 -15
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +5 -0
- data/lib/tasks/graphiti.rake +4 -0
- data/package.json +1 -1
- metadata +1 -1
data/lib/graphiti/serializer.rb
CHANGED
|
@@ -24,6 +24,16 @@ module Graphiti
|
|
|
24
24
|
self.relationship_condition_blocks ||= {}
|
|
25
25
|
self.relationship_sideloads ||= {}
|
|
26
26
|
|
|
27
|
+
# Keyed on the sideloads hash itself, which is reassigned whenever a
|
|
28
|
+
# relationship is applied, so the answer is recomputed exactly then.
|
|
29
|
+
def self.on_demand_links?
|
|
30
|
+
sideloads = relationship_sideloads
|
|
31
|
+
return @on_demand_links.last if @on_demand_links&.first.equal?(sideloads)
|
|
32
|
+
|
|
33
|
+
@on_demand_links = [sideloads, sideloads.each_value.any? { |sideload| sideload.link_mode == :on_demand }]
|
|
34
|
+
@on_demand_links.last
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
def self.inherited(klass)
|
|
28
38
|
super
|
|
29
39
|
klass.class_eval do
|
|
@@ -59,7 +69,7 @@ module Graphiti
|
|
|
59
69
|
starting_offset = 0
|
|
60
70
|
page_param = @proxy.query.pagination
|
|
61
71
|
if (page_number = page_param[:number])
|
|
62
|
-
page_size = page_param[:size] || @resource.
|
|
72
|
+
page_size = page_param[:size] || @resource.page_default_size
|
|
63
73
|
starting_offset = (page_number - 1) * page_size
|
|
64
74
|
end
|
|
65
75
|
|
|
@@ -74,7 +84,7 @@ module Graphiti
|
|
|
74
84
|
|
|
75
85
|
def as_jsonapi(kwargs = {})
|
|
76
86
|
super(**kwargs).tap do |hash|
|
|
77
|
-
strip_relationships!(hash)
|
|
87
|
+
strip_relationships!(hash)
|
|
78
88
|
add_links!(hash)
|
|
79
89
|
end
|
|
80
90
|
end
|
|
@@ -100,17 +110,29 @@ module Graphiti
|
|
|
100
110
|
hash[:links] = @resource.links(@object) if @resource.links?
|
|
101
111
|
end
|
|
102
112
|
|
|
113
|
+
# The meta: {included: false} stub is jsonapi-serializable's filler, not JSON:API.
|
|
103
114
|
def strip_relationships!(hash)
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
placeholders = relationship_placeholders?
|
|
116
|
+
return if placeholders && !strip_on_demand_relationships?
|
|
117
|
+
|
|
118
|
+
hash[:relationships]&.reject! do |name, payload|
|
|
119
|
+
next false if payload.key?(:data) || payload.key?(:links)
|
|
120
|
+
next true unless placeholders
|
|
121
|
+
|
|
122
|
+
self.class.relationship_sideloads[name]&.link_mode == :on_demand
|
|
106
123
|
end
|
|
107
124
|
end
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
# A remote resource exposes a stand-in object rather than a Resource.
|
|
127
|
+
def relationship_placeholders?
|
|
128
|
+
resource_class = @resource.class
|
|
129
|
+
return Resource.relationship_placeholders unless resource_class.respond_to?(:relationship_placeholders)
|
|
130
|
+
|
|
131
|
+
resource_class.relationship_placeholders
|
|
132
|
+
end
|
|
112
133
|
|
|
113
|
-
|
|
134
|
+
def strip_on_demand_relationships?
|
|
135
|
+
self.class.on_demand_links? && !@proxy&.query&.render_link?(:on_demand)
|
|
114
136
|
end
|
|
115
137
|
end
|
|
116
138
|
end
|
|
@@ -107,18 +107,18 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
def resolve(parents, query, graph_parent)
|
|
110
|
+
def resolve(parents, query, graph_parent, &proxy_block)
|
|
111
111
|
if ::Graphiti::Scope.resolve_synchronously?
|
|
112
|
-
sync_resolve(parents, query, graph_parent)
|
|
112
|
+
sync_resolve(parents, query, graph_parent, &proxy_block)
|
|
113
113
|
else
|
|
114
|
-
future_resolve(parents, query, graph_parent).value!
|
|
114
|
+
future_resolve(parents, query, graph_parent, &proxy_block).value!
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
def future_resolve(parents, query, graph_parent)
|
|
118
|
+
def future_resolve(parents, query, graph_parent, &proxy_block)
|
|
119
119
|
promises = []
|
|
120
120
|
each_resolvable_group(parents, query) do |child, group, child_query|
|
|
121
|
-
promises << child.future_resolve(group, child_query, graph_parent)
|
|
121
|
+
promises << child.future_resolve(group, child_query, graph_parent, &proxy_block)
|
|
122
122
|
end
|
|
123
123
|
return promises.first if promises.one?
|
|
124
124
|
|
|
@@ -130,14 +130,14 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
|
130
130
|
end
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def sync_resolve(parents, query, graph_parent)
|
|
133
|
+
def sync_resolve(parents, query, graph_parent, &proxy_block)
|
|
136
134
|
each_resolvable_group(parents, query) do |child, group, child_query|
|
|
137
|
-
child.
|
|
135
|
+
child.sync_resolve(group, child_query, graph_parent, &proxy_block)
|
|
138
136
|
end
|
|
139
137
|
end
|
|
140
138
|
|
|
139
|
+
private
|
|
140
|
+
|
|
141
141
|
# Group parents by their polymorphic type and yield each group's child
|
|
142
142
|
# sideload alongside a query pruned to the sideloads that child supports.
|
|
143
143
|
def each_resolvable_group(parents, query)
|
data/lib/graphiti/sideload.rb
CHANGED
|
@@ -31,6 +31,9 @@ module Graphiti
|
|
|
31
31
|
@writable = opts[:writable]
|
|
32
32
|
@as = opts[:as]
|
|
33
33
|
@link = opts[:link]
|
|
34
|
+
unless @link.nil? || Resource::LINK_MODES.include?(@link)
|
|
35
|
+
raise Errors::InvalidLinkRendering.new(@parent_resource_class, :"#{name} link", @link)
|
|
36
|
+
end
|
|
34
37
|
@single = opts[:single]
|
|
35
38
|
@remote = opts[:remote]
|
|
36
39
|
apply_belongs_to_many_filter if type == :many_to_many
|
|
@@ -84,7 +87,8 @@ module Graphiti
|
|
|
84
87
|
self.adapter = Graphiti::Adapters::GraphitiAPI
|
|
85
88
|
self.model = OpenStruct
|
|
86
89
|
self.remote = remote_url
|
|
87
|
-
self.
|
|
90
|
+
self.validate_requests = false
|
|
91
|
+
self.validate_links = false
|
|
88
92
|
}
|
|
89
93
|
name = "#{parent_resource_class.name}.#{@name}.remote"
|
|
90
94
|
klass.class_eval("def self.name;'#{name}';end", __FILE__, __LINE__)
|
|
@@ -148,8 +152,12 @@ module Graphiti
|
|
|
148
152
|
!!@polymorphic_as
|
|
149
153
|
end
|
|
150
154
|
|
|
155
|
+
# Every check behind the blocker is static sideload configuration, and this
|
|
156
|
+
# is asked once per rendered record per relationship.
|
|
151
157
|
def resource_ids_from_foreign_key?
|
|
152
|
-
|
|
158
|
+
return @resource_ids_from_foreign_key unless @resource_ids_from_foreign_key.nil?
|
|
159
|
+
|
|
160
|
+
@resource_ids_from_foreign_key = resource_ids_blocker.nil?
|
|
153
161
|
end
|
|
154
162
|
|
|
155
163
|
def resource_ids_blocker
|
|
@@ -166,14 +174,16 @@ module Graphiti
|
|
|
166
174
|
false
|
|
167
175
|
end
|
|
168
176
|
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
# A custom link block means the author wants the link, so a false default does not silence it.
|
|
178
|
+
def link_mode
|
|
179
|
+
return @link unless @link.nil?
|
|
171
180
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
default = @parent_resource_class.relationship_links
|
|
182
|
+
(link_proc && default == false) ? true : default
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def link?
|
|
186
|
+
link_mode != false
|
|
177
187
|
end
|
|
178
188
|
|
|
179
189
|
def link_filter(parents)
|
|
@@ -279,14 +289,20 @@ module Graphiti
|
|
|
279
289
|
proxy
|
|
280
290
|
end
|
|
281
291
|
|
|
282
|
-
def load(parents, query, graph_parent)
|
|
292
|
+
def load(parents, query, graph_parent, &proxy_block)
|
|
283
293
|
if Scope.resolve_synchronously?
|
|
284
|
-
|
|
294
|
+
sync_load(parents, query, graph_parent, &proxy_block)
|
|
285
295
|
else
|
|
286
|
-
future_load(parents, query, graph_parent).value!
|
|
296
|
+
future_load(parents, query, graph_parent, &proxy_block).value!
|
|
287
297
|
end
|
|
288
298
|
end
|
|
289
299
|
|
|
300
|
+
def sync_load(parents, query, graph_parent, &proxy_block)
|
|
301
|
+
proxy = build_resource_proxy(parents, query, graph_parent)
|
|
302
|
+
proxy_block&.call(proxy)
|
|
303
|
+
proxy.to_a
|
|
304
|
+
end
|
|
305
|
+
|
|
290
306
|
# Override in subclass
|
|
291
307
|
def infer_foreign_key
|
|
292
308
|
model = parent_resource_class.model
|
|
@@ -334,15 +350,30 @@ module Graphiti
|
|
|
334
350
|
children.replace(associated) if track_associated
|
|
335
351
|
end
|
|
336
352
|
|
|
337
|
-
def resolve(parents, query, graph_parent)
|
|
353
|
+
def resolve(parents, query, graph_parent, &proxy_block)
|
|
338
354
|
if Scope.resolve_synchronously?
|
|
339
|
-
sync_resolve(parents, query, graph_parent)
|
|
355
|
+
sync_resolve(parents, query, graph_parent, &proxy_block)
|
|
340
356
|
else
|
|
341
|
-
future_resolve(parents, query, graph_parent).value!
|
|
357
|
+
future_resolve(parents, query, graph_parent, &proxy_block).value!
|
|
342
358
|
end
|
|
343
359
|
end
|
|
344
360
|
|
|
345
|
-
|
|
361
|
+
# Called by a scope that already decided to stay inline, so it must not consult the pool again.
|
|
362
|
+
# A scope_proc builds a Scope rather than a proxy, and that nested scope decides for itself.
|
|
363
|
+
def sync_resolve(parents, query, graph_parent, &proxy_block)
|
|
364
|
+
assert_singular!(parents)
|
|
365
|
+
|
|
366
|
+
if self.class.scope_proc
|
|
367
|
+
build_sideload_scope(parents, query, graph_parent).resolve do |sideload_results|
|
|
368
|
+
fire_assign(parents, sideload_results)
|
|
369
|
+
end
|
|
370
|
+
else
|
|
371
|
+
sync_load(parents, query, graph_parent, &proxy_block)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# A scope_proc builds a Scope rather than a proxy, and a Scope's cache key omits what a proxy's carries.
|
|
376
|
+
def future_resolve(parents, query, graph_parent, &proxy_block)
|
|
346
377
|
assert_singular!(parents)
|
|
347
378
|
|
|
348
379
|
if self.class.scope_proc
|
|
@@ -350,7 +381,7 @@ module Graphiti
|
|
|
350
381
|
fire_assign(parents, sideload_results)
|
|
351
382
|
end
|
|
352
383
|
else
|
|
353
|
-
future_load(parents, query, graph_parent)
|
|
384
|
+
future_load(parents, query, graph_parent, &proxy_block)
|
|
354
385
|
end
|
|
355
386
|
end
|
|
356
387
|
|
|
@@ -414,21 +445,6 @@ module Graphiti
|
|
|
414
445
|
|
|
415
446
|
private
|
|
416
447
|
|
|
417
|
-
# Synchronous counterpart to #future_resolve, used when concurrency is off.
|
|
418
|
-
# Resolves inline via the synchronous Scope#resolve / #load paths (no
|
|
419
|
-
# promises). See Scope#sync_resolve_sideloads.
|
|
420
|
-
def sync_resolve(parents, query, graph_parent)
|
|
421
|
-
assert_singular!(parents)
|
|
422
|
-
|
|
423
|
-
if self.class.scope_proc
|
|
424
|
-
build_sideload_scope(parents, query, graph_parent).resolve do |sideload_results|
|
|
425
|
-
fire_assign(parents, sideload_results)
|
|
426
|
-
end
|
|
427
|
-
else
|
|
428
|
-
load(parents, query, graph_parent)
|
|
429
|
-
end
|
|
430
|
-
end
|
|
431
|
-
|
|
432
448
|
def assert_singular!(parents)
|
|
433
449
|
if single? && parents.length > 1
|
|
434
450
|
raise Errors::SingularSideload.new(self, parents.length)
|
|
@@ -445,8 +461,9 @@ module Graphiti
|
|
|
445
461
|
default_paginate: false
|
|
446
462
|
end
|
|
447
463
|
|
|
448
|
-
def future_load(parents, query, graph_parent)
|
|
464
|
+
def future_load(parents, query, graph_parent, &proxy_block)
|
|
449
465
|
proxy = build_resource_proxy(parents, query, graph_parent)
|
|
466
|
+
proxy_block&.call(proxy)
|
|
450
467
|
proxy.respond_to?(:future_resolve_data) ? proxy.future_resolve_data : Concurrent::Promises.fulfilled_future(proxy)
|
|
451
468
|
end
|
|
452
469
|
|
|
@@ -140,7 +140,7 @@ module Graphiti
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
class FilterAttributeMatcher < ResourceDSLMatcher
|
|
143
|
-
GRAPHITI_OPTS = %i[allow deny single required
|
|
143
|
+
GRAPHITI_OPTS = %i[allow deny single required blanks].freeze
|
|
144
144
|
GRAPHITI_CONFIG_KEY = :filters
|
|
145
145
|
EXPECTED_ACTION = "filter"
|
|
146
146
|
end
|
|
@@ -186,7 +186,7 @@ module Graphiti
|
|
|
186
186
|
# @param [Symbol] type
|
|
187
187
|
#
|
|
188
188
|
# @example expect(subject).to filter_attribute(:name, :string)
|
|
189
|
-
# @example expect(subject).to filter_attribute(:name, :string).with_options(
|
|
189
|
+
# @example expect(subject).to filter_attribute(:name, :string).with_options(blanks: :null)
|
|
190
190
|
# @example expect(subject).not_to filter_attribute(:name, :string)
|
|
191
191
|
def filter_attribute(attribute, type)
|
|
192
192
|
FilterAttributeMatcher.new(attribute, type)
|
|
@@ -39,14 +39,17 @@ resource_testing = proc do
|
|
|
39
39
|
let(:params) { {} }
|
|
40
40
|
|
|
41
41
|
around do |e|
|
|
42
|
-
|
|
43
|
-
Graphiti::Resource.
|
|
42
|
+
original_requests = Graphiti::Resource.validate_requests
|
|
43
|
+
original_links = Graphiti::Resource.validate_links
|
|
44
|
+
Graphiti::Resource.validate_requests = false
|
|
45
|
+
Graphiti::Resource.validate_links = false
|
|
44
46
|
|
|
45
47
|
Graphiti.with_context graphiti_context do
|
|
46
48
|
e.run
|
|
47
49
|
end
|
|
48
50
|
ensure
|
|
49
|
-
Graphiti::Resource.
|
|
51
|
+
Graphiti::Resource.validate_requests = original_requests
|
|
52
|
+
Graphiti::Resource.validate_links = original_links
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def graphiti_context
|
data/lib/graphiti/util/link.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Graphiti
|
|
|
30
30
|
@serializer.send(:"#{applied_method}=", [@name] | existing)
|
|
31
31
|
|
|
32
32
|
@serializer.meta do
|
|
33
|
-
if !!@resource.try(:
|
|
33
|
+
if !!@resource.try(:page_cursors?) && !Graphiti.context[:graphql]
|
|
34
34
|
{cursor: cursor}
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -121,10 +121,11 @@ module Graphiti
|
|
|
121
121
|
|
|
122
122
|
def default_proc
|
|
123
123
|
name_ref = @name
|
|
124
|
+
resource_ref = @resource
|
|
124
125
|
typecast_ref = typecast(Graphiti::Types[@attr[:type]][:read])
|
|
125
126
|
->(_) {
|
|
126
127
|
val = @object.send(name_ref)
|
|
127
|
-
if
|
|
128
|
+
if resource_ref.typecast_reads
|
|
128
129
|
typecast_ref.call(val)
|
|
129
130
|
else
|
|
130
131
|
val
|
|
@@ -133,10 +134,11 @@ module Graphiti
|
|
|
133
134
|
end
|
|
134
135
|
|
|
135
136
|
def wrap_proc(inner)
|
|
137
|
+
resource_ref = @resource
|
|
136
138
|
typecast_ref = typecast(Graphiti::Types[@attr[:type]][:read])
|
|
137
139
|
->(serializer_instance = nil) {
|
|
138
140
|
val = serializer_instance.instance_eval(&inner)
|
|
139
|
-
if
|
|
141
|
+
if resource_ref.typecast_reads
|
|
140
142
|
typecast_ref.call(val)
|
|
141
143
|
else
|
|
142
144
|
val
|
|
@@ -8,6 +8,8 @@ module Graphiti
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def apply
|
|
11
|
+
return unless @serializer
|
|
12
|
+
|
|
11
13
|
@sideloads.each_pair do |name, sideload|
|
|
12
14
|
if apply?(sideload)
|
|
13
15
|
SerializerRelationship
|
|
@@ -55,7 +57,6 @@ module Graphiti
|
|
|
55
57
|
private
|
|
56
58
|
|
|
57
59
|
def block
|
|
58
|
-
link_ref = link?
|
|
59
60
|
sideload_ref = @sideload
|
|
60
61
|
resource_class_ref = @resource_class
|
|
61
62
|
data_proc_ref = data_proc
|
|
@@ -93,13 +94,11 @@ module Graphiti
|
|
|
93
94
|
linkage always: sideload_ref.render_resource_ids?
|
|
94
95
|
end
|
|
95
96
|
|
|
96
|
-
if
|
|
97
|
-
|
|
98
|
-
self_ref.send(:validate_link!) unless self_ref.send(:eagerly_validate_links?)
|
|
97
|
+
if @proxy.query.render_link?(sideload_ref.link_mode) && self_ref.send(:linkable?)
|
|
98
|
+
self_ref.send(:validate_link!) unless self_ref.send(:eagerly_validate_links?)
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end
|
|
100
|
+
link(:related) do
|
|
101
|
+
::Graphiti::Util::Link.new(sideload_ref, @object).generate
|
|
103
102
|
end
|
|
104
103
|
end
|
|
105
104
|
end
|
|
@@ -162,8 +161,8 @@ module Graphiti
|
|
|
162
161
|
end
|
|
163
162
|
|
|
164
163
|
def validate_link!
|
|
165
|
-
return unless link?
|
|
166
|
-
return unless @resource_class.
|
|
164
|
+
return unless @sideload.link? && linkable?
|
|
165
|
+
return unless @resource_class.validate_links?
|
|
167
166
|
return if @sideload.link_proc
|
|
168
167
|
|
|
169
168
|
unless Graphiti.config.context_for_endpoint
|
|
@@ -192,14 +191,16 @@ module Graphiti
|
|
|
192
191
|
self.class.validated_link_cache << cache_key
|
|
193
192
|
end
|
|
194
193
|
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
# Checked lazily so a sideload with no endpoint only raises when a link is actually wanted.
|
|
195
|
+
def linkable?
|
|
196
|
+
return @linkable if defined?(@linkable)
|
|
197
197
|
|
|
198
|
-
if @sideload.
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
@linkable = if @sideload.link_proc
|
|
199
|
+
true
|
|
200
|
+
elsif @sideload.respond_to?(:children)
|
|
201
|
+
@sideload.children.values.all? { |c| !c.resource.endpoint.nil? }
|
|
201
202
|
else
|
|
202
|
-
|
|
203
|
+
!@sideload.resource.endpoint.nil?
|
|
203
204
|
end
|
|
204
205
|
end
|
|
205
206
|
end
|
data/lib/graphiti/version.rb
CHANGED
data/lib/graphiti.rb
CHANGED
data/lib/tasks/graphiti.rake
CHANGED
data/package.json
CHANGED
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
[
|
|
143
143
|
"@semantic-release/exec",
|
|
144
144
|
{
|
|
145
|
-
"prepareCmd": "bundle exec ruby spec/performance/measure_releases.rb --
|
|
145
|
+
"prepareCmd": "bundle exec ruby spec/performance/measure_releases.rb --promote v${nextRelease.version}"
|
|
146
146
|
}
|
|
147
147
|
],
|
|
148
148
|
[
|