libis-tools 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: abe45e9ce73f0da4087c1cbfe554a9d8d62680c9
4
- data.tar.gz: 845d2bbee0b78df26160501a4a15bb532ac62e1a
3
+ metadata.gz: 1e717837dc7e2b3f0549cf1b6abf6ca1240f1344
4
+ data.tar.gz: 8310be003731268e303dd930279638d61b6a5edf
5
5
  SHA512:
6
- metadata.gz: afa5e9bfd7b2ea4c908046159b718e70868fa3160559f5ebe350c5e81bf7d601dae1f7aa168cea4f914249e561fbdb627e3d67a9db19ce4b3e3fbd6e104ab4a0
7
- data.tar.gz: d1f52ac47989cc3bfe353d14b15ef581c3121826a06fb1c9e240364c03a291a5ec64c66da656c9bc1c4de0825f2593f92053cdac6bdee58e7e565a75bb8c18ca
6
+ metadata.gz: 8fce8638e6fc55803704fda53975fedce79a0ed127c2c8e2cf7dd8b989e6d87af001a56205d1091865764c5fa99dc864dd473dd23df637b24e305f29d49d8df0
7
+ data.tar.gz: 8b8f8b6b0af625240709d68fd48ea75003c7fe8dbf460cdaecd4d67d6f8a2a19a9aad07daae54ab9602237769ec118807eeb6d92107b34c64d48fcb9af15ad48
@@ -34,8 +34,7 @@ module Libis
34
34
  #
35
35
  # @param [String,Hash] file_or_hash optional String or Hash argument to initialize the data.
36
36
  def initialize(file_or_hash = nil, opt = {})
37
- super({}, opt)
38
- self << file_or_hash
37
+ super _file_to_hash(file_or_hash), opt
39
38
  end
40
39
 
41
40
  # Load configuration parameters from a YAML file or Hash.
@@ -48,7 +47,21 @@ module Libis
48
47
  #
49
48
  # @param [String,Hash] file_or_hash optional String or Hash argument to initialize the data.
50
49
  def <<(file_or_hash)
51
- return self if file_or_hash.nil? || (file_or_hash.respond_to?(:empty?) && file_or_hash.empty?)
50
+ _file_to_hash(file_or_hash).each { |key, value| self[key] = value }
51
+ self
52
+ end
53
+
54
+ # Save configuration parameters in a YAML file.
55
+ #
56
+ # @param [String] file path of the YAML file to save the configuration to.
57
+ def >>(file)
58
+ File.open(file, 'w') { |f| f.write to_hash.to_yaml }
59
+ end
60
+
61
+ protected
62
+
63
+ def _file_to_hash(file_or_hash)
64
+ return {} if file_or_hash.nil? || (file_or_hash.respond_to?(:empty?) && file_or_hash.empty?)
52
65
  hash = case file_or_hash
53
66
  when Hash
54
67
  yield file_or_hash if block_given?
@@ -61,16 +74,8 @@ module Libis
61
74
  else
62
75
  {}
63
76
  end
64
- return self unless hash.is_a? Hash
65
- hash.each { |key, value| self[key] = value }
66
- self
67
- end
68
-
69
- # Save configuration parameters in a YAML file.
70
- #
71
- # @param [String] file path of the YAML file to save the configuration to.
72
- def >>(file)
73
- File.open(file, 'w') { |f| f.write to_hash.to_yaml }
77
+ hash = {} unless hash.is_a? Hash
78
+ hash
74
79
  end
75
80
 
76
81
  end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ require 'nori'
