relaton-cli 1.20.5 → 2.0.0.pre.alpha.1

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.
@@ -13,7 +13,7 @@ module Relaton
13
13
  end
14
14
 
15
15
  def docidentifier
16
- @bibitem.docidentifier.first&.id
16
+ @bibitem.docidentifier.first&.content&.to_s
17
17
  end
18
18
 
19
19
  # def doctype
@@ -50,10 +50,9 @@ module Relaton
50
50
  end
51
51
 
52
52
  def to_h
53
- URL_TYPES.reduce(@bibitem.to_hash) do |h, t|
53
+ URL_TYPES.each_with_object(YAML.safe_load(@bibitem.to_yaml)) do |t, h|
54
54
  value = send t
55
- h[t.to_s] = value
56
- h
55
+ h[t.to_s] = value if value
57
56
  end
58
57
  end
59
58
 
@@ -68,19 +67,20 @@ module Relaton
68
67
  if @bibitem.respond_to?(meth)
69
68
  @bibitem.send meth, *args
70
69
  elsif URL_TYPES.include? meth
71
- link = @bibitem.link.detect do |l|
70
+ source = (@bibitem.source || []).detect do |l|
72
71
  l.type == meth.to_s || (meth == :uri && l.type.nil?)
73
72
  end
74
- link&.content&.to_s
73
+ source&.content&.to_s
75
74
  elsif URL_TYPES.include? meth.match(/^\w+(?==)/).to_s.to_sym
76
75
  /^(?<type>\w+)/ =~ meth
77
- link = @bibitem.link.detect do |l|
76
+ @bibitem.source ||= []
77
+ source = @bibitem.source.detect do |l|
78
78
  l.type == type || (type == "uri" && l.type.nil?)
79
79
  end
80
- if link
81
- link.content = args[0]
80
+ if source
81
+ source.content = args[0]
82
82
  else
83
- @bibitem.link << RelatonBib::TypedUri.new(type: type, content: args[0])
83
+ @bibitem.source << Relaton::Bib::Uri.new(type: type, content: args[0])
84
84
  end
85
85
  else
86
86
  super
@@ -1,3 +1,4 @@
1
+ require "date"
1
2
  require "relaton/cli/relaton_file"
2
3
  require "relaton/cli/xml_convertor"
3
4
  require "relaton/cli/yaml_convertor"
@@ -31,6 +32,12 @@ module Relaton
31
32
  option :retries, aliases: :r, type: :numeric,
32
33
  desc: "Number of network retries. Default 1."
33
34
  option :"no-cache", type: :boolean, desc: "Ignore cache"
35
+ option :"publication-date-before",
36
+ desc: "Fetch only documents published before the specified date " \
37
+ "(e.g. 2008, 2008-02, or 2008-02-02)"
38
+ option :"publication-date-after",
39
+ desc: "Fetch only documents published after the specified date " \
40
+ "(e.g. 2002, 2002-01, or 2002-01-01)"
34
41
 
35
42
  def fetch(code)
36
43
  io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
@@ -133,7 +140,7 @@ module Relaton
133
140
  xml = Nokogiri::XML(File.read(file, encoding: "UTF-8"))
134
141
  item = Relaton::Cli.parse_xml xml
135
142
  result = if /yaml|yml/.match?(options[:format])
136
- item.to_hash.to_yaml
143
+ item.to_yaml
137
144
  else item.send "to_#{options[:format]}"
138
145
  end
139
146
  ext = case options[:format]
@@ -174,6 +181,30 @@ module Relaton
174
181
 
175
182
  private
176
183
 
