ZMediumToMarkdown 2.0.12 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b93c95279c2e90820d9e0e9048c79f1fa5e5dd0a94dba690cf8a3c3b8679589
4
- data.tar.gz: e28c0042b45bf6f29377e3c8acf4e39dff68cda0b3d1b11cba153e95c97dc5aa
3
+ metadata.gz: 439f4734a4dde11659f6dede1e220f9edfd2203c0a025599c8c5ca78e2e91aab
4
+ data.tar.gz: c83396525f04b9313ed53b23d2bdf5b7b4ca5d8a7257c5364d9988c802160bcc
5
5
  SHA512:
6
- metadata.gz: 0f24fbe044fe188d121ca6486173ccd15cd8ca1de6aea5f3ef625a0929615ba33fb890a39b144d0d0914c5c78bb30d94007629b364f87577098607651bd0f4f6
7
- data.tar.gz: b4d939b9288c0f57ce3dd0b78bcb6d9abf672a7375cc5b3191d3820a80c9b43558f31fbafd2d5884b1b6b31b8bc9ac40406ef9fe8414964bcda4905f0b405c91
6
+ metadata.gz: 5b14cc13afd0e3b16ab5195465067ba98b61827171dc38412ca75467670010c4ff0753c7ad94996d29dd3fca5d489de9f055986c0fdbc81b74e83a16bbf9a53f
7
+ data.tar.gz: 7f9745e8c4608bea3cdf613d262c709d317a5fe1a3e3731d27db990d639e8c1e8b162f453ecded98e6dd6c7a1b4ed54c05262b264ce8b27acecefbbcc4bbdddb
data/lib/Helper.rb CHANGED
@@ -14,9 +14,9 @@ class Helper
14
14
 
15
15
  def self.fetchOGImage(url)
16
16
  html = Request.html(Request.URL(url))
17
- content = html.search("meta[property='og:image']").attribute('content')
18
-
19
- content
17
+ return "" unless html
18
+ image = html.search("meta[property='og:image']")
19
+ image.attribute('content') || ""
20
20
  end
21
21
 
22
22
  def self.escapeMarkdown(text)
@@ -9,8 +9,8 @@ class ImageDownloader
9
9
  Helper.createDirIfNotExist(dir.join("/"))
10
10
 
11
11
  begin
12
- imageResponse = open(url)
13
- File.write(path, imageResponse.read, {mode: 'wb'})
12
+ imageResponse = URI.open(url)
13
+ File.write(path, imageResponse.read)
14
14
  true
15
15
  rescue
16
16
  false
@@ -5,7 +5,7 @@ require 'Parsers/PParser'
5
5
  require 'securerandom'
6
6
 
7
7
  class Paragraph
8
- attr_accessor :postID, :name, :orgText, :text, :type, :href, :metadata, :mixtapeMetadata, :iframe, :oliIndex, :markups, :markupLinks
8
+ attr_accessor :postID, :name, :orgText, :text, :type, :href, :metadata, :mixtapeMetadata, :iframe, :oliIndex, :markups, :markupLinks, :codeBlockMetadata
9
9
 
10
10
  class Iframe
11
11
  attr_accessor :id, :title, :type, :src
@@ -49,6 +49,13 @@ class Paragraph
49
49
  end
50
50
  end
51
51
 
52
+ class CodeBlockMetadata
53
+ attr_accessor :lang
54
+ def initialize(json)
55
+ @lang = json['lang']
56
+ end
57
+ end
58
+
52
59
  def self.makeBlankParagraph(postID)