3
+ require 'libis/tools/assert'
4
+
5
+ module Libis
6
+ module Tools
7
+ module Metadata
8
+
9
+ class DublinCoreRecord < Libis::Tools::XmlDocument
10
+
11
+ DC_ELEMENTS = %w'contributor coverage creator date description format identifier language' +
12
+ %w'publisher relation rights source subject title type'
13
+ DCTERMS_ELEMENTS = %w'abstract accessRights accrualMethod accrualPeriodicity accrualPolicy alternative' +
14
+ %w'audience available bibliographicCitation conformsTo contributor coverage created creator date' +
15
+ %w'dateAccepted dateCopyrighted dateSubmitted description educationLevel extent format hasFormat' +
16
+ %w'hasPart hasVersion identifier instructionalMethod isFormatOf isPartOf isReferencedBy isReplacedBy' +
17
+ %w'isRequiredBy issued isVersionOf language license mediator medium modified provenance publisher' +
18
+ %w'references relation replaces requires rights rightsHolder source spatial subject tableOfContents' +
19
+ %w'temporal title type valid'
20
+
21
+ def initialize(doc = nil)
22
+ super()
23
+ xml_doc = case doc
24
+ when ::Libis::Tools::XmlDocument
25
+ doc
26
+ when String
27
+ # noinspection RubyResolve
28
+ File.exist?(doc) ? Libis::Tools::XmlDocument.load(doc) : Libis::Tools::XmlDocument.parse(doc)
29
+ when IO
30
+ Libis::Tools::XmlDocument.parse(doc.read)
31
+ when Hash
32
+ Libis::Tools::XmlDocument.from_hash(doc)
33
+ when NilClass
34
+ Libis::Tools::XmlDocument.new.build do |xml|
35
+ xml.record('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
36
+ 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
37
+ 'xmlns:dcterms' => 'http://purl.org/dc/terms/') {
38
+ yield xml
39
+ }
40
+ end
41
+ else
42
+ raise ArgumentError, "Invalid argument: #{doc.inspect}"
43
+ end
44
+ @document = xml_doc.document if xml_doc
45
+ end
46
+
47
+ def all
48
+ @all_records ||= get_all_records
49
+ end
50
+
51
+ def xpath(path)
52
+ m = /^([\/.]*\/)?(dc(terms)?:)?(.*)/.match(path.to_s)
53
+ return [] unless m[4]
54
+ path = (m[1] || '') + ('dc:' || m[2]) + m[4]
55
+ raise ArgumentError, 'XML document not valid.' if self.invalid?
56
+ @document.xpath(path.to_s)
57
+ end
58
+
59
+ def add_node(name, value = nil, parent = nil, attributes = {})
60
+ ns, tag = get_namespace(name.to_s)
61
+ (attributes[:namespaces] ||= {})[:node_ns] ||= ns if ns
62
+ super tag, value, parent, attributes
63
+ end
64
+
65
+ protected
66
+
67
+ def get_nodes(tag, parent = nil)
68
+ parent ||= root
69
+ m = /^([\/\.]*\/)?(dc(?:terms)?:)?(.*)/.match(tag.to_s)
70
+ return [] unless m[3]
71
+ path = (m[1] || '') + ('dc:' || m[2]) + m[3]
72
+ parent.xpath(path)
73
+ end
74
+
75
+ def get_namespace(tag)
76
+ m = /^((dc)?(terms)?(?:_|:)?)?([a-zA-Z_][-_.0-9a-zA-Z]+)(.*)/.match tag
77
+ ns = if m[1].nil?
78
+ if DC_ELEMENTS.include?(m[4])
79
+ :dc
80
+ else
81
+ DCTERMS_ELEMENTS.include?(m[4]) ? :dcterms : nil
82
+ end
83
+ elsif m[3].nil?
84
+ :dc
85
+ else
86
+ :dcterms
87
+ end
88
+ [ns, "#{m[4]}#{m[5]}"]
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,81 @@
1
+ # coding: utf-8
2
+
3
+ module Libis
4
+ module Tools
5
+ module Metadata
6
+
7
+ class FieldSpec
8
+
9
+ attr_accessor :parts
10
+ attr_accessor :prefix
11
+ attr_accessor :join
12
+ attr_accessor :postfix
13
+
14
+ def initialize(*parts)
15
+ @parts = []
16
+ self[*parts]
17
+ end
18
+
19
+ def add_options(options = {})
20
+ if options[:fix]
21
+ if options[:fix].size == 2
22
+ @prefix, @postfix = options[:fix].split('')
23
+ else
24
+ @prefix, @postfix = options[:fix].split('|')
25
+ end
26
+ end
27
+ @join = options[:join] if options[:join]
28
+ @prefix = FieldSpec::from(options[:prefix]) if options[:prefix]
29
+ @postfix = FieldSpec::from(options[:postfix]) if options[:postfix]
30
+ self
31
+ end
32
+
33
+ def add_default_options(options = {})
34
+ options.delete(:prefix) if @prefix
35
+ options.delete(:postfix) if @postfix
36
+ options.delete(:fix) if @prefix or @postfix
37
+ options.delete(:join) if @join
38
+ add_options options
39
+ end
40
+
41
+ def [](*parts)
42
+ options = parts.last.is_a?(Hash) ? parts.pop : {}
43
+ parts.each { |x| add x }
44
+ x = options.delete(:parts)
45
+ add x if x
46
+ add_options options
47
+ end
48
+
49
+ def self.from(*h)
50
+ FieldSpec.new(*h)
51
+ end
52
+
53
+ def to_s
54
+ @parts.delete_if { |x|
55
+ x.nil? or
56
+ (x.is_a? String and x.empty?) or
57
+ (x.is_a? FieldSpec and x.to_s.empty?)
58
+ }
59
+ result = @parts.join(@join)
60
+ unless result.empty?
61
+ result = (@prefix || '').to_s + result + (@postfix || '').to_s
62
+ end
63
+ result
64
+ end
65
+
66
+ def add(part)
67
+ case part
68
+ when Hash
69
+ @parts << FieldSpec::from(part)
70
+ when Array
71
+ part.each { |x| add x }
72
+ else
73
+ @parts << part
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ module Libis
4
+ module Tools
5
+ module Metadata
6
+
7
+ class FixField
8
+
9
+ attr_reader :tag
10
+ attr_reader :datas
11
+
12
+ def initialize(tag, datas)
13
+ @tag = tag
14
+ @datas = datas || ''
15
+ end
16
+
17
+ def dump
18
+ "#{@tag}:'#{@datas}'\n"
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+
3
+ require 'cgi'
4
+
5
+ require_relative 'marc_record'
6
+
7
+ module Libis
8
+ module Tools
9
+ module Metadata
10
+
11
+ class Marc21Record < Libis::Tools::Metadata::MarcRecord
12
+
13
+ private
14
+
15
+ def get_all_records
16
+
17
+ @all_records = Hash.new { |h, k| h[k] = [] }
18
+
19
+ @node.xpath('.//leader').each { |f|
20
+ @all_records['LDR'] << FixField.new('LDR', f.content)
21
+ }
22
+
23
+ @node.xpath('.//controlfield').each { |f|
24
+ tag = f['tag']
25
+ tag = '%03d' % tag.to_i if tag.size < 3
26
+ @all_records[tag] << FixField.new(tag, CGI::escapeHTML(f.content))
27
+ }
28
+
29
+ @node.xpath('.//datafield').each { |v|
30
+
31
+ tag = v['tag']
32
+ tag = '%03d' % tag.to_i if tag.size < 3
33
+
34
+ subfields = Hash.new { |h, k| h[k] = [] }
35
+ v.xpath('.//subfield').each { |s| subfields[s['code']] << CGI::escapeHTML(s.content) }
36
+
37
+ @all_records[tag] << VarField.new(tag, v['ind1'].to_s, v['ind2'].to_s, subfields)
38
+
39
+ }
40
+
41
+ @all_records
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,209 @@
1
+ # coding: utf-8
2
+
3
+ require 'set'
4
+ require 'cgi'
5
+
6
+ require 'libis/tools/xml_document'
7
+ require 'libis/tools/assert'
8
+
9
+ require_relative 'fix_field'
10
+ require_relative 'var_field'
11
+ require_relative 'field_spec'
12
+
13
+ module Libis
14
+ module Tools
15
+ module Metadata
16
+
17
+ # noinspection RubyTooManyMethodsInspection
18
+ class MarcRecord
19
+
20
+ def initialize(xml_node)
21
+ @node = xml_node
22
+ end
23
+
24
+ def to_raw
25
+ @node
26
+ end
27
+
28
+ def all
29
+ # noinspection RubyResolve
30
+ @all_records ||= get_all_records
31
+ end
32
+
33
+ def each
34
+ all.each do |k, v|
35
+ yield k, v
36
+ end
37
+ end
38
+
39
+ def all_tags(tag, subfields = '')
40
+ tag_, ind1, ind2 = tag =~ /^\d{3}/ ? [tag[0..2], tag[3], tag[4]] : [tag, nil, nil]
41
+ result = get_records(tag_, ind1, ind2, subfields)
42
+ return result unless block_given?
43
+ result.map { |record| yield record }
44
+ result.size > 0
45
+ end
46
+
47
+ def first_tag(t, subfields = '')
48
+ result = all_tags(t, subfields).first
49
+ return result unless block_given?
50
+ return false unless result
51
+ yield result
52
+ true
53
+ end
54
+
55
+ def each_tag(t, s = '')
56
+ all_tags(t, s).each do |record|
57
+ yield record
58
+ end
59
+ end
60
+
61
+ def all_fields(t, s)
62
+ r = all_tags(t, s).collect { |tag| tag.fields_array(s) }.flatten.compact
63
+ return r unless block_given?
64
+ r.map { |field| yield field }
65
+ r.size > 0
66
+ end
67
+
68
+ def first_field(t, s)
69
+ result = all_fields(t, s).first
70
+ return result unless block_given?
71
+ return false unless result
72
+ yield result
73
+ true
74
+ end
75
+
76
+
77
+ def each_field(t, s)
78
+ all_fields(t, s).each do |field|
79
+ yield field
80
+ end
81
+ end
82
+
83
+ def marc_dump
84
+ all.values.flatten.each_with_object([]) { |record, m| m << record.dump }.join
85
+ end
86
+
87
+ def save(filename)
88
+
89
+ doc = ::Libis::Tools::XmlDocument.new
90
+ doc.root = @node
91
+
92
+ return doc unless filename
93
+
94
+ doc.save filename, save_with: (::Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS |
95
+ ::Nokogiri::XML::Node::SaveOptions::AS_XML |
96
+ ::Nokogiri::XML::Node::SaveOptions::FORMAT
97
+ )
98
+
99
+ end
100
+
101
+ def self.load(filename)
102
+
103
+ doc = ::Libis::Tools::XmlDocument.open(filename)
104
+ self.new(doc.root)
105
+
106
+ end
107
+
108
+ def self.read(io)
109
+ io = StringIO.new(io) if io.is_a? String
110
+ doc = ::Libis::Tools::XmlDocument.parse(io)
111
+ self.new(doc.root)
112
+
113
+ end
114
+
115
+ def to_aseq
116
+ record = ''
117
+ doc_number = tag('001').datas
118
+
119
+ all.select { |t| t.is_a? FixField }.each { |t| record += "#{format('%09s', doc_number)} #{t.tag} L #{t.datas}\n" }
120
+ all.select { |t| t.is_a? VarField }.each { |t|
121
+ record += "#{format('%09s', doc_number)} #{t.tag}#{t.ind1}#{t.ind2} L "
122
+ t.keys.each { |k|
123
+ t.field_array(k).each { |f|
124
+ record += "$$#{k}#{CGI::unescapeHTML(f)}"
125
+ }
126
+ }
127
+ record += "\n"
128
+ }
129
+
130
+ record
131
+ end
132
+
133
+ protected
134
+
135
+ def element(*parts)
136
+ opts = options parts
137
+ field_spec(opts, *parts)
138
+ end
139
+
140
+ def list_s(*parts)
141
+ opts = options parts, join: ' '
142
+ field_spec(opts, *parts)
143
+ end
144
+
145
+ def list_c(*parts)
146
+ opts = options parts, join: ', '
147
+ field_spec(opts, *parts)
148
+ end
149
+
150
+ def list_d(*parts)
151
+ opts = options parts, join: ' - '
152
+ field_spec(opts, *parts)
153
+ end
154
+
155
+ def repeat(*parts)
156
+ opts = options parts, join: '; '
157
+ field_spec(opts, *parts)
158
+ end
159
+
160
+ def opt_r(*parts)
161
+ opts = options parts, fix: '()'
162
+ field_spec(opts, *parts)
163
+ end
164
+
165
+ def opt_s(*parts)
166
+ opts = options parts, fix: '[]'
167
+ field_spec(opts, *parts)
168
+ end
169
+
170
+ def odis_link(group, id, label)
171
+ "http://www.odis.be/lnk/#{group.downcase[0, 2]}_#{id}\##{label}"
172
+ end
173
+
174
+ private
175
+
176
+ def options(args, default = {})
177
+ default.merge(args.last.is_a?(::Hash) ? args.pop : {})
178
+ end
179
+
180
+ def field_spec(default_options, *parts)
181
+ FieldSpec.new(*parts).add_default_options(default_options).to_s
182
+ end
183
+
184
+ def get_records(tag, ind1 = '', ind2 = '', subfields = '')
185
+
186
+ ind1 ||= ''
187
+ ind2 ||= ''
188
+ subfields ||= ''
189
+
190
+ ind1.tr!('_', ' ')
191
+ ind1.tr!('#', '')
192
+
193
+ ind2.tr!('_', ' ')
194
+ ind2.tr!('#', '')
195
+
196
+ all[tag].select do |v|
197
+ v.is_a?(FixField) ||
198
+ ((ind1.empty? or v.ind1 == ind1) &&
199
+ (ind2.empty? or v.ind2 == ind2) &&
200
+ v.match_fieldspec?(subfields)
201
+ )
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,224 @@
1
+ # coding: utf-8
2
+
3
+ require 'libis/tools/assert'
4
+
5
+ module Libis
6
+ module Tools
7
+ module Metadata
8
+
9
+ class VarField
10
+
11
+ attr_reader :tag
12
+ attr_reader :ind1
13
+ attr_reader :ind2
14
+ attr_reader :subfield
15
+
16
+ def initialize(tag, ind1, ind2, subfield = {})
17
+ @tag = tag
18
+ @ind1 = ind1 || ' '
19
+ @ind2 = ind2 || ' '
20
+ @subfield = subfield || {}
21
+ end
22
+
23
+ # dump the contents
24
+ #
25
+ # @return [String] debug output to inspect the contents of the VarField
26
+ def dump
27
+ output = "#{@tag}:#{@ind1}:#{@ind2}:\n"
28
+ @subfield.each { |s, t| output += "\t#{s}:#{t}\n" }
29
+ output
30
+ end
31
+
32
+ # dump the contents
33
+ #
34
+ # @return [String] debug output to inspect the contents of the VarField - Single line version
35
+ def dump_line
36
+ output = "#{@tag}:#{@ind1}:#{@ind2}:"
37
+ @subfield.each { |s, t| output += "$#{s}#{t}" }
38
+ output
39
+ end
40
+
41
+ # list the subfield codes
42
+ #
43
+ # @return [Array] a list of all subfield codes
44
+ def keys
45
+ @subfield.keys
46
+ end
47
+
48
+ # get the first (or only) subfield value for the given code
49
+ #
50
+ # @return [String] the first or only entry of a subfield or nil if not present
51
+ # @param s [Character] the subfield code
52
+ def field(s)
53
+ field_array(s).first
54
+ end
55
+
56
+ # get a list of all subfield values for a given code
57
+ #
58
+ # @return [Array] all the entries of a repeatable subfield
59
+ # @param s [Character] the subfield code
60
+ def field_array(s)
61
+ assert(s.is_a?(String) && (s =~ /^[\da-z]$/) == 0, 'method expects a lower case alphanumerical char')
62
+ @subfield.has_key?(s) ? @subfield[s].dup : []
63
+ end
64
+
65
+ # get a list of the first subfield value for all the codes in the given string
66
+ #
67
+ # @return [Array] list of the first or only entries of all subfield codes in the input string
68
+ # @param s [String] subfield code specification (see match_fieldspec?)
69
+ #
70
+ # The subfield codes are cleaned and sorted first (see fieldspec_to_sorted_array)
71
+ def fields(s)
72
+ assert(s.is_a?(String), 'method expects a string')
73
+ return [] unless (match_array = match_fieldspec?(s))
74
+ fieldspec_to_array(match_array.join(' ')).collect { |i| send(:field, i) }.flatten.compact
75
+ end
76
+
77
+ # get a list of all the subfield values for all the codes in the given string
78
+ #
79
+ # @return [Array] list of the all the entries of all subfield codes in the input string
80
+ # @param s [String] subfield code specification (see match_fieldspec?)
81
+ #
82
+ # The subfield codes are cleaned and sorted first (see fieldspec_to_sorted_array)
83
+
84
+ def fields_array(s)
85
+ assert(s.is_a?(String), 'method expects a string')
86
+ return [] unless (match_array = match_fieldspec?(s))
87
+ fieldspec_to_array(match_array.join(' ')).collect { |i| send(:field_array, i) }.flatten.compact
88
+ end
89
+
90
+ # check if the current VarField matches the given field specification.
91
+ #
92
+ # @return [String] The matching part(s) of the specification or nil if no match
93
+ # @param fieldspec [String] field specification: sequence of alternative set of subfield codes that should-shouldn't be present
94
+ #
95
+ # The fieldspec consists of groups of characters. At least one of these groups should match for the test to succeed
96
+ # Within the group sets of codes may be divided by a hyphen (-). The first set of codes must all be present;
97
+ # the second set of codes must all <b>not</b> be present. Either set may be empty.
98
+ #
99
+ # Examples:
100
+ # 'ab' matches '$a...$b...' => ['ab']
101
+ # '$a...$b...$c...' => ['ab']
102
+ # but not '$a...' => nil # ($b missing)
103
+ # '$b...' => nil # ($a missing)
104
+ # 'a b' matches '$a...' => ['a']
105
+ # '$b...' => ['b']
106
+ # '$a...$b...' => ['a', 'b']
107
+ # '$a...$b...$c...' => ['a', 'b']
108
+ # but not '$c...' => nil # ($a or $b must be present)
109
+ # 'abc-d' matches '$a..,$b...$c...' => ['abc-d']
110
+ # '$a..,$b...$c...$e...' => ['abc-d']
111
+ # but not '$a...$b...$e...' => nil # ($c missing)
112
+ # '$a...$b...$c...$d...' => nil # ($d should not be present)
113
+ # 'a-b b-a' matches '$a...' => ['a-b']
114
+ # '$a...$c...' => ['a-b']
115
+ # '$b...' => ['b-a']
116
+ # '$b...$c...' => ['b-a']
117
+ # but not '$a...$b...' => nil
118
+ # 'a-b c-d' matches '$a...' => ['a-b']
119
+ # '$a...$c...' => ['a-b', 'c-d']
120
+ # '$a...$b...$c...' => ['c-d']
121
+ # '$b...$c...' => ['c-d']
122
+ # but not '$a...$b...' => nil
123
+ # '$c...$d...' => nil
124
+ # '$b...$c...$d...' => nil
125
+ # '$a...$b...$c...$d...' => nil
126
+ def match_fieldspec?(fieldspec)
127
+ return [] if fieldspec.empty?
128
+ result = fieldspec.split.collect { |fs|
129
+ fa = fs.split '-'
130
+ assert(fa.size <= 2, 'more than one "-" is not allowed in a fieldspec')
131
+ must_match = (fa[0] || '').split ''
132
+ must_not_match = (fa[1] || '').split ''
133
+ next unless (must_match == (must_match & keys)) && (must_not_match & keys).empty?
134
+ fs
135
+ }.compact
136
+ return nil if result.empty?
137
+ result
138
+ end
139
+
140
+ private
141
+
142
+ # @return [Array] cleaned up version of the input string
143
+ # @param fieldspec [String] subfield code specification
144
+ # cleans the subfield code specification and splits it into an array of characters
145
+ # Duplicates will be removed from the array and the order will be untouched.
146
+ def fieldspec_to_array(fieldspec)
147
+
148
+ # note that we remove the '-xxx' part as it is only required for matching
149
+ fieldspec.gsub(/ |-\w*/, '').split('').uniq
150
+ end
151
+
152
+ def sort_helper(x)
153
+ # make sure that everything below 'A' is higher than 'z'
154
+ # note that this only works for numbers, but that is fine in our case.
155
+ x < 'A' ? (x.to_i + 123).chr : x
156
+ end
157
+
158
+ # implementation for methods for retrieving subfield values
159
+ #
160
+ # The methods start with a single character: the operation
161
+ # 'f' for retrieving only the first occurence of the subfield
162
+ # 'a' for retrieving all the subfield values for each of the given subfields
163
+ # if omitted, 'f' is assumed
164
+ #
165
+ # Then a '_' acts as a subdivider between the operation and the subfield(s). It must always be present, even
166
+ # if the operation is omitted.
167
+ #
168
+ # The last past is a sequence of subfield codes that should be used for selecting the values. The order in which the
169
+ # subfields are listed is respected in the resulting array of values.
170
+ #
171
+ # Examples:
172
+ #
173
+ # t = VarField.new('100', '', '',
174
+ # { 'a' => %w'Name NickName',
175
+ # 'b' => %w'LastName MaidenName',
176
+ # 'c' => %w'eMail',
177
+ # '1' => %w'Age',
178
+ # '9' => %w'Score'})
179
+ #
180
+ # # >> 100##$aName$aNickName$bLastName$bMaidenName$ceMail$1Age$9Score <<
181
+ #
182
+ # t._1ab => ['Age', 'Name', 'LastName']
183
+ # # equivalent to: t.f_1av or t.fields('1ab')
184
+ #
185
+ # t.a_9ab => ['Score', 'Name', 'NickName', 'LastName', 'MaidenName']
186
+ # # equivalent to: t.fields_array('9ab')
187
+ #
188
+ # Note that it is not possible to use a fieldspec for the sequence of subfield codes. Spaces and '-' are not allowed
189
+ # in method calls. If you want this, use the #field(s) and #field(s)_array methods.
190
+ #
191
+ def method_missing(name, *args)
192
+ operation, subfields = name.to_s.split('_')
193
+ assert(subfields.size > 0, 'need to specify at least one subfield')
194
+ operation = 'f' if operation.empty?
195
+ # convert subfield list to fieldspec
196
+ subfields = subfields.split('').join(' ')
197
+ case operation
198
+ when 'f'
199
+ if subfields.size > 1
200
+ operation = :fields
201
+ else
202
+ operation = :field
203
+ end
204
+ when 'a'
205
+ if subfields.size > 1
206
+ operation = :fields_array
207
+ else
208
+ operation = :field_array
209
+ end
210
+ else
211
+ throw "Unknown method invocation: '#{name}' with: #{args}"
212
+ end
213
+ send(operation, subfields)
214
+ end
215
+
216
+ def to_ary
217
+ nil
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+ end
224
+ end
@@ -1,5 +1,5 @@
1
1
  module Libis
