marc4j4r 0.1.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.
data/lib/marc4j4r.rb ADDED
@@ -0,0 +1,345 @@
1
+ unless defined? JRUBY_VERSION
2
+ raise "Only works under JRUBY"
3
+ end
4
+
5
+ begin
6
+ include_class Java::org.marc4j.marc.impl.RecordImpl
7
+ rescue NameError => e
8
+ jardir = File.join(File.dirname(__FILE__), '..', 'jars')
9
+ require "#{jardir}/marc4j.jar"
10
+ end
11
+
12
+ require 'set'
13
+
14
+ module MARC4J4R
15
+
16
+ # Add some sugar to the MarcReader interface
17
+ #
18
+ # Adjust the interface so that a #new call to any implementations that
19
+ # implement it can take a java.io.InputStream, ruby IO obejct, or String
20
+ # (that will be interpreted as a filename) without complaining.
21
+ #
22
+ # The mechanism -- running module_eval on a string-representation of the
23
+ # new method in each of the hard-coded implementations of MarcReader
24
+ # (MarcStreamReader,MarcPermissiveStreamReader,MarcXmlReader) -- is ugly
25
+ # and deeply unsettling.
26
+ #
27
+ # @author Bill Dueber
28
+ #
29
+ # A string used to override the initializer for each stream reader
30
+ # Need to do it this ugly way because of the way java and ruby interact;
31
+ # can't just add it to the MarcReader interface the way I wanted to.
32
+
33
+ NEWINIT = <<-ENDBINDER
34
+ alias_method :oldinit, :initialize
35
+ def initialize(fromwhere)
36
+ stream = nil
37
+ if fromwhere.is_a? Java::JavaIO::InputStream
38
+ stream = fromwhere
39
+ elsif fromwhere.is_a? IO
40
+ stream = fromwhere.to_inputstream
41
+ else
42
+ stream = java.io.FileInputStream.new(fromwhere.to_java_string)
43
+ end
44
+ if self.class == Java::org.marc4j.MarcPermissiveStreamReader
45
+ self.oldinit(stream, true, true)
46
+ else
47
+ self.oldinit(stream)
48
+ end
49
+ end
50
+ ENDBINDER
51
+
52
+ Java::org.marc4j.MarcStreamReader.module_eval(NEWINIT)
53
+ Java::org.marc4j.MarcPermissiveStreamReader.module_eval(NEWINIT)
54
+ Java::org.marc4j.MarcXmlReader.module_eval(NEWINIT)
55
+
56
+
57
+ # Get a marc reader of the appropriate type
58
+ # @param [String, IO, java.io.InputStream] input The IO stream (or filename) from which you want to read
59
+ # @param [:strictmarc, :permissivemarc, :marcxml] The type of MARC reader you want.
60
+ # @return [MarcReader] A MarcReader object with the syntactic sugar added in this file (e.g, each)
61
+ #
62
+ # @example Get a strict binary MARC reader for the file 'test.mrc'
63
+ # reader = MARC4J4R.reader('test.mrc')
64
+ #
65
+ # @example Get a permissive binary MARC reader
66
+ # reader = MARC4J4R.reader('test.mrc', :permissivemarc)
67
+ #
68
+ # @example Get a reader for an xml file
69
+ # reader = MARC4J4R.reader('test.xml', :marcxml)
70
+ #
71
+ # @example Get a reader based on an existing IO object
72
+ # require 'open-uri'
73
+ # infile = open('http://my.machine.com/test.mrc')
74
+ # reader = MARC4J4R.reader(infile)
75
+
76
+ def reader(input, type = :strictmarc)
77
+ case type
78
+ when :strictmarc
79
+ return Java::org.marc4j.MarcStreamReader.new(input)
80
+ when :permissivemarc
81
+ return Java::org.marc4j.MarcPermissiveStreamReader.new(input)
82
+ when :marcxml
83
+ return Java::org.marc4j.MarcXmlReader.new(input)
84
+ else
85
+ raise ArgumentError, "Reader type must be :strictmarc, :permissivemarc, or :marcxml"
86
+ end
87
+ end
88
+ module_function :reader
89
+
90
+ end
91
+
92
+
93
+ # Re-open the MarcReader interface, define #each and include Enumerable
94
+ #
95
+ # We also automatically call #hashify on the records that stream through
96
+ # #each in order to speed up RecordImpl#[] when (a) doing many operations on a single
97
+ # record, and (b) we're not worried about interleaved tags (e.g., a 520 followed by a 510 followed
98
+ # by another 520)
99
+
100
+ module Java::OrgMarc4j::MarcReader
101
+ include Enumerable
102
+
103
+ # Return the next record, after calling #hashify on it
104
+ def each
105
+ while self.hasNext
106
+ r = self.next
107
+ r.hashify
108
+ yield r
109
+ end
110
+ end
111
+ end
112
+
113
+
114
+
115
+ include_class Java::org.marc4j.marc.impl::RecordImpl
116
+ include_class Java::org.marc4j.marc.impl::ControlFieldImpl
117
+ include_class Java::org.marc4j.marc.impl::DataFieldImpl
118
+ include_class Java::org.marc4j.marc.impl::SubfieldImpl
119
+
120
+ # Open up RecordImpl to add some sugar, including Enumberable as well
121
+ # @author Bill Dueber
122
+
123
+ class RecordImpl
124
+ include Enumerable
125
+
126
+ # Create a local hash by tag number; makes some stuff faster
127
+ # Called automatically if you use reader.each
128
+
129
+ def hashify
130
+ return if @hashedtags # don't do it more than once
131
+ @hashedtags = {}
132
+ self.getVariableFields.each do |f|
133
+ @hashedtags[f.tag] ||= []
134
+ @hashedtags[f.tag].push f
135
+ end
136
+ end
137
+
138
+ # Create a nice string of the record
139
+ def to_s
140
+ arr = ['LEADER ' + self.leader]
141
+ self.each do |f|
142
+ arr.push f.to_s
143
+ end
144
+ return arr.join("\n")
145
+ end
146
+
147
+ # Get the leader as a string (marc4j would otherwise return Leader object)
148
+ def leader
149
+ self.get_leader.toString
150
+ end
151
+
152
+ # Cycle through the fields in the order the appear in the record
153
+ def each
154
+ self.getVariableFields.each do |f|
155
+ yield f
156
+ end
157
+ end
158
+
159
+ # Get the first field associated with a tag
160
+ # @param [String] tag The tag
161
+ # @return [Field] The first matching field, or nil if none. Note that
162
+ # to mirror ruby-marc, this returns a single field
163
+
164
+ def [] tag
165
+ if defined? @hashedtags
166
+ return @hashedtags[tag][0]
167
+ else
168
+ return self.getVariableField(tag)
169
+ end
170
+ end
171
+
172
+
173
+ # Get a (possibly empty) list of fields with the given tag(s)
174
+ #
175
+ # @param [String, Array<String>] tags A string (or Array of strings) with the tags you're interested in
176
+ # @param [Boolean] originalorder Whether or not results should be presented in the original order within the
177
+ # record or with a two-column sort of (a) Order of the tag in the list of tags sent, (b) order within that tag
178
+ # in the record
179
+ # @return [Array<Field>] Either an empty list or a list of one or more matched fields will be returned.
180
+ #
181
+ # originalorder == false will use an internal hash and be faster in many cases (see #hashify)
182
+ #
183
+ # @example originalorder == false
184
+ # # Given a record that looks like
185
+ # # 010 $a 68027371
186
+ # # 035 $a (RLIN)MIUG0001728-B
187
+ # # 035 $a (CaOTULAS)159818044
188
+ # # 035 $a (OCoLC)ocm00001728
189
+ #
190
+ # r.find_by_tag(['035', '010']).each {|f| puts f.to_s}
191
+ # # 035 $a (RLIN)MIUG0001728-B
192
+ # # 035 $a (CaOTULAS)159818044
193
+ # # 035 $a (OCoLC)ocm00001728
194
+ # # 010 $a 68027371
195
+ #
196
+ # # The results are ordered first by tag as passed in, then by original order within the tag
197
+ #
198
+ # @example originalorder == true
199
+ # r.find_by_tag(['035', '010'], true).each {|f| puts f.to_s}
200
+ # # 010 $a 68027371
201
+ # # 035 $a (RLIN)MIUG0001728-B
202
+ # # 035 $a (CaOTULAS)159818044
203
+ # # 035 $a (OCoLC)ocm00001728
204
+
205
+
206
+ def find_by_tag(tags, originalorder = false)
207
+ if !tags.is_a? Array
208
+ tags = [tags]
209
+ end
210
+ if (originalorder == false and @hashedtags == nil)
211
+ self.hashify
212
+ end
213
+ if originalorder
214
+ return self.find_all {|f| tags.include? f.tag}
215
+ else
216
+ # puts "Tags is #{tags}: got #{@hashedtags.values_at(*tags)}"
217
+ return @hashedtags.values_at(*tags).flatten.compact
218
+ end
219
+ end
220
+
221
+
222
+ # Return the record as valid MARC-XML
223
+ # @return String A MARC-XML representation of the record, including the XML header
224
+ def to_xml
225
+ return @xml if @xml
226
+ @xml = java.io.StringWriter.new
227
+ res = javax.xml.transform.stream.StreamResult.new(@xml)
228
+ writer = org.marc4j.MarcXmlWriter.new(res)
229
+ writer.write(self)
230
+ return @xml.toString
231
+ end
232
+
233
+ end
234
+
235
+ class ControlFieldImpl
236
+ def value
237
+ return self.data
238
+ end
239
+
240
+ # Pretty-print
241
+ # @param [String] joiner What string to use to join the subfields
242
+ # @param [String] The pretty string
243
+ def to_s
244
+ return self.tag + " " + self.value
245
+ end
246
+
247
+ end
248
+
249
+ class DataFieldImpl
250
+ include Enumerable
251
+
252
+ # Pretty-print
253
+ # @param [String] joiner What string to use to join the subfields
254
+ # @param [String] The pretty string
255
+ def to_s (joiner = ' ')
256
+ arr = [self.tag + ' ' + self.indicator1 + self.indicator2]
257
+ self.each do |s|
258
+ arr.push s.to_s
259
+ end
260
+ return arr.join(joiner)
261
+ end
262
+
263
+ # Get the value of the first subfield of this field with the given code
264
+ # @param [String] code 1-character string of the subfield code
265
+ # @return [String] The value of the first matched subfield
266
+ def [] code
267
+ raise ArgumentError, "Code must be a one-character string, not #{code}" unless code.is_a? String and code.size == 1
268
+ # note that code[0] is just converting the one-character string into an integer
269
+ # char value that the underlying java can deal with
270
+ self.getSubfield(code[0]).getData
271
+ end
272
+
273
+
274
+ # Get all values from the subfields for the given code or array of codes
275
+ # @param [String, Array<String>] code (Array of?) 1-character string(s) of the subfield code
276
+ # @param [Boolean] myorder Use the order of subfields that I gave instead of the order they're in the record
277
+ # @return [Array<String>] A possibly-empty array of Strings made up of the values in the subfields whose
278
+ # code is included in the given codes. If myorder == true, use the order in which they are passed in; if a code is repeated
279
+ # (ocassionally legal) subfield values will appear first ordered by the passed array, then by order within
280
+ # the document.
281
+ #
282
+ # If myorder is false, just return the values for matching subfields in the order they appear in the field.
283
+ #
284
+ # @example Quick examples:
285
+ # # 260 $a New York, $b Van Nostrand Reinhold Co. $c 1969
286
+ # rec['260'].sub_values('a') #=> ["New York,"]
287
+ # rec['260'].sub_values(['a', 'c']) #=> ["New York,", "1969"]
288
+ # rec['260'].sub_values(['c', 'a']) #=> ["New York,", "1969"]
289
+ # rec['260'].sub_values(['c', 'a'], true) #=> ["1969", "New York"]
290
+
291
+ def sub_values(code, myorder = false)
292
+ unless [Set, Array].include? code.class
293
+ code = [code]
294
+ # puts "Arrayified for code #{code} / #{code.class}"
295
+ end
296
+ if myorder
297
+ subs = []
298
+ code.each do |c|
299
+ subs << self.find_all {|s| c == s.code}
300
+ end
301
+ return subs.flatten.map {|s| s.data}
302
+ else
303
+ return self.find_all{|s| code.include? s.code}.map {|s| s.data}
304
+ end
305
+ end
306
+
307
+ # Get first indicator as a one-character string
308
+ def indicator1
309
+ return self.getIndicator1.chr
310
+ end
311
+
312
+ # Get second indicator as a one-character string
313
+ def indicator2
314
+ return self.getIndicator2.chr
315
+ end
316
+
317
+ # Iterate over the subfields
318
+ def each
319
+ self.getSubfields.each do |s|
320
+ yield s
321
+ end
322
+ end
323
+
324
+ # Get the concatentated values of the subfields in order the appear in the field
325
+ # @param [String] joiner The string used to join the subfield values
326
+ def value joiner=' '
327
+ data = self.getSubfields.map {|s| s.data}
328
+ return data.join(joiner)
329
+ end
330
+ end
331
+
332
+ class SubfieldImpl
333
+
334
+ def value
335
+ return self.data
336
+ end
337
+
338
+ def code
339
+ return self.getCode.chr
340
+ end
341
+
342
+ def to_s
343
+ return '$' + self.code + " " + self.data
344
+ end
345
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'marc4j4r'
8
+
9
+ class Test::Unit::TestCase
10
+ end
data/test/one.dat ADDED
@@ -0,0 +1 @@
1
+ 00755cam 22002414a 4500001001300000003000600013005001700019008004100036010001700077020004300094040001800137042000800155050002600163082001700189100003100206245005400237260004200291300007200333500003300405650003700438630002500475630001300500fol05731351 IMchF20000613133448.0000107s2000 nyua 001 0 eng  a 00020737  a0471383147 (paper/cd-rom : alk. paper) aDLCcDLCdDLC apcc00aQA76.73.P22bM33 200000a005.13/32211 aMartinsson, Tobias,d1976-10aActivePerl with ASP and ADO /cTobias Martinsson. aNew York :bJohn Wiley & Sons,c2000. axxi, 289 p. :bill. ;c23 cm. +e1 computer laser disc (4 3/4 in.) a"Wiley Computer Publishing." 0aPerl (Computer program language)00aActive server pages.00aActiveX.
data/test/one.xml ADDED
@@ -0,0 +1,55 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.loc.gov/MARC21/slim" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd">
3
+ <record>
4
+ <leader> njm a22 uu 4500</leader>
5
+ <controlfield tag="001">afc99990058366</controlfield>
6
+ <controlfield tag="003">DLC</controlfield>
7
+ <controlfield tag="005">20071104155141.9</controlfield>
8
+ <controlfield tag="007">sd ummunniauub</controlfield>
9
+ <controlfield tag="008">071103s1939 xxufmnne||||||||| u eng||</controlfield>
10
+ <datafield tag="010" ind1=" " ind2=" ">
11
+ <subfield code="a">afc99990058366</subfield>
12
+ </datafield>
13
+ <datafield tag="040" ind1=" " ind2=" ">
14
+ <subfield code="a">DLC</subfield>
15
+ <subfield code="c">DLC</subfield>
16
+ </datafield>
17
+ <datafield tag="245" ind1="0" ind2="4">
18
+ <subfield code="a">The Texas ranger</subfield>
19
+ <subfield code="h">[sound recording] /</subfield>
20
+ <subfield code="c">Sung by Beale D. Taylor.</subfield>
21
+ </datafield>
22
+ <datafield tag="260" ind1=" " ind2=" ">
23
+ <subfield code="a">Medina, Texas,</subfield>
24
+ <subfield code="c">1939.</subfield>
25
+ </datafield>
26
+ <datafield tag="300" ind1=" " ind2=" ">
27
+ <subfield code="a">1 sound disc :</subfield>
28
+ <subfield code="b">analog, 33 1/3 rpm, mono. ;</subfield>
29
+ <subfield code="c">12 in.</subfield>
30
+ </datafield>
31
+ <datafield tag="651" ind1=" " ind2="0">
32
+ <subfield code="a">Medina</subfield>
33
+ <subfield code="z">Texas</subfield>
34
+ <subfield code="z">United States of America.</subfield>
35
+ </datafield>
36
+ <datafield tag="700" ind1="1" ind2=" ">
37
+ <subfield code="a">Lomax, John Avery, 1867-1948</subfield>
38
+ <subfield code="e">Recording engineer.</subfield>
39
+ </datafield>
40
+ <datafield tag="700" ind1="1" ind2=" ">
41
+ <subfield code="a">Lomax, Ruby T. (Ruby Terrill)</subfield>
42
+ <subfield code="e">Recording engineer.</subfield>
43
+ </datafield>
44
+ <datafield tag="700" ind1="1" ind2=" ">
45
+ <subfield code="a">Taylor, Beale D.</subfield>
46
+ <subfield code="e">Singer.</subfield>
47
+ </datafield>
48
+ <datafield tag="852" ind1=" " ind2=" ">
49
+ <subfield code="a">American Folklife Center, Library of Congress</subfield>
50
+ </datafield>
51
+ <datafield tag="852" ind1=" " ind2=" ">
52
+ <subfield code="a">DLC</subfield>
53
+ </datafield>
54
+ </record>
55
+ </collection>
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ # one.xml
4
+ # LEADER 00000njm a2200000uu 4500
5
+ # 001 afc99990058366
6
+ # 003 DLC
7
+ # 005 20071104155141.9
8
+ # 007 sd ummunniauub
9
+ # 008 071103s1939 xxufmnne||||||||| u eng||
10
+ # 010 $a afc99990058366
11
+ # 040 $a DLC $c DLC
12
+ # 245 04 $a The Texas ranger $h [sound recording] / $c Sung by Beale D. Taylor.
13
+ # 260 $a Medina, Texas, $c 1939.
14
+ # 300 $a 1 sound disc : $b analog, 33 1/3 rpm, mono. ; $c 12 in.
15
+ # 651 0 $a Medina $z Texas $z United States of America.
16
+ # 700 1 $a Lomax, John Avery, 1867-1948 $e Recording engineer.
17
+ # 700 1 $a Lomax, Ruby T. (Ruby Terrill) $e Recording engineer.
18
+ # 700 1 $a Taylor, Beale D. $e Singer.
19
+ # 852 $a American Folklife Center, Library of Congress
20
+ # 852 $a DLC
21
+
22
+ class TestMarc4j4r < Test::Unit::TestCase
23
+
24
+ def setup
25
+ reader = MARC4J4R.reader(File.dirname(__FILE__) + '/one.xml', :marcxml)
26
+ @r = reader.next
27
+ end
28
+
29
+ should "get the leader as a string" do
30
+ assert_equal '00000njm a2200000uu 4500', @r.leader
31
+ end
32
+
33
+ should "get all fields with the given tag" do
34
+ assert_equal 3, @r.find_by_tag('700').size
35
+ end
36
+
37
+ should "get all fields with any of the given tags" do
38
+ assert_equal 6, @r.find_by_tag(['010','700', '852']).size
39
+ end
40
+
41
+ should "get an empty array trying to find a non-existent tag" do
42
+ assert_equal [], @r.find_by_tag('002')
43
+ end
44
+
45
+ should "not return anything for a non-existent tag" do
46
+ assert_equal 1, @r.find_by_tag(['010', '002']).size
47
+ end
48
+
49
+ should "get the value of a control tag" do
50
+ assert_equal 'DLC', @r['003'].value
51
+ end
52
+
53
+ should "get a subfield value via field[]" do
54
+ assert_equal 'Sung by Beale D. Taylor.', @r['245']['c']
55
+ end
56
+
57
+ should "joing all values of a field with a space" do
58
+ assert_equal "DLC DLC", @r['040'].value
59
+ end
60
+
61
+ should "Get the first field with a given tag via []" do
62
+ assert_equal '700 1 $a Lomax, John Avery, 1867-1948 $e Recording engineer.', @r['700'].to_s
63
+ end
64
+
65
+ should "get the subfield values in order of the original record" do
66
+ assert_equal ['Medina, Texas,', '1939.'], @r['260'].sub_values(['a', 'c'])
67
+ assert_equal ['Medina, Texas,', '1939.'], @r['260'].sub_values(['c', 'a'])
68
+ end
69
+
70
+ should "get the subfield values in order of the codes I pass" do
71
+ assert_equal [ '1939.', 'Medina, Texas,'], @r['260'].sub_values(['c', 'a'], true)
72
+ end
73
+
74
+
75
+
76
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marc4j4r
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - BillDueber
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-14 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Syntactic sugar and some extra methods to deal with classes in the marc4j java package
36
+ email: bill@dueber.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.markdown
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.markdown
49
+ - Rakefile
50
+ - VERSION
51
+ - doc/ControlFieldImpl.html
52
+ - doc/DataFieldImpl.html
53
+ - doc/Java/OrgMarc4j/MarcReader.html
54
+ - doc/MARC4J4R.html
55
+ - doc/MARC4J4R/Reader.html
56
+ - doc/RecordImpl.html
57
+ - doc/SubfieldImpl.html
58
+ - doc/_index.html
59
+ - doc/class_list.html
60
+ - doc/css/common.css
61
+ - doc/css/full_list.css
62
+ - doc/css/style.css
63
+ - doc/file.README.html
64
+ - doc/file_list.html
65
+ - doc/frames.html
66
+ - doc/index.html
67
+ - doc/js/app.js
68
+ - doc/js/full_list.js
69
+ - doc/js/jquery.js
70
+ - doc/method_list.html
71
+ - doc/top-level-namespace.html
72
+ - jars/marc4j.jar
73
+ - lib/marc4j4r.rb
74
+ - test/helper.rb
75
+ - test/one.dat
76
+ - test/one.xml
77
+ - test/test_marc4j4r.rb
78
+ has_rdoc: true
79
+ homepage: http://github.com/billdueber/marc4j4r
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --charset=UTF-8
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ version:
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: "0"
98
+ version:
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.3.5
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Use marc4j java library in JRuby in a more ruby-ish way
106
+ test_files:
107
+ - test/helper.rb
108
+ - test/test_marc4j4r.rb