184
+ DATE_FILTER_FORMAT = /\A\d{4}(-\d{2}(-\d{2})?)?\z/
185
+
186
+ def parse_date_option(value, name)
187
+ return unless value
188
+
189
+ unless value.match?(DATE_FILTER_FORMAT)
190
+ raise ArgumentError,
191
+ "Invalid #{name}: #{value.inspect}. Expected YYYY, YYYY-MM, or YYYY-MM-DD."
192
+ end
193
+ parts = value.split("-").map(&:to_i)
194
+ Date.new(*parts.concat([1] * (3 - parts.size)))
195
+ rescue Date::Error
196
+ raise ArgumentError,
197
+ "Invalid #{name}: #{value.inspect}. Date components are out of range."
198
+ end
199
+
200
+ def validate_date_range(date_after, date_before)
201
+ return unless date_after && date_before
202
+ return if date_after < date_before
203
+
204
+ raise ArgumentError,
205
+ "Invalid date range: --publication-date-after (#{date_after}) must be before --publication-date-before (#{date_before})."
206
+ end
207
+
177
208
  # @param code [String]
178
209
  # @param options [Hash]
179
210
  # @option options [String] :type
@@ -183,6 +214,10 @@ module Relaton
183
214
  def fetch_document(code, options) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize,Metrics/MethodLength
184
215
  year = options[:year]&.to_s
185
216
  dup_opts = options.dup.transform_keys { |k| k.to_s.gsub("-", "_").to_sym }
217
+ %i[publication_date_before publication_date_after].each do |key|
218
+ dup_opts[key] = parse_date_option(dup_opts[key], key.to_s.tr("_", "-").prepend("--")) if dup_opts[key]
219
+ end
220
+ validate_date_range dup_opts[:publication_date_after], dup_opts[:publication_date_before]
186
221
  if (processor = Relaton::Registry.instance.by_type options[:type]&.upcase)
187
222
  doc = Relaton.db.fetch_std code, year, processor.short, **dup_opts
188
223
  elsif options[:type] then return
@@ -191,16 +226,16 @@ module Relaton
191
226
  return "No matching bibliographic entry found" unless doc
192
227
 
193
228
  serialize doc, options[:format]
194
- rescue RelatonBib::RequestError => e
229
+ rescue Relaton::RequestError => e
195
230
  e.message
196
231
  end
197
232
 
198
- # @param doc [RelatonBib::BibliographicItem]
233
+ # @param doc [Relaton::Bib::ItemData]
199
234
  # @param format [String]
200
235
  # @return [String]
201
236
  def serialize(doc, format)
202
237
  case format
203
- when "yaml", "yml" then doc.to_hash.to_yaml
238
+ when "yaml", "yml" then doc.to_yaml
204
239
  when "bibtex" then doc.to_bibtex
205
240
  else doc.to_xml bibdata: true
206
241
  end
@@ -119,15 +119,15 @@ module Relaton
119
119
  if (bib = xml.at("//bibdata"))
120
120
  bib = nokogiri_document(bib.to_xml)
121
121
  elsif (rfc = xml.at("//rfc"))
122
- require "relaton_ietf/bibxml_parser"
123
- ietf = RelatonIetf::BibXMLParser.fetch_rfc rfc
122
+ require "relaton/ietf"
123
+ require "relaton/ietf/bibxml_parser"
124
+ ietf = Relaton::Ietf::BibXMLParser.parse_rfc rfc.to_xml
124
125
  bib = nokogiri_document(ietf.to_xml(bibdata: true))
125
126
  else
126
127
  next
127
128
  end
128
129
 
129
130
  bib.remove_namespaces!
130
- bib.root.add_namespace(nil, "xmlns")
131
131
 
132
132
  bibdata = Relaton::Bibdata.from_xml(bib.root)
133
133
  if bibdata
@@ -147,8 +147,9 @@ module Relaton
147
147
  xml_files.flatten.reduce([]) do |mem, xml|
148
148
  doc = nokogiri_document(xml[:content])
149
149
  if (rfc = doc.at("/rfc"))