2
2
  module Tools
3
- VERSION = '0.9.6'
3
+ VERSION = '0.9.7'
4
4
  end
5
5
  end
@@ -280,13 +280,19 @@ module Libis
280
280
  # </jkr:books>
281
281
  # </patron>
282
282
  #
283
- # @param [String] name tag for the new node
284
- # @param [String] value optional content for new node; empty if nil
285
- # @param [Node] parent optional parent node for new node; root if nil; xml document if root is not defined
286
- # @param [Hash] attributes a Hash containing tag-value pairs for each attribute; the special key ':namespaces'
287
- # contains a Hash of namespace definitions as in {#add_namespaces}
283
+ # @param [Array] args arguments being:
284
+ # - tag for the new node
285
+ # - optional content for new node; empty if nil or not present
286
+ # - optional parent node for new node; root if nil or not present; xml document if root is not defined
287
+ # - a Hash containing tag-value pairs for each attribute; the special key ':namespaces'
288
+ # contains a Hash of namespace definitions as in {#add_namespaces}
288
289
  # @return [Nokogiri::XML::Node] the new node
289
- def add_node(name, value = nil, parent = nil, attributes = {})
290
+ def add_node(*args)
291
+ attributes = {}
292
+ attributes = args.pop if args.last.is_a? Hash
293
+ name, value, parent = *args
294
+
295
+ return nil if name.nil?
290
296
 
