neu-mods 0.1.1 → 0.2.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/.version +1 -1
- data/README.md +15 -0
- data/lib/neu/mods/projection.rb +28 -0
- data/lib/neu/mods/selectors.rb +55 -0
- metadata +11 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb4c30744c189d3e0b4bfc0f84bb75175685bcb142dd6dc35dc55cfbcbdaf0a2
|
|
4
|
+
data.tar.gz: a5cd73f764edf2da97e05787788124bed168c9dba8caf997307fc53591f05661
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 785e3c7485886be0fd7946b61463e2e2b66319cf4c4f30980b6e4b2b0b8ed2ae4348be41a48caa9fbad536901a89e9c73d4d4fd005283bc2cc0dc9c9102d6ff6
|
|
7
|
+
data.tar.gz: 7a331ed220702dbab23bd6d16637f33a3da2f431a17939647ee7d1fd21898b9cf76a4594b26300eb01f8c9ad1b50d0cabcd298cdbff9c363a01a7f7bba6229a7
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0
|
data/README.md
CHANGED
|
@@ -44,8 +44,23 @@ NEU::MODS.compose_title(non_sort: "", title: "What's New",
|
|
|
44
44
|
node = doc.primary_title_info.at_xpath("mods:title", NEU::MODS::NAMESPACE)
|
|
45
45
|
node.content = "New Title" unless NEU::MODS.whitespace_equivalent?(node.text, "New Title")
|
|
46
46
|
doc.to_xml
|
|
47
|
+
|
|
48
|
+
# Editable creators (for an "advanced metadata" form): structured read,
|
|
49
|
+
# node selection (for replace-on-save), and structure-aware build.
|
|
50
|
+
doc.editable_personal_creators # => [{ given:, family: }] (plain, Creator role)
|
|
51
|
+
doc.editable_corporate_creators # => [{ name: }]
|
|
52
|
+
doc.preserved_names # => [{ name:, role: }] (authority-bearing / non-Creator — read-only)
|
|
53
|
+
doc.editable_creator_nodes("personal") # => live <name> nodes to replace
|
|
54
|
+
doc.build_personal_name(given: "Jenny", family: "Smith") # => a plain personal <name> node
|
|
55
|
+
doc.build_corporate_name(name: "Northeastern University") # => a plain corporate <name> node
|
|
47
56
|
```
|
|
48
57
|
|
|
58
|
+
The "editable creator" set is plain names — **no `@authority`/`@authorityURI`/
|
|
59
|
+
`@valueURI`** — with a **Creator** role; everything else (authority-controlled or
|
|
60
|
+
other-role names) is `preserved_names`, shown read-only. This mirrors the
|
|
61
|
+
keyword-subject curated-vs-editable split. `build_*_name`'s `role:` defaults to
|
|
62
|
+
`"Creator"` but is parameterised, so a later role-selectable form is non-breaking.
|
|
63
|
+
|
|
49
64
|
## Two normalizers, two jobs
|
|
50
65
|
|
|
51
66
|
- `NEU::MODS.whitespace_equivalent?` / `.canonical_ws` — the **no-op guard**: did an
|
data/lib/neu/mods/projection.rb
CHANGED
|
@@ -84,6 +84,28 @@ module NEU
|
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
# Editable (depositor-managed) creators: the plain names (no authority
|
|
88
|
+
# markers) with a Creator role, as STRUCTURED parts for form pre-fill --
|
|
89
|
+
# distinct from #names, which composes display strings for the access copy.
|
|
90
|
+
def editable_personal_creators
|
|
91
|
+
editable_creator_nodes("personal").map do |node|
|
|
92
|
+
{ given: clean_part(joined_parts(node, "given")), family: clean_part(joined_parts(node, "family")) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def editable_corporate_creators
|
|
97
|
+
editable_creator_nodes("corporate").map { |node| { name: clean_part(non_date_parts_joined(node)) } }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Names the editable form does NOT manage (authority-bearing or non-Creator)
|
|
101
|
+
# -- for read-only display ("these exist; edit via the XML tab"). Composed
|
|
102
|
+
# display string + role, like #names but filtered to the preserved set.
|
|
103
|
+
def preserved_names
|
|
104
|
+
doc.xpath("/mods:mods/mods:name", NAMESPACE)
|
|
105
|
+
.reject { |node| editable_creator_name?(node) }
|
|
106
|
+
.map { |node| { name: name_display_value_w_date(node), role: name_role(node) } }
|
|
107
|
+
end
|
|
108
|
+
|
|
87
109
|
# --- Scalars / simple arrays --------------------------------------------
|
|
88
110
|
|
|
89
111
|
def languages
|
|
@@ -184,6 +206,12 @@ module NEU
|
|
|
184
206
|
v.empty? ? nil : v
|
|
185
207
|
end
|
|
186
208
|
|
|
209
|
+
# canonical_ws keeping "" for blank -- for structured form-field values
|
|
210
|
+
# (an empty given/family/org renders as an empty input, not a dropped key).
|
|
211
|
+
def clean_part(str)
|
|
212
|
+
NEU::MODS.canonical_ws(str)
|
|
213
|
+
end
|
|
214
|
+
|
|
187
215
|
def join_paragraphs(nodes)
|
|
188
216
|
nodes.map { |n| NEU::MODS.normalize_paragraphs(n.text) }.reject(&:empty?).join("\n\n")
|
|
189
217
|
end
|
data/lib/neu/mods/selectors.rb
CHANGED
|
@@ -39,6 +39,39 @@ module NEU
|
|
|
39
39
|
node
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# The "editable creator" <name> nodes of a given @type ("personal" /
|
|
43
|
+
# "corporate") that the Advanced form manages: plain names (no authority
|
|
44
|
+
# markers) with a Creator role. The write-path counterpart to the
|
|
45
|
+
# editable_*_creators projections; everything else (authority-bearing or
|
|
46
|
+
# non-Creator) is curated and left untouched. Mirrors keyword_subjects.
|
|
47
|
+
def editable_creator_nodes(type)
|
|
48
|
+
doc.xpath("/mods:mods/mods:name[@type='#{type}']", NAMESPACE)
|
|
49
|
+
.select { |n| editable_creator_name?(n) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build a plain personal-creator <name> node: namePart[@type=given]/[family]
|
|
53
|
+
# + a text roleTerm. No authority/valueURI (the editable set). `role` is
|
|
54
|
+
# parameterised (default "Creator") so a later role-selectable form is a
|
|
55
|
+
# non-breaking change.
|
|
56
|
+
def build_personal_name(given:, family:, role: "Creator")
|
|
57
|
+
name = build_node("name")
|
|
58
|
+
name["type"] = "personal"
|
|
59
|
+
name.add_child(name_part(given, "given")) unless given.to_s.strip.empty?
|
|
60
|
+
name.add_child(name_part(family, "family")) unless family.to_s.strip.empty?
|
|
61
|
+
name.add_child(role_node(role))
|
|
62
|
+
name
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Build a plain corporate-creator <name> node: a single namePart + a text
|
|
66
|
+
# roleTerm. No authority/valueURI.
|
|
67
|
+
def build_corporate_name(name:, role: "Creator")
|
|
68
|
+
node = build_node("name")
|
|
69
|
+
node["type"] = "corporate"
|
|
70
|
+
node.add_child(name_part(name)) unless name.to_s.strip.empty?
|
|
71
|
+
node.add_child(role_node(role))
|
|
72
|
+
node
|
|
73
|
+
end
|
|
74
|
+
|
|
42
75
|
private
|
|
43
76
|
|
|
44
77
|
def keyword_subject?(subject)
|
|
@@ -47,6 +80,28 @@ module NEU
|
|
|
47
80
|
topics = subject.element_children
|
|
48
81
|
topics.any? && topics.all? { |c| c.name == "topic" }
|
|
49
82
|
end
|
|
83
|
+
|
|
84
|
+
# A name is "editable" (depositor-managed) when it carries no authority
|
|
85
|
+
# markers and resolves to a Creator role. Shared by editable_creator_nodes
|
|
86
|
+
# (write/select) and the editable_*_creators projections (read).
|
|
87
|
+
def editable_creator_name?(node)
|
|
88
|
+
%w[authority authorityURI valueURI].none? { |attr| node[attr] } &&
|
|
89
|
+
name_role(node) == "Creator"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def name_part(text, type = nil)
|
|
93
|
+
np = build_node("namePart", text.to_s.strip)
|
|
94
|
+
np["type"] = type if type
|
|
95
|
+
np
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def role_node(role)
|
|
99
|
+
role_el = build_node("role")
|
|
100
|
+
term = build_node("roleTerm", role)
|
|
101
|
+
term["type"] = "text"
|
|
102
|
+
role_el.add_child(term)
|
|
103
|
+
role_el
|
|
104
|
+
end
|
|
50
105
|
end
|
|
51
106
|
end
|
|
52
107
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neu-mods
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cliff
|
|
@@ -14,42 +14,42 @@ dependencies:
|
|
|
14
14
|
name: nokogiri
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '1.13'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.13'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rspec
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - ~>
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '3.12'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - ~>
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '3.12'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rubocop
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - ~>
|
|
45
|
+
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: '1.60'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - ~>
|
|
52
|
+
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '1.60'
|
|
55
55
|
description: 'Nokogiri-native, dependency-light reading/projection contract over MODS
|
|
@@ -62,7 +62,7 @@ executables: []
|
|
|
62
62
|
extensions: []
|
|
63
63
|
extra_rdoc_files: []
|
|
64
64
|
files:
|
|
65
|
-
- .version
|
|
65
|
+
- ".version"
|
|
66
66
|
- Gemfile
|
|
67
67
|
- README.md
|
|
68
68
|
- Rakefile
|
|
@@ -83,16 +83,16 @@ require_paths:
|
|
|
83
83
|
- lib
|
|
84
84
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
|
-
- -
|
|
86
|
+
- - ">="
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '3.0'
|
|
89
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
90
|
requirements:
|
|
91
|
-
- -
|
|
91
|
+
- - ">="
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
93
|
version: '0'
|
|
94
94
|
requirements: []
|
|
95
|
-
rubygems_version: 3.
|
|
95
|
+
rubygems_version: 3.4.10
|
|
96
96
|
signing_key:
|
|
97
97
|
specification_version: 4
|
|
98
98
|
summary: Northeastern-flavored MODS XML projection + selection for the DRS.
|