150
- require "relaton_ietf/bibxml_parser"
151
- ietf = RelatonIetf::BibXMLParser.fetch_rfc rfc
150
+ require "relaton/ietf"
151
+ require "relaton/ietf/bibxml_parser"
152
+ ietf = Relaton::Ietf::BibXMLParser.parse_rfc rfc.to_xml
152
153
  d = nokogiri_document ietf.to_xml(bibdata: true)
153
154
  mem << bibdata_instance(d, xml[:file])
154
155
  elsif %w[bibitem bibdata].include? doc&.root&.name
@@ -231,7 +232,6 @@ module Relaton
231
232
  # @return [Nokogiri::XML::Document]
232
233
  def clean_nokogiri_document(document)
233
234
  document.remove_namespaces!
234
- document.root.add_namespace(nil, "xmlns")
235
235
  nokogiri_document(document.to_xml)
236
236
  end
237
237
 
@@ -102,13 +102,27 @@ module Relaton
102
102
  desc: "Type of standard to get bibliographic entry for"
103
103
  option :year, aliases: :y, type: :numeric,
104
104
  desc: "Year the standard was published"
105
+ option :"publication-date-before",
106
+ desc: "Fetch only documents published before the specified date " \
107
+ "(e.g. 2008, 2008-02, or 2008-02-02)"
108
+ option :"publication-date-after",
109
+ desc: "Fetch only documents published after the specified date " \
110
+ "(e.g. 2002, 2002-01, or 2002-01-01)"
105
111
  option :collection, aliases: :c, required: true,
106
112
  desc: "Collection to store a document"
107
113
  option :dir, aliases: :d, desc: "Directory with collections. Default is " \
108
114
  "$HOME/.relaton/collections."
109
115
 
110
116
  def fetch(code) # rubocop:disable Metrics/AbcSize
111
- doc = Relaton.db.fetch(code, options[:year]&.to_s)
117
+ opts = {}
118
+ if options[:"publication-date-before"]
119
+ opts[:publication_date_before] = parse_date_option(options[:"publication-date-before"], "--publication-date-before")
120
+ end
121
+ if options[:"publication-date-after"]
122
+ opts[:publication_date_after] = parse_date_option(options[:"publication-date-after"], "--publication-date-after")
123
+ end
124
+ validate_date_range opts[:publication_date_after], opts[:publication_date_before]
125
+ doc = Relaton.db.fetch(code, options[:year]&.to_s, **opts)
112
126
  if doc
113
127
  colfile = File.join directory, options[:collection]
114
128
  coll = read_collection colfile
@@ -130,11 +144,13 @@ module Relaton
130
144
  collfile = File.join directory, options[:collection]
131
145
  coll = read_collection collfile
132
146
  xml = Nokogiri::XML File.read(file, encoding: "UTF-8")
147
+ xml.remove_namespaces!
133
148
  if xml.at "relaton-collection"
149
+ imported = import_collection(xml)
134
150
  if coll
135
- coll << Relaton::Bibcollection.from_xml(xml)
151
+ coll << imported
136
152
  else
137
- coll = Relaton::Bibcollection.from_xml(xml)
153
+ coll = imported
138
154
  end
139
155
  else
140
156
  coll ||= Relaton::Bibcollection.new({})
@@ -155,6 +171,22 @@ module Relaton
155
171
 
156
172
  private
157
173
 
174
+ # Parse a namespace-free collection XML document.
175
+ # Bibcollection.from_xml expects namespaced docs (via apply_namespace),
176
+ # so we use plain XPath on the namespace-stripped document instead.
177
+ def import_collection(xml)
178
+ title = xml.at("relaton-collection/title")&.text
179
+ author = xml.at_xpath(
180
+ "./relaton-collection/contributor[role/@type='author']"\
181
+ "/organization/name",
182
+ )&.text
183
+ items = xml.xpath("relaton-collection/relation").map do |rel|
184
+ el = rel.at("bibdata") || rel.at("bibitem")
185
+ Relaton::Bibdata.from_xml(el || rel)
186
+ end.compact
187
+ Relaton::Bibcollection.new(title: title, author: author, items: items)
188
+ end
189
+
158
190
  # @return [String]