291
297
  node = Nokogiri::XML::Node.new name.to_s, @document
292
298
  node.content = value
@@ -386,7 +392,7 @@ module Libis
386
392
  end
387
393
 
388
394
  node.namespace_scopes.each do |ns|
389
- node.namespace = ns if ns.prefix == node_ns
395
+ node.namespace = ns if ns.prefix == node_ns.to_s
390
396
  end if node_ns
391
397
 
392
398
  node.default_namespace = default_ns if default_ns
@@ -430,12 +436,24 @@ module Libis
430
436
  # xml_doc.value('//email') # => "harry.potter@hogwarts.edu"
431
437
  #
432
438
  # @param [String] path the name or XPath term to search the node(s)
439
+ # @param [Node] parent parent node; document if nil
433
440
  # @return [String] content or nil if not found
434
- def value(path)
435
- xpath(path).first.content rescue nil
441
+ def value(path, parent = nil)
442
+ parent ||= document
443
+ parent.xpath(path).first.content rescue nil
436
444
  end
437
445
 
438
- alias_method :[], :value
446
+ # Return the content of the first element found.
447
+ #
448
+ # Example:
449
+ #
450
+ # xml_doc['email'] # => "harry.potter@hogwarts.edu"
451
+ #
452
+ # @param [String] path the name or XPath term to search the node(s)
453
+ # @return [String] content or nil if not found
454
+ def [](path)
455
+ xpath(path).first.content rescue nil
456
+ end
439
457
 
440
458
  # Return the content of all elements found.
441
459
  # Example:
@@ -548,8 +566,6 @@ module Libis
548
566
  node
549
567
  end
550
568
 
551
- protected
552
-
553
569
  # Get the first node matching the tag. The node will be seached with XPath search term = "//#{tag}".
554
570
  #
555
571
  # @param [String] tag XML tag to look for; XPath syntax is allowed
data/lib/libis/tools.rb CHANGED
@@ -4,7 +4,9 @@ module Libis
4
4
  autoload :Checksum, 'libis/tools/checksum'
5
5
  autoload :Command, 'libis/tools/command'
6
6
  autoload :Config, 'libis/tools/config'
7
+ autoload :ConfigFile, 'libis/tools/config_file'
7
8
  autoload :DCRecord, 'libis/tools/dc_record'
9
+ autoload :DeepStruct, 'libis/tools/deep_struct'
8
10
  autoload :Logger, 'libis/tools/logger'
9
11
  autoload :MetsFile, 'libis/tools/mets_file'
10
12
  autoload :Parameter, 'libis/tools/parameter'
@@ -30,6 +30,45 @@ describe ::Libis::Tools::ConfigFile do
30
30
  expect(subject.to_hash).to eq hash
31
31
  end
32
32
 
33
+ it 'loads a hash' do
34
+ subject << hash
35
+ expect(subject.to_hash).to eq hash
36
+ end
37
+
38
+ it 'allows to change sub-hash' do
39
+ subject << hash
40
+ # noinspection RubyResolve
41
+ subject.b.v = 1
42
+ hash[:b]['v'] = 1
43
+ expect(subject.to_hash).to eq hash
44
+ end
45
+
46
+ it 'allows to change hash in array' do
47
+ subject << hash
48
+ # noinspection RubyResolve
49
+ subject.c[0][0].a[0].v = 1
50
+ hash[:c][0][0][:a][0]['v'] = 1
51
+ expect(subject.to_hash).to eq hash
52
+ end
53
+
54
+ end
55
+
56
+ context 'initialization with hash' do
57
+ subject { ::Libis::Tools::ConfigFile.new hash }
58
+
59
+ it 'has hash' do
60
+ expect(subject.to_hash).to eq hash
61
+ end
62
+
63
+ end
64
+
65
+ context 'initialization with file' do
66
+ subject { ::Libis::Tools::ConfigFile.new test_file }
67
+
68
+ it 'has hash' do
69
+ expect(subject.to_hash).to eq hash
70
+ end
71
+
33
72
  end
