metanorma-release 0.2.23 → 0.2.25
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/.rubocop_todo.yml +21 -319
- data/README.adoc +306 -91
- data/lib/metanorma/release/aggregation_pipeline.rb +65 -40
- data/lib/metanorma/release/asset_processor.rb +0 -2
- data/lib/metanorma/release/cache_store.rb +1 -0
- data/lib/metanorma/release/change_detector.rb +3 -2
- data/lib/metanorma/release/cli.rb +26 -2
- data/lib/metanorma/release/commands/aggregate.rb +0 -16
- data/lib/metanorma/release/commands/package.rb +9 -17
- data/lib/metanorma/release/commands/release_command.rb +19 -14
- data/lib/metanorma/release/config.rb +16 -7
- data/lib/metanorma/release/dependency_validation.rb +19 -0
- data/lib/metanorma/release/document_flattener.rb +173 -0
- data/lib/metanorma/release/index.rb +1 -1
- data/lib/metanorma/release/interfaces.rb +12 -0
- data/lib/metanorma/release/platform/github/manifest_reader.rb +3 -1
- data/lib/metanorma/release/platform/github/release_fetcher.rb +4 -10
- data/lib/metanorma/release/platform/github/topic_discoverer.rb +1 -1
- data/lib/metanorma/release/platform/github.rb +0 -4
- data/lib/metanorma/release/platform/local/fetcher.rb +5 -17
- data/lib/metanorma/release/platform/null/manifest_reader.rb +15 -0
- data/lib/metanorma/release/platform/null/publisher.rb +1 -1
- data/lib/metanorma/release/platform/null.rb +2 -0
- data/lib/metanorma/release/platform/static_discoverer.rb +19 -0
- data/lib/metanorma/release/platform.rb +1 -0
- data/lib/metanorma/release/platform_factory.rb +6 -21
- data/lib/metanorma/release/publication.rb +23 -161
- data/lib/metanorma/release/publication_serializer.rb +59 -0
- data/lib/metanorma/release/release_pipeline.rb +7 -15
- data/lib/metanorma/release/rxl_extractor.rb +106 -0
- data/lib/metanorma/release/site.rb +4 -154
- data/lib/metanorma/release/slug_strategy.rb +30 -15
- data/lib/metanorma/release/version.rb +1 -1
- data/lib/metanorma/release.rb +36 -19
- metadata +8 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Release
|
|
5
|
+
module Platform
|
|
6
|
+
class StaticDiscoverer
|
|
7
|
+
include RepoDiscoverer
|
|
8
|
+
|
|
9
|
+
def initialize(repos:)
|
|
10
|
+
@repos = repos
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def discover
|
|
14
|
+
@repos
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -6,6 +6,7 @@ module Metanorma
|
|
|
6
6
|
autoload :GitHub, "metanorma/release/platform/github"
|
|
7
7
|
autoload :Local, "metanorma/release/platform/local"
|
|
8
8
|
autoload :Null, "metanorma/release/platform/null"
|
|
9
|
+
autoload :StaticDiscoverer, "metanorma/release/platform/static_discoverer"
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
end
|
|
@@ -24,14 +24,17 @@ module Metanorma
|
|
|
24
24
|
|
|
25
25
|
discoverer = if opts[:repos]
|
|
26
26
|
repos = opts[:repos].map { |r| RepoRef.from_string(r) }
|
|
27
|
-
StaticDiscoverer.new(repos: repos)
|
|
27
|
+
Platform::StaticDiscoverer.new(repos: repos)
|
|
28
28
|
else
|
|
29
29
|
Platform::GitHub::TopicDiscoverer.new(
|
|
30
30
|
client: client, organizations: opts[:organizations], topic: opts[:topic],
|
|
31
31
|
)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
download_cache =
|
|
34
|
+
download_cache = if opts[:cache_dir]
|
|
35
|
+
File.join(opts[:cache_dir],
|
|
36
|
+
"downloads")
|
|
37
|
+
end
|
|
35
38
|
|
|
36
39
|
{
|
|
37
40
|
discoverer: discoverer,
|
|
@@ -58,7 +61,7 @@ module Metanorma
|
|
|
58
61
|
if source.start_with?("local:")
|
|
59
62
|
adapters = AGGREGATION_REGISTRY["local"].call(options,
|
|
60
63
|
options[:token])
|
|
61
|
-
adapters[:manifest_reader] =
|
|
64
|
+
adapters[:manifest_reader] = Platform::Null::ManifestReader.new
|
|
62
65
|
return adapters
|
|
63
66
|
end
|
|
64
67
|
|
|
@@ -78,24 +81,6 @@ module Metanorma
|
|
|
78
81
|
def self.register_aggregation(name, factory)
|
|
79
82
|
AGGREGATION_REGISTRY[name] = factory
|
|
80
83
|
end
|
|
81
|
-
|
|
82
|
-
class StaticDiscoverer
|
|
83
|
-
include RepoDiscoverer
|
|
84
|
-
|
|
85
|
-
def initialize(repos:)
|
|
86
|
-
@repos = repos
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def discover
|
|
90
|
-
@repos
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
class NullManifestReader
|
|
95
|
-
include Metanorma::Release::ManifestReader
|
|
96
|
-
|
|
97
|
-
def read(_repo) = nil
|
|
98
|
-
end
|
|
99
84
|
end
|
|
100
85
|
end
|
|
101
86
|
end
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
begin
|
|
4
|
-
require "relaton/bib"
|
|
5
|
-
rescue LoadError
|
|
6
|
-
raise LoadError,
|
|
7
|
-
"The relaton-bib gem is required. Add `gem 'relaton-bib'` to your Gemfile."
|
|
8
|
-
end
|
|
9
|
-
|
|
10
3
|
require "json"
|
|
11
4
|
|
|
12
5
|
module Metanorma
|
|
@@ -67,6 +60,11 @@ module Metanorma
|
|
|
67
60
|
class Publication
|
|
68
61
|
METADATA_VERSION = 1
|
|
69
62
|
|
|
63
|
+
DRAFT_STAGES = %w[
|
|
64
|
+
20 30 40 50
|
|
65
|
+
working-draft committee-draft draft-standard final-draft
|
|
66
|
+
].freeze
|
|
67
|
+
|
|
70
68
|
attr_reader :identifier, :slug, :title, :edition, :stage, :doctype,
|
|
71
69
|
:revdate, :files, :channels, :source
|
|
72
70
|
|
|
@@ -94,14 +92,23 @@ module Metanorma
|
|
|
94
92
|
files.any? ? File.dirname(files.first.path) : "."
|
|
95
93
|
end
|
|
96
94
|
|
|
97
|
-
def content_hash
|
|
98
|
-
|
|
95
|
+
def content_hash(from_directory: nil)
|
|
96
|
+
dir = if from_directory && base_dir == "."
|
|
97
|
+
from_directory
|
|
98
|
+
else
|
|
99
|
+
base_dir
|
|
100
|
+
end
|
|
101
|
+
ContentHash.of_directory(dir, base: slug)
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
def file?(format)
|
|
102
105
|
formats.include?(format)
|
|
103
106
|
end
|
|
104
107
|
|
|
108
|
+
def draft?
|
|
109
|
+
DRAFT_STAGES.include?(stage.to_s)
|
|
110
|
+
end
|
|
111
|
+
|
|
105
112
|
def eql?(other)
|
|
106
113
|
other.is_a?(self.class) && identifier == other.identifier &&
|
|
107
114
|
edition == other.edition && stage == other.stage
|
|
@@ -154,40 +161,26 @@ module Metanorma
|
|
|
154
161
|
end
|
|
155
162
|
|
|
156
163
|
def to_release_body
|
|
157
|
-
|
|
164
|
+
PublicationSerializer.to_release_body(self)
|
|
158
165
|
end
|
|
159
166
|
|
|
160
167
|
def to_json(*_args)
|
|
161
|
-
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def self.from_json(json_string)
|
|
165
|
-
data = JSON.parse(json_string)
|
|
166
|
-
raise ArgumentError, "Missing required field: id" unless data["id"]
|
|
167
|
-
unless data["title"]
|
|
168
|
-
raise ArgumentError,
|
|
169
|
-
"Missing required field: title"
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
from_metadata_hash(data)
|
|
168
|
+
PublicationSerializer.to_json(self)
|
|
173
169
|
end
|
|
174
170
|
|
|
175
171
|
def self.from_release_body(body)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
match = body.match(/<!--\s*mn-release-metadata\s*\n(.*?)\n-->/m)
|
|
179
|
-
return nil unless match
|
|
172
|
+
PublicationSerializer.from_release_body(body)
|
|
173
|
+
end
|
|
180
174
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
nil
|
|
175
|
+
def self.from_json(json_string)
|
|
176
|
+
PublicationSerializer.from_json(json_string)
|
|
184
177
|
end
|
|
185
178
|
|
|
186
179
|
def self.from_metadata_hash(data)
|
|
187
180
|
ident = data["identifier"] || data["id"]
|
|
188
181
|
new(
|
|
189
182
|
identifier: ident,
|
|
190
|
-
slug: slug_from_identifier(ident),
|
|
183
|
+
slug: SlugStrategy.slug_from_identifier(ident),
|
|
191
184
|
title: data["title"],
|
|
192
185
|
edition: data["edition"],
|
|
193
186
|
stage: data["stage"],
|
|
@@ -199,137 +192,6 @@ module Metanorma
|
|
|
199
192
|
metadata_formats: data["formats"],
|
|
200
193
|
)
|
|
201
194
|
end
|
|
202
|
-
|
|
203
|
-
private
|
|
204
|
-
|
|
205
|
-
def metadata_hash
|
|
206
|
-
{
|
|
207
|
-
"version" => METADATA_VERSION,
|
|
208
|
-
"id" => slug,
|
|
209
|
-
"identifier" => identifier,
|
|
210
|
-
"title" => title,
|
|
211
|
-
"edition" => edition,
|
|
212
|
-
"stage" => stage,
|
|
213
|
-
"doctype" => doctype,
|
|
214
|
-
"revdate" => revdate,
|
|
215
|
-
"formats" => formats,
|
|
216
|
-
"channels" => channels,
|
|
217
|
-
"publisher" => Publication.publisher_from_identifier(identifier),
|
|
218
|
-
}
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def self.slug_from_identifier(identifier)
|
|
222
|
-
identifier.to_s.strip
|
|
223
|
-
.gsub(/\s+/, "-")
|
|
224
|
-
.gsub(/:+/, "-")
|
|
225
|
-
.downcase
|
|
226
|
-
.gsub(/--+/, "-")
|
|
227
|
-
.gsub(/[-.]+$/, "")
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def self.publisher_from_identifier(identifier)
|
|
231
|
-
return nil if identifier.nil? || identifier.strip.empty?
|
|
232
|
-
|
|
233
|
-
identifier.strip.split(/[\s-]/).first&.downcase
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
# -- RXL extraction --
|
|
237
|
-
|
|
238
|
-
def self.discover(output_dir)
|
|
239
|
-
Dir.glob(File.join(output_dir, "**", "*.rxl")).filter_map do |path|
|
|
240
|
-
from_rxl(path)
|
|
241
|
-
rescue StandardError => e
|
|
242
|
-
warn "Warning: Skipping #{path}: #{e.message}"
|
|
243
|
-
nil
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def self.from_rxl(rxl_path)
|
|
248
|
-
unless File.exist?(rxl_path)
|
|
249
|
-
raise ArgumentError,
|
|
250
|
-
"RXL file not found: #{rxl_path}"
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
content = File.read(rxl_path)
|
|
254
|
-
bib = Relaton::Bib::Item.from_xml(content)
|
|
255
|
-
build_from_bib(bib, rxl_path)
|
|
256
|
-
rescue StandardError => e
|
|
257
|
-
warn "Warning: Failed to parse RXL #{rxl_path}: #{e.message}"
|
|
258
|
-
fallback_from_rxl(rxl_path)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
class << self
|
|
262
|
-
private
|
|
263
|
-
|
|
264
|
-
def build_from_bib(bib, rxl_path)
|
|
265
|
-
identifier = bib.docidentifier&.first&.content || ""
|
|
266
|
-
slug = slug_from_identifier(identifier)
|
|
267
|
-
output_dir = File.dirname(rxl_path)
|
|
268
|
-
base_name = File.basename(rxl_path, ".rxl")
|
|
269
|
-
|
|
270
|
-
new(
|
|
271
|
-
identifier: identifier, slug: slug,
|
|
272
|
-
title: bib.title&.first&.content || "",
|
|
273
|
-
edition: extract_edition(bib),
|
|
274
|
-
stage: extract_stage(bib),
|
|
275
|
-
doctype: extract_doctype(bib),
|
|
276
|
-
revdate: extract_revdate(bib),
|
|
277
|
-
files: discover_files(output_dir, base_name),
|
|
278
|
-
channels: [], source: nil
|
|
279
|
-
)
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def extract_edition(bib)
|
|
283
|
-
ed = bib.edition
|
|
284
|
-
return "1" unless ed
|
|
285
|
-
|
|
286
|
-
ed.respond_to?(:content) ? ed.content.to_s : ed.to_s
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def extract_stage(bib)
|
|
290
|
-
stage = bib.status&.stage
|
|
291
|
-
return "" unless stage
|
|
292
|
-
|
|
293
|
-
stage.respond_to?(:content) ? stage.content.to_s : stage.to_s
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
def extract_doctype(bib)
|
|
297
|
-
doctype = bib.ext&.doctype
|
|
298
|
-
return "" unless doctype
|
|
299
|
-
|
|
300
|
-
doctype.respond_to?(:content) ? doctype.content.to_s : doctype.to_s
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
def extract_revdate(bib)
|
|
304
|
-
date = bib.date&.find { |d| d.type == "published" } || bib.date&.first
|
|
305
|
-
return nil unless date
|
|
306
|
-
|
|
307
|
-
val = date.at
|
|
308
|
-
val ? val.to_s : nil
|
|
309
|
-
rescue StandardError
|
|
310
|
-
nil
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def discover_files(output_dir, base_name)
|
|
314
|
-
Dir.glob(File.join(output_dir, "#{base_name}.*")).filter_map do |path|
|
|
315
|
-
next if File.directory?(path)
|
|
316
|
-
|
|
317
|
-
name = File.basename(path)
|
|
318
|
-
ext = File.extname(name).delete_prefix(".")
|
|
319
|
-
PublicationFile.new(format: ext, name: name, path: name)
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
def fallback_from_rxl(rxl_path)
|
|
324
|
-
base_name = File.basename(rxl_path, ".rxl")
|
|
325
|
-
slug = slug_from_identifier(base_name)
|
|
326
|
-
new(
|
|
327
|
-
identifier: base_name, slug: slug, title: "",
|
|
328
|
-
edition: "0", stage: "", doctype: "",
|
|
329
|
-
revdate: nil, files: [], channels: [], source: nil
|
|
330
|
-
)
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
195
|
end
|
|
334
196
|
end
|
|
335
197
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Release
|
|
5
|
+
module PublicationSerializer
|
|
6
|
+
METADATA_COMMENT_PATTERN = /<!--\s*mn-release-metadata\s*\n(.*?)\n-->/m
|
|
7
|
+
|
|
8
|
+
def self.to_release_body(publication)
|
|
9
|
+
"<!-- mn-release-metadata\n#{JSON.generate(metadata_hash(publication))}\n-->"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.from_release_body(body)
|
|
13
|
+
return nil if body.nil? || body.empty?
|
|
14
|
+
|
|
15
|
+
match = body.match(METADATA_COMMENT_PATTERN)
|
|
16
|
+
return nil unless match
|
|
17
|
+
|
|
18
|
+
from_json(match[1])
|
|
19
|
+
rescue JSON::ParserError
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.to_json(publication)
|
|
24
|
+
JSON.generate(metadata_hash(publication))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_json(json_string)
|
|
28
|
+
data = JSON.parse(json_string)
|
|
29
|
+
raise ArgumentError, "Missing required field: id" unless data["id"]
|
|
30
|
+
unless data["title"]
|
|
31
|
+
raise ArgumentError,
|
|
32
|
+
"Missing required field: title"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Publication.from_metadata_hash(data)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def metadata_hash(publication)
|
|
42
|
+
{
|
|
43
|
+
"version" => Publication::METADATA_VERSION,
|
|
44
|
+
"id" => publication.slug,
|
|
45
|
+
"identifier" => publication.identifier,
|
|
46
|
+
"title" => publication.title,
|
|
47
|
+
"edition" => publication.edition,
|
|
48
|
+
"stage" => publication.stage,
|
|
49
|
+
"doctype" => publication.doctype,
|
|
50
|
+
"revdate" => publication.revdate,
|
|
51
|
+
"formats" => publication.formats,
|
|
52
|
+
"channels" => publication.channels,
|
|
53
|
+
"publisher" => SlugStrategy.publisher_from_identifier(publication.identifier),
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -15,6 +15,8 @@ module Metanorma
|
|
|
15
15
|
:manifest, :channel_override, :config,
|
|
16
16
|
keyword_init: true
|
|
17
17
|
) do
|
|
18
|
+
include DependencyValidation
|
|
19
|
+
|
|
18
20
|
def initialize(**kwargs)
|
|
19
21
|
super
|
|
20
22
|
validate_types!
|
|
@@ -23,9 +25,9 @@ module Metanorma
|
|
|
23
25
|
private
|
|
24
26
|
|
|
25
27
|
def validate_types!
|
|
26
|
-
unless extractor.
|
|
28
|
+
unless extractor.is_a?(Class) && extractor.singleton_class.ancestors.include?(Extractor)
|
|
27
29
|
raise ArgumentError,
|
|
28
|
-
"extractor must
|
|
30
|
+
"extractor must extend Extractor, got #{extractor}"
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
validate_interface!(change_detector, ChangeDetector,
|
|
@@ -33,21 +35,11 @@ module Metanorma
|
|
|
33
35
|
validate_interface!(packager, Packager, "packager")
|
|
34
36
|
validate_interface!(publisher, Publisher, "publisher")
|
|
35
37
|
end
|
|
36
|
-
|
|
37
|
-
def validate_interface!(obj, mod, name)
|
|
38
|
-
return if obj.is_a?(mod) || begin
|
|
39
|
-
obj.class.ancestors.include?(mod)
|
|
40
|
-
rescue StandardError
|
|
41
|
-
false
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
raise ArgumentError, "#{name} must include #{mod}, got #{obj.class}"
|
|
45
|
-
end
|
|
46
38
|
end
|
|
47
39
|
|
|
48
40
|
Config = Struct.new(
|
|
49
|
-
:output_dir, :
|
|
50
|
-
:concurrency,
|
|
41
|
+
:output_dir, :force, :force_replace_patterns,
|
|
42
|
+
:concurrency,
|
|
51
43
|
keyword_init: true
|
|
52
44
|
)
|
|
53
45
|
|
|
@@ -72,7 +64,7 @@ module Metanorma
|
|
|
72
64
|
|
|
73
65
|
def phase_one(publications, config)
|
|
74
66
|
publications.map do |pub|
|
|
75
|
-
publisher =
|
|
67
|
+
publisher = SlugStrategy.publisher_from_identifier(pub.identifier)
|
|
76
68
|
strategy = @deps.slug_registry.resolve(publisher)
|
|
77
69
|
tag_info = strategy.compute_tag(pub)
|
|
78
70
|
canonical_base = strategy.compute_asset_name(pub).sub(/\.zip$/, "")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Release
|
|
5
|
+
class RxlExtractor
|
|
6
|
+
extend Extractor
|
|
7
|
+
|
|
8
|
+
def self.discover(output_dir)
|
|
9
|
+
require "relaton/bib"
|
|
10
|
+
Dir.glob(File.join(output_dir, "**", "*.rxl")).filter_map do |path|
|
|
11
|
+
from_rxl(path)
|
|
12
|
+
rescue StandardError => e
|
|
13
|
+
Metanorma::Release.logger.warn "Skipping #{path}: #{e.message}"
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.from_rxl(rxl_path)
|
|
19
|
+
unless File.exist?(rxl_path)
|
|
20
|
+
raise ArgumentError,
|
|
21
|
+
"RXL file not found: #{rxl_path}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
content = File.read(rxl_path)
|
|
25
|
+
bib = Relaton::Bib::Item.from_xml(content)
|
|
26
|
+
build_from_bib(bib, rxl_path)
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
Metanorma::Release.logger.warn "Failed to parse RXL #{rxl_path}: #{e.message}"
|
|
29
|
+
fallback_from_rxl(rxl_path)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def build_from_bib(bib, rxl_path)
|
|
36
|
+
identifier = bib.docidentifier&.first&.content || ""
|
|
37
|
+
slug = SlugStrategy.slug_from_identifier(identifier)
|
|
38
|
+
output_dir = File.dirname(rxl_path)
|
|
39
|
+
base_name = File.basename(rxl_path, ".rxl")
|
|
40
|
+
|
|
41
|
+
Publication.new(
|
|
42
|
+
identifier: identifier, slug: slug,
|
|
43
|
+
title: bib.title&.first&.content || "",
|
|
44
|
+
edition: extract_edition(bib),
|
|
45
|
+
stage: extract_stage(bib),
|
|
46
|
+
doctype: extract_doctype(bib),
|
|
47
|
+
revdate: extract_revdate(bib),
|
|
48
|
+
files: discover_files(output_dir, base_name),
|
|
49
|
+
channels: [], source: nil
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def extract_edition(bib)
|
|
54
|
+
ed = bib.edition
|
|
55
|
+
return "1" unless ed
|
|
56
|
+
|
|
57
|
+
ed.is_a?(String) ? ed : ed.content.to_s
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def extract_stage(bib)
|
|
61
|
+
stage = bib.status&.stage
|
|
62
|
+
return "" unless stage
|
|
63
|
+
|
|
64
|
+
stage.is_a?(String) ? stage : stage.content.to_s
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def extract_doctype(bib)
|
|
68
|
+
doctype = bib.ext&.doctype
|
|
69
|
+
return "" unless doctype
|
|
70
|
+
|
|
71
|
+
doctype.is_a?(String) ? doctype : doctype.content.to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def extract_revdate(bib)
|
|
75
|
+
date = bib.date&.find { |d| d.type == "published" } || bib.date&.first
|
|
76
|
+
return nil unless date
|
|
77
|
+
|
|
78
|
+
val = date.at
|
|
79
|
+
val&.to_s
|
|
80
|
+
rescue StandardError
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def discover_files(output_dir, base_name)
|
|
85
|
+
Dir.glob(File.join(output_dir, "#{base_name}.*")).filter_map do |path|
|
|
86
|
+
next if File.directory?(path)
|
|
87
|
+
|
|
88
|
+
name = File.basename(path)
|
|
89
|
+
ext = File.extname(name).delete_prefix(".")
|
|
90
|
+
PublicationFile.new(format: ext, name: name, path: name)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def fallback_from_rxl(rxl_path)
|
|
95
|
+
base_name = File.basename(rxl_path, ".rxl")
|
|
96
|
+
slug = SlugStrategy.slug_from_identifier(base_name)
|
|
97
|
+
Publication.new(
|
|
98
|
+
identifier: base_name, slug: slug, title: "",
|
|
99
|
+
edition: "0", stage: "", doctype: "",
|
|
100
|
+
revdate: nil, files: [], channels: [], source: nil
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|