pennmarc 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d1e26fab095596cdef4b9e0cf3b8f0c571ce7526b5bf8a8b17b6b1240d1c2b9
4
- data.tar.gz: 133829f994db6bc1ee7f341e6750c4202126dcc0dbe5bdaec3610170317164ab
3
+ metadata.gz: ea8b17f4fa038de9e5c44d384b82dfce5cc7a6cb909a5dc63fa1080351096e69
4
+ data.tar.gz: 58c48f68947d697689499018ca2abdc89d791db97e0ee076b74a12bf161d4470
5
5
  SHA512:
6
- metadata.gz: 2616ca6b451b3dc99caef45e63d444b8e1d4b3d87e8cac920f68ec54bcddfe6193392e34d6efe23f9144173baa0fc271ff0b94b2f1f7eb7676d5ee0beb8d800a
7
- data.tar.gz: f0a69e601d112df2b53b1107ff33e421d0d9da92845601bc24ed96980a6876bf56c79c25d3301f412d1dc20815ed591875aed0248957bff4c9179b14aa263cfd
6
+ metadata.gz: dad1e73ee44fa2635d81c6e0f1b7abc54d648b81679298121e6358c92f6872bf2f22d87f493005dad17b94402166cecb5c4751b3da33ae3a8ca35d90bc283e56
7
+ data.tar.gz: a23a12db492f9449632cfba5c7d1d247ab8302ede1094ac12ed0ff175f796787a2fa99dc9b17672d35ff67747523d9b4e40b31be7b602d910ae9a220d8f572a3
data/.pipeline.yml CHANGED
@@ -1,3 +1,7 @@
1
+ variables:
2
+ FF_SCRIPT_SECTIONS: "true"
3
+ GIT_CLONE_PATH: "${CI_BUILDS_DIR}/${CI_PROJECT_NAME}/${CI_JOB_ID}"
4
+
1
5
  include:
2
6
  - component: gitlab.library.upenn.edu/devops/gitlab/components/general/install_hashicorp_vault@~latest