34
73
 
35
74
  end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+ require 'libis/tools/metadata/dublin_core_record'
4
+
5
+ require 'rspec/matchers'
6
+ require 'equivalent-xml'
7
+
8
+ describe 'DublinCoreRecord' do
9
+
10
+ let(:header) { '<?xml version="1.0" encoding="utf-8"?>' }
11
+
12
+ subject(:dc) { Libis::Tools::Metadata::DublinCoreRecord.new(data) {} }
13
+
14
+ def dc_xml(tag, value = '', attributes = {})
15
+ "<dc:#{tag}#{attributes.sort.each{|k,v| " #{k}=\"#{v}\""}.join}>#{value}</dc:#{tag}>"
16
+ end
17
+
18
+ def dcterms_xml(tag, value = '', attributes = {})
19
+ "<dcterms:#{tag}#{attributes.sort.each{|k,v| " #{k}=\"#{v}\""}.join}>#{value}</dcterms:#{tag}>"
20
+ end
21
+
22
+ def match_xml(doc1, doc2)
23
+ xml1 = doc1.is_a?(::Libis::Tools::XmlDocument) ? doc1.document : ::Nokogiri::XML(doc1.to_s)
24
+ xml2 = doc2.is_a?(::Libis::Tools::XmlDocument) ? doc2.document : ::Nokogiri::XML(doc2.to_s)
25
+ # noinspection RubyResolve
26
+ expect(xml1).to be_equivalent_to(xml2).respecting_element_order
27
+ end
28
+
29
+ context 'Empty record' do
30
+ let(:data) { nil }
31
+ let(:root) { <<STR.chomp
32
+ <record \
33
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \
34
+ xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/"
35
+ STR
36
+ }
37
+ let(:record_start) { root + '>' }
38
+ let(:record_end) { '</record>' }
39
+ let(:empty_record) { header + root + '/>' }
40
+
41
+
42
+ it 'contains emtpy record' do
43
+ match_xml dc.document, empty_record
44
+ end
45
+
46
+ it 'add dc:title' do
47
+ dc.title = 'abc'
48
+ match_xml dc.document, header + record_start + dc_xml('title', 'abc') + record_end
49
+ end
50
+
51
+ it 'add dc:date' do
52
+ dc.date = '2001'
53
+ dc.dcdate = '2002'
54
+ dc.dc_date = '2003'
55
+ match_xml dc.document,
56
+ header +
57
+ record_start +
58
+ dc_xml('date', '2001') +
59
+ dc_xml('date', '2002') +
60
+ dc_xml('date', '2003') +
61
+ record_end
62
+ end
63
+
64
+ it 'add dcterms:date' do
65
+ dc.termsdate = '2001'
66
+ dc.dctermsdate = '2002'
67
+ dc.terms_date = '2003'
68
+ dc.dcterms_date = '2004'
69
+ match_xml dc.document,
70
+ header +
71
+ record_start +
72
+ dcterms_xml('date', '2001') +
73
+ dcterms_xml('date', '2002') +
74
+ dcterms_xml('date', '2003') +
75
+ dcterms_xml('date', '2004') +
76
+ record_end
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -199,7 +199,7 @@ describe 'XML Document' do
199
199
 
200
200
  xml_doc.add_node :patron
201
201
  xml_doc.add_node :name, 'Harry Potter'
202
- books = xml_doc.add_node :books, nil, nil, namespaces: { jkr: 'http://JKRowling.com' , node_ns: 'jkr' }
202
+ books = xml_doc.add_node :books, namespaces: { jkr: 'http://JKRowling.com' , node_ns: 'jkr' }
203
203
  xml_doc.add_node :book, nil, books,
204
204
  title: 'Quidditch Through the Ages', author: 'Kennilworthy Whisp', due_date: '1992-4-23',
205
205
  namespaces: {node_ns: 'jkr'}
@@ -362,7 +362,7 @@ describe 'XML Document' do
362
362
 
363
363
  end
364
364
 
365
- it 'should work' do
365
+ it 'allows to parse xml string, save and reload' do
366
366
  xml_doc = ::Libis::Tools::XmlDocument.parse(<<-END.align_left)
367
367
  <patron>
368
368
  <name>Harry Potter</name>
@@ -381,6 +381,10 @@ describe 'XML Document' do
381
381
 
382
382
  match_xml xml_doc.document, @xml_template
383
383
 
384
+ end
385
+
386
+ it 'supports build to create XML document' do
387
+
384
388
  xml_doc = ::Libis::Tools::XmlDocument.build do