159
191
  def directory
160
192
  options.fetch :dir, File.join(Dir.home, ".relaton/collections")
@@ -217,9 +249,9 @@ module Relaton
217
249
  # @param item [Relaton::Bibdata]
218
250
  def puts_human_readable_item(item) # rubocop:disable Metrics/AbcSize
219
251
  puts "Document identifier: #{item.docidentifier}"
220
- puts "Title: #{item.title.first.title.content}"
252
+ puts "Title: #{item.title.first.content}"
221
253
  puts "Status: #{item.status.stage}"
222
- item.date.each { |d| puts "Date #{d.type}: #{d.on || d.from}" }
254
+ item.date.each { |d| puts "Date #{d.type}: #{d.at || d.from}" }
223
255
  end
224
256
  end
225
257
  end
@@ -40,6 +40,12 @@ module Relaton
40
40
  "Default xml."
41
41
  option :year, aliases: :y, type: :numeric, desc: "Year the standard " \
42
42
  "was published"
43
+ option :"publication-date-before",
44
+ desc: "Fetch only documents published before the specified date " \
45
+ "(e.g. 2008, 2008-02, or 2008-02-02)"
46
+ option :"publication-date-after",
47
+ desc: "Fetch only documents published after the specified date " \
48
+ "(e.g. 2002, 2002-01, or 2002-01-01)"
43
49
 
44
50
  def fetch(code)
45
51
  io = IO.new($stdout.fcntl(::Fcntl::F_DUPFD), mode: "w:UTF-8")
@@ -1,7 +1,7 @@
1
1
  module Relaton
2
2
  module Cli
3
3
  module Util
4
- extend RelatonBib::Util
4
+ extend Relaton::Bib::Util
5
5
  PROGNAME = "relaton-cli".freeze
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
1
  module Relaton
2
2
  module Cli
3
- VERSION = "1.20.5".freeze
3
+ VERSION = "2.0.0-alpha.1".freeze
4
4
  end
5
5
  end
@@ -90,12 +90,13 @@ module Relaton::Cli
90
90
  elsif value.is_a?(Hash) then v = value["content"]
91
91
  else v = value
92
92
  end