3
7
  - component: gitlab.library.upenn.edu/devops/gitlab/components/general/vault_jwt_auth@~latest
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 10000`
3
- # on 2025-08-15 20:13:24 UTC using RuboCop version 1.79.2.
3
+ # on 2025-10-22 21:06:47 UTC using RuboCop version 1.79.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 27
9
+ # Offense count: 28
10
10
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
11
11
  Metrics/AbcSize:
12
12
  Exclude:
@@ -33,7 +33,7 @@ Metrics/ClassLength:
33
33
  - 'lib/pennmarc/helpers/subject.rb'
34
34
  - 'lib/pennmarc/helpers/title.rb'
35
35
 
36
- # Offense count: 21
36
+ # Offense count: 22
37
37
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
38
38
  Metrics/CyclomaticComplexity:
39
39
  Exclude:
@@ -50,7 +50,7 @@ Metrics/CyclomaticComplexity:
50
50
  - 'lib/pennmarc/helpers/title.rb'
51
51
  - 'lib/pennmarc/util.rb'
52
52
 
53
- # Offense count: 17
53
+ # Offense count: 18
54
54
  # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
55
55
  Metrics/MethodLength:
56
56
  Exclude:
@@ -70,7 +70,7 @@ Metrics/ModuleLength:
70
70
  Exclude:
71
71
  - 'lib/pennmarc/util.rb'
72
72
 
73
- # Offense count: 16
73
+ # Offense count: 17
74
74
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
75
75
  Metrics/PerceivedComplexity:
76
76
  Exclude:
@@ -109,9 +109,18 @@ RSpec/ExampleLength:
109
109
  Exclude:
110
110
  - 'spec/lib/pennmarc/marc_util_spec.rb'
111
111
 
112
- # Offense count: 1
112
+ # Offense count: 2
113
113
  # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
114
114
  # Include: **/*_spec.rb
115
115
  RSpec/SpecFilePathFormat:
116
116
  Exclude:
117
117
  - 'spec/lib/pennmarc/parser_spec.rb'
118
+ - 'spec/lib/pennmarc/services/title_suggestion_weight_service_spec.rb'
119
+
120
+ # Offense count: 1
121
+ # This cop supports unsafe autocorrection (--autocorrect-all).
122
+ # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
123
+ # SupportedStyles: predicate, comparison
124
+ Style/NumericPredicate:
125
+ Exclude:
126
+ - 'lib/pennmarc/services/title_suggestion_weight_service.rb'
data/Gemfile CHANGED
@@ -19,6 +19,7 @@ group :test do
19
19
  end
20
20
 
21
21
  group :development do
22
- gem 'webrick', '~> 1.8'
22
+ gem 'puma'
23
+ gem 'rackup'
23
24
  gem 'yard', '~> 0.9'
24
25
  end
data/Gemfile.lock CHANGED
@@ -35,6 +35,7 @@ GEM
35
35
  nokogiri (~> 1.0)
36
36
  rexml
37
37
  minitest (5.26.0)
38
+ nio4r (2.7.4)
38
39
  nokogiri (1.18.9-arm64-darwin)
39
40
  racc (~> 1.4)
40
41
  nokogiri (1.18.9-x64-mingw-ucrt)
@@ -48,8 +49,12 @@ GEM
48
49
  ast (~> 2.4.1)
49
50
  racc
50
51
  prism (1.4.0)
52
+ puma (7.1.0)
53
+ nio4r (~> 2.0)
51
54
  racc (1.8.1)
52
- rack (3.2.0)
55
+ rack (3.2.3)
56
+ rackup (2.2.1)
57
+ rack (>= 3)
53
58
  rainbow (3.1.1)
54
59
  rake (13.3.0)
55
60
  regexp_parser (2.11.2)
@@ -121,7 +126,6 @@ GEM
121
126
  rubocop-rake
122
127
  rubocop-rspec
123
128
  uri (1.0.4)
124
- webrick (1.8.1)
125
129
  yard (0.9.37)
126
130
 
127
131
  PLATFORMS
@@ -137,11 +141,12 @@ DEPENDENCIES
137
141
  library_stdnums (~> 1.6)
138
142
  marc (~> 1.2)
139
143
  nokogiri (~> 1.15)
144
+ puma
145
+ rackup
140
146
  rake (~> 13.0)
141
147
  rspec (~> 3.12)
142
148
  simplecov (~> 0.22)
143
149
  upennlib-rubocop
144
- webrick (~> 1.8)
145
150
  yard (~> 0.9)
146
151
 
147
152
  BUNDLED WITH
@@ -36,7 +36,7 @@ module PennMARC
36
36
 
37
37
  Time.strptime(date_added, format)
38
38
  rescue StandardError => e
39
- puts 'Error parsing date in date added subfield. ' \
39
+ warn 'Error parsing date in date added subfield. ' \
40
40
  "mmsid: #{Identifier.mmsid(record)}, value: #{date_added}, error: #{e}"
41
41
  nil
42
42
  end
@@ -59,7 +59,7 @@ module PennMARC
59
59
 
60
60
  Time.strptime(date_time_string, '%Y%m%d%H%M%S.%N')
61
61
  rescue StandardError => e
62
- puts 'Error parsing last updated date. ' \
62
+ warn 'Error parsing last updated date. ' \
63
63
  "mmsid: #{Identifier.mmsid(record)}, value: #{date_time_string}, error: #{e}"
64
64
  nil
65
65
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../services/title_suggestion_weight_service'
4
+
3
5
  module PennMARC
4
6
  # This helper contains logic for parsing out Title and Title-related fields.
5
7
  class Title < Helper
@@ -36,6 +38,27 @@ module PennMARC
36
38
  NO_TITLE_PROVIDED = '[No title provided]'
37
39
 
38
40
  class << self
41
+ # Values for title suggester, including only ǂa and ǂb from
42
+ # {https://www.loc.gov/marc/bibliographic/bd245.html 245} field. Limits the output to 20 words and strips any
43
+ # trailing slashes.
44
+ # @param record [MARC::Record]
45
+ # @return [Array<String>] array of all title values for suggestion
46
+ def suggest(record)
47
+ record.fields(%w[245]).filter_map do |field|
48
+ join_subfields(field, &subfield_in?(%w[a b]))
49
+ .squish
50
+ .truncate_words(20)
51
+ .sub(%r{ /$}, '')
52
+ end
53
+ end
54
+
55
+ # An integer value used for weighing title suggest values. See {PennMARC::TitleSuggestionWeightService} for logic.
56
+ # @param record [MARC::Record]
57
+ # @return [Integer]
58
+ def suggest_weight(record)
59
+ PennMARC::TitleSuggestionWeightService.weight record
60
+ end
61
+
39
62
  # Main Title Search field. Takes from {https://www.loc.gov/marc/bibliographic/bd245.html 245} and linked 880.
40
63
  # @note Ported from get_title_1_search_values.
41
64
  # @param record [MARC::Record]
@@ -50,10 +50,9 @@ module PennMARC
50
50
  end
51
51
 
52
52
  # @param filename [String] name of mapping file in config directory, with file extension
53
- # @param symbolize_names [Boolean] whether or not to symbolize keys in returned hash
53
+ # @param symbolize_names [Boolean] whether to symbolize keys in returned hash
54
54
  # @return [Hash, nil] mapping as hash
55
55
  def load_map(filename, symbolize_names: true)
56
- puts { "Loading #{filename}" }
57
56
  YAML.safe_load(File.read(File.join(File.expand_path(__dir__), 'mappings', filename)),
58
57
  symbolize_names: symbolize_names)
59
58
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PennMARC
4
+ # A service to calculate suggestion weights based on a variety of criteria
5
+ class TitleSuggestionWeightService
6
+ # Starting score
7
+ BASE_WEIGHT = 10
8
+
9
+ # Array of symbols referring to methods on this object that return a boolean and the scoring factor if the
10
+ # method returns true.
11
+ FACTORS = [
12
+ [:targeted_format?, 8],
13
+ [:published_in_last_ten_years?, 5],
14
+ [:electronic_holdings?, 3],
15
+ [:high_encoding_level?, 2],
16
+ [:physical_holdings?, 1],
17
+ [:low_encoding_level?, -2],
18
+ [:weird_format?, -5],
19
+ [:no_holdings?, -10]
20
+ ].freeze
21
+
22
+ # Score higher records with these formats
23
+ TARGETED_FORMATS = [Format::BOOK, Format::WEBSITE_DATABASE, Format::JOURNAL_PERIODICAL, Format::NEWSPAPER,
24
+ Format::SOUND_RECORDING, Format::MUSICAL_SCORE].freeze
25
+ # Score lower these formats
26
+ WEIRD_FORMATS = [Format::OTHER, Format::THREE_D_OBJECT].freeze
27
+
28
+ # See #{PennMARC::EncodingLevel} for more of the logic that determines sort values
29
+ # An encoding sort level of this value is considered good
30
+ HIGH_ENCODING_SORT_LEVEL = 0
31
+ # An encoding sort level higher than this is considered poor
32
+ LOW_ENCODING_SORT_LEVEL = 4
33
+
34
+ class << self
35
+ # Calculate a weight for use in sorting good title suggestions from bad
36
+ # @param record [MARC::Record]
37
+ # @return [Integer]
38
+ def weight(record)
39
+ factors.reduce(BASE_WEIGHT) do |weight, (call, score)|
40
+ weight + (public_send(call, record) ? score : 0)
41
+ end
42
+ end
43
+
44
+ # @return [Array[Array]]
45
+ def factors
46
+ FACTORS
47
+ end
48
+
49
+ # @param record [MARC::Record]
50
+ # @return [Boolean]
51
+ def published_in_last_ten_years?(record)
52
+ return false unless Date.publication(record).present?
53
+
54
+ Date.publication(record) > 10.years.ago
55
+ end
56
+
57
+ # @param record [MARC::Record]
58
+ # @return [Boolean]
59
+ def electronic_holdings?(record)
60
+ Inventory.electronic(record)&.any? || false
61
+ end
62
+
63
+ # @param record [MARC::Record]
64
+ # @return [Boolean]
65
+ def physical_holdings?(record)
66
+ Inventory.physical(record)&.any? || false
67
+ end
68
+
69
+ # @param record [MARC::Record]
70
+ # @return [Boolean]
71
+ def targeted_format?(record)
72
+ (Format.facet(record) & TARGETED_FORMATS).any?
73
+ end
74
+
75
+ # @param record [MARC::Record]
76
+ # @return [Boolean]
77
+ def high_encoding_level?(record)
78
+ Encoding.level_sort(record) == HIGH_ENCODING_SORT_LEVEL
79
+ end
80
+
81
+ # @param record [MARC::Record]
82
+ # @return [Boolean]
83
+ def weird_format?(record)
84
+ (Format.facet(record) & WEIRD_FORMATS).any?
85
+ end
86
+
87
+ # @param record [MARC::Record]
88
+ # @return [Boolean]
89
+ def no_holdings?(record)
90
+ !electronic_holdings?(record) && !physical_holdings?(record)
91
+ end
92
+
93
+ # @param record [MARC::Record]
94
+ # @return [Boolean]
95
+ def low_encoding_level?(record)
96
+ return false unless Encoding.level_sort(record).present?
97
+
98
+ Encoding.level_sort(record) > LOW_ENCODING_SORT_LEVEL
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PennMARC
4
- VERSION = '1.3.5'
4
+ VERSION = '1.4.0'
5
5
  end
@@ -90,7 +90,7 @@ describe 'PennMARC::Date' do
90
90
  expect {
91
91
  helper.added(record)
92
92
  }.to output('Error parsing date in date added subfield. mmsid: mmsid, value: invalid date, ' \
93
- "error: invalid date or strptime format - `invalid date' `%Y-%m-%d %H:%M:%S'\n").to_stdout
93
+ "error: invalid date or strptime format - `invalid date' `%Y-%m-%d %H:%M:%S'\n").to_stderr
94
94
  end
95
95
  end
96
96
  end
@@ -120,7 +120,7 @@ describe 'PennMARC::Date' do
120
120
  expect {
121
121
  helper.last_updated(record)
122
122
  }.to output('Error parsing last updated date. mmsid: mmsid, value: invalid date, ' \
123
- "error: invalid date or strptime format - `invalid date' `%Y%m%d%H%M%S.%N'\n").to_stdout
123
+ "error: invalid date or strptime format - `invalid date' `%Y%m%d%H%M%S.%N'\n").to_stderr
124
124
  end
125
125
  end
126
126
  end
@@ -6,6 +6,39 @@ describe 'PennMARC::Title' do
6
6
  let(:fields) { [marc_field(tag: '245', subfields: subfields)] }
7
7
  let(:record) { marc_record fields: fields, leader: leader }
8
8
 
9
+ describe '.suggest' do
10
+ context 'with slashes in the title as well as at the end of ǂb' do
11
+ let(:subfields) { { a: 'Title /', b: 'Subtitle /' } }
12
+
13
+ it 'removes only the trailing slash' do
14
+ expect(helper.suggest(record).first).to eq 'Title / Subtitle'
15
+ end
16
+ end
17
+
18
+ context 'with a long title more than twenty words' do
19
+ let(:subfields) do
20
+ { a: 'The Book of Very Short Words With a Lot of Words',
21
+ b: 'Containing many words with that may or may not have many characters /' }
22
+ end
23
+
24
+ it 'truncates the title to twenty words and adds a trailing ellipsis' do
25
+ truncated_suggestion = helper.suggest(record).first
26
+ expect(truncated_suggestion).to eq(
27
+ 'The Book of Very Short Words With a Lot of Words Containing many words with that may or may not...'
28
+ )
29
+ expect(truncated_suggestion.split(' ').count).to eq 20
30
+ end
31
+ end
32
+
33
+ context 'with other subfields present' do
34
+ let(:subfields) { { a: 'Title', b: 'Subtitle', c: 'Author' } }
35
+
36
+ it 'returns only title fields' do
37
+ expect(helper.suggest(record).first).not_to include 'Author'
38
+ end
39
+ end
40
+ end
41
+
9
42
  describe '.search' do
