marc 1.0.4 → 1.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/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- data/.github/workflows/ruby.yml +24 -0
- data/.gitignore +17 -0
- data/.standard.yml +1 -0
- data/{Changes → CHANGELOG.md} +106 -29
- data/Gemfile +15 -0
- data/README.md +240 -47
- data/Rakefile +14 -14
- data/bin/marc +14 -0
- data/bin/marc2xml +17 -0
- data/examples/xml2marc.rb +10 -0
- data/lib/marc/constants.rb +3 -3
- data/lib/marc/controlfield.rb +35 -23
- data/lib/marc/datafield.rb +70 -63
- data/lib/marc/dublincore.rb +59 -41
- data/lib/marc/exception.rb +9 -1
- data/lib/marc/jsonl_reader.rb +33 -0
- data/lib/marc/jsonl_writer.rb +44 -0
- data/lib/marc/marc8/map_to_unicode.rb +16417 -16420
- data/lib/marc/marc8/to_unicode.rb +80 -86
- data/lib/marc/reader.rb +119 -121
- data/lib/marc/record.rb +72 -62
- data/lib/marc/subfield.rb +12 -10
- data/lib/marc/unsafe_xmlwriter.rb +93 -0
- data/lib/marc/version.rb +1 -1
- data/lib/marc/writer.rb +27 -30
- data/lib/marc/xml_parsers.rb +222 -197
- data/lib/marc/xmlreader.rb +131 -114
- data/lib/marc/xmlwriter.rb +93 -81
- data/lib/marc.rb +20 -18
- data/marc.gemspec +23 -0
- data/test/marc8/tc_marc8_mapping.rb +3 -3
- data/test/marc8/tc_to_unicode.rb +28 -32
- data/test/messed_up_leader.xml +9 -0
- data/test/tc_controlfield.rb +37 -34
- data/test/tc_datafield.rb +65 -60
- data/test/tc_dublincore.rb +9 -11
- data/test/tc_hash.rb +10 -13
- data/test/tc_jsonl.rb +19 -0
- data/test/tc_marchash.rb +17 -21
- data/test/tc_parsers.rb +108 -144
- data/test/tc_reader.rb +35 -36
- data/test/tc_reader_char_encodings.rb +149 -169
- data/test/tc_record.rb +143 -148
- data/test/tc_subfield.rb +14 -13
- data/test/tc_unsafe_xml.rb +95 -0
- data/test/tc_writer.rb +101 -108
- data/test/tc_xml.rb +99 -87
- data/test/tc_xml_error_handling.rb +7 -8
- data/test/ts_marc.rb +8 -8
- metadata +94 -9
data/lib/marc/xmlreader.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
require File.dirname(__FILE__) +
|
1
|
+
require File.dirname(__FILE__) + "/xml_parsers"
|
2
2
|
module MARC
|
3
|
-
|
4
3
|
# the constructor which you can pass either a filename:
|
5
4
|
#
|
6
5
|
# reader = MARC::XMLReader.new('/Users/edsu/marc.xml')
|
7
6
|
#
|
8
|
-
# or a File object,
|
7
|
+
# or a File object,
|
9
8
|
#
|
10
9
|
# reader = Marc::XMLReader.new(File.new('/Users/edsu/marc.xml'))
|
11
10
|
#
|
12
11
|
# or really any object that responds to read(n)
|
13
|
-
#
|
12
|
+
#
|
14
13
|
# reader = MARC::XMLReader.new(StringIO.new(xml))
|
15
14
|
#
|
16
15
|
# By default, XMLReader uses REXML's pull parser, but you can swap
|
@@ -18,7 +17,7 @@ module MARC
|
|
18
17
|
# 'best' one). The :parser can either be one of the defined constants
|
19
18
|
# or the constant's value.
|
20
19
|
#
|
21
|
-
# reader = MARC::XMLReader.new(fh, :parser=>'magic')
|
20
|
+
# reader = MARC::XMLReader.new(fh, :parser=>'magic')
|
22
21
|
#
|
23
22
|
# It is also possible to set the default parser at the class level so
|
24
23
|
# all subsequent instances will use it instead:
|
@@ -28,151 +27,169 @@ module MARC
|
|
28
27
|
#
|
29
28
|
# Use:
|
30
29
|
# MARC::XMLReader.best_available!
|
31
|
-
#
|
30
|
+
#
|
32
31
|
# or
|
33
32
|
# MARC::XMLReader.nokogiri!
|
34
|
-
#
|
33
|
+
#
|
34
|
+
# By default, all XML parsers except REXML require the MARC namespace
|
35
|
+
# (http://www.loc.gov/MARC21/slim) to be included. Adding the option
|
36
|
+
# `ignore_namespace` to the call to `new` with a true value
|
37
|
+
# will allow parsing to proceed, e.g.,
|
38
|
+
#
|
39
|
+
# reader = MARC::XMLReader.new(filename, parser: :nokogiri, ignore_namespace: true)
|
40
|
+
#
|
41
|
+
# You can also pass in an error_handler option that will be called if
|
42
|
+
# there are any validation errors found when parsing a record.
|
43
|
+
#
|
44
|
+
# reader = MARC::XMLReader.new(fh, error_handler: ->(reader, record, block) { ... })
|
45
|
+
#
|
46
|
+
# By default, a MARC::RecordException is raised halting all future parsing.
|
35
47
|
class XMLReader
|
36
48
|
include Enumerable
|
37
|
-
USE_BEST_AVAILABLE =
|
38
|
-
USE_REXML =
|
39
|
-
USE_NOKOGIRI =
|
40
|
-
USE_JREXML =
|
41
|
-
USE_JSTAX =
|
42
|
-
USE_LIBXML =
|
49
|
+
USE_BEST_AVAILABLE = "magic"
|
50
|
+
USE_REXML = "rexml"
|
51
|
+
USE_NOKOGIRI = "nokogiri"
|
52
|
+
USE_JREXML = "jrexml"
|
53
|
+
USE_JSTAX = "jstax"
|
54
|
+
USE_LIBXML = "libxml"
|
43
55
|
@@parser = USE_REXML
|
44
|
-
attr_reader :parser
|
45
|
-
|
56
|
+
attr_reader :parser, :error_handler
|
57
|
+
|
46
58
|
def initialize(file, options = {})
|
47
59
|
if file.is_a?(String)
|
48
60
|
handle = File.new(file)
|
49
|
-
elsif file.respond_to?(
|
61
|
+
elsif file.respond_to?(:read, 5)
|
50
62
|
handle = file
|
51
63
|
else
|
52
64
|
raise ArgumentError, "must pass in path or File"
|
53
65
|
end
|
54
66
|
@handle = handle
|
55
67
|
|
56
|
-
if options[:
|
57
|
-
|
68
|
+
if options[:ignore_namespace]
|
69
|
+
@ignore_namespace = options[:ignore_namespace]
|
70
|
+
end
|
71
|
+
|
72
|
+
parser = if options[:parser]
|
73
|
+
self.class.choose_parser(options[:parser].to_s)
|
58
74
|
else
|
59
|
-
|
75
|
+
@@parser
|
60
76
|
end
|
77
|
+
|
61
78
|
case parser
|
62
|
-
when
|
63
|
-
when
|
64
|
-
when
|
79
|
+
when "magic" then extend MagicReader
|
80
|
+
when "rexml" then extend REXMLReader
|
81
|
+
when "jrexml"
|
65
82
|
raise ArgumentError, "jrexml only available under jruby" unless defined? JRUBY_VERSION
|
66
83
|
extend JREXMLReader
|
67
|
-
when
|
68
|
-
when
|
84
|
+
when "nokogiri" then extend NokogiriReader
|
85
|
+
when "jstax"
|
69
86
|
raise ArgumentError, "jstax only available under jruby" unless defined? JRUBY_VERSION
|
70
87
|
extend JRubySTAXReader
|
71
|
-
when
|
72
|
-
|
88
|
+
when "libxml" then extend LibXMLReader
|
89
|
+
raise ArgumentError, "libxml not available under jruby" if defined? JRUBY_VERSION
|
73
90
|
end
|
74
|
-
end
|
75
91
|
|
76
|
-
|
77
|
-
def self.parser
|
78
|
-
return @@parser
|
79
|
-
end
|
80
|
-
|
81
|
-
# Returns an array of all the parsers available
|
82
|
-
def self.parsers
|
83
|
-
p = []
|
84
|
-
self.constants.each do | const |
|
85
|
-
next unless const.match("^USE_")
|
86
|
-
p << const
|
87
|
-
end
|
88
|
-
return p
|
89
|
-
end
|
90
|
-
|
91
|
-
# Sets the class parser
|
92
|
-
def self.parser=(p)
|
93
|
-
@@parser = choose_parser(p)
|
92
|
+
@error_handler = options[:error_handler]
|
94
93
|
end
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
parser
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
95
|
+
class << self
|
96
|
+
# Returns the currently set parser type
|
97
|
+
def parser
|
98
|
+
@@parser
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns an array of all the parsers available
|
102
|
+
def parsers
|
103
|
+
p = []
|
104
|
+
constants.each do |const|
|
105
|
+
next unless const.match?("^USE_")
|
106
|
+
p << const
|
108
107
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
p
|
109
|
+
end
|
110
|
+
|
111
|
+
# Sets the class parser
|
112
|
+
def parser=(p)
|
113
|
+
@@parser = choose_parser(p)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the value of the best available parser
|
117
|
+
def best_available
|
118
|
+
parser = nil
|
119
|
+
if defined? JRUBY_VERSION
|
120
|
+
unless parser
|
121
|
+
begin
|
122
|
+
require "nokogiri"
|
123
|
+
parser = USE_NOKOGIRI
|
124
|
+
rescue LoadError
|
125
|
+
end
|
115
126
|
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
127
|
+
unless parser
|
128
|
+
begin
|
129
|
+
# try to find the class, so we throw an error if not found
|
130
|
+
java.lang.Class.forName("javax.xml.stream.XMLInputFactory")
|
131
|
+
parser = USE_JSTAX
|
132
|
+
rescue java.lang.ClassNotFoundException
|
133
|
+
end
|
122
134
|
end
|
123
|
-
end
|
124
|
-
else
|
125
|
-
begin
|
126
|
-
require 'nokogiri'
|
127
|
-
parser = USE_NOKOGIRI
|
128
|
-
rescue LoadError
|
129
|
-
end
|
130
|
-
unless defined? JRUBY_VERSION
|
131
135
|
unless parser
|
132
136
|
begin
|
133
|
-
require
|
134
|
-
parser =
|
137
|
+
require "jrexml"
|
138
|
+
parser = USE_JREXML
|
135
139
|
rescue LoadError
|
136
140
|
end
|
137
|
-
end
|
141
|
+
end
|
142
|
+
else
|
143
|
+
begin
|
144
|
+
require "nokogiri"
|
145
|
+
parser = USE_NOKOGIRI
|
146
|
+
rescue LoadError
|
147
|
+
end
|
148
|
+
unless defined? JRUBY_VERSION
|
149
|
+
unless parser
|
150
|
+
begin
|
151
|
+
require "xml"
|
152
|
+
parser = USE_LIBXML
|
153
|
+
rescue LoadError
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
138
157
|
end
|
158
|
+
parser ||= USE_REXML
|
159
|
+
parser
|
139
160
|
end
|
140
|
-
|
141
|
-
parser
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
next unless const.to_s.match("^USE_")
|
170
|
-
if self.const_get(const) == p
|
171
|
-
match = true
|
172
|
-
return p
|
161
|
+
|
162
|
+
# Sets the best available parser as the default
|
163
|
+
def best_available!
|
164
|
+
@@parser = best_available
|
165
|
+
end
|
166
|
+
|
167
|
+
# Sets Nokogiri as the default parser
|
168
|
+
def nokogiri!
|
169
|
+
@@parser = USE_NOKOGIRI
|
170
|
+
end
|
171
|
+
|
172
|
+
# Sets jrexml as the default parser
|
173
|
+
def jrexml!
|
174
|
+
@@parser = USE_JREXML
|
175
|
+
end
|
176
|
+
|
177
|
+
# Sets REXML as the default parser
|
178
|
+
def rexml!
|
179
|
+
@@parser = USE_REXML
|
180
|
+
end
|
181
|
+
|
182
|
+
def choose_parser(p)
|
183
|
+
match = false
|
184
|
+
constants.each do |const|
|
185
|
+
next unless const.to_s.match?("^USE_")
|
186
|
+
if const_get(const) == p
|
187
|
+
match = true
|
188
|
+
return p
|
189
|
+
end
|
173
190
|
end
|
191
|
+
raise ArgumentError.new("Parser '#{p}' not defined") unless match
|
174
192
|
end
|
175
|
-
raise ArgumentError.new("Parser '#{p}' not defined") unless match
|
176
193
|
end
|
177
194
|
end
|
178
195
|
end
|
data/lib/marc/xmlwriter.rb
CHANGED
@@ -1,155 +1,167 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "rexml/document"
|
2
|
+
require "rexml/text"
|
3
|
+
require "rexml/formatters/default"
|
4
4
|
|
5
5
|
module MARC
|
6
|
-
|
7
6
|
# A class for writing MARC records as MARCXML.
|
8
7
|
# BIG CAVEAT! XMLWriter will *not* convert your MARC8 to UTF8
|
9
8
|
# bug the authors to do this if you need it
|
10
|
-
|
9
|
+
|
11
10
|
class XMLWriter
|
12
|
-
|
13
11
|
# the constructor which you must pass a file path
|
14
12
|
# or an object that responds to a write message
|
15
13
|
# the second argument is a hash of options, currently
|
16
14
|
# only supporting one option, stylesheet
|
17
|
-
#
|
15
|
+
#
|
18
16
|
# writer = XMLWriter.new 'marc.xml', :stylesheet => 'style.xsl'
|
19
17
|
# writer.write record
|
20
|
-
|
21
|
-
|
18
|
+
#
|
19
|
+
|
20
|
+
COLLECTION_TAG = %(<collection xmlns='#{MARC_NS}'
|
21
|
+
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
22
|
+
xsi:schemaLocation="#{MARC_NS} #{MARC_XSD}">).freeze
|
23
|
+
|
24
|
+
def initialize(file, opts = {}, &blk)
|
22
25
|
@writer = REXML::Formatters::Default.new
|
23
|
-
if file.
|
24
|
-
@fh = File.new(file,"w")
|
25
|
-
elsif file.respond_to?(
|
26
|
+
if file.instance_of?(String)
|
27
|
+
@fh = File.new(file, "w")
|
28
|
+
elsif file.respond_to?(:write)
|
26
29
|
@fh = file
|
27
30
|
else
|
28
31
|
raise ArgumentError, "must pass in file name or handle"
|
29
32
|
end
|
30
|
-
|
33
|
+
|
34
|
+
@stylesheet = opts[:stylesheet]
|
35
|
+
|
31
36
|
@fh.write("<?xml version='1.0'?>\n")
|
32
|
-
|
33
|
-
|
34
|
-
%Q{<?xml-stylesheet type="text/xsl" href="#{opts[:stylesheet]}"?>\n})
|
35
|
-
end
|
36
|
-
@fh.write("<collection xmlns='" + MARC_NS + "' " +
|
37
|
-
"xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
|
38
|
-
"xsi:schemaLocation='" + MARC_NS + " " + MARC_XSD + "'>")
|
37
|
+
@fh.write(stylesheet_tag)
|
38
|
+
@fh.write(COLLECTION_TAG)
|
39
39
|
@fh.write("\n")
|
40
|
+
|
41
|
+
if block_given?
|
42
|
+
blk.call(self)
|
43
|
+
self.close
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def stylesheet_tag
|
48
|
+
if @stylesheet
|
49
|
+
%(<?xml-stylesheet type="text/xsl" href="#{@stylesheet}"?>\n)
|
50
|
+
else
|
51
|
+
""
|
52
|
+
end
|
40
53
|
end
|
41
|
-
|
42
|
-
|
54
|
+
|
43
55
|
# write a record to the file or handle
|
44
|
-
|
56
|
+
|
45
57
|
def write(record)
|
46
58
|
@writer.write(MARC::XMLWriter.encode(record), @fh)
|
47
59
|
@fh.write("\n")
|
48
60
|
end
|
49
|
-
|
50
|
-
|
61
|
+
|
51
62
|
# close underlying filehandle
|
52
|
-
|
63
|
+
|
53
64
|
def close
|
54
65
|
@fh.write("</collection>")
|
55
66
|
@fh.close
|
56
67
|
end
|
57
68
|
|
58
|
-
|
69
|
+
def self.fix_leader(leader)
|
70
|
+
fixed_leader = leader.gsub(/[^\w|^\s]/, "Z")
|
71
|
+
|
72
|
+
# The leader must have at least 24 characters
|
73
|
+
fixed_leader = fixed_leader.ljust(24) if fixed_leader.length < 24
|
74
|
+
|
75
|
+
# MARCXML is particular about last four characters; ILSes aren't
|
76
|
+
if fixed_leader[20..23] != "4500"
|
77
|
+
fixed_leader[20..23] = "4500"
|
78
|
+
end
|
79
|
+
|
80
|
+
# MARCXML doesn't like a space here so we need a filler character: Z
|
81
|
+
if fixed_leader[6..6] == " "
|
82
|
+
fixed_leader[6..6] = "Z"
|
83
|
+
end
|
84
|
+
|
85
|
+
fixed_leader
|
86
|
+
end
|
87
|
+
|
59
88
|
# a static method that accepts a MARC::Record object
|
60
89
|
# and returns a REXML::Document for the XML serialization.
|
90
|
+
def self.encode(record, opts = {})
|
91
|
+
single_char = Regexp.new('[\da-z ]{1}')
|
92
|
+
subfield_char = Regexp.new('[\dA-Za-z!"#$%&\'()*+,-./:;<=>?{}_^`~\[\]\\\]{1}')
|
93
|
+
control_field_tag = Regexp.new("00[1-9A-Za-z]{1}")
|
61
94
|
|
62
|
-
def self.encode(record, opts={})
|
63
|
-
singleChar = Regexp.new('[\da-z ]{1}')
|
64
|
-
ctrlFieldTag = Regexp.new('00[1-9A-Za-z]{1}')
|
65
|
-
|
66
95
|
# Right now, this writer handles input from the strict and
|
67
96
|
# lenient MARC readers. Because it can get 'loose' MARC in, it
|
68
97
|
# attempts to do some cleanup on data values that are not valid
|
69
98
|
# MARCXML.
|
70
|
-
|
99
|
+
|
71
100
|
# TODO? Perhaps the 'loose MARC' checks should be split out
|
72
101
|
# into a tolerant MARCXMLWriter allowing the main one to skip
|
73
102
|
# this extra work.
|
74
|
-
|
103
|
+
|
75
104
|
# TODO: At the very least there should be some logging
|
76
105
|
# to record our attempts to account for less than perfect MARC.
|
77
|
-
|
78
|
-
e = REXML::Element.new(
|
106
|
+
|
107
|
+
e = REXML::Element.new("record")
|
79
108
|
e.add_namespace(MARC_NS) if opts[:include_namespace]
|
80
109
|
|
81
|
-
|
82
|
-
record.leader
|
83
|
-
|
84
|
-
# MARCXML is particular about last four characters; ILSes aren't
|
85
|
-
if (record.leader[20..23] != "4500")
|
86
|
-
record.leader[20..23] = "4500"
|
87
|
-
end
|
110
|
+
leader_element = REXML::Element.new("leader")
|
111
|
+
leader_element.add_text(fix_leader(record.leader))
|
112
|
+
e.add_element(leader_element)
|
88
113
|
|
89
|
-
# MARCXML doesn't like a space here so we need a filler character: Z
|
90
|
-
if (record.leader[6..6] == " ")
|
91
|
-
record.leader[6..6] = "Z"
|
92
|
-
end
|
93
|
-
|
94
|
-
leader = REXML::Element.new("leader")
|
95
|
-
leader.add_text(record.leader)
|
96
|
-
e.add_element(leader)
|
97
|
-
|
98
114
|
record.each do |field|
|
99
|
-
if field.
|
115
|
+
if field.instance_of?(MARC::DataField)
|
100
116
|
datafield_elem = REXML::Element.new("datafield")
|
101
|
-
|
117
|
+
|
118
|
+
ind1 = field.indicator1
|
102
119
|
# If marc is leniently parsed, we may have some dirty data; using
|
103
120
|
# the 'z' ind1 value should help us locate these later to fix
|
104
|
-
if
|
105
|
-
|
106
|
-
end
|
107
|
-
|
121
|
+
ind1 = "z" if ind1.nil? || !ind1.match?(single_char)
|
122
|
+
ind2 = field.indicator2
|
108
123
|
# If marc is leniently parsed, we may have some dirty data; using
|
109
124
|
# the 'z' ind2 value should help us locate these later to fix
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
125
|
+
|
126
|
+
ind2 = "z" if field.indicator2.nil? || !ind2.match?(single_char)
|
127
|
+
|
114
128
|
datafield_elem.add_attributes({
|
115
|
-
"tag"=>field.tag,
|
116
|
-
"ind1"=>
|
117
|
-
"ind2"=>
|
129
|
+
"tag" => field.tag,
|
130
|
+
"ind1" => ind1,
|
131
|
+
"ind2" => ind2
|
118
132
|
})
|
119
133
|
|
120
|
-
|
134
|
+
field.subfields.each do |subfield|
|
121
135
|
subfield_element = REXML::Element.new("subfield")
|
122
|
-
|
136
|
+
|
137
|
+
code = subfield.code
|
123
138
|
# If marc is leniently parsed, we may have some dirty data; using
|
124
139
|
# the blank subfield code should help us locate these later to fix
|
125
|
-
if
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
subfield_element.add_attribute("code", subfield.code)
|
140
|
+
code = " " if subfield.code.match(subfield_char).nil?
|
141
|
+
|
142
|
+
subfield_element.add_attribute("code", code)
|
130
143
|
text = subfield.value
|
131
144
|
subfield_element.add_text(text)
|
132
145
|
datafield_elem.add_element(subfield_element)
|
133
146
|
end
|
134
|
-
|
147
|
+
|
135
148
|
e.add_element datafield_elem
|
136
|
-
elsif field.
|
149
|
+
elsif field.instance_of?(MARC::ControlField)
|
137
150
|
control_element = REXML::Element.new("controlfield")
|
138
|
-
|
151
|
+
|
152
|
+
tag = field.tag
|
139
153
|
# We need a marker for invalid tag values (we use 000)
|
140
|
-
unless
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
control_element.add_attribute("tag", field.tag)
|
154
|
+
tag = "00z" unless tag.match(control_field_tag) || MARC::ControlField.control_tag?(tag)
|
155
|
+
|
156
|
+
control_element.add_attribute("tag", tag)
|
145
157
|
text = field.value
|
146
158
|
control_element.add_text(text)
|
147
159
|
e.add_element(control_element)
|
148
160
|
end
|
149
161
|
end
|
150
|
-
|
162
|
+
|
151
163
|
# return xml
|
152
|
-
|
164
|
+
e
|
153
165
|
end
|
154
166
|
end
|
155
167
|
end
|
data/lib/marc.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#marc is a ruby library for reading and writing MAchine Readable Cataloging
|
2
|
-
#(MARC). More information about MARC can be found at <http://www.loc.gov/marc>.
|
1
|
+
# marc is a ruby library for reading and writing MAchine Readable Cataloging
|
2
|
+
# (MARC). More information about MARC can be found at <http://www.loc.gov/marc>.
|
3
3
|
#
|
4
|
-
#USAGE
|
4
|
+
# USAGE
|
5
5
|
#
|
6
6
|
# require 'marc'
|
7
7
|
#
|
@@ -11,7 +11,7 @@
|
|
11
11
|
# puts record['245']['a']
|
12
12
|
# end
|
13
13
|
#
|
14
|
-
# # creating a record
|
14
|
+
# # creating a record
|
15
15
|
# record = MARC::Record.new()
|
16
16
|
# record.add_field(MARC::DataField.new('100', '0', ' ', ['a', 'John Doe']))
|
17
17
|
#
|
@@ -30,17 +30,19 @@
|
|
30
30
|
# record = MARC::Record.new()
|
31
31
|
# record.add_field(MARC::ControlField.new('FMT', 'Book')) # doesn't raise an error
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
require_relative "marc/version"
|
34
|
+
require_relative "marc/constants"
|
35
|
+
require_relative "marc/record"
|
36
|
+
require_relative "marc/datafield"
|
37
|
+
require_relative "marc/controlfield"
|
38
|
+
require_relative "marc/subfield"
|
39
|
+
require_relative "marc/reader"
|
40
|
+
require_relative "marc/writer"
|
41
|
+
require_relative "marc/exception"
|
42
|
+
require_relative "marc/xmlwriter"
|
43
|
+
require_relative "marc/unsafe_xmlwriter"
|
44
|
+
require_relative "marc/xmlreader"
|
45
|
+
require_relative "marc/dublincore"
|
46
|
+
require_relative "marc/xml_parsers"
|
47
|
+
require_relative "marc/jsonl_reader"
|
48
|
+
require_relative "marc/jsonl_writer"
|
data/marc.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "lib/marc/version")
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "marc"
|
5
|
+
s.version = MARC::VERSION
|
6
|
+
s.author = "Ed Summers"
|
7
|
+
s.email = "ehs@pobox.com"
|
8
|
+
s.homepage = "https://github.com/ruby-marc/ruby-marc/"
|
9
|
+
s.summary = "A ruby library for working with Machine Readable Cataloging"
|
10
|
+
s.license = "MIT"
|
11
|
+
s.required_ruby_version = ">= 1.8.6"
|
12
|
+
s.authors = ["Kevin Clarke", "Bill Dueber", "William Groppe", "Jonathan Rochkind", "Ross Singer", "Ed Summers", "Chris Beer"]
|
13
|
+
|
14
|
+
s.files = `git ls-files -z`.split("\x0")
|
15
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_development_dependency "standard", "~>1.0"
|
20
|
+
s.add_dependency "scrub_rb", ">= 1.0.1", "< 2" # backport for ruby 2.1 String#scrub
|
21
|
+
s.add_dependency "unf" # unicode normalization
|
22
|
+
s.add_dependency "rexml" # rexml was unbundled from the stdlib in ruby 3
|
23
|
+
end
|