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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
  3. data/.github/workflows/ruby.yml +24 -0
  4. data/.gitignore +17 -0
  5. data/.standard.yml +1 -0
  6. data/{Changes → CHANGELOG.md} +106 -29
  7. data/Gemfile +15 -0
  8. data/README.md +240 -47
  9. data/Rakefile +14 -14
  10. data/bin/marc +14 -0
  11. data/bin/marc2xml +17 -0
  12. data/examples/xml2marc.rb +10 -0
  13. data/lib/marc/constants.rb +3 -3
  14. data/lib/marc/controlfield.rb +35 -23
  15. data/lib/marc/datafield.rb +70 -63
  16. data/lib/marc/dublincore.rb +59 -41
  17. data/lib/marc/exception.rb +9 -1
  18. data/lib/marc/jsonl_reader.rb +33 -0
  19. data/lib/marc/jsonl_writer.rb +44 -0
  20. data/lib/marc/marc8/map_to_unicode.rb +16417 -16420
  21. data/lib/marc/marc8/to_unicode.rb +80 -86
  22. data/lib/marc/reader.rb +119 -121
  23. data/lib/marc/record.rb +72 -62
  24. data/lib/marc/subfield.rb +12 -10
  25. data/lib/marc/unsafe_xmlwriter.rb +93 -0
  26. data/lib/marc/version.rb +1 -1
  27. data/lib/marc/writer.rb +27 -30
  28. data/lib/marc/xml_parsers.rb +222 -197
  29. data/lib/marc/xmlreader.rb +131 -114
  30. data/lib/marc/xmlwriter.rb +93 -81
  31. data/lib/marc.rb +20 -18
  32. data/marc.gemspec +23 -0
  33. data/test/marc8/tc_marc8_mapping.rb +3 -3
  34. data/test/marc8/tc_to_unicode.rb +28 -32
  35. data/test/messed_up_leader.xml +9 -0
  36. data/test/tc_controlfield.rb +37 -34
  37. data/test/tc_datafield.rb +65 -60
  38. data/test/tc_dublincore.rb +9 -11
  39. data/test/tc_hash.rb +10 -13
  40. data/test/tc_jsonl.rb +19 -0
  41. data/test/tc_marchash.rb +17 -21
  42. data/test/tc_parsers.rb +108 -144
  43. data/test/tc_reader.rb +35 -36
  44. data/test/tc_reader_char_encodings.rb +149 -169
  45. data/test/tc_record.rb +143 -148
  46. data/test/tc_subfield.rb +14 -13
  47. data/test/tc_unsafe_xml.rb +95 -0
  48. data/test/tc_writer.rb +101 -108
  49. data/test/tc_xml.rb +99 -87
  50. data/test/tc_xml_error_handling.rb +7 -8
  51. data/test/ts_marc.rb +8 -8
  52. metadata +94 -9
@@ -1,16 +1,15 @@
1
- require File.dirname(__FILE__) + '/xml_parsers'
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 = 'magic'
38
- USE_REXML = 'rexml'
39
- USE_NOKOGIRI = 'nokogiri'
40
- USE_JREXML = 'jrexml'
41
- USE_JSTAX = 'jstax'
42
- USE_LIBXML = '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?("read", 5)
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[:parser]
57
- parser = self.class.choose_parser(options[:parser].to_s)
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
- parser = @@parser
75
+ @@parser
60
76
  end
77
+
61
78
  case parser
62
- when 'magic' then extend MagicReader
63
- when 'rexml' then extend REXMLReader
64
- when 'jrexml' then
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 'nokogiri' then extend NokogiriReader
68
- when 'jstax' then
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 'libxml' then extend LibXMLReader
72
- raise ArgumentError, "libxml not available under jruby" if defined? JRUBY_VERSION
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
- # Returns the currently set parser type
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
- # Returns the value of the best available parser
97
- def self.best_available
98
- parser = nil
99
- jruby = [USE_NOKOGIRI, USE_JSTAX, USE_JREXML]
100
- ruby = [USE_NOKOGIRI, USE_LIBXML]
101
- if defined? JRUBY_VERSION
102
- unless parser
103
- begin
104
- require 'nokogiri'
105
- parser = USE_NOKOGIRI
106
- rescue LoadError
107
- end
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
- unless parser
110
- begin
111
- # try to find the class, so we throw an error if not found
112
- java.lang.Class.forName("javax.xml.stream.XMLInputFactory")
113
- parser = USE_JSTAX
114
- rescue java.lang.ClassNotFoundException
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
- end
117
- unless parser
118
- begin
119
- require 'jrexml'
120
- parser = USE_JREXML
121
- rescue LoadError
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 'xml'
134
- parser = USE_LIBXML
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
- parser = USE_REXML unless parser
141
- parser
142
- end
143
-
144
- # Sets the best available parser as the default
145
- def self.best_available!
146
- @@parser = self.best_available
147
- end
148
-
149
- # Sets Nokogiri as the default parser
150
- def self.nokogiri!
151
- @@parser = USE_NOKOGIRI
152
- end
153
-
154
- # Sets jrexml as the default parser
155
- def self.jrexml!
156
- @@parser = USE_JREXML
157
- end
158
-
159
- # Sets REXML as the default parser
160
- def self.rexml!
161
- @@parser = USE_REXML
162
- end
163
-
164
- protected
165
-
166
- def self.choose_parser(p)
167
- match = false
168
- self.constants.each do | const |
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
@@ -1,155 +1,167 @@
1
- require 'rexml/document'
2
- require 'rexml/text'
3
- require 'rexml/formatters/default'
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
- def initialize(file, opts={})
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.class == String
24
- @fh = File.new(file,"w")
25
- elsif file.respond_to?('write')
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
- if opts[:stylesheet]
33
- @fh.write(
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('record')
106
+
107
+ e = REXML::Element.new("record")
79
108
  e.add_namespace(MARC_NS) if opts[:include_namespace]
80
109
 
81
- # MARCXML only allows alphanumerics or spaces in the leader
82
- record.leader.gsub!(/[^\w|^\s]/, 'Z')
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.class == MARC::DataField
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 field.indicator1.nil? || (field.indicator1.match(singleChar) == nil)
105
- field.indicator1 = 'z'
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
- if field.indicator2.nil? || (field.indicator2.match(singleChar) == nil)
111
- field.indicator2 = 'z'
112
- end
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"=>field.indicator1,
117
- "ind2"=>field.indicator2
129
+ "tag" => field.tag,
130
+ "ind1" => ind1,
131
+ "ind2" => ind2
118
132
  })
119
133
 
120
- for subfield in field.subfields
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 (subfield.code.match(singleChar) == nil)
126
- subfield.code = ' '
127
- end
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.class == MARC::ControlField
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 field.tag.match(ctrlFieldTag) or MARC::ControlField.control_tag?(ctrlFieldTag)
141
- field.tag = "00z"
142
- end
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
- return e
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
- require File.dirname(__FILE__) + '/marc/version'
35
- require File.dirname(__FILE__) + '/marc/constants'
36
- require File.dirname(__FILE__) + '/marc/record'
37
- require File.dirname(__FILE__) + '/marc/datafield'
38
- require File.dirname(__FILE__) + '/marc/controlfield'
39
- require File.dirname(__FILE__) + '/marc/subfield'
40
- require File.dirname(__FILE__) + '/marc/reader'
41
- require File.dirname(__FILE__) + '/marc/writer'
42
- require File.dirname(__FILE__) + '/marc/exception'
43
- require File.dirname(__FILE__) + '/marc/xmlwriter'
44
- require File.dirname(__FILE__) + '/marc/xmlreader'
45
- require File.dirname(__FILE__) + '/marc/dublincore'
46
- require File.dirname(__FILE__) + '/marc/xml_parsers'
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
@@ -1,6 +1,6 @@
1
- require 'test/unit'
2
- require 'marc'
3
- require 'marc/marc8/map_to_unicode'
1
+ require "test/unit"
2
+ require "marc"
3
+ require "marc/marc8/map_to_unicode"
4
4
 
5
5
  class TestMarc8Mapping < Test::Unit::TestCase
6
6
  def test_codesets_just_exist