385
389
  # noinspection RubyResolve
386
390
  patron {
@@ -394,6 +398,10 @@ describe 'XML Document' do
394
398
 
395
399
  match_xml xml_doc.document, @xml_template
396
400
 
401
+ end
402
+
403
+ it 'supports different ways to create nodes' do
404
+
397
405
  xml_doc = ::Libis::Tools::XmlDocument.new
398
406
  xml_doc.add_node :patron
399
407
  xml_doc.name = 'Harry Potter'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libis-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kris Dekeyser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-21 00:00:00.000000000 Z
11
+ date: 2015-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -194,10 +194,16 @@ files:
194
194
  - lib/libis/tools/extend/string.rb
195
195
  - lib/libis/tools/extend/struct.rb
196
196
  - lib/libis/tools/logger.rb
197
+ - lib/libis/tools/metadata/dublin_core_record.rb
198
+ - lib/libis/tools/metadata/field_spec.rb
199
+ - lib/libis/tools/metadata/fix_field.rb
200
+ - lib/libis/tools/metadata/marc21_record.rb
201
+ - lib/libis/tools/metadata/marc_record.rb
202
+ - lib/libis/tools/metadata/sharepoint_mapping.rb
203
+ - lib/libis/tools/metadata/sharepoint_record.rb
204
+ - lib/libis/tools/metadata/var_field.rb
197
205
  - lib/libis/tools/mets_file.rb
198
206
  - lib/libis/tools/parameter.rb
199
- - lib/libis/tools/sharepoint_mapping.rb
200
- - lib/libis/tools/sharepoint_record.rb
201
207
  - lib/libis/tools/version.rb
202
208
  - lib/libis/tools/xml_document.rb
203
209
  - libis-tools.gemspec
@@ -212,6 +218,7 @@ files:
212
218
  - spec/data/test_config.yml
213
219
  - spec/deep_struct_spec.rb
214
220
  - spec/logger_spec.rb
221
+ - spec/metadata/dublin_core_spec.rb
215
222
  - spec/parameter_container_spec.rb
216
223
  - spec/parameter_spec.rb
217
224
  - spec/spec_helper.rb
@@ -256,6 +263,7 @@ test_files:
256
263
  - spec/data/test_config.yml
257
264
  - spec/deep_struct_spec.rb
258
265
  - spec/logger_spec.rb
266
+ - spec/metadata/dublin_core_spec.rb
259
267
  - spec/parameter_container_spec.rb
260
268
  - spec/parameter_spec.rb
261
269
  - spec/spec_helper.rb