53
60
  json = {
54
61
  "name" => "fakeBlankParagraph_#{SecureRandom.uuid}",
@@ -72,13 +79,19 @@ class Paragraph
72
79
  @metadata = MetaData.new(json['metadata'])
73
80
  end
74
81
 
82
+ if json['codeBlockMetadata'].nil?
83
+ @codeBlockMetadata = nil
84
+ else
85
+ @codeBlockMetadata = CodeBlockMetadata.new(json['codeBlockMetadata'])
86
+ end
87
+
75
88
  if json['mixtapeMetadata'].nil?
76
89
  @mixtapeMetadata = nil
77
90
  else
78
91
  @mixtapeMetadata = MixtapeMetadata.new(json['mixtapeMetadata'])
79
92
  end
80
93
 
81
- if json['iframe'].nil?
94
+ if json['iframe'].nil? || !json['iframe'] || !json['iframe']['mediaResource']
82
95
  @iframe = nil
83
96
  else
84
97
  @iframe = Iframe.new(json['iframe']['mediaResource'])
@@ -26,7 +26,7 @@ class IframeParser < Parser
26
26
  end
27
27
 
28
28
  if paragraph.type == 'IFRAME'
29
-
29
+ return unless paragraph.iframe
30
30
  if !paragraph.iframe.src.nil? && paragraph.iframe.src != ""
31
31
  url = paragraph.iframe.src
32
32
  else
@@ -65,9 +65,11 @@ class IframeParser < Parser
65
65
  end
66
66
  else
67
67
  html = Request.html(Request.URL(url))
68
- src = html.search('script').first.attribute('src')
68
+ return "" unless html
69
+ src = html.search('script').first
70
+ srce = src.attribute('src') if src
69
71
  result = nil
70
- if !src.to_s[/^(https\:\/\/gist\.github\.com)/].nil?
72
+ if !srce.to_s[/^(https\:\/\/gist\.github\.com)/].nil?
71
73
  # is gist
72
74
  gist = Request.body(Request.URL(src)).scan(/(document\.write\('){1}(.*)(\)){1}/)[1][1]
73
75
  gist.gsub! '\n', ''
@@ -20,7 +20,13 @@ class PREParser < Parser
20
20
 
21
21
  def parse(paragraph)
22
22
  if PREParser.isPRE(paragraph)
23
- result = "```\n"
23
+
24
+ lang = ""
25
+ if !paragraph.codeBlockMetadata.nil?
26
+ lang = paragraph.codeBlockMetadata.lang
27
+ end
28
+
29
+ result = "```#{lang}\n"
24
30
 
25
31
  paragraph.text.each_line do |p|
26
32
  result += p
data/lib/Post.rb CHANGED
@@ -23,13 +23,12 @@ class Post
23
23
 
24
24
  def self.getPostPathFromPostURLString(postURLString)
25
25
  uri = URI.parse(postURLString)
26
- postPath = uri.path.split('/').last
27
-
28
- URI.decode(postPath)
26
+ uri.path.split('/').last
29
27
  end
30
28
 
31
29
  def self.parsePostContentFromHTML(html)
32
30
  json = nil
31
+ return "" unless html
33
32
  html.search('script').each do |script|
34
33
  match = script.to_s[/(<script>window\.__APOLLO_STATE__ \= ){1}(.*)(<\/script>){1}/,2]
35
34
  if !match.nil? && match != ""
@@ -47,7 +46,7 @@ class Post
47
46
  "variables": {
48
47
  "postId": postID
49
48
  },
50
- "query": "query PostViewerEdgeContentQuery($postId: ID!, $postMeteringOptions: PostMeteringOptions) {\n post(id: $postId) {\n ... on Post {\n id\n viewerEdge {\n id\n fullContent(postMeteringOptions: $postMeteringOptions) {\n isLockedPreviewOnly\n validatedShareKey\n bodyModel {\n ...PostBody_bodyModel\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment PostBody_bodyModel on RichText {\n sections {\n name\n startIndex\n textLayout\n imageLayout\n backgroundImage {\n id\n originalHeight\n originalWidth\n __typename\n }\n videoLayout\n backgroundVideo {\n videoId\n originalHeight\n originalWidth\n previewImageId\n __typename\n }\n __typename\n }\n paragraphs {\n id\n ...PostBodySection_paragraph\n __typename\n }\n ...normalizedBodyModel_richText\n __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n name\n ...PostBodyParagraph_paragraph\n __typename\n id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n name\n type\n ...ImageParagraph_paragraph\n ...TextParagraph_paragraph\n ...IframeParagraph_paragraph\n ...MixtapeParagraph_paragraph\n __typename\n id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n href\n layout\n metadata {\n id\n originalHeight\n originalWidth\n focusPercentX\n focusPercentY\n alt\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n ...PostAnnotationsMarker_paragraph\n __typename\n id\n}\n\nfragment Markups_paragraph on Paragraph {\n name\n text\n hasDropCap\n dropCapImage {\n ...MarkupNode_data_dropCapImage\n __typename\n id\n }\n markups {\n type\n start\n end\n href\n anchorType\n userId\n linkMetadata {\n httpStatus\n __typename\n }\n __typename\n }\n __typename\n id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n ...DropCap_image\n __typename\n id\n}\n\nfragment DropCap_image on ImageMetadata {\n id\n originalHeight\n originalWidth\n __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n id\n name\n text\n __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n ...PostViewNoteCard_paragraph\n __typename\n id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n type\n hasDropCap\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n __typename\n id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n iframe {\n mediaResource {\n id\n iframeSrc\n iframeHeight\n iframeWidth\n title\n __typename\n }\n __typename\n }\n layout\n ...getEmbedlyCardUrlParams_paragraph\n ...Markups_paragraph\n __typename\n id\n}\n\nfragment getEmbedlyCardUrlParams_paragraph on Paragraph {\n type\n iframe {\n mediaResource {\n iframeSrc\n __typename\n }\n __typename\n }\n __typename\n id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n type\n mixtapeMetadata {\n href\n mediaResource {\n mediumCatalog {\n id\n __typename\n }\n __typename\n }\n __typename\n }\n ...GenericMixtapeParagraph_paragraph\n __typename\n id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n text\n mixtapeMetadata {\n href\n thumbnailImageId\n __typename\n }\n markups {\n start\n end\n type\n href\n __typename\n }\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n paragraphs {\n markups {\n type\n __typename\n }\n ...getParagraphHighlights_paragraph\n ...getParagraphPrivateNotes_paragraph\n __typename\n }\n sections {\n startIndex\n ...getSectionEndIndex_section\n __typename\n }\n ...getParagraphStyles_richText\n ...getParagraphSpaces_richText\n __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getSectionEndIndex_section on Section {\n startIndex\n __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n paragraphs {\n text\n type\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n paragraphs {\n layout\n metadata {\n originalHeight\n originalWidth\n __typename\n }\n type\n ...paragraphExtendsImageGrid_paragraph\n __typename\n }\n ...getSeriesParagraphTopSpacings_richText\n ...getPostParagraphTopSpacings_richText\n __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n layout\n type\n __typename\n id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n paragraphs {\n id\n __typename\n }\n sections {\n startIndex\n __typename\n }\n __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n paragraphs {\n layout\n text\n __typename\n }\n sections {\n startIndex\n __typename\n }\n __typename\n}\n"
49
+ "query": "query PostViewerEdgeContentQuery($postId: ID!, $postMeteringOptions: PostMeteringOptions) {\n post(id: $postId) {\n ... on Post {\n id\n viewerEdge {\n id\n fullContent(postMeteringOptions: $postMeteringOptions) {\n isLockedPreviewOnly\n validatedShareKey\n bodyModel {\n ...PostBody_bodyModel\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment PostBody_bodyModel on RichText {\n sections {\n name\n startIndex\n textLayout\n imageLayout\n backgroundImage {\n id\n originalHeight\n originalWidth\n __typename\n }\n videoLayout\n backgroundVideo {\n videoId\n originalHeight\n originalWidth\n previewImageId\n __typename\n }\n __typename\n }\n paragraphs {\n id\n ...PostBodySection_paragraph\n __typename\n }\n ...normalizedBodyModel_richText\n __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n name\n ...PostBodyParagraph_paragraph\n __typename\n id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n name\n type\n ...ImageParagraph_paragraph\n ...TextParagraph_paragraph\n ...IframeParagraph_paragraph\n ...MixtapeParagraph_paragraph\n ...CodeBlockParagraph_paragraph\n __typename\n id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n href\n layout\n metadata {\n id\n originalHeight\n originalWidth\n focusPercentX\n focusPercentY\n alt\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n ...PostAnnotationsMarker_paragraph\n __typename\n id\n}\n\nfragment Markups_paragraph on Paragraph {\n name\n text\n hasDropCap\n dropCapImage {\n ...MarkupNode_data_dropCapImage\n __typename\n id\n }\n markups {\n type\n start\n end\n href\n anchorType\n userId\n linkMetadata {\n httpStatus\n __typename\n }\n __typename\n }\n __typename\n id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n ...DropCap_image\n __typename\n id\n}\n\nfragment DropCap_image on ImageMetadata {\n id\n originalHeight\n originalWidth\n __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n id\n name\n text\n __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n ...PostViewNoteCard_paragraph\n __typename\n id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n type\n hasDropCap\n codeBlockMetadata {\n mode\n lang\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n __typename\n id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n iframe {\n mediaResource {\n id\n iframeSrc\n iframeHeight\n iframeWidth\n title\n __typename\n }\n __typename\n }\n layout\n ...getEmbedlyCardUrlParams_paragraph\n ...Markups_paragraph\n __typename\n id\n}\n\nfragment getEmbedlyCardUrlParams_paragraph on Paragraph {\n type\n iframe {\n mediaResource {\n iframeSrc\n __typename\n }\n __typename\n }\n __typename\n id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n type\n mixtapeMetadata {\n href\n mediaResource {\n mediumCatalog {\n id\n __typename\n }\n __typename\n }\n __typename\n }\n ...GenericMixtapeParagraph_paragraph\n __typename\n id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n text\n mixtapeMetadata {\n href\n thumbnailImageId\n __typename\n }\n markups {\n start\n end\n type\n href\n __typename\n }\n __typename\n id\n}\n\nfragment CodeBlockParagraph_paragraph on Paragraph {\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n paragraphs {\n markups {\n type\n __typename\n }\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n ...getParagraphHighlights_paragraph\n ...getParagraphPrivateNotes_paragraph\n __typename\n }\n sections {\n startIndex\n ...getSectionEndIndex_section\n __typename\n }\n ...getParagraphStyles_richText\n ...getParagraphSpaces_richText\n __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getSectionEndIndex_section on Section {\n startIndex\n __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n paragraphs {\n text\n type\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n paragraphs {\n layout\n metadata {\n originalHeight\n originalWidth\n id\n __typename\n }\n type\n ...paragraphExtendsImageGrid_paragraph\n __typename\n }\n ...getSeriesParagraphTopSpacings_richText\n ...getPostParagraphTopSpacings_richText\n __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n layout\n type\n __typename\n id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n paragraphs {\n id\n __typename\n }\n sections {\n startIndex\n __typename\n }\n __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n paragraphs {\n layout\n text\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n }\n sections {\n startIndex\n __typename\n }\n __typename\n}\n"
51
50
  }
52
51
  ]
53
52
 
data/lib/Request.rb CHANGED
@@ -22,34 +22,40 @@ class Request
22
22
  end
23
23
  end
24
24
 
25
- response = https.request(request)
26
-
27
- # 3XX Redirect
28
- if response.code.to_i >= 300 && response.code.to_i <= 399 && !response['location'].nil? && response['location'] != ''
29
- if retryCount >= 10
30
- raise "Error: Retry limit reached. path: #{url}"
31
- else
32
- location = response['location']
33
- if !location.match? /^(http)/
34
- location = "#{uri.scheme}://#{uri.host}#{location}"
35
- end
36
- response = self.URL(location, method, data)
37
- end
25
+ begin
26
+ response = https.request(request)
27
+ # 3XX Redirect
28
+ if response.code.to_i >= 300 && response.code.to_i <= 399 && !response['location'].nil? && response['location'] != ''
29
+ if retryCount >= 10
30
+ raise "Error: Retry limit reached. path: #{url}"
31
+ else
32
+ location = response['location']
33
+ if !location.match? /^(http)/
34
+ location = "#{uri.scheme}://#{uri.host}#{location}"
35
+ end
36
+ response = self.URL(location, method, data)
37
+ end
38
+ end
39
+ rescue
40
+
38
41
  end
42
+
39
43
  response
40
44
  end
41
45
 
42
46
  def self.html(response)
43
- if response.code.to_i != 200
44
- nil
45
- end
47
+ if response.nil? || (response && response.code.to_i != 200)
48
+ nil
49
+ else
46
50
  Nokogiri::HTML(response.read_body)
51
+ end
47
52
  end
48
53
 
49
54
  def self.body(response)
50
- if response.code.to_i != 200
51
- nil
52
- end
55
+ if response.nil? || (response && response.code.to_i != 200)
56
+ nil
57
+ else
53
58
  response.read_body
59
+ end
54
60
  end
55
61
  end
@@ -26,6 +26,14 @@ require "Request"
26
26
  require "Post"
27
27
  require "User"
28
28
  require 'date'
29
+ require "uri"
30
+
31
+ module URI
32
+ def self.decode url
33
+ url ? URI.decode_www_form_component(url).gsub(" ", "%20") : ""
34
+ end
35
+ end
36
+
29
37
 
30
38
  class ZMediumFetcher
31
39
 
@@ -128,7 +136,7 @@ class ZMediumFetcher
128
136
  imagePathPolicy = PathPolicy.new(postPathPolicy.getAbsolutePath("assets"), "assets")
129
137
  end
130
138
 
131
- progress.postPath = postPath
139
+ progress.postPath = URI.decode(postPath)
132
140
  progress.message = "Downloading Post..."
133
141
  progress.printLog()
134
142
 
@@ -154,6 +162,7 @@ class ZMediumFetcher
154
162
  previousParagraph = nil
155
163
  preTypeParagraphs = []
156
164
  sourceParagraphs.each do |sourcParagraph|
165
+ return if (!sourcParagraph || !postID)
157
166
  paragraph = Paragraph.new(sourcParagraph, postID)
158
167
  if OLIParser.isOLI(paragraph)
159
168
  oliIndex += 1
@@ -223,8 +232,7 @@ class ZMediumFetcher
223
232
  progress.printLog()
224
233
 
225
234
  postWithDatePath = "#{postInfo.firstPublishedAt.strftime("%Y-%m-%d")}-#{postPath}"
226
-
227
- absolutePath = postPathPolicy.getAbsolutePath("#{postWithDatePath}.md")
235
+ absolutePath = URI.decode(postPathPolicy.getAbsolutePath("#{postWithDatePath}")) + ".md"
228
236
 
229
237
  fileLatestPublishedAt = nil
230
238
 
@@ -287,7 +295,7 @@ class ZMediumFetcher
287
295
 
288
296
  def downloadPostsByUsername(username, pathPolicy)
289
297
  progress.username = username
290
- progress.message = "Fetching infromation..."
298
+ progress.message = "Fetching posts..."
291
299
  progress.printLog()
292
300
 
293
301
  userID = User.convertToUserIDFromUsername(username)
@@ -295,9 +303,6 @@ class ZMediumFetcher
295
303
  raise "Medium's Username:#{username} not found!"
296
304
  end
297
305
 
298
- progress.message = "Fetching posts..."
299
- progress.printLog()
300
-
301
306
  postURLS = []
302
307
  nextID = nil
303
308
  begin
@@ -321,14 +326,19 @@ class ZMediumFetcher
321
326
  downloadPathPolicy = PathPolicy.new(pathPolicy.getAbsolutePath("users/#{username}"), pathPolicy.getRelativePath("users/#{username}"))
322
327
  end
323
328
 
324
- index = 0
329
+ index = 1
325
330
  postURLS.each do |postURL|
326
- downloadPost(postURL, downloadPathPolicy)
327
-
328
- index += 1
329
- progress.currentPostIndex = index
330
- progress.message = "Downloading posts..."
331
- progress.printLog()
331
+ begin
332
+ # todo: unless File.exists? Post.getPostPathFromPostURLString(postURL) +".md"
333
+ downloadPost(postURL, downloadPathPolicy)
334
+ rescue => e
335
+ puts e
336
+ end
337
+
338
+ index += 1
339
+ progress.currentPostIndex = index
340
+ progress.message = "Downloading posts..."
341
+ progress.printLog()
332
342
  end
333
343
 
334
344
  progress.message = "All posts has been downloaded!, Total posts: #{postURLS.length}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ZMediumToMarkdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.12
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ZhgChgLi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-19 00:00:00.000000000 Z
11
+ date: 2023-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri