berkeley_library-marc 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/inspectionProfiles/Project_Default.xml +3 -0
  3. data/.idea/marc.iml +56 -54
  4. data/.ruby-version +1 -1
  5. data/.yardopts +6 -1
  6. data/CHANGES.md +18 -0
  7. data/LICENSE.md +1 -1
  8. data/berkeley_library-marc.gemspec +2 -1
  9. data/lib/berkeley_library/marc/field_info/var_fields/ind_def.rb +4 -0
  10. data/lib/berkeley_library/marc/field_info/var_fields/ind_val_def.rb +2 -0
  11. data/lib/berkeley_library/marc/field_info/var_fields/instrument_or_voices_code.rb +2 -0
  12. data/lib/berkeley_library/marc/field_info/var_fields/section.rb +12 -0
  13. data/lib/berkeley_library/marc/field_info/var_fields/subfield_def.rb +12 -1
  14. data/lib/berkeley_library/marc/field_info/var_fields/subfield_val.rb +2 -0
  15. data/lib/berkeley_library/marc/field_info/var_fields/var_field_def.rb +3 -0
  16. data/lib/berkeley_library/marc/field_info/var_fields/var_field_list.rb +11 -0
  17. data/lib/berkeley_library/marc/field_info/var_fields/var_field_parser.rb +20 -0
  18. data/lib/berkeley_library/marc/field_info/var_fields/var_field_transform.rb +19 -0
  19. data/lib/berkeley_library/marc/field_info/var_fields.rb +8 -0
  20. data/lib/berkeley_library/marc/field_info.rb +8 -0
  21. data/lib/berkeley_library/marc/module_info.rb +20 -1
  22. data/lib/berkeley_library/marc.rb +5 -0
  23. data/lib/marc_extensions/data_field.rb +16 -3
  24. data/lib/marc_extensions/field_map.rb +19 -14
  25. data/lib/marc_extensions/record.rb +15 -5
  26. data/lib/marc_extensions/subfield.rb +10 -2
  27. data/lib/marc_extensions/xml_reader.rb +62 -2
  28. data/lib/marc_extensions.rb +3 -0
  29. data/spec/data/record-187888.xml +0 -1
  30. data/spec/marc_extensions/record_spec.rb +33 -37
  31. data/spec/marc_extensions/xml_reader_spec.rb +47 -0
  32. data/spec/spec_helper.rb +0 -4
  33. metadata +22 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c479c395bc201b47ebfc171b3ee402e5962a4111f618ee26473aeb6d689011d5
4
- data.tar.gz: 3197dc1cfbc96c65a31f6528351ce1b39dbea33e8142245e7527c55342c98f26
3
+ metadata.gz: 25ed54f0bd91408a2bc16d7d51525e4bfe7086bf9b3a507b2e40a846432fff32
4
+ data.tar.gz: 135562e29c813d72f4830bfd8413a79c6480c2f7c5c396621a14ccdc22732432
5
5
  SHA512:
6
- metadata.gz: c6e8adff15efbc36fea120f03481c59cc451831c40447a06c0451733d6660fb0fb2766ca0955a7214b0b941f861421f7d1ccfbacc41d37b6f8a012b67bddea1d
7
- data.tar.gz: 62cb9959145a7b8a490b2cc7fe88657e6ca26233dc9dde6a7f8b9ec146fb5303bb17bdabcc8c96da97ac408aa3d795c46aa9954fd49d757acf049bd7c5829897
6
+ metadata.gz: 968f53042e1d9b390c842ecc30f2b531ac58a17b49670ede19d9458a5559d93bb7c689c3665028a5c77d80cd892112468ad20b424aac72b420850b759529d6c1
7
+ data.tar.gz: d454a28e68812c807dcab0908317a566ff7949a3c66e3d22bb2374a457cee4dcb86c2cbb78ea69bcc4cd855cd74cb3b5e27a4cad016faec80276d3eec4860d03
@@ -10,6 +10,9 @@
10
10
  <inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
11
11
  <inspection_tool class="Rubocop" enabled="false" level="WARNING" enabled_by_default="false" />
12
12
  <inspection_tool class="RubyCaseWithoutElseBlockInspection" enabled="false" level="WARNING" enabled_by_default="false" />
13
+ <inspection_tool class="RubyMismatchedArgumentType" enabled="true" level="WARNING" enabled_by_default="true">
14
+ <option name="myCheckNilability" value="false" />
15
+ </inspection_tool>
13
16
  <inspection_tool class="RubyStringKeysInHashInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
14
17
  <inspection_tool class="RubyUnnecessaryReturnStatement" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
15
18
  <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
data/.idea/marc.iml CHANGED
@@ -6,63 +6,65 @@
6
6
  <component name="NewModuleRootManager">
7
7
  <content url="file://$MODULE_DIR$">
8
8
  <sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
9
- <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
10
9
  <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
11
10
  <excludeFolder url="file://$MODULE_DIR$/artifacts" />
12
11
  </content>
13
- <orderEntry type="jdk" jdkName="RVM: ruby-2.7.4" jdkType="RUBY_SDK" />
12
+ <orderEntry type="jdk" jdkName="RVM: ruby-2.7.5" jdkType="RUBY_SDK" />
14
13
  <orderEntry type="sourceFolder" forTests="false" />
15
- <orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.0, RVM: ruby-2.7.4) [gem]" level="application" />
16
- <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
17
- <orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, RVM: ruby-2.7.4) [gem]" level="application" />
18
- <orderEntry type="library" scope="PROVIDED" name="bundle-audit (v0.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
19
- <orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.14, RVM: ruby-2.7.4) [gem]" level="application" />
20
- <orderEntry type="library" scope="PROVIDED" name="bundler-audit (v0.9.0.1, RVM: ruby-2.7.4) [gem]" level="application" />
21
- <orderEntry type="library" scope="PROVIDED" name="ci_reporter (v2.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
22
- <orderEntry type="library" scope="PROVIDED" name="ci_reporter_rspec (v1.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
23
- <orderEntry type="library" scope="PROVIDED" name="colorize (v0.8.1, RVM: ruby-2.7.4) [gem]" level="application" />
24
- <orderEntry type="library" scope="PROVIDED" name="crack (v0.4.5, RVM: ruby-2.7.4) [gem]" level="application" />
25
- <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.4.4, RVM: ruby-2.7.4) [gem]" level="application" />
26
- <orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, RVM: ruby-2.7.4) [gem]" level="application" />
27
- <orderEntry type="library" scope="PROVIDED" name="dotenv (v2.7.6, RVM: ruby-2.7.4) [gem]" level="application" />
28
- <orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.4, RVM: ruby-2.7.4) [gem]" level="application" />
29
- <orderEntry type="library" scope="PROVIDED" name="hashdiff (v1.0.1, RVM: ruby-2.7.4) [gem]" level="application" />
30
- <orderEntry type="library" scope="PROVIDED" name="listen (v3.1.5, RVM: ruby-2.7.4) [gem]" level="application" />
31
- <orderEntry type="library" scope="PROVIDED" name="marc (v1.1.1, RVM: ruby-2.7.4) [gem]" level="application" />
32
- <orderEntry type="library" scope="PROVIDED" name="parallel (v1.21.0, RVM: ruby-2.7.4) [gem]" level="application" />
33
- <orderEntry type="library" scope="PROVIDED" name="parser (v3.0.2.0, RVM: ruby-2.7.4) [gem]" level="application" />
34
- <orderEntry type="library" scope="PROVIDED" name="parslet (v2.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
35
- <orderEntry type="library" scope="PROVIDED" name="public_suffix (v4.0.6, RVM: ruby-2.7.4) [gem]" level="application" />
36
- <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
37
- <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, RVM: ruby-2.7.4) [gem]" level="application" />
38
- <orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
39
- <orderEntry type="library" scope="PROVIDED" name="rb-inotify (v0.10.1, RVM: ruby-2.7.4) [gem]" level="application" />
40
- <orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.1.1, RVM: ruby-2.7.4) [gem]" level="application" />
41
- <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, RVM: ruby-2.7.4) [gem]" level="application" />
42
- <orderEntry type="library" scope="PROVIDED" name="rspec (v3.10.0, RVM: ruby-2.7.4) [gem]" level="application" />
43
- <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.10.1, RVM: ruby-2.7.4) [gem]" level="application" />
44
- <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.10.1, RVM: ruby-2.7.4) [gem]" level="application" />
45
- <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.10.2, RVM: ruby-2.7.4) [gem]" level="application" />
46
- <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.10.2, RVM: ruby-2.7.4) [gem]" level="application" />
47
- <orderEntry type="library" scope="PROVIDED" name="rubocop (v1.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
48
- <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
49
- <orderEntry type="library" scope="PROVIDED" name="rubocop-rake (v0.6.0, RVM: ruby-2.7.4) [gem]" level="application" />
50
- <orderEntry type="library" scope="PROVIDED" name="rubocop-rspec (v2.4.0, RVM: ruby-2.7.4) [gem]" level="application" />
51
- <orderEntry type="library" scope="PROVIDED" name="ruby-marc-spec (v0.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
52
- <orderEntry type="library" scope="PROVIDED" name="ruby-prof (v0.17.0, RVM: ruby-2.7.4) [gem]" level="application" />
53
- <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
54
- <orderEntry type="library" scope="PROVIDED" name="ruby_dep (v1.5.0, RVM: ruby-2.7.4) [gem]" level="application" />
55
- <orderEntry type="library" scope="PROVIDED" name="scrub_rb (v1.0.1, RVM: ruby-2.7.4) [gem]" level="application" />
56
- <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.21.2, RVM: ruby-2.7.4) [gem]" level="application" />
57
- <orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.12.3, RVM: ruby-2.7.4) [gem]" level="application" />
58
- <orderEntry type="library" scope="PROVIDED" name="simplecov-rcov (v0.2.3, RVM: ruby-2.7.4) [gem]" level="application" />
59
- <orderEntry type="library" scope="PROVIDED" name="simplecov_json_formatter (v0.1.3, RVM: ruby-2.7.4) [gem]" level="application" />
60
- <orderEntry type="library" scope="PROVIDED" name="thor (v1.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
61
- <orderEntry type="library" scope="PROVIDED" name="typesafe_enum (v0.3.0, RVM: ruby-2.7.4) [gem]" level="application" />
62
- <orderEntry type="library" scope="PROVIDED" name="unf (v0.1.4, RVM: ruby-2.7.4) [gem]" level="application" />
63
- <orderEntry type="library" scope="PROVIDED" name="unf_ext (v0.0.8, RVM: ruby-2.7.4) [gem]" level="application" />
64
- <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
65
- <orderEntry type="library" scope="PROVIDED" name="webmock (v3.14.0, RVM: ruby-2.7.4) [gem]" level="application" />
14
+ <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-2.7.5) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, RVM: ruby-2.7.5) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="bundle-audit (v0.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.31, RVM: ruby-2.7.5) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="bundler-audit (v0.9.0.1, RVM: ruby-2.7.5) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="ci_reporter (v2.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
20
+ <orderEntry type="library" scope="PROVIDED" name="ci_reporter_rspec (v1.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="colorize (v0.8.1, RVM: ruby-2.7.5) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, RVM: ruby-2.7.5) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, RVM: ruby-2.7.5) [gem]" level="application" />
24
+ <orderEntry type="library" scope="PROVIDED" name="dotenv (v2.7.6, RVM: ruby-2.7.5) [gem]" level="application" />
25
+ <orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.4, RVM: ruby-2.7.5) [gem]" level="application" />
26
+ <orderEntry type="library" scope="PROVIDED" name="listen (v3.1.5, RVM: ruby-2.7.5) [gem]" level="application" />
27
+ <orderEntry type="library" scope="PROVIDED" name="marc (v1.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
28
+ <orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.13.1, RVM: ruby-2.7.5) [gem]" level="application" />
29
+ <orderEntry type="library" scope="PROVIDED" name="parallel (v1.21.0, RVM: ruby-2.7.5) [gem]" level="application" />
30
+ <orderEntry type="library" scope="PROVIDED" name="parser (v3.1.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
31
+ <orderEntry type="library" scope="PROVIDED" name="parslet (v2.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
32
+ <orderEntry type="library" scope="PROVIDED" name="racc (v1.6.0, RVM: ruby-2.7.5) [gem]" level="application" />
33
+ <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, RVM: ruby-2.7.5) [gem]" level="application" />
34
+ <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, RVM: ruby-2.7.5) [gem]" level="application" />
35
+ <orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
36
+ <orderEntry type="library" scope="PROVIDED" name="rb-inotify (v0.10.1, RVM: ruby-2.7.5) [gem]" level="application" />
37
+ <orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.2.0, RVM: ruby-2.7.5) [gem]" level="application" />
38
+ <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, RVM: ruby-2.7.5) [gem]" level="application" />
39
+ <orderEntry type="library" scope="PROVIDED" name="rspec (v3.10.0, RVM: ruby-2.7.5) [gem]" level="application" />
40
+ <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.10.1, RVM: ruby-2.7.5) [gem]" level="application" />
41
+ <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.10.1, RVM: ruby-2.7.5) [gem]" level="application" />
42
+ <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.10.2, RVM: ruby-2.7.5) [gem]" level="application" />
43
+ <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.10.3, RVM: ruby-2.7.5) [gem]" level="application" />
44
+ <orderEntry type="library" scope="PROVIDED" name="rubocop (v1.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
45
+ <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.15.1, RVM: ruby-2.7.5) [gem]" level="application" />
46
+ <orderEntry type="library" scope="PROVIDED" name="rubocop-rake (v0.6.0, RVM: ruby-2.7.5) [gem]" level="application" />
47
+ <orderEntry type="library" scope="PROVIDED" name="rubocop-rspec (v2.4.0, RVM: ruby-2.7.5) [gem]" level="application" />
48
+ <orderEntry type="library" scope="PROVIDED" name="ruby-marc-spec (v0.1.1, RVM: ruby-2.7.5) [gem]" level="application" />
49
+ <orderEntry type="library" scope="PROVIDED" name="ruby-prof (v0.17.0, RVM: ruby-2.7.5) [gem]" level="application" />
50
+ <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, RVM: ruby-2.7.5) [gem]" level="application" />
51
+ <orderEntry type="library" scope="PROVIDED" name="ruby_dep (v1.5.0, RVM: ruby-2.7.5) [gem]" level="application" />
52
+ <orderEntry type="library" scope="PROVIDED" name="scrub_rb (v1.0.1, RVM: ruby-2.7.5) [gem]" level="application" />
53
+ <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.21.2, RVM: ruby-2.7.5) [gem]" level="application" />
54
+ <orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.12.3, RVM: ruby-2.7.5) [gem]" level="application" />
55
+ <orderEntry type="library" scope="PROVIDED" name="simplecov-rcov (v0.2.3, RVM: ruby-2.7.5) [gem]" level="application" />
56
+ <orderEntry type="library" scope="PROVIDED" name="simplecov_json_formatter (v0.1.3, RVM: ruby-2.7.5) [gem]" level="application" />
57
+ <orderEntry type="library" scope="PROVIDED" name="thor (v1.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
58
+ <orderEntry type="library" scope="PROVIDED" name="typesafe_enum (v0.3.0, RVM: ruby-2.7.5) [gem]" level="application" />
59
+ <orderEntry type="library" scope="PROVIDED" name="unf (v0.1.4, RVM: ruby-2.7.5) [gem]" level="application" />
60
+ <orderEntry type="library" scope="PROVIDED" name="unf_ext (v0.0.8, RVM: ruby-2.7.5) [gem]" level="application" />
61
+ <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.1.0, RVM: ruby-2.7.5) [gem]" level="application" />
62
+ <orderEntry type="library" scope="PROVIDED" name="webrick (v1.7.0, RVM: ruby-2.7.5) [gem]" level="application" />
63
+ <orderEntry type="library" scope="PROVIDED" name="yard (v0.9.27, RVM: ruby-2.7.5) [gem]" level="application" />
64
+ </component>
65
+ <component name="RModuleSettingsStorage">
66
+ <LOAD_PATH number="2" string0="$MODULE_DIR$/lib" string1="$MODULE_DIR$/spec" />
67
+ <I18N_FOLDERS number="0" />
66
68
  </component>
67
69
  <component name="RakeTasksCache">
68
70
  <option name="myRootTask">
@@ -75,7 +77,7 @@
75
77
  </RakeTaskImpl>
76
78
  <RakeTaskImpl description="Run all specs in spec directory, with coverage" fullCommand="coverage" id="coverage" />
77
79
  <RakeTaskImpl description="Run tests, check test coverage, check code style, check for vulnerabilities, build gem" fullCommand="default" id="default" />
78
- <RakeTaskImpl description="Build berkeley_library-marc.gemspec as berkeley_library-marc-0.2.0.gem" fullCommand="gem" id="gem" />
80
+ <RakeTaskImpl description="Build berkeley_library-marc.gemspec as berkeley_library-marc-0.2.2.gem" fullCommand="gem" id="gem" />
79
81
  <RakeTaskImpl description="Run RuboCop with auto-correct, and output results to console" fullCommand="ra" id="ra" />
80
82
  <RakeTaskImpl description="Run rubocop with HTML output" fullCommand="rubocop" id="rubocop" />
81
83
  <RakeTaskImpl id="rubocop">
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7
1
+ 2.7.5
data/.yardopts CHANGED
@@ -1 +1,6 @@
1
- --no-private --protected lib/**/*.rb -m markdown
1
+ --no-private
2
+ --protected
3
+ -m markdown
4
+ -o ./artifacts/doc
5
+ --exclude lib/berkeley_library/marc/field_info/var_fields/data
6
+ lib/**/*.rb
data/CHANGES.md CHANGED
@@ -1,3 +1,21 @@
1
+ # 0.3.0 (2022-01-25)
2
+
3
+ Fixes:
4
+
5
+ - Methods in `MARCExtensions::RecordExtensions` and `MARCExtensions::FieldMapExtensions`
6
+ now correctly return `Enumerator::Lazy` as documented.
7
+ - Fix issue where `freeze:` option to `MARC::XMLReader#new` or `MARC::XMLReader#read`
8
+ was accepted, but not implemented
9
+ - Test `MARC::XMLReader` extensions with both `MARC::REXMLReader` and `MARC::NokogiriReader`
10
+ - **Note:** the extensions _should_ work with `JREXMLReader`, `JRubyStaxReader`, and
11
+ `LibXMLReader`, but these have not been tested.
12
+
13
+ Possible breaking changes:
14
+
15
+ - The unused constant `MARCExtensions::FieldMapExtensions::VALID_TAGS` has been removed.
16
+ - `MARC::XMLReader#read` now takes an `options` hash instead of a `freeze:` keyword
17
+ parameter for better compatibility with `MARC::XMLReader#new`, esp. in Ruby 2.x.
18
+
1
19
  # 0.2.1 (2021-10-16)
2
20
 