10
43
  let(:fields) do
11
44
  [marc_field(tag: '245', subfields: { a: 'Title', b: 'Subtitle', c: 'Responsibility', h: 'Medium' }),
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PennMARC::TitleSuggestionWeightService do
4
+ let(:record) { instance_double MARC::Record }
5
+
6
+ describe '.weight' do
7
+ context 'with defined factors' do
8
+ before do
9
+ allow(described_class).to receive_messages(
10
+ targeted_format?: true,
11
+ published_in_last_ten_years?: false,
12
+ electronic_holdings?: false,
13
+ high_encoding_level?: false,
14
+ physical_holdings?: false,
15
+ low_encoding_level?: false,
16
+ weird_format?: false,
17
+ no_holdings?: false
18
+ )
19
+ end
20
+
21
+ it 'scores properly based on factor responce valence' do
22
+ expected_score = described_class::BASE_WEIGHT + described_class::FACTORS[0].second
23
+ expect(described_class.weight(record)).to eq expected_score
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '.published_in_the_last_ten_years' do
29
+ before { allow(PennMARC::Date).to receive(:publication).with(record).and_return(record_date) }
30
+
31
+ let(:value) { described_class.published_in_last_ten_years?(record) }
32
+
33
+ context 'with no date' do
34
+ let(:record_date) { nil }
35
+
36
+ it 'returns true' do
37
+ expect(value).to be false
38
+ end
39
+ end
40
+
41
+ context 'with a recent date' do
42
+ let(:record_date) { Time.now }
43
+
44
+ it 'returns true' do
45
+ expect(value).to be true
46
+ end
47
+ end
48
+
49
+ context 'with an ancient date' do
50
+ let(:record_date) { Time.now - 400.years }
51
+
52
+ it 'returns false' do
53
+ expect(value).to be false
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '.targeted_format?' do
59
+ before { allow(PennMARC::Format).to receive(:facet).with(record).and_return([record_format]) }
60
+
61
+ let(:value) { described_class.targeted_format?(record) }
62
+
63
+ context 'with no format' do
64
+ let(:record_format) { nil }
65
+
66
+ it 'returns false' do
67
+ expect(value).to be false
68
+ end
69
+ end
70
+
71
+ context 'with a targeted format' do
72
+ let(:record_format) { PennMARC::TitleSuggestionWeightService::TARGETED_FORMATS.sample }
73
+
74
+ it 'returns true' do
75
+ expect(value).to be true
76
+ end
77
+ end
78
+
79
+ context 'with a non-targeted format' do
80
+ let(:record_format) { PennMARC::TitleSuggestionWeightService::WEIRD_FORMATS.sample }
81
+
82
+ it 'returns false' do
83
+ expect(value).to be false
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '.weird_format?' do
89
+ before { allow(PennMARC::Format).to receive(:facet).with(record).and_return([record_format]) }
90
+
91
+ let(:value) { described_class.weird_format?(record) }
92
+
93
+ context 'with no format' do
94
+ let(:record_format) { nil }
95
+
96
+ it 'returns false' do
97
+ expect(value).to be false
98
+ end
99
+ end
100
+
101
+ context 'with a weird format' do
102
+ let(:record_format) { PennMARC::TitleSuggestionWeightService::WEIRD_FORMATS.sample }
103
+
104
+ it 'returns true' do
105
+ expect(value).to be true
106
+ end
107
+ end
108
+
109
+ context 'with a non-weird format' do
110
+ let(:record_format) { PennMARC::TitleSuggestionWeightService::TARGETED_FORMATS.sample }
111
+
112
+ it 'returns false' do
113
+ expect(value).to be false
114
+ end
115
+ end
116
+ end
117
+
118
+ describe '.low_encoding_level?' do
119
+ before { allow(PennMARC::Encoding).to receive(:level_sort).with(record).and_return(encoding_sort_score) }
120
+
121
+ let(:value) { described_class.low_encoding_level?(record) }
122
+
123
+ context 'with no encoding level' do
124
+ let(:encoding_sort_score) { nil }
125
+
126
+ it 'returns false' do
127
+ expect(value).to be false
128
+ end
129
+ end
130
+
131
+ context 'with a low encoding level' do
132
+ let(:encoding_sort_score) { 11 }
133
+
134
+ it 'returns true' do
135
+ expect(value).to be true
136
+ end
137
+ end
138
+
139
+ context 'with a high encoding level' do
140
+ let(:encoding_sort_score) { PennMARC::TitleSuggestionWeightService::HIGH_ENCODING_SORT_LEVEL }
141
+
142
+ it 'returns false' do
143
+ expect(value).to be false
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '.electronic_holdings?' do
149
+ before do
150
+ allow(PennMARC::Inventory).to receive(:electronic).with(record).and_return(holdings)
151
+ end
152
+
153
+ let(:value) { described_class.electronic_holdings?(record) }
154
+
155
+ context 'with electronic holdings' do
156
+ let(:holdings) { [PennMARC::InventoryEntry::Electronic] }
157
+
158
+ it 'returns true' do
159
+ expect(value).to be true
160
+ end
161
+ end
162
+
163
+ context 'without any holdings' do
164
+ let(:holdings) { [] }
165
+
166
+ it 'returns false' do
167
+ expect(value).to be false
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '.physical_holdings?' do
173
+ before do
174
+ allow(PennMARC::Inventory).to receive(:physical).with(record).and_return(holdings)
175
+ end
176
+
177
+ let(:value) { described_class.physical_holdings?(record) }
178
+
179
+ context 'with physical holdings' do
180
+ let(:holdings) { [PennMARC::InventoryEntry::Physical] }
181
+
182
+ it 'returns true' do
183
+ expect(value).to be true
184
+ end
185
+ end
186
+
187
+ context 'without any holdings' do
188
+ let(:holdings) { [] }
189
+
190
+ it 'returns false' do
191
+ expect(value).to be false
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '.no_holdings?' do
197
+ before do
198
+ allow(PennMARC::Inventory).to receive(:physical).with(record).and_return(physical_holdings)
199
+ allow(PennMARC::Inventory).to receive(:electronic).with(record).and_return(electronic_holdings)
200
+ end
201
+
202
+ let(:value) { described_class.no_holdings?(record) }
203
+
204
+ context 'with neither physical nor electronic holdings' do
205
+ let(:physical_holdings) { [] }
206
+ let(:electronic_holdings) { [] }
207
+
208
+ it 'returns true' do
209
+ expect(value).to be true
210
+ end
211
+ end
212
+
213
+ context 'with only electronic holdings' do
214
+ let(:physical_holdings) { [] }
215
+ let(:electronic_holdings) { [instance_double(PennMARC::InventoryEntry::Electronic)] }
216
+
217
+ it 'returns false' do
218
+ expect(value).to be false
219
+ end
220
+ end
221
+
222
+ context 'with only physical holdings' do
223
+ let(:physical_holdings) { [instance_double(PennMARC::InventoryEntry::Physical)] }
224
+ let(:electronic_holdings) { [] }
225
+
226
+ it 'returns false' do
227
+ expect(value).to be false
228
+ end
229
+ end
230
+ end
231
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pennmarc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.5
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Kanning
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2025-10-16 00:00:00.000000000 Z
15
+ date: 2025-10-31 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport
@@ -143,6 +143,7 @@ files:
143
143
  - lib/pennmarc/mappings/locations.yml
144
144
  - lib/pennmarc/mappings/relator.yml
145
145
  - lib/pennmarc/parser.rb
146
+ - lib/pennmarc/services/title_suggestion_weight_service.rb
146
147
  - lib/pennmarc/test/marc_helpers.rb
147
148
  - lib/pennmarc/util.rb
148
149
  - lib/pennmarc/version.rb
@@ -172,6 +173,7 @@ files:
172
173
  - spec/lib/pennmarc/helpers/title_spec.rb
173
174
  - spec/lib/pennmarc/marc_util_spec.rb
174
175
  - spec/lib/pennmarc/parser_spec.rb
176
+ - spec/lib/pennmarc/services/title_suggestion_weight_service_spec.rb
175
177
  - spec/spec_helper.rb
176
178
  - spec/support/fixture_helpers.rb
177
179
  homepage: https://gitlab.library.upenn.edu/dld/catalog/pennmarc