mods 0.0.2 → 0.0.4

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.
@@ -1,10 +1,12 @@
1
1
  = Mods
2
2
 
3
- A Ruby gem to parse MODS (Metadata Object Description Schema) records. More information about MODS can be found at http://www.loc.gov/standards/mods/registry.php.
3
+ {<img src="https://secure.travis-ci.org/sul-dlss/mods.png?branch=master" alt="Build Status" />}[http://travis-ci.org/sul-dlss/mods]
4
+
5
+ A Gem to parse MODS (Metadata Object Description Schema) records. More information about MODS can be found at http://www.loc.gov/standards/mods/registry.php.
4
6
 
5
7
  Source code at https://github.com/sul-dlss/mods/
6
8
 
7
- Generated API docs at http://rubydoc.info/gems/mods/
9
+ Generated API docs at http://rubydoc.info/github/sul-dlss/mods/
8
10
 
9
11
  == Installation
10
12
 
@@ -35,5 +37,7 @@ TODO: Write usage instructions here
35
37
 
36
38
  == Releases
37
39
 
40
+ 0.0.4 implement language, location, origin_info, physical_description
41
+ 0.0.3 use nom-xml gem and make this more nokogiri-ish; implement name, title, and simple top level elements with no subelements
38
42
  0.0.2 Set up rake tasks, publishing rdoc, and continuous integration.
39
43
  0.0.1 Grab the name
@@ -1,5 +1,15 @@
1
- require "mods/version"
1
+ require 'mods/constants'
2
+ require 'mods/marc_relator_codes'
3
+ require 'mods/name'
4
+ require 'mods/nom_terminology'
5
+ require 'mods/reader'
6
+ require 'mods/record'
7
+ require 'mods/title_info'
8
+ require 'mods/version'
9
+
10
+ require 'nokogiri'
11
+ require 'nom/xml'
2
12
 
3
13
  module Mods
4
- # Your code goes here...
14
+
5
15
  end
@@ -0,0 +1,60 @@
1
+ module Mods
2
+ # the version of MODS supported by this gem
3
+ MODS_VERSION = '3.4'
4
+
5
+ MODS_NS_V3 = "http://www.loc.gov/mods/v3"
6
+ MODS_NS = MODS_NS_V3
7
+ MODS_XSD = "http://www.loc.gov/standards/mods/mods.xsd"
8
+
9
+ DOC_URL = "http://www.loc.gov/standards/mods/"
10
+
11
+ # top level elements that cannot have subelement children
12
+ TOP_LEVEL_ELEMENTS_SIMPLE = [
13
+ 'abstract',
14
+ 'accessCondition',
15
+ 'classification',
16
+ 'extension',
17
+ 'genre',
18
+ 'identifier',
19
+ 'note',
20
+ 'tableOfContents',
21
+ 'targetAudience',
22
+ 'typeOfResource',
23
+ ]
24
+
25
+ # top level elements that can have subelement children
26
+ TOP_LEVEL_ELEMENTS_COMPLEX = [
27
+ 'language',
28
+ 'location',
29
+ 'name',
30
+ 'originInfo',
31
+ 'part',
32
+ 'physicalDescription',
33
+ 'recordInfo',
34
+ 'relatedItem',
35
+ 'subject',
36
+ 'titleInfo' ]
37
+
38
+ TOP_LEVEL_ELEMENTS = Array.new(TOP_LEVEL_ELEMENTS_SIMPLE).concat(TOP_LEVEL_ELEMENTS_COMPLEX)
39
+
40
+ # enumerated attribute values
41
+ TITLE_INFO_TYPES = ['abbreviated', 'translated', 'alternative', 'uniform']
42
+ RELATED_ITEM_TYPES = [
43
+ 'preceding', 'succeeding', 'original', 'host', 'constituent', 'series',
44
+ 'otherVersion', 'otherFormat', 'isReferencedBy', 'references', 'reviewOf'
45
+ ]
46
+
47
+ # enumerated values
48
+ TYPE_OF_RESOURCE_VALUES = [
49
+ 'text', 'cartographic', 'notated music', 'sound recording-musical', 'sound recording-nonmusical',
50
+ 'sound recording',
51
+ 'still image',
52
+ 'moving image',
53
+ 'three dimensional object',
54
+ 'software',
55
+ 'multimedia',
56
+ 'mixed material']
57
+
58
+ ORIGIN_INFO_DATE_ELEMENTS = ['dateIssued', 'dateCreated', 'dateCaptured', 'dateValid', 'dateModified', 'copyrightDate', 'dateOther']
59
+
60
+ end
@@ -0,0 +1,228 @@
1
+ # Represents the Marc Relator Codes mapped to Relators, from http://www.loc.gov/marc/relators/relacode.html
2
+ # key - Marc Relator code
3
+ # value - Marc Relator term
4
+ MARC_RELATOR = {
5
+ 'acp' => 'Art copyist',
6
+ 'act' => 'Actor',
7
+ 'adp' => 'Adapter',
8
+ 'aft' => 'Author of afterword, colophon, etc.',
9
+ 'anl' => 'Analyst',
10
+ 'anm' => 'Animator',
11
+ 'ann' => 'Annotator',
12
+ 'ant' => 'Bibliographic antecedent',
13
+ 'app' => 'Applicant',
14
+ 'aqt' => 'Author in quotations or text abstracts',
15
+ 'arc' => 'Architect',
16
+ 'ard' => 'Artistic director',
17
+ 'arr' => 'Arranger',
18
+ 'art' => 'Artist',
19
+ 'asg' => 'Assignee',
20
+ 'asn' => 'Associated name',
21
+ 'att' => 'Attributed name',
22
+ 'auc' => 'Auctioneer',
23
+ 'aud' => 'Author of dialog',
24
+ 'aui' => 'Author of introduction',
25
+ 'aus' => 'Author of screenplay',
26
+ 'aut' => 'Author',
27
+ 'bdd' => 'Binding designer',
28
+ 'bjd' => 'Bookjacket designer',
29
+ 'bkd' => 'Book designer',
30
+ 'bkp' => 'Book producer',
31
+ 'blw' => 'Blurb writer',
32
+ 'bnd' => 'Binder',
33
+ 'bpd' => 'Bookplate designer',
34
+ 'bsl' => 'Bookseller',
35
+ 'ccp' => 'Conceptor',
36
+ 'chr' => 'Choreographer',
37
+ 'clb' => 'Collaborator',
38
+ 'cli' => 'Client',
39
+ 'cll' => 'Calligrapher',
40
+ 'clr' => 'Colorist',
41
+ 'clt' => 'Collotyper',
42
+ 'cmm' => 'Commentator',
43
+ 'cmp' => 'Composer',
44
+ 'cmt' => 'Compositor',
45
+ 'cng' => 'Cinematographer',
46
+ 'cnd' => 'Conductor',
47
+ 'cns' => 'Censor',
48
+ 'coe' => 'Contestant -appellee',
49
+ 'col' => 'Collector',
50
+ 'com' => 'Compiler',
51
+ 'con' => 'Conservator',
52
+ 'cos' => 'Contestant',
53
+ 'cot' => 'Contestant -appellant',
54
+ 'cov' => 'Cover designer',
55
+ 'cpc' => 'Copyright claimant',
56
+ 'cpe' => 'Complainant-appellee',
57
+ 'cph' => 'Copyright holder',
58
+ 'cpl' => 'Complainant',
59
+ 'cpt' => 'Complainant-appellant',
60
+ 'cre' => 'Creator',
61
+ 'crp' => 'Correspondent',
62
+ 'crr' => 'Corrector',
63
+ 'csl' => 'Consultant',
64
+ 'csp' => 'Consultant to a project',
65
+ 'cst' => 'Costume designer',
66
+ 'ctb' => 'Contributor',
67
+ 'cte' => 'Contestee-appellee',
68
+ 'ctg' => 'Cartographer',
69
+ 'ctr' => 'Contractor',
70
+ 'cts' => 'Contestee',
71
+ 'ctt' => 'Contestee-appellant',
72
+ 'cur' => 'Curator',
73
+ 'cwt' => 'Commentator for written text',
74
+ 'dfd' => 'Defendant',
75
+ 'dfe' => 'Defendant-appellee',
76
+ 'dft' => 'Defendant-appellant',
77
+ 'dgg' => 'Degree grantor',
78
+ 'dis' => 'Dissertant',
79
+ 'dln' => 'Delineator',
80
+ 'dnc' => 'Dancer',
81
+ 'dnr' => 'Donor',
82
+ 'dpb' => 'Distribution place',
83
+ 'dpc' => 'Depicted',
84
+ 'dpt' => 'Depositor',
85
+ 'drm' => 'Draftsman',
86
+ 'drt' => 'Director',
87
+ 'dsr' => 'Designer',
88
+ 'dst' => 'Distributor',
89
+ 'dtc' => 'Data contributor',
90
+ 'dte' => 'Dedicatee',
91
+ 'dtm' => 'Data manager',
92
+ 'dto' => 'Dedicator',
93
+ 'dub' => 'Dubious author',
94
+ 'edt' => 'Editor',
95
+ 'egr' => 'Engraver',
96
+ 'elg' => 'Electrician',
97
+ 'elt' => 'Electrotyper',
98
+ 'eng' => 'Engineer',
99
+ 'etr' => 'Etcher',
100
+ 'evp' => 'Event place',
101
+ 'exp' => 'Expert',
102
+ 'fac' => 'Facsimilist',
103
+ 'fld' => 'Field director',
104
+ 'flm' => 'Film editor',
105
+ 'fmo' => 'Former owner',
106
+ 'fpy' => 'First party',
107
+ 'fnd' => 'Funder',
108
+ 'frg' => 'Forger',
109
+ 'gis' => 'Geographic information specialist',
110
+ 'grt' => 'Graphic technician',
111
+ 'hnr' => 'Honoree',
112
+ 'hst' => 'Host',
113
+ 'ill' => 'Illustrator',
114
+ 'ilu' => 'Illuminator',
115
+ 'ins' => 'Inscriber',
116
+ 'inv' => 'Inventor',
117
+ 'itr' => 'Instrumentalist',
118
+ 'ive' => 'Interviewee',
119
+ 'ivr' => 'Interviewer',
120
+ 'lbr' => 'Laboratory',
121
+ 'lbt' => 'Librettist',
122
+ 'ldr' => 'Laboratory director',
123
+ 'led' => 'Lead',
124
+ 'lee' => 'Libelee-appellee',
125
+ 'lel' => 'Libelee',
126
+ 'len' => 'Lender',
127
+ 'let' => 'Libelee-appellant',
128
+ 'lgd' => 'Lighting designer',
129
+ 'lie' => 'Libelant-appellee',
130
+ 'lil' => 'Libelant',
131
+ 'lit' => 'Libelant-appellant',
132
+ 'lsa' => 'Landscape architect',
133
+ 'lse' => 'Licensee',
134
+ 'lso' => 'Licensor',
135
+ 'ltg' => 'Lithographer',
136
+ 'lyr' => 'Lyricist',
137
+ 'mcp' => 'Music copyist',
138
+ 'mfp' => 'Manufacture place',
139
+ 'mfr' => 'Manufacturer',
140
+ 'mdc' => 'Metadata contact',
141
+ 'mod' => 'Moderator',
142
+ 'mon' => 'Monitor',
143
+ 'mrb' => 'Marbler',
144
+ 'mrk' => 'Markup editor',
145
+ 'msd' => 'Musical director',
146
+ 'mte' => 'Metal-engraver',
147
+ 'mus' => 'Musician',
148
+ 'nrt' => 'Narrator',
149
+ 'opn' => 'Opponent',
150
+ 'org' => 'Originator',
151
+ 'orm' => 'Organizer of meeting',
152
+ 'oth' => 'Other',
153
+ 'own' => 'Owner',
154
+ 'pat' => 'Patron',
155
+ 'pbd' => 'Publishing director',
156
+ 'pbl' => 'Publisher',
157
+ 'pdr' => 'Project director',
158
+ 'pfr' => 'Proofreader',
159
+ 'pht' => 'Photographer',
160
+ 'plt' => 'Platemaker',
161
+ 'pma' => 'Permitting agency',
162
+ 'pmn' => 'Production manager',
163
+ 'pop' => 'Printer of plates',
164
+ 'ppm' => 'Papermaker',
165
+ 'ppt' => 'Puppeteer',
166
+ 'prc' => 'Process contact',
167
+ 'prd' => 'Production personnel',
168
+ 'prf' => 'Performer',
169
+ 'prg' => 'Programmer',
170
+ 'prm' => 'Printmaker',
171
+ 'pro' => 'Producer',
172
+ 'prp' => 'Production place',
173
+ 'prt' => 'Printer',
174
+ 'pta' => 'Patent applicant',
175
+ 'pte' => 'Plaintiff -appellee',
176
+ 'ptf' => 'Plaintiff',
177
+ 'pth' => 'Patent holder',
178
+ 'ptt' => 'Plaintiff-appellant',
179
+ 'pup' => 'Publication place',
180
+ 'rbr' => 'Rubricator',
181
+ 'rce' => 'Recording engineer',
182
+ 'rcp' => 'Recipient',
183
+ 'red' => 'Redactor',
184
+ 'ren' => 'Renderer',
185
+ 'res' => 'Researcher',
186
+ 'rev' => 'Reviewer',
187
+ 'rps' => 'Repository',
188
+ 'rpt' => 'Reporter',
189
+ 'rpy' => 'Responsible party',
190
+ 'rse' => 'Respondent-appellee',
191
+ 'rsg' => 'Restager',
192
+ 'rsp' => 'Respondent',
193
+ 'rst' => 'Respondent-appellant',
194
+ 'rth' => 'Research team head',
195
+ 'rtm' => 'Research team member',
196
+ 'sad' => 'Scientific advisor',
197
+ 'sce' => 'Scenarist',
198
+ 'scl' => 'Sculptor',
199
+ 'scr' => 'Scribe',
200
+ 'sds' => 'Sound designer',
201
+ 'sec' => 'Secretary',
202
+ 'sgn' => 'Signer',
203
+ 'sht' => 'Supporting host',
204
+ 'sng' => 'Singer',
205
+ 'spk' => 'Speaker',
206
+ 'spn' => 'Sponsor',
207
+ 'spy' => 'Second party',
208
+ 'srv' => 'Surveyor',
209
+ 'std' => 'Set designer',
210
+ 'stl' => 'Storyteller',
211
+ 'stm' => 'Stage manager',
212
+ 'stn' => 'Standards body',
213
+ 'str' => 'Stereotyper',
214
+ 'tcd' => 'Technical director',
215
+ 'tch' => 'Teacher',
216
+ 'ths' => 'Thesis advisor',
217
+ 'trc' => 'Transcriber',
218
+ 'trl' => 'Translator',
219
+ 'tyd' => 'Type designer',
220
+ 'tyg' => 'Typographer',
221
+ 'uvp' => 'University place',
222
+ 'vdg' => 'Videographer',
223
+ 'voc' => 'Vocalist',
224
+ 'wam' => 'Writer of accompanying material',
225
+ 'wdc' => 'Woodcutter',
226
+ 'wde' => 'Wood-engraver',
227
+ 'wit' => 'Witness'
228
+ }
@@ -0,0 +1,18 @@
1
+ module Mods
2
+
3
+ class Name
4
+
5
+ NS_HASH = {'m' => MODS_NS_V3}
6
+ SUBELEMENTS = ['namePart', 'displayForm', 'affiliation', 'role', 'description']
7
+
8
+ # attributes on name node
9
+ ATTRIBUTES = ['type', 'authority', 'authorityURI', 'valueURI', 'displayLabel', 'usage', 'altRepGroup', 'nameTitleGroup']
10
+
11
+ # valid values for type attribute on name node <name type="val"/>
12
+ TYPES = ['personal', 'corporate', 'conference', 'family']
13
+ # valid values for type attribute on namePart node <name><namePart type="val"/></name>
14
+ NAME_PART_TYPES = ['date', 'family', 'given', 'termsOfAddress']
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,221 @@
1
+ module Mods
2
+
3
+ class Record
4
+
5
+ # set the NOM terminology; do NOT use namespaces
6
+ # @param mods_ng_xml a Nokogiri::Xml::Document object containing MODS (without namespaces)
7
+ def set_terminology_no_ns(mods_ng_xml)
8
+ mods_ng_xml.set_terminology() do |t|
9
+
10
+ # FIXME: may want to deal with camelcase vs. underscore in method_missing
11
+
12
+ # These elements have no subelements - w00t!
13
+ Mods::TOP_LEVEL_ELEMENTS_SIMPLE.each { |elname|
14
+ t.send elname, :path => "/mods/#{elname}"
15
+ }
16
+
17
+ # TITLE_INFO ----------------------------------------------------------------------------
18
+ # note - titleInfo can be a top level element or a sub-element of relatedItem
19
+ # (<titleInfo> as subelement of <subject> is not part of the MODS namespace)
20
+
21
+ t.title_info :path => '/mods/titleInfo' do |n|
22
+ n.type_at :path => '@type'
23
+ n.title :path => 'title'
24
+ n.subTitle :path => 'subTitle'
25
+ n.nonSort :path => 'nonSort'
26
+ n.partNumber :path => 'partNumber'
27
+ n.partName :path => 'partName'
28
+ n.sort_title :path => '.', :accessor => lambda { |node|
29
+ if node.type_at.text != "alternative" || (node.type_at.text == "alternative" && mods_ng_xml.xpath('/mods/titleInfo').size == 1)
30
+ node.title.text + (!node.subTitle.text.empty? ? "#{@title_delimiter}#{node.subTitle.text}" : "" )
31
+ end
32
+ }
33
+ n.full_title :path => '.', :accessor => lambda { |node|
34
+ (!node.nonSort.text.empty? ? "#{node.nonSort.text} " : "" ) +
35
+ node.title.text +
36
+ (!node.subTitle.text.empty? ? "#{@title_delimiter}#{node.subTitle.text}" : "" )
37
+ }
38
+ n.short_title :path => '.', :accessor => lambda { |node|
39
+ if node.type_at.text != "alternative"
40
+ (!node.nonSort.text.empty? ? "#{node.nonSort.text} " : "" ) +
41
+ node.title.text
42
+ end
43
+ }
44
+ n.alternative_title :path => '.', :accessor => lambda { |node|
45
+ if node.type_at.text == "alternative"
46
+ (!node.nonSort.text.empty? ? "#{node.nonSort.text} " : "" ) +
47
+ node.title.text
48
+ end
49
+ }
50
+ end
51
+
52
+ # current way to do short_title correctly
53
+ # t.short_title :path => '/mods/titleInfo[not(@type=alternative)]', :accessor => lambda { |node|
54
+ # (!node.nonSort.text.empty? ? "#{node.nonSort.text} " : "" ) +
55
+ # node.title.text
56
+ # }
57
+
58
+
59
+ # NAME ------------------------------------------------------------------------------------
60
+
61
+ t.plain_name :path => '/mods/name' do |n|
62
+
63
+ Mods::Name::ATTRIBUTES.each { |attr_name|
64
+ if attr_name != 'type'
65
+ n.send attr_name, :path => "@#{attr_name}", :accessor => lambda { |a| a.text }
66
+ else
67
+ n.type_at :path => "@#{attr_name}", :accessor => lambda { |a| a.text }
68
+ end
69
+ }
70
+
71
+ n.namePart :path => 'namePart' do |np|
72
+ np.type_at :path => '@type'
73
+ end
74
+ n.displayForm :path => 'displayForm'
75
+ n.affiliation :path => 'affiliation'
76
+ n.description_el :path => 'description' # description is used by Nokogiri
77
+ n.role :path => 'role/roleTerm' do |r|
78
+ r.type_at :path => "@type", :accessor => lambda { |a| a.text }
79
+ r.authority :path => "@authority", :accessor => lambda { |a| a.text }
80
+ end
81
+ end
82
+
83
+ t.personal_name :path => '/mods/name[@type="personal"]' do |n|
84
+ n.family_name :path => 'namePart[@type="family"]'
85
+ n.given_name :path => 'namePart[@type="given"]'
86
+ n.termsOfAddress :path => 'namePart[@type="termsOfAddress"]'
87
+ n.date :path => 'namePart[@type="date"]'
88
+ end
89
+
90
+ t.corporate_name :path => '/mods/name[@type="corporate"]'
91
+ t.conference_name :path => '/mods/name[@type="conference"]'
92
+
93
+ # LANGUAGE -------------------------------------------------------------------------------
94
+
95
+ t.language :path => '/mods/language' do |n|
96
+ n.languageTerm :path => 'languageTerm' do |lt|
97
+ lt.type_at :path => '@type', :accessor => lambda { |a| a.text }
98
+ lt.authority :path => '@authority', :accessor => lambda { |a| a.text }
99
+ end
100
+ n.code_term :path => 'languageTerm[@type="code"]'
101
+ n.text_term :path => 'languageTerm[@type="text"]'
102
+ n.scriptTerm :path => 'scriptTerm'
103
+ end
104
+
105
+ # PHYSICAL_DESCRIPTION -------------------------------------------------------------------
106
+ t.physical_description :path => '/mods/physicalDescription' do |n|
107
+ n.digitalOrigin :path => 'digitalOrigin'
108
+ n.extent :path => 'extent'
109
+ n.form :path => 'form' do |f|
110
+ f.authority :path => '@authority', :accessor => lambda { |a| a.text }
111
+ f.type_at :path => '@type', :accessor => lambda { |a| a.text }
112
+ end
113
+ n.internetMediaType :path => 'internetMediaType'
114
+ n.note :path => 'note' do |nn|
115
+ nn.displayLabel :path => '@displayLabel', :accessor => lambda { |a| a.text }
116
+ nn.type_at :path => '@type', :accessor => lambda { |a| a.text }
117
+ end
118
+ n.reformattingQuality :path => 'reformattingQuality'
119
+ end
120
+
121
+ # LOCATION -------------------------------------------------------------------------------
122
+ t.location :path => '/mods/location' do |n|
123
+ n.physicalLocation :path => 'physicalLocation' do |e|
124
+ e.authority :path => '@authority', :accessor => lambda { |a| a.text }
125
+ e.displayLabel :path => '@displayLabel', :accessor => lambda { |a| a.text }
126
+ end
127
+ n.shelfLocator :path => 'shelfLocator'
128
+ n.url :path => 'url' do |e|
129
+ e.dateLastAccessed :path => '@dateLastAccessed', :accessor => lambda { |a| a.text }
130
+ e.displayLabel :path => '@displayLabel', :accessor => lambda { |a| a.text }
131
+ e.note :path => '@note', :accessor => lambda { |a| a.text }
132
+ e.access :path => '@access', :accessor => lambda { |a| a.text }
133
+ e.usage :path => '@usage', :accessor => lambda { |a| a.text }
134
+ end
135
+ n.holdingSimple :path => 'holdingSimple'
136
+ n.holdingExternal :path => 'holdingExternal'
137
+ end
138
+
139
+ # ORIGIN_INFO --------------------------------------------------------------------------
140
+ t.origin_info :path => '/mods/originInfo' do |n|
141
+ n.place :path => 'place' do |e|
142
+ e.placeTerm :path => 'placeTerm' do |ee|
143
+ ee.type_at :path => '@type', :accessor => lambda { |a| a.text }
144
+ ee.authority :path => '@authority', :accessor => lambda { |a| a.text }
145
+ end
146
+ end
147
+ n.publisher :path => 'publisher'
148
+ Mods::ORIGIN_INFO_DATE_ELEMENTS.each { |date_el|
149
+ n.send date_el, :path => "#{date_el}" do |d|
150
+ d.encoding :path => '@encoding', :accessor => lambda { |a| a.text }
151
+ d.point :path => '@point', :accessor => lambda { |a| a.text }
152
+ d.keyDate :path => '@keyDate', :accessor => lambda { |a| a.text }
153
+ d.qualifier :path => '@qualifier', :accessor => lambda { |a| a.text }
154
+ if date_el == 'dateOther'
155
+ d.type_at :path => '@type', :accessor => lambda { |a| a.text }
156
+ end
157
+ end
158
+ }
159
+ n.edition :path => 'edition'
160
+ n.issuance :path => 'issuance'
161
+ n.frequency :path => 'frequency' do |f|
162
+ f.authority :path => '@authority', :accessor => lambda { |a| a.text }
163
+ end
164
+ end
165
+
166
+ end # terminology
167
+
168
+ mods_ng_xml.nom!
169
+
170
+ mods_ng_xml
171
+ end # set_terminology_no_ns
172
+
173
+ # TODO: common top level element attributes: ID; xlink; lang; xml:lang; script; transliteration
174
+ # authority, authorityURI, valueURI
175
+ # displayLabel, usage altRepGroup
176
+ # type
177
+ # TODO: common subelement attributes: lang, xml:lang, script, transliteration
178
+ # TODO: other common attribute: supplied
179
+
180
+
181
+ # set the NOM terminology, with namespaces
182
+ # @param mods_ng_xml a Nokogiri::Xml::Document object containing MODS (with namespaces)
183
+ def set_terminology_ns(mods_ng_xml)
184
+ mods_ng_xml.set_terminology(:namespaces => { 'm' => Mods::MODS_NS}) do |t|
185
+
186
+ # note - titleInfo can be a top level element or a sub-element of subject and relatedItem
187
+ t.title_info :path => '//m:titleInfo' do |n|
188
+ n.title :path => 'm:title'
189
+ n.subTitle :path => 'm:subTitle'
190
+ n.nonSort :path => 'm:nonSort'
191
+ n.partNumber :path => 'm:partNumber'
192
+ n.partName :path => 'm:partName'
193
+ n.type_at :path => '@type'
194
+ end
195
+
196
+ t.author :path => '//m:name' do |n|
197
+ n.valueURI :path => '@valueURI'
198
+ n.namePart :path => 'm:namePart', :single => true
199
+ end
200
+
201
+ t.corporate_authors :path => '//m:name[@type="corporate"]'
202
+ t.personal_authors :path => 'm:name[@type="personal"]' do |n|
203
+ n.roleTerm :path => 'm:role/m:roleTerm'
204
+ n.name_role_pair :path => '.', :accessor => lambda { |node| node.roleTerm.text + ": " + node.namePart.text }
205
+ end
206
+
207
+ t.language :path => 'm:language' do |n|
208
+ n.value :path => 'm:languageTerm', :accessor => :text
209
+ end
210
+
211
+ end
212
+
213
+ mods_ng_xml.nom!
214
+
215
+ mods_ng_xml
216
+ end # set_terminology_ns
217
+
218
+ end
219
+
220
+ end
221
+