synvert-core 1.6.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +6 -4
- data/lib/synvert/core/configuration.rb +16 -8
- data/lib/synvert/core/rewriter/gem_spec.rb +2 -2
- data/lib/synvert/core/rewriter/instance.rb +72 -6
- data/lib/synvert/core/rewriter/ruby_version.rb +3 -3
- data/lib/synvert/core/rewriter.rb +38 -14
- data/lib/synvert/core/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/synvert/core/rewriter/instance_spec.rb +49 -0
- data/spec/synvert/core/rewriter_spec.rb +41 -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: 00661750a3522a9983d5eec1f064f62bf0d335e05e3b2a9cb7bb7222b04e20a2
|
4
|
+
data.tar.gz: 5a35b831ca678486f4a51db2ae8a57bed58617299a0c26c9cbc0c462351ffd3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b18bcf0f9f344c2c8661d7e4edf1a4f768d67175f45b34d2bb37b321f38681840c29b8e9a0fa61ee295329909f3093de4b7703e6dec21066190336ee8ceb1b5a
|
7
|
+
data.tar.gz: 0f334dc34d82d15235381f3a9be7651707b5a7a0e5614e29f48310407c70384ac6f1594f2b8643dc97577e0e55a4b6165f3174071e2887f424a76ed4d5f3b3cb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.8.0 (2022-09-17)
|
4
|
+
|
5
|
+
* Rename config `path` to `root_path`
|
6
|
+
* Rename config `skip_files` to `skip_paths`
|
7
|
+
* Add config `only_paths`
|
8
|
+
* Change dir to `root_path`
|
9
|
+
|
10
|
+
## 1.7.0 (2022-09-16)
|
11
|
+
|
12
|
+
* Add `Rewriter#test`
|
13
|
+
* Use option `run_instance` instead of `sandbox`
|
14
|
+
|
3
15
|
## 1.6.0 (2022-09-15)
|
4
16
|
|
5
17
|
* Make use of `NodeQuery` to query nodes
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
synvert-core (1.
|
4
|
+
synvert-core (1.8.0)
|
5
5
|
activesupport (< 7.0.0)
|
6
6
|
erubis
|
7
7
|
node_mutation
|
@@ -23,6 +23,7 @@ GEM
|
|
23
23
|
concurrent-ruby (1.1.10)
|
24
24
|
diff-lcs (1.5.0)
|
25
25
|
erubis (2.7.0)
|
26
|
+
fakefs (1.8.0)
|
26
27
|
ffi (1.15.5)
|
27
28
|
formatador (1.1.0)
|
28
29
|
guard (2.18.0)
|
@@ -48,10 +49,10 @@ GEM
|
|
48
49
|
method_source (1.0.0)
|
49
50
|
minitest (5.16.3)
|
50
51
|
nenv (0.3.0)
|
51
|
-
node_mutation (1.
|
52
|
-
activesupport
|
52
|
+
node_mutation (1.3.3)
|
53
|
+
activesupport (< 7.0.0)
|
53
54
|
erubis
|
54
|
-
node_query (1.
|
55
|
+
node_query (1.6.0)
|
55
56
|
activesupport (< 7.0.0)
|
56
57
|
notiffany (0.1.3)
|
57
58
|
nenv (~> 0.1)
|
@@ -90,6 +91,7 @@ PLATFORMS
|
|
90
91
|
ruby
|
91
92
|
|
92
93
|
DEPENDENCIES
|
94
|
+
fakefs
|
93
95
|
guard
|
94
96
|
guard-rspec
|
95
97
|
rake
|
@@ -4,23 +4,31 @@ module Synvert::Core
|
|
4
4
|
# Synvert global configuration.
|
5
5
|
class Configuration
|
6
6
|
class << self
|
7
|
-
# @!attribute [w]
|
8
|
-
# @!attribute [w]
|
7
|
+
# @!attribute [w] root_path
|
8
|
+
# @!attribute [w] skip_paths
|
9
|
+
# @!attribute [w] only_paths
|
9
10
|
# @!attribute [w] show_run_process
|
10
|
-
attr_writer :
|
11
|
+
attr_writer :root_path, :skip_paths, :only_paths, :show_run_process
|
11
12
|
|
12
13
|
# Get the path.
|
13
14
|
#
|
14
15
|
# @return [String] default is '.'
|
15
|
-
def
|
16
|
-
@
|
16
|
+
def root_path
|
17
|
+
@root_path || '.'
|
17
18
|
end
|
18
19
|
|
19
|
-
# Get a list of skip
|
20
|
+
# Get a list of skip paths.
|
20
21
|
#
|
21
22
|
# @return [Array<String>] default is [].
|
22
|
-
def
|
23
|
-
@
|
23
|
+
def skip_paths
|
24
|
+
@skip_paths || []
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get a list of only paths.
|
28
|
+
#
|
29
|
+
# @return [Array<String>] default is [].
|
30
|
+
def only_paths
|
31
|
+
@only_paths || []
|
24
32
|
end
|
25
33
|
|
26
34
|
# Check if show run process.
|
@@ -22,12 +22,12 @@ module Synvert::Core
|
|
22
22
|
#
|
23
23
|
# @return [Boolean] true if matches, otherwise false.
|
24
24
|
def match?
|
25
|
-
gemfile_lock_path = File.expand_path(File.join(Configuration.
|
25
|
+
gemfile_lock_path = File.expand_path(File.join(Configuration.root_path, 'Gemfile.lock'))
|
26
26
|
|
27
27
|
# if Gemfile.lock does not exist, just ignore this check
|
28
28
|
return true unless File.exist?(gemfile_lock_path)
|
29
29
|
|
30
|
-
ENV['BUNDLE_GEMFILE'] = Configuration.
|
30
|
+
ENV['BUNDLE_GEMFILE'] = Configuration.root_path # make sure bundler reads Gemfile.lock in the correct path
|
31
31
|
parser = Bundler::LockfileParser.new(File.read(gemfile_lock_path))
|
32
32
|
parser.specs.any? { |spec| Gem::Dependency.new(@name, @version).match?(spec) }
|
33
33
|
end
|
@@ -30,14 +30,19 @@ module Synvert::Core
|
|
30
30
|
|
31
31
|
# Process the instance.
|
32
32
|
# It finds specified files, for each file, it executes the block code, rewrites the original code,
|
33
|
-
# then
|
33
|
+
# then writes the code back to the original file.
|
34
34
|
def process
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
get_file_paths.each do |file_path|
|
36
|
+
process_file(file_path)
|
37
|
+
end
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
# Test the instance.
|
41
|
+
# It finds specified files, for each file, it executes the block code, tests the original code,
|
42
|
+
# then returns the actions.
|
43
|
+
def test
|
44
|
+
get_file_paths.each do |file_path|
|
45
|
+
test_file(file_path)
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
@@ -378,6 +383,67 @@ module Synvert::Core
|
|
378
383
|
end
|
379
384
|
end
|
380
385
|
|
386
|
+
# Test one file.
|
387
|
+
#
|
388
|
+
# @param file_path [String]
|
389
|
+
def test_file(file_path)
|
390
|
+
@current_file = file_path
|
391
|
+
source = read_source(file_path)
|
392
|
+
@current_mutation = NodeMutation.new(source)
|
393
|
+
begin
|
394
|
+
node = parse_code(file_path, source)
|
395
|
+
|
396
|
+
process_with_node(node) do
|
397
|
+
instance_eval(&@block)
|
398
|
+
rescue NoMethodError => e
|
399
|
+
puts [
|
400
|
+
"error: #{e.message}",
|
401
|
+
"file: #{file_path}",
|
402
|
+
"source: #{source}",
|
403
|
+
"line: #{current_node.line}"
|
404
|
+
].join("\n")
|
405
|
+
raise
|
406
|
+
end
|
407
|
+
|
408
|
+
result = @current_mutation.test
|
409
|
+
result.file_path = file_path
|
410
|
+
result
|
411
|
+
rescue Parser::SyntaxError
|
412
|
+
puts "[Warn] file #{file_path} was not parsed correctly."
|
413
|
+
# do nothing, iterate next file
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# Get file paths.
|
418
|
+
# @return [Array<String>] file paths
|
419
|
+
def get_file_paths
|
420
|
+
Dir.chdir(Configuration.root_path) do
|
421
|
+
only_paths = Configuration.only_paths.size > 0 ? Configuration.only_paths : ["."]
|
422
|
+
only_paths.flat_map do |only_path|
|
423
|
+
@file_patterns.flat_map do |file_pattern|
|
424
|
+
pattern = only_path == "." ? file_pattern : File.join(only_path, file_pattern)
|
425
|
+
Dir.glob(pattern) - get_skip_files
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Get skip files.
|
432
|
+
# @return [Array<String>] skip files
|
433
|
+
def get_skip_files
|
434
|
+
@skip_files ||= Configuration.skip_paths.flat_map do |skip_path|
|
435
|
+
if File.directory?(skip_path)
|
436
|
+
Dir.glob(File.join(skip_path, "**/*"))
|
437
|
+
elsif File.file?(skip_path)
|
438
|
+
[skip_path]
|
439
|
+
elsif skip_path.end_with?("**") || skip_path.end_with?("**/")
|
440
|
+
Dir.glob(File.join(skip_path, "*"))
|
441
|
+
else
|
442
|
+
Dir.glob(skip_path)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
381
447
|
# Read file source.
|
382
448
|
# @param file_path [String] file path
|
383
449
|
# @return [String] file source
|
@@ -16,14 +16,14 @@ module Synvert::Core
|
|
16
16
|
#
|
17
17
|
# @return [Boolean] true if matches, otherwise false.
|
18
18
|
def match?
|
19
|
-
if File.exist?(File.join(Configuration.
|
19
|
+
if File.exist?(File.join(Configuration.root_path, '.ruby-version'))
|
20
20
|
version_file = '.ruby-version'
|
21
|
-
elsif File.exist?(File.join(Configuration.
|
21
|
+
elsif File.exist?(File.join(Configuration.root_path, '.rvmrc'))
|
22
22
|
version_file = '.rvmrc'
|
23
23
|
end
|
24
24
|
return true unless version_file
|
25
25
|
|
26
|
-
version = File.read(File.join(Configuration.
|
26
|
+
version = File.read(File.join(Configuration.root_path, version_file))
|
27
27
|
Gem::Version.new(version) >= Gem::Version.new(@version)
|
28
28
|
end
|
29
29
|
end
|
@@ -72,15 +72,16 @@ module Synvert::Core
|
|
72
72
|
#
|
73
73
|
# @param group [String] the rewriter group.
|
74
74
|
# @param name [String] the rewriter name.
|
75
|
-
# @param
|
75
|
+
# @param options [Hash]
|
76
|
+
# @option options [Boolean] :run_instance (true) process the instance.
|
76
77
|
# @return [Synvert::Core::Rewriter] the registered rewriter.
|
77
78
|
# @raise [Synvert::Core::RewriterNotFound] if the registered rewriter is not found.
|
78
|
-
def call(group, name,
|
79
|
+
def call(group, name, options = { run_instance: true })
|
79
80
|
rewriter = fetch(group, name)
|
80
|
-
if
|
81
|
-
rewriter.process_with_sandbox
|
82
|
-
else
|
81
|
+
if options[:run_instance]
|
83
82
|
rewriter.process
|
83
|
+
else
|
84
|
+
rewriter.process_with_sandbox
|
84
85
|
end
|
85
86
|
rewriter
|
86
87
|
end
|
@@ -137,6 +138,8 @@ module Synvert::Core
|
|
137
138
|
@warnings = []
|
138
139
|
@affected_files = Set.new
|
139
140
|
@redo_until_no_change = false
|
141
|
+
@options = { run_instance: true, write_to_file: true }
|
142
|
+
@test_results = []
|
140
143
|
self.class.register(@group, @name, self)
|
141
144
|
end
|
142
145
|
|
@@ -152,14 +155,29 @@ module Synvert::Core
|
|
152
155
|
# Process rewriter with sandbox mode.
|
153
156
|
# It will call the block but doesn't change any file.
|
154
157
|
def process_with_sandbox
|
155
|
-
@
|
158
|
+
@options[:run_instance] = false
|
156
159
|
begin
|
157
160
|
process
|
158
161
|
ensure
|
159
|
-
@
|
162
|
+
@options[:run_instance] = true
|
160
163
|
end
|
161
164
|
end
|
162
165
|
|
166
|
+
def test
|
167
|
+
@options[:write_to_file] = false
|
168
|
+
begin
|
169
|
+
@affected_files = Set.new
|
170
|
+
instance_eval(&@block)
|
171
|
+
|
172
|
+
if !@affected_files.empty? && @redo_until_no_change # redo
|
173
|
+
test
|
174
|
+
end
|
175
|
+
ensure
|
176
|
+
@options[:write_to_file] = true
|
177
|
+
end
|
178
|
+
@test_results
|
179
|
+
end
|
180
|
+
|
163
181
|
# Add a warning.
|
164
182
|
#
|
165
183
|
# @param warning [Synvert::Core::Rewriter::Warning]
|
@@ -225,12 +243,18 @@ module Synvert::Core
|
|
225
243
|
# @param file_patterns [String|Array<String>] string pattern or list of string pattern to find files, e.g. ['spec/**/*_spec.rb']
|
226
244
|
# @param block [Block] the block to rewrite code in the matching files.
|
227
245
|
def within_files(file_patterns, &block)
|
228
|
-
return
|
246
|
+
return unless @options[:run_instance]
|
229
247
|
|
230
248
|
return if @ruby_version && !@ruby_version.match?
|
231
249
|
return if @gem_spec && !@gem_spec.match?
|
232
250
|
|
233
|
-
Rewriter::Instance.new(self, Array(file_patterns), &block)
|
251
|
+
instance = Rewriter::Instance.new(self, Array(file_patterns), &block)
|
252
|
+
if @options[:write_to_file]
|
253
|
+
instance.process
|
254
|
+
else
|
255
|
+
results = instance.test
|
256
|
+
@test_results += results.select { |result| result.affected? }
|
257
|
+
end
|
234
258
|
end
|
235
259
|
|
236
260
|
# Parse +within_file+ dsl, it finds a specifiled file.
|
@@ -248,9 +272,9 @@ module Synvert::Core
|
|
248
272
|
# @param filename [String] file name of newly created file.
|
249
273
|
# @param content [String] file body of newly created file.
|
250
274
|
def add_file(filename, content)
|
251
|
-
return
|
275
|
+
return unless @options[:run_instance]
|
252
276
|
|
253
|
-
filepath = File.join(Configuration.
|
277
|
+
filepath = File.join(Configuration.root_path, filename)
|
254
278
|
if File.exist?(filepath)
|
255
279
|
puts "File #{filepath} already exists."
|
256
280
|
return
|
@@ -267,9 +291,9 @@ module Synvert::Core
|
|
267
291
|
# end
|
268
292
|
# @param filename [String] file name.
|
269
293
|
def remove_file(filename)
|
270
|
-
return
|
294
|
+
return unless @options[:run_instance]
|
271
295
|
|
272
|
-
file_path = File.join(Configuration.
|
296
|
+
file_path = File.join(Configuration.root_path, filename)
|
273
297
|
File.delete(file_path) if File.exist?(file_path)
|
274
298
|
end
|
275
299
|
|
@@ -293,7 +317,7 @@ module Synvert::Core
|
|
293
317
|
# @param group [String] group of another rewriter.
|
294
318
|
# @param name [String] name of another rewriter.
|
295
319
|
def add_snippet(group, name)
|
296
|
-
@sub_snippets << self.class.call(group.to_s, name.to_s, @
|
320
|
+
@sub_snippets << self.class.call(group.to_s, name.to_s, @options)
|
297
321
|
end
|
298
322
|
|
299
323
|
# Parse +helper_method+ dsl, it defines helper method for {Synvert::Core::Rewriter::Instance}.
|
data/lib/synvert/core/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
4
|
|
5
5
|
require 'synvert/core'
|
6
|
+
require 'fakefs/spec_helpers'
|
6
7
|
|
7
8
|
Dir[File.join(File.dirname(__FILE__), 'support', '*')].each do |path|
|
8
9
|
require path
|
@@ -10,6 +11,7 @@ end
|
|
10
11
|
|
11
12
|
RSpec.configure do |config|
|
12
13
|
config.include ParserHelper
|
14
|
+
config.include FakeFS::SpecHelpers, fakefs: true
|
13
15
|
|
14
16
|
config.run_all_when_everything_filtered = true
|
15
17
|
config.filter_run :focus
|
@@ -267,6 +267,55 @@ module Synvert::Core
|
|
267
267
|
end
|
268
268
|
end
|
269
269
|
|
270
|
+
describe '#test' do
|
271
|
+
let(:rewriter) { Rewriter.new('foo', 'bar') }
|
272
|
+
|
273
|
+
it 'writes new code to file' do
|
274
|
+
instance =
|
275
|
+
Rewriter::Instance.new rewriter, ['spec/**/*_spec.rb'] do
|
276
|
+
with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
|
277
|
+
replace_with 'create {{arguments}}'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
input = <<~EOS
|
281
|
+
it 'uses factory_girl' do
|
282
|
+
user = FactoryGirl.create :user
|
283
|
+
post = FactoryGirl.create :post, user: user
|
284
|
+
assert post.valid?
|
285
|
+
end
|
286
|
+
EOS
|
287
|
+
expect(Dir).to receive(:glob).with('./spec/**/*_spec.rb').and_return(['spec/models/post_spec.rb'])
|
288
|
+
expect(File).to receive(:read).with('spec/models/post_spec.rb', encoding: 'UTF-8').and_return(input)
|
289
|
+
results = instance.test
|
290
|
+
expect(results[0].file_path).to eq 'spec/models/post_spec.rb'
|
291
|
+
expect(results[0].actions).to eq [
|
292
|
+
OpenStruct.new(start: 35, end: 59, new_code: 'create :user'),
|
293
|
+
OpenStruct.new(start: 69, end: 105, new_code: 'create :post, user: user')
|
294
|
+
]
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'does not write if file content is not changed' do
|
298
|
+
instance =
|
299
|
+
Rewriter::Instance.new rewriter, ['spec/spec_helper.rb'] do
|
300
|
+
with_node type: 'block', caller: { receiver: 'RSpec', message: 'configure' } do
|
301
|
+
unless_exist_node type: 'send', message: 'include', arguments: ['FactoryGirl::Syntax::Methods'] do
|
302
|
+
insert '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
input = <<~EOS
|
307
|
+
RSpec.configure do |config|
|
308
|
+
config.include FactoryGirl::Syntax::Methods
|
309
|
+
end
|
310
|
+
EOS
|
311
|
+
expect(Dir).to receive(:glob).with('./spec/spec_helper.rb').and_return(['spec/spec_helper.rb'])
|
312
|
+
expect(File).to receive(:read).with('spec/spec_helper.rb', encoding: 'UTF-8').and_return(input)
|
313
|
+
result = instance.test
|
314
|
+
expect(result[0].file_path).to eq 'spec/spec_helper.rb'
|
315
|
+
expect(result[0].actions).to eq []
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
270
319
|
describe '#process_with_node' do
|
271
320
|
it 'resets current_node' do
|
272
321
|
node1 = double
|
@@ -37,6 +37,46 @@ module Synvert::Core
|
|
37
37
|
rewriter.process
|
38
38
|
end
|
39
39
|
|
40
|
+
describe '#process' do
|
41
|
+
it 'rewrites the file' do
|
42
|
+
rewriter = Rewriter.new('group', 'name') do
|
43
|
+
within_files '**/*.rb' do
|
44
|
+
with_node node_type: 'class', name: 'Foobar' do
|
45
|
+
replace :name, with: 'Synvert'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
input = "class Foobar\nend"
|
50
|
+
output = "class Synvert\nend"
|
51
|
+
FakeFS do
|
52
|
+
File.write("code.rb", input)
|
53
|
+
rewriter.process
|
54
|
+
expect(File.read("code.rb")).to eq output
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#test' do
|
60
|
+
it 'gets test results' do
|
61
|
+
rewriter = Rewriter.new('group', 'name') do
|
62
|
+
within_files '**/*.rb' do
|
63
|
+
with_node node_type: 'class', name: 'Foobar' do
|
64
|
+
replace :name, with: 'Synvert'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
input = "class Foobar\nend"
|
69
|
+
FakeFS do
|
70
|
+
File.write("code.rb", input)
|
71
|
+
results = rewriter.test
|
72
|
+
expect(results[0].file_path).to eq '/code.rb'
|
73
|
+
expect(results[0].affected?).to be_truthy
|
74
|
+
expect(results[0].conflicted?).to be_falsey
|
75
|
+
expect(results[0].actions).to eq [OpenStruct.new(start: 6, end: 12, new_code: 'Synvert')]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
40
80
|
describe 'parses within_file' do
|
41
81
|
it 'does nothing if if_ruby does not match' do
|
42
82
|
expect(File).to receive(:exist?).with('./.ruby-version').and_return(true)
|
@@ -245,7 +285,7 @@ module Synvert::Core
|
|
245
285
|
it 'registers and calls rewriter in sandbox mode' do
|
246
286
|
rewriter = Rewriter.new 'group', 'rewriter'
|
247
287
|
expect(rewriter).to receive(:process_with_sandbox)
|
248
|
-
Rewriter.call 'group', 'rewriter',
|
288
|
+
Rewriter.call 'group', 'rewriter', run_instance: false
|
249
289
|
end
|
250
290
|
|
251
291
|
it 'raises RewriterNotFound if rewriter not found' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synvert-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|