collectionspace-client 0.3.0 → 0.15.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/.git-blame-ignore-revs +8 -0
- data/.github/workflows/ci.yml +30 -0
- data/.github/workflows/publish.yml +42 -0
- data/.gitignore +6 -0
- data/.rubocop.yml +4 -8
- data/.ruby-version +1 -0
- data/Gemfile +3 -1
- data/README.md +11 -14
- data/Rakefile +44 -2
- data/bin/console +26 -0
- data/bin/rspec +29 -0
- data/collectionspace-client.gemspec +26 -23
- data/examples/batches.rb +50 -0
- data/examples/demo.rb +10 -8
- data/examples/media_with_external_file.rb +11 -9
- data/examples/purge_empty_vocabs.rb +10 -8
- data/examples/reports.rb +45 -0
- data/examples/reset_media_blob.rb +35 -0
- data/examples/search.rb +25 -12
- data/examples/update_password.rb +1 -31
- data/lib/collectionspace/client/batch.rb +55 -0
- data/lib/collectionspace/client/client.rb +19 -6
- data/lib/collectionspace/client/configuration.rb +16 -14
- data/lib/collectionspace/client/helpers.rb +197 -15
- data/lib/collectionspace/client/refname.rb +114 -0
- data/lib/collectionspace/client/report.rb +180 -0
- data/lib/collectionspace/client/request.rb +12 -9
- data/lib/collectionspace/client/response.rb +14 -3
- data/lib/collectionspace/client/search.rb +9 -5
- data/lib/collectionspace/client/service.rb +204 -0
- data/lib/collectionspace/client/template.rb +26 -0
- data/lib/collectionspace/client/templates/batch.xml.erb +18 -0
- data/lib/collectionspace/client/templates/reindex_by_csids.xml.erb +10 -0
- data/lib/collectionspace/client/templates/reindex_by_doctype.xml.erb +5 -0
- data/lib/collectionspace/client/templates/reindex_full_text.xml.erb +51 -0
- data/lib/collectionspace/client/templates/report.xml.erb +16 -0
- data/lib/collectionspace/client/templates/reset_media_blob.xml.erb +6 -0
- data/lib/collectionspace/client/version.rb +3 -1
- data/lib/collectionspace/client.rb +25 -12
- metadata +68 -6
data/examples/update_password.rb
CHANGED
@@ -1,33 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'awesome_print'
|
5
|
-
require 'base64'
|
6
|
-
require 'collectionspace/client'
|
7
|
-
|
8
|
-
CS_CFG_URL = ENV.fetch('CS_CFG_URL', 'https://core.dev.collectionspace.org/cspace-services')
|
9
|
-
CS_CFG_USER = ENV.fetch('CS_CFG_USER', 'admin@core.collectionspace.org')
|
10
|
-
CS_CFG_PASS = ENV.fetch('CS_CFG_PASS', 'Administrator')
|
11
|
-
CS_UPD_USER = ENV.fetch('CS_UPD_USER', 'admin@core.collectionspace.org')
|
12
|
-
CS_UPD_PASS = Base64.encode64(ENV.fetch('CS_UPD_PASS', 'Administrator')).chomp
|
13
|
-
|
14
|
-
client = CollectionSpace::Client.new(
|
15
|
-
CollectionSpace::Configuration.new(
|
16
|
-
base_uri: CS_CFG_URL,
|
17
|
-
username: CS_CFG_USER,
|
18
|
-
password: CS_CFG_PASS
|
19
|
-
)
|
20
|
-
)
|
21
|
-
|
22
|
-
PAYLOAD = <<~XML
|
23
|
-
<ns2:accounts_common xmlns:ns2="http://collectionspace.org/services/account">
|
24
|
-
<userId>#{CS_UPD_USER}</userId>
|
25
|
-
<password>#{CS_UPD_PASS}</password>
|
26
|
-
</ns2:accounts_common>
|
27
|
-
XML
|
28
|
-
|
29
|
-
client.all('accounts').each do |item|
|
30
|
-
next unless item['email'] == CS_UPD_USER
|
31
|
-
|
32
|
-
ap client.put(item['uri'], PAYLOAD).parsed
|
33
|
-
end
|
3
|
+
puts "Update password has been moved to Rake task: `cli:update_password[args...]`"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CollectionSpace
|
4
|
+
# CollectionSpace batch
|
5
|
+
class Batch
|
6
|
+
def self.all
|
7
|
+
[
|
8
|
+
{
|
9
|
+
name: "Update Current Location",
|
10
|
+
notes: "Recompute the current location of Object records, based on the " \
|
11
|
+
"related Location/Movement/Inventory records. Runs on a single record " \
|
12
|
+
"or all records.",
|
13
|
+
doctype: %w[CollectionObject],
|
14
|
+
supports_single_doc: "true",
|
15
|
+
supports_doc_list: "false",
|
16
|
+
supports_group: "false",
|
17
|
+
supports_no_context: "true",
|
18
|
+
creates_new_focus: "false",
|
19
|
+
classname:
|
20
|
+
"org.collectionspace.services.batch.nuxeo.UpdateObjectLocationBatchJob"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
name: "Update Inventory Status",
|
24
|
+
notes: "Set the inventory status of selected Object records. Runs on a " \
|
25
|
+
"record list only.",
|
26
|
+
doctype: %w[CollectionObject],
|
27
|
+
supports_single_doc: "false",
|
28
|
+
supports_doc_list: "true",
|
29
|
+
supports_group: "false",
|
30
|
+
supports_no_context: "false",
|
31
|
+
creates_new_focus: "false",
|
32
|
+
classname:
|
33
|
+
"org.collectionspace.services.batch.nuxeo.UpdateInventoryStatusBatchJob"
|
34
|
+
},
|
35
|
+
{
|
36
|
+
name: "Merge Authority Items",
|
37
|
+
notes: "Merge an authority item into a target, and update all " \
|
38
|
+
"referencing records. Runs on a single record only.",
|
39
|
+
doctype: %w[],
|
40
|
+
supports_single_doc: "true",
|
41
|
+
supports_doc_list: "false",
|
42
|
+
supports_group: "false",
|
43
|
+
supports_no_context: "false",
|
44
|
+
creates_new_focus: "false",
|
45
|
+
classname:
|
46
|
+
"org.collectionspace.services.batch.nuxeo.MergeAuthorityItemsBatchJob"
|
47
|
+
}
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.find(key, value)
|
52
|
+
all.find { |batch| batch[key] == value }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CollectionSpace
|
2
4
|
# CollectionSpace client
|
3
5
|
class Client
|
@@ -6,28 +8,39 @@ module CollectionSpace
|
|
6
8
|
|
7
9
|
def initialize(config = Configuration.new)
|
8
10
|
unless config.is_a? CollectionSpace::Configuration
|
9
|
-
raise CollectionSpace::ArgumentError,
|
11
|
+
raise CollectionSpace::ArgumentError, "Invalid configuration object"
|
10
12
|
end
|
11
13
|
|
12
14
|
@config = config
|
13
15
|
end
|
14
16
|
|
15
17
|
def get(path, options = {})
|
16
|
-
request
|
18
|
+
request "GET", path, options
|
17
19
|
end
|
18
20
|
|
19
21
|
def post(path, payload, options = {})
|
20
22
|
check_payload(payload)
|
21
|
-
request
|
23
|
+
request "POST", path, {body: payload}.merge(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_file(file, options = {})
|
27
|
+
file = File.expand_path(file)
|
28
|
+
raise ArgumentError, "cannot find file #{file}" unless File.exist? file
|
29
|
+
|
30
|
+
request "POST", "blobs", {
|
31
|
+
body: {
|
32
|
+
file: File.open(file)
|
33
|
+
}
|
34
|
+
}.merge(options)
|
22
35
|
end
|
23
36
|
|
24
|
-
def put(path, payload)
|
37
|
+
def put(path, payload, options = {})
|
25
38
|
check_payload(payload)
|
26
|
-
request
|
39
|
+
request "PUT", path, {body: payload}.merge(options)
|
27
40
|
end
|
28
41
|
|
29
42
|
def delete(path)
|
30
|
-
request
|
43
|
+
request "DELETE", path
|
31
44
|
end
|
32
45
|
|
33
46
|
private
|
@@ -1,25 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CollectionSpace
|
2
4
|
# CollectionSpace configuration
|
3
5
|
class Configuration
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
DEFAULTS = {
|
7
|
+
base_uri: nil,
|
8
|
+
username: nil,
|
9
|
+
password: nil,
|
10
|
+
page_size: 25,
|
11
|
+
include_deleted: false,
|
12
|
+
throttle: 0,
|
13
|
+
verbose: false,
|
14
|
+
verify_ssl: true
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_accessor :base_uri, :username, :password, :page_size, :include_deleted, :throttle, :verbose, :verify_ssl
|
15
18
|
|
16
19
|
def initialize(settings = {})
|
17
|
-
settings =
|
20
|
+
settings = DEFAULTS.merge(settings)
|
18
21
|
settings.each do |property, value|
|
19
|
-
next unless
|
22
|
+
next unless DEFAULTS.key?(property)
|
20
23
|
|
21
24
|
instance_variable_set("@#{property}", value)
|
22
|
-
self.class.send(:attr_accessor, property)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -1,6 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CollectionSpace
|
2
4
|
# Helper methods for client requests
|
3
5
|
module Helpers
|
6
|
+
# add / update batch job
|
7
|
+
def add_batch_job(name, template, data = {}, params = {pgSz: 100})
|
8
|
+
payload = Template.process(template, data)
|
9
|
+
response = get("batch", {query: params})
|
10
|
+
create_or_update(response, "batch", "name", name, payload)
|
11
|
+
end
|
12
|
+
|
13
|
+
# add / update batches and data updates
|
14
|
+
def add_batch(data = {}, params = {pgSz: 100})
|
15
|
+
payload = Template.process("batch", data)
|
16
|
+
response = get("batch", {query: params})
|
17
|
+
create_or_update(response, "batch", "name", data[:name], payload)
|
18
|
+
end
|
19
|
+
|
20
|
+
# add / update reports
|
21
|
+
def add_report(data = {}, params = {pgSz: 100})
|
22
|
+
payload = Template.process("report", data)
|
23
|
+
response = get("reports", {query: params})
|
24
|
+
create_or_update(response, "reports", "name", data[:name], payload)
|
25
|
+
end
|
26
|
+
|
27
|
+
# returns Array of authority doctypes for use in setting up batches
|
28
|
+
def authority_doctypes
|
29
|
+
response = get("/servicegroups/authority")
|
30
|
+
unless response.result.success?
|
31
|
+
raise CollectionSpace::RequestError, response.result.body
|
32
|
+
end
|
33
|
+
|
34
|
+
result = response.result.parsed_response
|
35
|
+
result.dig("document", "servicegroups_common", "hasDocTypes", "hasDocType")
|
36
|
+
end
|
37
|
+
|
4
38
|
# get ALL records at path by paging through record set
|
5
39
|
def all(path, options = {})
|
6
40
|
list_type, list_item = get_list_types(path)
|
@@ -8,12 +42,10 @@ module CollectionSpace
|
|
8
42
|
return [] unless iterations.positive?
|
9
43
|
|
10
44
|
Enumerator::Lazy.new(0...iterations) do |yielder, i|
|
11
|
-
response = request(
|
12
|
-
unless response.result.success?
|
13
|
-
raise CollectionSpace::RequestError, response.result.body
|
14
|
-
end
|
45
|
+
response = request("GET", path, options.merge(query: {pgNum: i}))
|
46
|
+
raise CollectionSpace::RequestError, response.result.body unless response.result.success?
|
15
47
|
|
16
|
-
items_in_page = response.parsed[list_type].fetch(
|
48
|
+
items_in_page = response.parsed[list_type].fetch("itemsInPage", 0).to_i
|
17
49
|
list_items = items_in_page.positive? ? response.parsed[list_type][list_item] : []
|
18
50
|
list_items = [list_items] if items_in_page == 1
|
19
51
|
|
@@ -23,25 +55,175 @@ module CollectionSpace
|
|
23
55
|
|
24
56
|
def count(path)
|
25
57
|
list_type, = get_list_types(path)
|
26
|
-
response
|
27
|
-
response.
|
58
|
+
response = request("GET", path, query: {pgNum: 0, pgSz: 1})
|
59
|
+
raise CollectionSpace::RequestError, response.result.body unless response.result.success?
|
60
|
+
|
61
|
+
response.parsed[list_type]["totalItems"].to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
# get the tenant domain from a system required top level authority (person)
|
65
|
+
def domain
|
66
|
+
path = "personauthorities"
|
67
|
+
response = request("GET", path, query: {pgNum: 0, pgSz: 1})
|
68
|
+
raise CollectionSpace::RequestError, response.result.body unless response.result.success?
|
69
|
+
|
70
|
+
refname = response.parsed.dig(*get_list_types(path), "refName")
|
71
|
+
CollectionSpace::RefName.parse(refname)[:domain]
|
72
|
+
end
|
73
|
+
|
74
|
+
# find procedure or object by type and id
|
75
|
+
# find authority/vocab term by type, subtype, and refname
|
76
|
+
def find(type:, value:, subtype: nil, field: nil, schema: "common", sort: nil, operator: "=")
|
77
|
+
service = CollectionSpace::Service.get(type: type, subtype: subtype)
|
78
|
+
field ||= service[:term] # this will be set if it is an authority or vocabulary, otherwise nil
|
79
|
+
field ||= service[:identifier]
|
80
|
+
search_args = CollectionSpace::Search.new.from_hash(
|
81
|
+
path: service[:path],
|
82
|
+
namespace: "#{service[:ns_prefix]}_#{schema}",
|
83
|
+
field: field,
|
84
|
+
expression: "#{operator} '#{value.gsub(/'/, "\\\\'")}'"
|
85
|
+
)
|
86
|
+
search(search_args, sortBy: CollectionSpace::Search::DEFAULT_SORT)
|
87
|
+
end
|
88
|
+
# rubocop:enable Metrics/ParameterLists
|
89
|
+
|
90
|
+
# @param subject_csid [String] to be searched as `sbj` value
|
91
|
+
# @param object_csid [String] to be searched as `obj` value
|
92
|
+
# @param rel_type [String<'affects', 'hasBroader'>, nil] to be searched as `prd` value
|
93
|
+
def find_relation(subject_csid:, object_csid:, rel_type: nil)
|
94
|
+
if rel_type
|
95
|
+
get("relations", query: {"sbj" => subject_csid, "obj" => object_csid, "prd" => rel_type})
|
96
|
+
else
|
97
|
+
warn(
|
98
|
+
"No rel_type specified, so multiple types of relations between #{subject_csid} and #{object_csid} may be returned",
|
99
|
+
uplevel: 1
|
100
|
+
)
|
101
|
+
get("relations", query: {"sbj" => subject_csid, "obj" => object_csid})
|
102
|
+
end
|
28
103
|
end
|
29
104
|
|
30
105
|
def get_list_types(path)
|
31
106
|
{
|
32
|
-
|
33
|
-
|
107
|
+
"accounts" => %w[accounts_common_list account_list_item],
|
108
|
+
"relations" => %w[relations_common_list relation_list_item]
|
34
109
|
}.fetch(path, %w[abstract_common_list list_item])
|
35
110
|
end
|
36
111
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
112
|
+
def reindex_full_text(doctype, csids = [])
|
113
|
+
if csids.any?
|
114
|
+
run_job(
|
115
|
+
"Reindex Full Text", :reindex_full_text, :reindex_by_csids, {doctype: doctype, csids: csids}
|
116
|
+
)
|
117
|
+
else
|
118
|
+
run_job(
|
119
|
+
"Reindex Full Text", :reindex_full_text, :reindex_by_doctype, {doctype: doctype}
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param id [String] media record's identificationNumber value
|
125
|
+
# @param url [String] blobUri value
|
126
|
+
# @param verbose [Boolean] whether to put brief report of outcome to STDOUT
|
127
|
+
# @param ensure_safe_url [Boolean] set to false if using FILE URIs or
|
128
|
+
# other non-HTTPS URIs
|
129
|
+
# @param delete_existing_blob [Boolean] set to false if you have already
|
130
|
+
# manually deleted blobs
|
131
|
+
def reset_media_blob(id:, url:, verbose: false,
|
132
|
+
ensure_safe_url: true,
|
133
|
+
delete_existing_blob: true)
|
134
|
+
if ensure_safe_url
|
135
|
+
unless URI.parse(url).instance_of? URI::HTTPS
|
136
|
+
raise CollectionSpace::ArgumentError, "Not a valid url #{url}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
response = find(type: "media", value: id, field: "identificationNumber")
|
141
|
+
unless response.result.success?
|
142
|
+
if verbose
|
143
|
+
puts "#{id}\tfailure\tAPI request error: #{response.result.body}"
|
144
|
+
else
|
145
|
+
raise CollectionSpace::RequestError, response.result.body
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
found = response.parsed
|
150
|
+
total = found["abstract_common_list"]["totalItems"].to_i
|
151
|
+
|
152
|
+
if total.zero?
|
153
|
+
msg = "Media #{id} not found"
|
154
|
+
if verbose
|
155
|
+
puts "#{id}\tfailure\t#{msg}"
|
156
|
+
else
|
157
|
+
raise CollectionSpace::NotFoundError, msg
|
158
|
+
end
|
159
|
+
elsif total > 1
|
160
|
+
msg = "Found multiple media records for #{id}"
|
161
|
+
if verbose
|
162
|
+
puts "#{id}\tfailure\t#{msg}"
|
163
|
+
else
|
164
|
+
raise CollectionSpace::DuplicateIdFound, msg
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
media_uri = found["abstract_common_list"]["list_item"]["uri"]
|
169
|
+
|
170
|
+
if delete_existing_blob
|
171
|
+
blob_csid = found["abstract_common_list"]["list_item"]["blobCsid"]
|
172
|
+
delete("/blobs/#{blob_csid}") if blob_csid
|
173
|
+
end
|
174
|
+
|
175
|
+
payload = Template.process(:reset_media_blob, {id: id})
|
176
|
+
response = put(media_uri, payload, query: {"blobUri" => url})
|
177
|
+
if verbose
|
178
|
+
if response.result.success?
|
179
|
+
puts "#{id}\tsuccess\t"
|
180
|
+
else
|
181
|
+
puts "#{id}\tfailure\t#{response.parsed}"
|
182
|
+
end
|
183
|
+
else
|
184
|
+
response
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def run_job(name, template, invoke_template, data = {})
|
189
|
+
payload = Template.process(invoke_template, data)
|
190
|
+
job = add_batch_job(name, template)
|
191
|
+
path = job.parsed["document"]["collectionspace_core"]["uri"]
|
192
|
+
post(path, payload)
|
193
|
+
end
|
194
|
+
|
195
|
+
def search(query, params = {})
|
196
|
+
options = prepare_query(query, params)
|
197
|
+
request "GET", query.path, options
|
198
|
+
end
|
199
|
+
|
200
|
+
def keyword_search(type:, value:, subtype: nil, sort: nil)
|
201
|
+
service = CollectionSpace::Service.get(type: type, subtype: subtype)
|
202
|
+
options = prepare_keyword_query(value, {sortBy: CollectionSpace::Search::DEFAULT_SORT})
|
203
|
+
request "GET", service[:path], options
|
204
|
+
end
|
205
|
+
|
206
|
+
def service(type:, subtype: "")
|
207
|
+
CollectionSpace::Service.get(type: type, subtype: subtype)
|
208
|
+
end
|
209
|
+
|
210
|
+
private
|
211
|
+
|
212
|
+
def create_or_update(response, path, property, value, payload)
|
213
|
+
list_type, item_type = get_list_types(path)
|
214
|
+
item = response.find(list_type, item_type, property, value)
|
215
|
+
path = item ? "#{path}/#{item["csid"]}" : path
|
216
|
+
item ? put(path, payload) : post(path, payload)
|
217
|
+
end
|
218
|
+
|
219
|
+
def prepare_query(query, params = {})
|
220
|
+
query_string = "#{query.namespace}:#{query.field} #{query.expression}"
|
221
|
+
{query: {as: query_string}.merge(params)}
|
40
222
|
end
|
41
223
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
224
|
+
def prepare_keyword_query(query, sort = {})
|
225
|
+
query_string = query.downcase.tr(" ", "+")
|
226
|
+
{query: {kw: query_string}.merge(sort)}
|
45
227
|
end
|
46
228
|
end
|
47
229
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
|
5
|
+
module CollectionSpace
|
6
|
+
# CollectionSpace RefName
|
7
|
+
#
|
8
|
+
# There are four patterns we need to handle:
|
9
|
+
#
|
10
|
+
# - urn:cspace:domain:type:name(subtype)'label' : Top level authority/vocabulary
|
11
|
+
# - urn:cspace:domain:type:name(subtype):item:name(identifier)'label' : Authority/vocabulary term
|
12
|
+
# - urn:cspace:domain:type:id(identifier)'label' : Collectionobject
|
13
|
+
# - urn:cspace:domain:type:id(identifier) : Procedures, relations, blobs
|
14
|
+
class RefName
|
15
|
+
attr_reader :domain, :type, :subtype, :identifier, :label
|
16
|
+
|
17
|
+
def initialize(refname)
|
18
|
+
@refname = refname
|
19
|
+
@domain = nil
|
20
|
+
@type = nil
|
21
|
+
@subtype = nil
|
22
|
+
@identifier = nil
|
23
|
+
@label = nil
|
24
|
+
parse
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse
|
28
|
+
scanner = StringScanner.new(@refname)
|
29
|
+
scanner.skip("urn:cspace:")
|
30
|
+
@domain = to_next_colon(scanner)
|
31
|
+
@type = to_next_colon(scanner)
|
32
|
+
|
33
|
+
case next_segment(scanner)
|
34
|
+
when "name"
|
35
|
+
set_subtype(scanner)
|
36
|
+
when "id"
|
37
|
+
set_identifier(scanner)
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convenience class method, so new instance of RefName does not have to be instantiated in order to parse
|
44
|
+
#
|
45
|
+
# As of v0.13.1, return_class is added and defaults to nil for backward compatibility
|
46
|
+
# Eventually this default will be deprecated, and a parsed RefName object will be returned as the default.
|
47
|
+
# Any new code written using this method should set the return_class parameter to :refname_obj
|
48
|
+
def self.parse(refname, return_class = nil)
|
49
|
+
(return_class == :refname_obj) ? new(refname) : new(refname).to_h
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a parsed RefName object as a hash.
|
53
|
+
# As of v0.13.1, this is equivalent to calling RefName.parse('refnamevalue', :hash)
|
54
|
+
# This was added to simplify the process of updating existing code that expects a hash when calling RefName.parse
|
55
|
+
def to_h
|
56
|
+
{
|
57
|
+
domain: domain,
|
58
|
+
type: type,
|
59
|
+
subtype: subtype,
|
60
|
+
identifier: identifier,
|
61
|
+
label: label
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def next_segment(scanner)
|
68
|
+
segment = scanner.check_until(/\(/)
|
69
|
+
return nil unless segment
|
70
|
+
|
71
|
+
segment.delete_suffix("(")
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_identifier(scanner)
|
75
|
+
scanner.skip("id(")
|
76
|
+
@identifier = to_end_paren(scanner)
|
77
|
+
return if scanner.eos?
|
78
|
+
|
79
|
+
set_label(scanner)
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_label(scanner)
|
83
|
+
scanner.skip("'")
|
84
|
+
@label = scanner.rest.delete_suffix("'")
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_subtype(scanner)
|
88
|
+
scanner.skip("name(")
|
89
|
+
@subtype = to_end_paren(scanner)
|
90
|
+
|
91
|
+
case next_segment(scanner)
|
92
|
+
when nil
|
93
|
+
set_label(scanner)
|
94
|
+
when ":item:name"
|
95
|
+
set_term_identifier(scanner)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_term_identifier(scanner)
|
100
|
+
scanner.skip(":item:name(")
|
101
|
+
@identifier = to_end_paren(scanner)
|
102
|
+
scanner.skip("'")
|
103
|
+
set_label(scanner)
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_end_paren(scanner)
|
107
|
+
scanner.scan_until(/\)/).delete_suffix(")")
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_next_colon(scanner)
|
111
|
+
scanner.scan_until(/:/).delete_suffix(":")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|