3
21
  - Add [MARC::Spec](https://github.com/BerkeleyLibrary/marc-spec)
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The MIT License (MIT)
2
2
 
3
- Copyright © 2021 The Regents of the University of California
3
+ Copyright © 2022 The Regents of the University of California
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a
6
6
  copy of this software and associated documentation files (the “Software”),
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'colorize', '~> 0.8'
32
32
  spec.add_development_dependency 'dotenv', '~> 2.7'
33
33
  spec.add_development_dependency 'listen', '>= 3.0.5', '< 3.2'
34
+ spec.add_development_dependency 'nokogiri', '~> 1.13'
34
35
  spec.add_development_dependency 'rake', '~> 13.0'
35
36
  spec.add_development_dependency 'rspec', '~> 3.10'
36
37
  spec.add_development_dependency 'rubocop', '= 1.11'
@@ -39,5 +40,5 @@ Gem::Specification.new do |spec|
39
40
  spec.add_development_dependency 'ruby-prof', '~> 0.17.0'
40
41
  spec.add_development_dependency 'simplecov', '~> 0.21'
41
42
  spec.add_development_dependency 'simplecov-rcov', '~> 0.2'
42
- spec.add_development_dependency 'webmock', '~> 3.12'
43
+ spec.add_development_dependency 'yard', '~> 0.9.27'
43
44
  end
@@ -5,10 +5,12 @@ module BerkeleyLibrary
5
5
  module Marc
6
6
  module FieldInfo
7
7
  module VarFields
8
+ # An indicator definition.
8
9
  class IndDef
9
10
  # TODO: include Comparable
10
11
  include Obsolescible
11
12
 
13
+ # Human-readable position names
12
14
  POS_NAMES = { 1 => 'First', 2 => 'Second' }.freeze
13
15
 
14
16
  attr_reader :pos
@@ -23,10 +25,12 @@ module BerkeleyLibrary
23
25
  @val_defs = val_defs
24
26
  end
25
27
 
28
+ # @see Obsolescible#reject_obsoletes
26
29
  def reject_obsoletes
27
30
  IndDef.new(pos: pos, desc: desc, val_defs: _reject_obsolete(val_defs))
28
31
  end
29
32
 
33
+ # see Object#to_s
30
34
  def to_s
31
35
  lines = ["#{POS_NAMES[pos]} - #{desc}"]
32
36
  val_defs.each { |v| lines << " #{v}" }
@@ -4,6 +4,7 @@ module BerkeleyLibrary
4
4
  module Marc
5
5
  module FieldInfo
6
6
  module VarFields
7
+ # An indicator value definition.
7
8
  class IndValDef
8
9
  # TODO: include Comparable
9
10
  include Obsolescible
@@ -17,6 +18,7 @@ module BerkeleyLibrary
17
18
  @desc = desc
18
19
  end
19
20
 
21
+ # see Object#to_s
20
22
  def to_s
21
23
  "#{val} - #{desc}"
22
24
  end
@@ -4,6 +4,7 @@ module BerkeleyLibrary
4
4
  module Marc
5
5
  module FieldInfo
6
6
  module VarFields
7
+ # An instrument or voices code.
7
8
  class InstrumentOrVoicesCode
8
9
  # TODO: include Comparable
9
10
  include Obsolescible
@@ -16,6 +17,7 @@ module BerkeleyLibrary
16
17
  @desc = desc
17
18
  end
18
19
 
20
+ # see Object#to_s
19
21
  def to_s
20
22
  "#{val} - #{desc}"
21
23
  end
@@ -5,6 +5,7 @@ module BerkeleyLibrary
5
5
  module Marc
6
6
  module FieldInfo
7
7
  module VarFields
8
+ # A section of a MARC field list
8
9
  class Section
9
10
  include Enumerable
10
11
  include Obsolescible
@@ -17,20 +18,30 @@ module BerkeleyLibrary
17
18
  @var_fields = var_fields
18
19
  end
19
20
 
21
+ # Each variable field definition in this section.
22
+ # @overload each
23
+ # An enumerator of the fields in this section.
24
+ # @return [Enumerator<VarFieldDef>] the fields
25
+ # @overload each(&block)
26
+ # Yields each field in this section.
27
+ # @yieldparam vf [VarFieldDef] the field.
20
28
  def each(&block)
21
29
  return to_enum(:each) unless block_given?
22
30
 
23
31
  var_fields.each(&block)
24
32
  end
25
33
 
34
+ # The number of variable fields in this section.
26
35
  def size
27
36
  var_fields.size
28
37
  end
29
38
 
39
+ # Returns true if this section contains no variable fields.
30
40
  def empty?
31
41
  var_fields.empty?
32
42
  end
33
43
 
44
+ # @see Obsolescible#reject_obsoletes
34
45
  def reject_obsoletes
35
46
  Section.new(
36
47
  desc: desc,
@@ -38,6 +49,7 @@ module BerkeleyLibrary
38
49
  )
39
50
  end
40
51
 
52
+ # see Object#to_s
41
53
  def to_s
42
54
  blocks = ["--#{desc}--"]
43
55
  blocks.concat(var_fields.map(&:to_s))
@@ -5,8 +5,9 @@ module BerkeleyLibrary
5
5
  module Marc
6
6
  module FieldInfo
7
7
  module VarFields
8
- # TODO: destructure range codes in some useful way (e.g. 886a-z, 0-9)
8
+ # A subfield definition.
9
9
  class SubfieldDef
10
+ # TODO: destructure range codes in some useful way (e.g. 886a-z, 0-9)
10
11
  include Obsolescible
11
12
 
12
13
  attr_reader :code, :desc, :values
@@ -17,19 +18,29 @@ module BerkeleyLibrary
17
18
  @values = values
18
19
  end
19
20
 
21
+ # @see Obsolescible#reject_obsoletes
20
22
  def reject_obsoletes
21
23
  SubfieldDef.new(code: code, desc: desc, values: _reject_obsolete(values))
22
24
  end
23
25
 
26
+ # Each value definition for this subfield.
27
+ # @overload each
28
+ # An enumerator of the value definitions for this subfield.
29
+ # @return [Enumerator<SubfieldVal>] the subfields.
30
+ # @overload each(&block)
31
+ # Yields each value definition for this subfield.
32
+ # @yieldparam v [SubfieldVal] the subfield.
24
33
  def each_value(&block)
25
34
  return to_enum(:each_value) unless block_given?
26
35
 
36
+ # noinspection RubyMismatchedReturnType
27
37
  values.each(&block)
28
38
  end
29
39
 
30
40
  INDENT = ' '.freeze
31
41
  private_constant :INDENT
32
42
 
43
+ # see Object#to_s
33
44
  def to_s
34
45
  lines = ["$#{code_str} - #{desc}"]
35
46
  values.each { |v| lines << INDENT + v.to_s }
@@ -2,6 +2,7 @@ module BerkeleyLibrary
2
2
  module Marc
3
3
  module FieldInfo
4
4
  module VarFields
5
+ # A subfield value definition.
5
6
  class SubfieldVal
6
7
  # TODO: include Comparable
7
8
  include Obsolescible
@@ -14,6 +15,7 @@ module BerkeleyLibrary
14
15
  @desc = desc
15
16
  end
16
17
 
18
+ # see Object#to_s
17
19
  def to_s
18
20
  "#{val} - #{desc}"
19
21
  end
@@ -7,6 +7,7 @@ module BerkeleyLibrary
7
7
  module Marc
8
8
  module FieldInfo
9
9
  module VarFields
10
+ # A variable field definition.
10
11
  class VarFieldDef
11
12
  include Obsolescible
12
13
 
@@ -20,6 +21,7 @@ module BerkeleyLibrary
20
21
  @inst_or_voices_codes = inst_or_voices_codes
21
22
  end
22
23
 
24
+ # see Object#to_s
23
25
  def to_s
24
26
  blocks = ["#{tag} - #{desc}"]
25
27
 
@@ -30,6 +32,7 @@ module BerkeleyLibrary
30
32
  blocks.join("\n")
31
33
  end
32
34
 
35
+ # @see Obsolescible#reject_obsoletes
33
36
  def reject_obsoletes
34
37
  VarFieldDef.new(
35
38
  tag: tag,
@@ -5,6 +5,7 @@ module BerkeleyLibrary
5
5
  module Marc
6
6
  module FieldInfo
7
7
  module VarFields
8
+ # A list of variable fields grouped into sections.
8
9
  class VarFieldList
9
10
  include Enumerable
10
11
  include Obsolescible
@@ -16,16 +17,25 @@ module BerkeleyLibrary
16
17
  @sections = sections
17
18
  end
18
19
 
20
+ # Each section in this list.
21
+ # @overload each
22
+ # An enumerator of the sections in this list.
23
+ # @return [Enumerator<Section>] the sections.
24
+ # @overload each(&block)
25
+ # Yields each section in this list.
26
+ # @yieldparam s [Section] the section.
19
27
  def each(&block)
20
28
  return to_enum(:each) unless block_given?
21
29
 
22
30
  sections.each { |section| section.each(&block) }
23
31
  end
24
32
 
33
+ # The number of sections in this list.
25
34
  def size
26
35
  sections.sum(&:size)
27
36
  end
28
37
 
38
+ # @see Obsolescible#reject_obsoletes
29
39
  def reject_obsoletes
30
40
  VarFieldList.new(
31
41
  desc: desc,
@@ -33,6 +43,7 @@ module BerkeleyLibrary
33
43
  )
34
44
  end
35
45
 
46
+ # see Object#to_s
36
47
  def to_s
37
48
  sections.map(&:to_s).join("\n")
38
49
  end
@@ -6,6 +6,9 @@ module BerkeleyLibrary
6
6
  module VarFields
7
7
 
8
8
  # rubocop:disable Style/BlockDelimiters
9
+
10
+ # Parses MARC documentation in the format used by the
11
+ # Library of Congress [field list](https://www.loc.gov/marc/bibliographic/ecbdlist.html).
9
12
  class VarFieldParser < Parslet::Parser
10
13
 
11
14
  # ------------------------------------------------------------
@@ -122,10 +125,27 @@ module BerkeleyLibrary
122
125
  # ------------------------------------------------------------
123
126
  # Parser
124
127
 
128
+ # Parses the specified field documentation.
129
+ # Usage:
130
+ #
131
+ # ```ruby
132
+ # parser = VarFieldParser.new
133
+ # parse_tree = parser.parse('docs.txt')
134
+ # xform = VarFieldTransform.new
135
+ # var_field_list = xform.apply(parse_tree)
136
+ # ```
137
+ #
138
+ # @param io [String, Source] input for the parse process
139
+ # @option options [Parslet::ErrorReporter] :reporter error reporter to use,
140
+ # defaults to Parslet::ErrorReporter::Tree
141
+ # @option options [Boolean] :prefix Should a prefix match be accepted?
142
+ # (default: false)
143
+ # @return [Hash] a parse tree suitable for input to {VarFieldTransform}
125
144
  def parse(io, options = nil)
126
145
  opts = { reporter: Parslet::ErrorReporter::Deepest.new }
127
146
  opts.merge!(options) if options
128
147
 
148
+ # noinspection RubyMismatchedReturnType
129
149
  super(io, opts)
130
150
  end
131
151
  end
@@ -7,8 +7,21 @@ module BerkeleyLibrary
7
7
  module VarFields
8
8
  # TODO: flag [OBSOLETE], (R), (NR)
9
9
  # rubocop:disable Style/BlockDelimiters
10
+
11
+ # Transformer converting {VarFieldParser} output to a {VarFieldList}.
12
+ # Usage:
13
+ #
14
+ # ```ruby
15
+ # parser = VarFieldParser.new
16
+ # parse_tree = parser.parse('docs.txt')
17
+ # xform = VarFieldTransform.new
18
+ # var_field_list = xform.apply(parse_tree)
19
+ # ```
10
20
  class VarFieldTransform < Parslet::Transform
11
21
 
22
+ # Intermediate representation of structures constisting of
23
+ # a `val` and a `desc` (includes {IndValDef}, {SubfieldVal},
24
+ # and {InstrumentOrVoicesCode}).
12
25
  class AnyValue
13
26
  attr_reader :val, :desc
14
27
 
@@ -17,14 +30,20 @@ module BerkeleyLibrary
17
30
  @desc = desc
18
31
  end
19
32
 
33
+ # Converts this {AnyValue} to an {IndValDef}.
34
+ # @return [IndValDef] the {IndValDef}
20
35
  def to_ind_val_def
21
36
  IndValDef.new(val: val, desc: desc)
22
37
  end
23
38
 
39
+ # Converts this {AnyValue} to an {SubfieldVal}.
40
+ # @return [SubfieldVal] the {SubfieldVal}
24
41
  def to_subfield_val
25
42
  SubfieldVal.new(val: val, desc: desc)
26
43
  end
27
44
 
45
+ # Converts this {AnyValue} to an {InstrumentOrVoicesCode}.
46
+ # @return [InstrumentOrVoicesCode] the {InstrumentOrVoicesCode}
28
47
  def to_ivc
29
48
  InstrumentOrVoicesCode.new(val: val, desc: desc)
30
49
  end
@@ -4,13 +4,19 @@ require 'berkeley_library/marc/field_info/var_fields/data'
4
4
  module BerkeleyLibrary
5
5
  module Marc
6
6
  module FieldInfo
7
+ # Parsed documentation for variable fields.
7
8
  module VarFields
8
9
 
10
+ # Path to machine-readable field documentation directory.
9
11
  DATA_DIR = File.expand_path('var_fields/data', __dir__)
12
+
13
+ # Path to LOC standard variable field documentation.
10
14
  PATH_STANDARD = File.join(DATA_DIR, 'var_fields_standard.txt')
11
15
 
12
16
  class << self
13
17
 
18
+ # Return information on all LOC standard variable fields.
19
+ # @param obsolete [Boolean] whether to include fields documented as obsolete.
14
20
  def standard(obsolete: false)
15
21
  @standard_all ||= VarFieldTransform.new.apply(BerkeleyLibrary::Marc::FieldInfo::VarFields::STANDARD_PARSED)
16
22
  return @standard_all if obsolete
@@ -18,6 +24,8 @@ module BerkeleyLibrary
18
24
  @standard ||= @standard_all.reject_obsoletes
19
25
  end
20
26
 
27
+ # Return information on UC Berkeley special 9xx fields.
28
+ # @param obsolete [Boolean] whether to include fields documented as obsolete.
21
29
  def berkeley_9xx(obsolete: false)
22
30
  @berkeley_9xx_all ||= VarFieldTransform.new.apply(BerkeleyLibrary::Marc::FieldInfo::VarFields::BERKELEY_9XX_PARSED)
23
31
  return @berkeley_9xx_all if obsolete
@@ -1 +1,9 @@
1
+ module BerkeleyLibrary
2
+ module Marc
3
+ # Experimental module for parsing MARC documentation in the format used by the
4
+ # Library of Congress [field list](https://www.loc.gov/marc/bibliographic/ecbdlist.html).
5
+ module FieldInfo; end
6
+ end
7
+ end
8
+
1
9
  Dir.glob(File.expand_path('field_info/*.rb', __dir__)).sort.each(&method(:require))
@@ -1,13 +1,32 @@
1
+ # Umbrella module for UC Berkeley Library Ruby code
1
2
  module BerkeleyLibrary
3
+ # MARC utilities for the UC Berkeley Library
2
4
  module Marc
5
+ # Build information for this module
3
6
  class ModuleInfo
7
+
8
+ # The gem name
4
9
  NAME = 'berkeley_library-marc'.freeze
10
+
11
+ # The author
5
12
  AUTHOR = 'David Moles'.freeze
13
+
14
+ # Author contact email
6
15
  AUTHOR_EMAIL = 'dmoles@berkeley.edu'.freeze
16
+
17
+ # Gem summary
7
18
  SUMMARY = 'MARC utilities for the UC Berkeley Library'.freeze
19
+
20
+ # Gem description
8
21
  DESCRIPTION = 'A gem providing MARC-related utility code and extensions to ruby-marc for the UC Berkeley Library'.freeze
22
+
23
+ # Gem license
9
24
  LICENSE = 'MIT'.freeze
10
- VERSION = '0.2.1'.freeze
25
+
26
+ # Gem version
27
+ VERSION = '0.3.0'.freeze
28
+
29
+ # Gem homepage
11
30
  HOMEPAGE = 'https://github.com/BerkeleyLibrary/marc'.freeze
12
31
  end
13
32
  end
@@ -1,3 +1,8 @@
1
1
  require 'marc_extensions'
2
2
 
3
+ module BerkeleyLibrary
4
+ # Umbrella module for UC Berkeley Library MARC code.
5
+ module Marc; end
6
+ end
7
+
3
8
  Dir.glob(File.expand_path('marc/*.rb', __dir__)).sort.each(&method(:require))
@@ -2,26 +2,39 @@ require 'marc'
2
2
  require 'marc_extensions/subfield'
3
3
 
4
4
  module MARCExtensions
5
+ # Extensions for [MARC::XMLReader](https://rubydoc.info/gems/marc/MARC/DataField).
5
6
  module DataFieldExtensions
7
+ # Returns a list of all the subfield codes in this data field.
8
+ # @return [Array] the subfield codes
6
9
  def subfield_codes
7
10
  subfields.map(&:code)
8
11
  end
9
12
 
13
+ # Whether this datafield and all its tag, indicators, and
14
+ # subfields are frozen.
15
+ #
16
+ # @return [Boolean] true if the field is frozen, false otherwise.
10
17
  def frozen?
11
- [tag, indicator1, indicator2, subfields].all?(&:frozen?)
12
- subfields.all?(&:frozen?)
18
+ [tag, indicator1, indicator2, subfields].all?(&:frozen?) &&
19
+ subfields.all?(&:frozen?) &&
20
+ super
13
21
  end
14
22
 
23
+ # Recursively freezes this datafield, along with its tag, indicators,
24
+ # and subfields.
25
+ #
26
+ # @return [MARC::DataField] this data field.
15
27
  def freeze
16
28
  [tag, indicator1, indicator2].each(&:freeze)
17
29
  subfields.each(&:freeze)
18
30
  subfields.freeze
19
- self
31
+ super
20
32
  end
21
33
  end
22
34
  end
23
35
 
24
36
  module MARC
37
+ # Applies the extensions in {MARCExtensions::DataFieldExtensions}
25
38
  # @see https://rubydoc.info/gems/marc/MARC/DataField RubyGems documentation
26
39
  class DataField
27
40
  prepend MARCExtensions::DataFieldExtensions
@@ -1,9 +1,9 @@
1
1
  require 'marc'
2
+
2
3
  module MARCExtensions
4
+ # Extensions to [MARC::FieldMap](https://rubydoc.info/gems/marc/MARC/FieldMap).
3
5
  module FieldMapExtensions
4
6
 
5
- VALID_TAGS = ('000'..'999').freeze
6
-
7
7
  # Gets the specified fields in order by tag.
8
8
  #
9
9
  # @overload each_sorted_by_tag(tags, &block)
@@ -23,29 +23,33 @@ module MARCExtensions
23
23
  def each_sorted_by_tag(tags = nil, &block)
24
24
  reindex unless @clean
25
25
 
26
- indices_for(tags).map { |i| self[i] }.each(&block)
26
+ indices = indices_for(tags)
27
+ fields = indices.map { |i| self[i] }
28
+ # noinspection RubyMismatchedReturnType
29
+ fields.each(&block)
30
+ end
31
+
32
+ # Recursively freezes all fields.
33
+ # @return [MARC::FieldMap] this FieldMap.
34
+ def freeze
35
+ unless frozen?
36
+ reindex unless @clean
37
+ each(&:freeze)
38
+ end
39
+
40
+ super
27
41
  end
28
42
 
29
43
  private
30
44
 
31
45
  def indices_for(tags)
32
- return all_indices unless tags
33
-
34
- sorted_tag_array(tags)
46
+ sorted_tag_array(tags || tag_list)
35
47
  .lazy # prevent unnecessary allocations
36
48
  .map { |t| @tags[t] } # get indices for each tag
37
49
  .reject(&:nil?) # ignoring any tags we don't have fields for
38
50
  .flat_map { |x| x } # flatten list of indices -- equiv. Array#flatten
39
51
  end
40
52
 
41
- def all_indices
42
- [].tap do |a|
43
- @tags.keys.sort.map do |t|
44
- a.concat(@tags[t])
45
- end
46
- end
47
- end
48
-
49
53
  def sorted_tag_array(tags)
50
54
  return Array(tags) if tags.is_a?(Range)
51
55
 
@@ -56,6 +60,7 @@ module MARCExtensions
56
60
  end
57
61
 
58
62
  module MARC
63
+ # Applies the extensions in {MARCExtensions::FieldMapExtensions}.
59
64
  # @see https://rubydoc.info/gems/marc/MARC/FieldMap RubyGems documentation
60
65
  class FieldMap
61
66
  prepend MARCExtensions::FieldMapExtensions
@@ -4,6 +4,7 @@ require 'marc_extensions/field_map'
4
4
  require 'marc_extensions/data_field'
5
5
 
6
6
  module MARCExtensions
7
+ # Extensions to [MARC::Record](https://rubydoc.info/gems/marc/MARC/Record).
7
8
  module RecordExtensions
8
9
 
9
10
  # Gets the specified fields in order by tag.
@@ -38,6 +39,7 @@ module MARCExtensions
38
39
  # Yields each control field.
39
40
  # @yieldparam field [MARC::ControlField] Each control field.
40
41
  def each_control_field(&block)
42
+ # noinspection RubyMismatchedReturnType
41
43
  each_sorted_by_tag.take_while { |df| df.tag.to_i <= 10 }.each(&block)
42
44
  end
43
45
 
@@ -52,6 +54,7 @@ module MARCExtensions
52
54
  # Yields each data field.
53
55
  # @yieldparam field [MARC::DataField] Each data field.
54
56
  def each_data_field(&block)
57
+ # noinspection RubyMismatchedReturnType
55
58
  each_sorted_by_tag.select { |df| df.tag.to_i > 10 }.each(&block)
56
59
  end
57
60
 
@@ -72,20 +75,24 @@ module MARCExtensions
72
75
  data_fields_by_tag.values.flatten
73
76
  end
74
77
 
75
- # Freezes the leader and fields.
78
+ # Recursively freezes this record, along with its leader and fields.
79
+ # @return [MARC::Record] this record.
76
80
  def freeze
81
+ return if frozen?
82
+
77
83
  leader.freeze
78
- fields.each(&:freeze)
79
84
  fields.freeze
80
- self
85
+ super
81
86
  end
82
87
 
88
+ # Whether this record, its fields, and leader are all frozen.
83
89
  # @return [Boolean] true if the fields and leader are frozen
84
90
  def frozen?
85
- (fields.frozen? && leader.frozen?)
91
+ (fields.frozen? && leader.frozen?) && super
86
92
  end
87
93
 
88
- # TODO: use info from parsed documentation? or move to TIND-specific extension
94
+ # Returns the canonical ID from the 001 control field.
95
+ # @return [String, nil] the 001 control field value, or nil if not present
89
96
  def record_id
90
97
  cf_001 = self['001']
91
98
  return cf_001.value if cf_001
@@ -102,7 +109,10 @@ module MARCExtensions
102
109
  end
103
110
  end
104
111
 
112
+ # Extensions to [ruby-marc](https://rubydoc.info/gems/marc/)
113
+ # @see https://rubydoc.info/gems/marc/MARC RubyGems documentation
105
114
  module MARC
115
+ # Applies the extensions in {MARCExtensions::RecordExtensions}.
106
116
  # @see https://rubydoc.info/gems/marc/MARC/Record RubyGems documentation
107
117
  class Record
108
118
  prepend MARCExtensions::RecordExtensions
@@ -1,19 +1,27 @@
1
1
  require 'marc'
2
2
 
3
3
  module MARCExtensions
4
+ # Extensions to [MARC::Subfield](https://rubydoc.info/gems/marc/MARC/Subfield).
4
5
  module SubfieldExtensions
6
+
7
+ # Recursively freezes this subfield, including its code and value.
8
+ #
9
+ # @return [Boolean] true if this subfield is frozen, false otherwise
5
10
  def frozen?
6
- [code, value].all?(&:frozen?)
11
+ [code, value].all?(&:frozen?) && super
7
12
  end
8
13
 
14
+ # Whether this subfield and its code and value are frozen.
15
+ # @return [MARC::Subfield] this subfield.
9
16
  def freeze
10
17
  [code, value].each(&:freeze)
11
- self
18
+ super
12
19
  end
13
20
  end
14
21
  end
15
22
 
16
23
  module MARC
24
+ # Applies the extensions in {MARCExtensions::SubfieldExtensions}.
17
25
  # @see https://rubydoc.info/gems/marc/MARC/Subfield RubyGems documentation
18
26
  class Subfield
19
27
  prepend MARCExtensions::SubfieldExtensions
@@ -2,16 +2,76 @@ require 'marc'
2
2
  require 'marc_extensions/record'
3
3
 
4
4
  module MARCExtensions
5
+ # Extends [MARC::XMLReader](https://rubydoc.info/gems/marc/MARC/XMLReader).
6
+ module XMLReaderExtensions
7
+ # Adds a `:freeze` option to [MARC::XMLReader](https://rubydoc.info/gems/marc/MARC/XMLReader)
8
+ # which freezes each record as it's constructed:
9
+ #
10
+ # ```ruby
11
+ # reader = MARC::XMLReader.new('marc.xml', { freeze: true })
12
+ # record = reader.first
13
+ # record.frozen?
14
+ # # => true
15
+ # ```
16
+ #
17
+ # @see MARCExtensions::RecordExtensions#freeze
18
+ def initialize(file, options = {})
19
+ @freeze = options[:freeze]
20
+ super(file, options)
21
+
22
+ # It's surprisingly tricky to get these into the general superclass
23
+ # chain, so instead we just prepend them to the eigenclass
24
+ if respond_to?(:yield_record)
25
+ class << self; prepend(MARCExtensions::YieldFrozenRecord); end
26
+ elsif respond_to?(:build_record, true)
27
+ class << self; prepend(MARCExtensions::BuildFrozenRecord); end
28
+ end
29
+ end
30
+ end
31
+
32
+ # Class extensions for [MARC::XMLReader](https://rubydoc.info/gems/marc/MARC/XMLReader).
5
33
  module XMLReaderClassExtensions
6
- def read(file, freeze: false)
7
- new(file, freeze: freeze)
34
+ # Reads MARC records from the specified file or IO
35
+ #
36
+ # @param file [String, IO] a string file path, or an IO object
37
+ # @return [MARC::XMLReader] a reader for the specified file or IO
38
+ def read(file, options = {})
39
+ new(file, options)
40
+ end
41
+ end
42
+
43
+ # Extends [MARC::GenericPullParser](https://rubydoc.info/gems/marc/MARC/GenericPullParser)
44
+ # to support `:freeze` option
45
+ module YieldFrozenRecord
46
+ # Builds a MARC record, freezing it if the `:freeze` option
47
+ # was passed to the reader.
48
+ # @@yieldparam record [MARC::Record] the record
49
+ def yield_record
50
+ @record[:record].freeze if @freeze
51
+ super
52
+ end
53
+ end
54
+
55
+ # Extends [MARC::REXMLReader](https://rubydoc.info/gems/marc/MARC/REXMLReader)
56
+ # and [MARC::LibXMLReader](https://rubydoc.info/gems/marc/MARC/LibXMLReader)
57
+ # to support `:freeze` option
58
+ module BuildFrozenRecord
59
+ # Builds a MARC record, freezing it if the `:freeze` option
60
+ # was passed to the reader.
61
+ # @return [MARC::Record] the record
62
+ def build_record
63
+ super.tap { |record| record.freeze if @freeze }
8
64
  end
9
65
  end
10
66
  end
11
67
 
12
68
  module MARC
69
+ # Applies the extensions in {MARCExtensions::XMLReaderExtensions}
70
+ # and {MARCExtensions::XMLReaderClassExtensions}.
13
71
  # @see https://rubydoc.info/gems/marc/MARC/XMLReader RubyGems documentation
14
72
  class XMLReader
73
+ prepend MARCExtensions::XMLReaderExtensions
74
+
15
75
  class << self
16
76
  prepend MARCExtensions::XMLReaderClassExtensions
17
77
  end
@@ -1 +1,4 @@
1
+ # Extensions to [ruby-marc](https://rubydoc.info/gems/marc/)
2
+ module MARCExtensions; end
3
+
1
4
  Dir.glob(File.expand_path('marc_extensions/*.rb', __dir__)).sort.each(&method(:require))
@@ -1,4 +1,3 @@
1
-
2
1
  <?xml version="1.0" encoding="UTF-8"?>
3
2
  <collection xmlns="http://www.loc.gov/MARC21/slim">
4
3
  <record>
@@ -36,42 +36,6 @@ describe MARC::Record do
36
36
  end
37
37
  end
38
38
 
39
- describe :frozen? do
40
- it 'returns false neither fields nor leader are frozen' do
41
- expect(marc_record.leader).not_to be_frozen # just to be sure
42
- expect(marc_record.fields).not_to be_frozen # just to be sure
43
- expect(marc_record).not_to be_frozen
44
- end
45
-
46
- it 'returns false if only fields are frozen' do
47
- expect(marc_record.leader).not_to be_frozen # just to be sure
48
- expect(marc_record.fields).not_to be_frozen # just to be sure
49
- marc_record.leader.freeze
50
- expect(marc_record).not_to be_frozen
51
- end
52
-
53
- it 'returns false if only leader is frozen' do
54
- expect(marc_record.leader).not_to be_frozen # just to be sure
55
- expect(marc_record.fields).not_to be_frozen # just to be sure
56
- marc_record.fields.freeze
57
- expect(marc_record).not_to be_frozen
58
- end
59
-
60
- it 'returns false if neither fields nor leader are frozen' do
61
- expect(marc_record.leader).not_to be_frozen # just to be sure
62
- expect(marc_record.fields).not_to be_frozen # just to be sure
63
- expect(marc_record).not_to be_frozen
64
- end
65
-
66
- it 'returns true if both fields and leader are frozen' do
67
- expect(marc_record.leader).not_to be_frozen # just to be sure
68
- expect(marc_record.fields).not_to be_frozen # just to be sure
69
- marc_record.fields.freeze
70
- marc_record.leader.freeze
71
- expect(marc_record).to be_frozen
72
- end
73
- end
74
-
75
39
  describe :data_fields do
76
40
  it 'returns only the data fields' do
77
41
  expected_tags = %w[024 035 245 336 505 505 505 505 505 505 540 852 856 856 901 902 902 980 982 991]
@@ -111,7 +75,8 @@ describe MARC::Record do
111
75
 
112
76
  describe :each_data_field do
113
77
  it 'returns only the data fields, in order' do
114
- expect(marc_record.each_data_field.to_a).to eq(marc_record.data_fields)
78
+ each_data_field = marc_record.each_data_field
79
+ expect(each_data_field.to_a).to eq(marc_record.data_fields)
115
80
  end
116
81
 
117
82
  it 'groups fields by tag even when disordered' do
@@ -126,6 +91,16 @@ describe MARC::Record do
126
91
  df_852_ix = dff.find_index { |df| df.tag == '852' }
127
92
  expect(dff[df_852_ix, 3]).to eq([df_852, df_856_1, df_856_2])
128
93
  end
94
+
95
+ it 'returns a lazy enumerator if not passed a block' do
96
+ en = marc_record.each_data_field
97
+ expect(en).to be_a(Enumerator::Lazy)
98
+ end
99
+
100
+ it 'returns nil if passed a block' do
101
+ result = marc_record.each_data_field { |_| next }
102
+ expect(result).to be_nil
103
+ end
129
104
  end
130
105
 
131
106
  describe :each_sorted_by_tag do
@@ -140,6 +115,14 @@ describe MARC::Record do
140
115
  end
141
116
  end
142
117
 
118
+ it 'returns a lazy enumerator' do
119
+ en = marc_record.each_sorted_by_tag
120
+ expect(en).to be_a(Enumerator::Lazy)
121
+
122
+ en = marc_record.each_sorted_by_tag(tags)
123
+ expect(en).to be_a(Enumerator::Lazy)
124
+ end
125
+
143
126
  it 'returns only fields with the specified tags, sorted, in original order' do
144
127
  result = []
145
128
  marc_record.each_sorted_by_tag(tags) { |f| result << f }
@@ -149,6 +132,9 @@ describe MARC::Record do
149
132
  it 'returns nil if passed a block' do
150
133
  result = marc_record.each_sorted_by_tag(tags) { |_| next }
151
134
  expect(result).to be_nil
135
+
136
+ result = marc_record.each_sorted_by_tag { |_| next }
137
+ expect(result).to be_nil
152
138
  end
153
139
 
154
140
  it 'returns an enum if not passed a block' do
@@ -207,6 +193,16 @@ describe MARC::Record do
207
193
  expect(cff.size).to eq(expected_tags.size)
208
194
  expect(cff.map(&:tag)).to eq(expected_tags)
209
195
  end
196
+
197
+ it 'returns nil if passed a block' do
198
+ result = marc_record.each_control_field { |_| next }
199
+ expect(result).to be_nil
200
+ end
201
+
202
+ it 'returns a lazy enumerator if not passed a block' do
203
+ en = marc_record.each_control_field
204
+ expect(en).to be_a(Enumerator::Lazy)
205
+ end
210
206
  end
211
207
 
212
208
  describe :spec do
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module MARC
4
+ describe XMLReader do
5
+ let(:infile_path) { 'spec/data/record-187888.xml' }
6
+ describe :new do
7
+ # not our first choice, but it's the ruby-marc default behavior
8
+ it 'returns a REXMLParser by default' do
9
+ reader = XMLReader.new(infile_path)
10
+ expect(reader).to be_a(REXMLReader)
11
+ end
12
+
13
+ it 'works with Nokogiri' do
14
+ reader = XMLReader.new(infile_path, { parser: 'nokogiri' })
15
+ expect(reader).to be_a(NokogiriReader)
16
+ end
17
+ end
18
+
19
+ describe :read do
20
+ it 'reads a file' do
21
+ reader = XMLReader.read(infile_path)
22
+ expect(reader).to be_a(MARC::XMLReader)
23
+ record = reader.first
24
+ expect(record).to be_a(MARC::Record)
25
+ end
26
+
27
+ describe 'freeze: true' do
28
+ it 'works with REXML' do
29
+ reader = XMLReader.read(infile_path, { parser: 'rexml', freeze: true })
30
+ expect(reader).to be_a(MARC::REXMLReader)
31
+ record = reader.first
32
+ expect(record).to be_a(MARC::Record)
33
+ expect(record).to be_frozen
34
+ end
35
+
36
+ it 'works with Nokogiri' do
37
+ reader = XMLReader.read(infile_path, { parser: 'nokogiri', freeze: true })
38
+ expect(reader).to be_a(MARC::NokogiriReader)
39
+ record = reader.first
40
+ expect(record).to be_a(MARC::Record)
41
+ expect(record).to be_frozen
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,14 +7,10 @@ require 'simplecov' if ENV['COVERAGE']
7
7
  # ------------------------------------------------------------
8
8
  # RSpec
9
9
 
10
- require 'webmock/rspec'
11
-
12
10
  RSpec.configure do |config|
13
11
  config.color = true
14
12
  config.tty = true
15
13
  config.formatter = :documentation
16
- config.before(:each) { WebMock.disable_net_connect!(allow_localhost: true) }
17
- config.after(:each) { WebMock.allow_net_connect! }
18
14
  config.mock_with :rspec do |mocks|
19
15
  mocks.verify_partial_doubles = true
20
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berkeley_library-marc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Moles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-16 00:00:00.000000000 Z
11
+ date: 2022-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: marc
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - "<"
129
129
  - !ruby/object:Gem::Version
130
130
  version: '3.2'
131
+ - !ruby/object:Gem::Dependency
132
+ name: nokogiri
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.13'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.13'
131
145
  - !ruby/object:Gem::Dependency
132
146
  name: rake
133
147
  requirement: !ruby/object:Gem::Requirement
@@ -241,19 +255,19 @@ dependencies:
241
255
  - !ruby/object:Gem::Version
242
256
  version: '0.2'
243
257
  - !ruby/object:Gem::Dependency
244
- name: webmock
258
+ name: yard
245
259
  requirement: !ruby/object:Gem::Requirement
246
260
  requirements:
247
261
  - - "~>"
248
262
  - !ruby/object:Gem::Version
249
- version: '3.12'
263
+ version: 0.9.27
250
264
  type: :development
251
265
  prerelease: false
252
266
  version_requirements: !ruby/object:Gem::Requirement
253
267
  requirements:
254
268
  - - "~>"
255
269
  - !ruby/object:Gem::Version
256
- version: '3.12'
270
+ version: 0.9.27
257
271
  description: A gem providing MARC-related utility code and extensions to ruby-marc
258
272
  for the UC Berkeley Library
259
273
  email: dmoles@berkeley.edu
@@ -330,6 +344,7 @@ files:
330
344
  - spec/data/record-187888.xml
331
345
  - spec/marc_extensions/data_field_spec.rb
332
346
  - spec/marc_extensions/record_spec.rb
347
+ - spec/marc_extensions/xml_reader_spec.rb
333
348
  - spec/spec_helper.rb
334
349
  homepage: https://github.com/BerkeleyLibrary/marc
335
350
  licenses:
@@ -350,7 +365,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
350
365
  - !ruby/object:Gem::Version
351
366
  version: '0'
352
367
  requirements: []
353
- rubygems_version: 3.1.4
368
+ rubygems_version: 3.1.6
354
369
  signing_key:
355
370
  specification_version: 4
356
371
  summary: MARC utilities for the UC Berkeley Library
@@ -365,4 +380,5 @@ test_files:
365
380
  - spec/data/record-187888.xml
366
381
  - spec/marc_extensions/data_field_spec.rb
367
382
  - spec/marc_extensions/record_spec.rb
383
+ - spec/marc_extensions/xml_reader_spec.rb
368
384
  - spec/spec_helper.rb