93
- when "docid"
94
- v = if value.is_a?(Array)
95
- value.detect { |did| did["id"] !~ /^(http|https):\/\// } ||
96
- value.first
97
- else value
98
- end
93
+ when "docidentifier"
94
+ did = if value.is_a?(Array)
95
+ value.detect { |d| (d["id"] || d["content"]).to_s !~ /^(http|https):\/\// } ||
96
+ value.first
97
+ else value
98
+ end
99
+ v = did
99
100
  else v = value
100
101
  end
101
102
  [key.to_s, empty2nil(v)]
@@ -1,6 +1,6 @@
1
1
  require "yaml"
2
2
  require "relaton/cli/base_convertor"
3
- require "relaton_bib"
3
+ require "relaton/bib"
4
4
 
5
5
  module Relaton
6
6
  module Cli
@@ -33,13 +33,15 @@ module Relaton
33
33
  # @return [RelatonBib::BibliographicItem,
34
34
  # RelatonIso::IsoBiblioraphicItem]
35
35
  def convert_single_file(content)
36
- flavor = content.dig("ext", "flavor") || doctype(content["docid"])
36
+ flavor = content.dig("ext", "flavor") || doctype(content["docidentifier"])
37
37
  if (processor = Registry.instance.by_type(flavor))
38
- processor.hash_to_bib content
38
+ begin
39
+ processor.from_yaml content.to_yaml
40
+ rescue RuntimeError
41
+ Relaton::Bib::Item.from_yaml(content.to_yaml)
42
+ end
39
43
  else
40
- RelatonBib::BibliographicItem.new(
41
- **RelatonBib::HashConverter::hash_to_bib(content)
42
- )
44
+ Relaton::Bib::Item.from_yaml(content.to_yaml)
43
45
  end
44
46
  end
45
47
 
@@ -51,7 +53,7 @@ module Relaton
51
53
  did = docid.is_a?(Array) ? docid.fetch(0) : docid
52
54
  return unless did
53
55
 
54
- did["type"] || did.fetch("id")&.match(/^\w+/)&.to_s
56
+ did["type"] || did.fetch("content")&.match(/^\w+/)&.to_s
55
57
  end
56
58
  end
57
59
 
data/lib/relaton/cli.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "fileutils"
1
2
  require "thor"
2
3
  require "thor/hollaback"
3
4
  require_relative "cli/version"
@@ -19,6 +20,7 @@ module Relaton
19
20
  # @return [Relaton::Db]
20
21
  def db(dir)
21
22
  if dir
23
+ FileUtils.mkdir_p File.dirname(DBCONF)
22
24
  File.write DBCONF, dir, encoding: "UTF-8"
23
25
  @db = Relaton::Db.new dir, nil
24
26
  else
@@ -39,19 +41,14 @@ module Relaton
39
41
 
40
42
  class << self
41
43
  def version
42
- require "relaton_bib"
43
- require "relaton_iso_bib"
44
+ require "relaton/bib"
44
45
  registry = Relaton::Registry.instance
45
46
  puts "CLI => #{Relaton::Cli::VERSION}"
46
- puts "relaton => #{Relaton::VERSION}"
47
- puts "relaton-bib => #{RelatonBib::VERSION}"
48
- puts "relaton-iso-bib => #{RelatonIsoBib::VERSION}"
47
+ puts "relaton => #{Gem.loaded_specs['relaton'].version}"
48
+ puts "relaton-bib => #{Gem.loaded_specs['relaton-bib'].version}"
49
49
  registry.processors.each_key do |k|
50
- require k.to_s
51
- klass = registry.send(:camel_case, k.to_s)
52
- klass = "#{klass}::VERSION"
53
- version = Kernel.const_get(klass)
54
- puts "#{k.to_s.sub('_', '-')} => #{version}"
50
+ name = k.to_s.sub("_", "-")
51
+ puts "#{name} => #{Gem.loaded_specs[name].version}"
55
52
  end
56
53
  end
57
54
 
@@ -76,10 +73,11 @@ module Relaton
76
73
  # @return [RelatonBib::BibliographicItem,
77
74
  # RelatonIsoBib::IsoBibliongraphicItem]
78
75
  def parse_xml(doc)
76
+ doc.remove_namespaces! if doc.respond_to?(:remove_namespaces!)
79
77
  if (proc = Cli.processor(doc))
80
78
  proc.from_xml(doc.to_s)
81
79
  else
82
- RelatonBib::XMLParser.from_xml(doc.to_s)
80
+ Relaton::Bib::Item.from_xml(doc.to_s) rescue nil
83
81
  end
84
82
  end
85
83
 
data/relaton-cli.gemspec CHANGED
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.bindir = "exe"
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
- spec.required_ruby_version = ">= 3.0.0"
23
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
24
24
 
25
25
  spec.add_runtime_dependency "liquid", "~> 5"
26
- spec.add_runtime_dependency "relaton", "~> 1.20.0"
26
+ spec.add_runtime_dependency "relaton", "~> 2.0.0-alpha.1"
27
27
  spec.add_runtime_dependency "thor"
28
28
  spec.add_runtime_dependency "thor-hollaback"
29
29
 
@@ -20,16 +20,16 @@
20
20
  <div class="doc-identifier">
21
21
  <h{{ depth }}>
22
22
  {% if default_link %}
23
- <a href="{{ default_link }}">{{ document.docid.id }}</a>
23
+ <a href="{{ default_link }}">{{ document.docidentifier.content }}</a>
24
24
  {% else %}
25
- {{ document.docid.id }}
25
+ {{ document.docidentifier.content }}
26
26
  {% endif %}
27
27
  </h{{ depth }}>
28
28
  </div>
29
29
 
30
30
  <div class="doc-type-wrap">
31
- <div class="doc-type {{ document.doctype.type | downcase | split: " " | join: "-" }}">
32
- {{ document.doctype.type }}
31
+ <div class="doc-type {{ document.ext.doctype.content | downcase | split: " " | join: "-" }}">
32
+ {{ document.ext.doctype.content }}
33
33
  </div>
34
34
  </div>
35
35
 
@@ -50,18 +50,17 @@
50
50
  </h{{ depth | plus: 1 }}>
51
51
  </div>
52
52
 
53
- {% if document.docstatus %}
54
- <div class="doc-info {{ document.docstatus.stage.value | downcase }}">
55
- <div class="doc-stage {{ document.docstatus.stage.value | downcase }}">
56
- {{ document.docstatus.stage.abbreviation }}
57
- {% if document.docstatus.substage %}
58
- {{ document.docstatus.stage.value }}.{{ document.docstatus.substage.value }}
53
+ {% if document.status %}
54
+ <div class="doc-info {{ document.status.stage.content | downcase }}">
55
+ <div class="doc-stage {{ document.status.stage.content | downcase }}">
56
+ {% if document.status.substage %}
57
+ {{ document.status.stage.content }}.{{ document.status.substage.content }}
59
58
  {% else %}
60
- {{ document.docstatus.stage.value }}
59
+ {{ document.status.stage.content }}
61
60
  {% endif %}
62
61
  </div>
63
62
  <div class="doc-dates">
64
- {% unless document.docstatus.stage.value == "published" %}
63
+ {% unless document.status.stage.content == "published" %}
65
64
  <div class="doc-updated">
66
65
  {% else %}
67
66
  <div class="doc-published">
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaton-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.20.5
4
+ version: 2.0.0.pre.alpha.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-11-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: liquid
@@ -30,14 +29,14 @@ dependencies:
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: 1.20.0
32
+ version: 2.0.0.pre.alpha.1
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: 1.20.0
39
+ version: 2.0.0.pre.alpha.1
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: thor
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -87,8 +86,8 @@ executables:
87
86
  - relaton
88
87
  extensions: []
89
88
  extra_rdoc_files:
90
- - docs/README.adoc
91
89
  - LICENSE
90
+ - docs/README.adoc
92
91
  files:
93
92
  - ".github/workflows/rake.yml"
94
93
  - ".github/workflows/release.yml"
@@ -97,6 +96,7 @@ files:
97
96
  - ".rspec"
98
97
  - ".rubocop.yml"
99
98
  - CHANGELOG.md
99
+ - CLAUDE.md
100
100
  - Gemfile
101
101
  - LICENSE
102
102
  - Rakefile
@@ -131,7 +131,6 @@ homepage: https://github.com/metanorma/relaton-cli
131
131
  licenses:
132
132
  - BSD-2-Clause
133
133
  metadata: {}
134
- post_install_message:
135
134
  rdoc_options: []
136
135
  require_paths:
137
136
  - lib
@@ -139,15 +138,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
138
  requirements:
140
139
  - - ">="
141
140
  - !ruby/object:Gem::Version
142
- version: 3.0.0
141
+ version: 3.2.0
143
142
  required_rubygems_version: !ruby/object:Gem::Requirement
144
143
  requirements:
145
144
  - - ">="
146
145
  - !ruby/object:Gem::Version
147
146
  version: '0'
148
147
  requirements: []
149
- rubygems_version: 3.5.22
150
- signing_key:
148
+ rubygems_version: 3.6.9
151
149
  specification_version: 4
152
150
  summary: Relaton Command-line Interface
153
151
  test_files: []