marcspec 1.1.1 → 1.5.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/CHANGES CHANGED
@@ -1,3 +1,5 @@
1
+ 1.5.0
2
+ * Added DSL; making it the preferred way to operate. Totally deprecating the aray-of-hashes stuff.
1
3
  1.1.1
2
4
  * Fixed bug with :noMapKeyDefault => :passthrough for MVMap
3
5
  1.1.0
data/README.rdoc CHANGED
@@ -4,7 +4,10 @@ The MARCSpec contains classes designed to make it (relatively) easy to specify
4
4
  a data field (my use case specifically is solr) in terms of sets of MARC fields and subfields.
5
5
 
6
6
 
7
- == Docs and examples in marc2solr
7
+ == Docs and examples
8
+
9
+ Docs are hosted at the [[wiki|http://github.com/billdueber/marcspec/wiki/]]
10
+
8
11
 
9
12
  Documented samples are available as part of the marc2solr project
10
13
  at http://github.com/billdueber/marc2solr -- look in the simple_sample area.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.5.0
@@ -17,7 +17,7 @@ module MARCSpec
17
17
  # substrings are specified.
18
18
 
19
19
  class ControlFieldSpec
20
- attr_accessor :tag, :range
20
+ attr_accessor :tag, :range, :rangehistory
21
21
 
22
22
  def initialize (tag, range=nil)
23
23
  unless MARC4J4R::ControlField.control_tag? tag
@@ -25,11 +25,12 @@ module MARCSpec
25
25
  end
26
26
  @tag = tag
27
27
  self.range = range
28
+ @rangehistory = []
28
29
  end
29
30
 
30
31
  def == other
31
32
  return ((self.tag == other.tag) and
32
- (self.range = other.range))
33
+ (self.range == other.range))
33
34
  end
34
35
 
35
36
 
@@ -41,6 +42,7 @@ module MARCSpec
41
42
  # @return [MARCSpec::ControlFieldSpec] self
42
43
 
43
44
  def range= range
45
+ @rangehistory << @range if @range
44
46
  if range.nil?
45
47
  @range = nil
46
48
  return self
@@ -50,24 +50,19 @@ module MARCSpec
50
50
 
51
51
  def initialize(opts)
52
52
  @solrField = opts[:solrField]
53
- @module = opts[:module]
54
- @functionSymbol = opts[:functionSymbol]
53
+ @module = opts[:module] || nil
54
+ @functionSymbol = opts[:functionSymbol] || nil
55
55
 
56
- unless @solrField and @module and @functionSymbol
57
- raise ArgumentError, "Custom solr spec must have a field name in :solrField, module in :module, and the function name as a symbol in :functionSymbol"
58
- end
59
-
60
-
61
56
  @functionArgs = opts[:functionArgs] || []
62
57
 
63
58
  @first = opts[:firstOnly] || false
64
- @default = opts[:default] || nil
59
+ @defaultValue = opts[:default] || nil
65
60
  @map = opts[:map] || nil
66
61
  @noMapKeyDefault = opts[:noMapKeyDefault] || nil
67
62
 
68
63
  if @solrField.is_a? Array
69
64
  @arity = @solrField.size
70
- if @first or @default or @map or @noMapKeyDefault
65
+ if @first or @defaultValue or @map or @noMapKeyDefault
71
66
  raise ArgumentError, "Custom spec with multiple solrFields can't have :first, :map, :default, or :noMapKeyDefault set"
72
67
  end
73
68
  else
@@ -101,9 +96,9 @@ module MARCSpec
101
96
  PP.singleline_pp(@solrField, s)
102
97
  s.print(",\n ")
103
98
  s.print ":firstOnly => true,\n " if @first
104
- if @default
99
+ if @defaultValue
105
100
  s.print(":default => ")
106
- PP.singleline_pp(@default, s)
101
+ PP.singleline_pp(@defaultValue, s)
107
102
  s.print(",\n ")
108
103
  end
109
104
  if @map
@@ -0,0 +1,139 @@
1
+ module MARCSpec
2
+ # Here's where we put a simple DSL hook for SpecSet
3
+
4
+ def self.build (&blk)
5
+ ss = SpecSet.new
6
+ ss.instance_eval(&blk)
7
+ return ss
8
+ end
9
+
10
+ # Re-open SpecSet to add the necessary methods
11
+
12
+ class SpecSet
13
+
14
+ # create a normal field
15
+ def field(name, &blk)
16
+ $LOG.debug "Creating regular field #{name}"
17
+ sfs = SolrFieldSpec.new(:solrField=>name)
18
+ sfs.instance_eval(&blk)
19
+ self << sfs
20
+ return sfs
21
+ end
22
+
23
+ # Create a constant field
24
+ def constant(name, &blk)
25
+ $LOG.debug "Creating constant field #{name}"
26
+
27
+ constant = ConstantSolrSpec.new(:solrField=>name)
28
+ constant.instance_eval(&blk)
29
+ self << constant
30
+ return constant
31
+ end
32
+
33
+ def custom(name, &blk)
34
+ $LOG.debug "Creating custom field #{name}"
35
+ custom = CustomSolrSpec.new(:solrField=>name)
36
+ custom.instance_eval(&blk)
37
+
38
+ ##### Check to make sure it's all ok in here#####
39
+ self << custom
40
+ return custom
41
+ end
42
+
43
+ end
44
+
45
+
46
+ class SolrFieldSpec
47
+ def spec(tag, &blk)
48
+ if tag.to_i == tag
49
+ tag = '%03d' % tag
50
+ end
51
+
52
+ marcfieldspec = nil
53
+ if MARC4J4R::ControlField.control_tag? tag
54
+ marcfieldspec = MARCSpec::ControlFieldSpec.new(tag)
55
+ else
56
+ marcfieldspec = MARCSpec::VariableFieldSpec.new(tag)
57
+ end
58
+
59
+ marcfieldspec.instance_eval(&blk) if block_given?
60
+
61
+ # If we had multiple sub calls, get them from the codehistory
62
+ if marcfieldspec.is_a? MARCSpec::VariableFieldSpec
63
+ marcfieldspec.codehistory.uniq.compact.each do |c|
64
+ newmfs = marcfieldspec.clone
65
+ newmfs.codes = c
66
+ self << newmfs
67
+ end
68
+ end
69
+
70
+ if marcfieldspec.is_a? MARCSpec::ControlFieldSpec
71
+ marcfieldspec.rangehistory.uniq.compact.each do |r|
72
+ newcfs = marcfieldspec.clone
73
+ newcfs.range = r
74
+ self << newcfs
75
+ end
76
+ end
77
+
78
+ self << marcfieldspec
79
+ return marcfieldspec
80
+ end
81
+
82
+ def firstOnly val=true
83
+ @first = val
84
+ end
85
+
86
+ def default val
87
+ @defaultValue = val
88
+ end
89
+
90
+ def mapname str
91
+ @_mapname = str
92
+ end
93
+
94
+ def mapMissDefault str
95
+ @noMapKeyDefault = str
96
+ end
97
+
98
+ end
99
+
100
+ class ControlFieldSpec
101
+ def char c
102
+ self.range = c
103
+ return self
104
+ end
105
+
106
+ alias_method :chars, :char
107
+ end
108
+
109
+ class VariableFieldSpec
110
+ def sub c
111
+ self.codes = c
112
+ return self
113
+ end
114
+
115
+ alias_method :subs, :sub
116
+ end
117
+
118
+
119
+ class CustomSolrSpec
120
+ def function(name, &blk)
121
+ self.functionSymbol = name.to_sym
122
+ self.instance_eval(&blk)
123
+ end
124
+
125
+ def mod(constant)
126
+ self.module = constant
127
+ end
128
+
129
+ def args(*arg_or_args)
130
+ self.functionArgs = arg_or_args
131
+ end
132
+ end
133
+
134
+ class ConstantSolrSpec
135
+ def value(val)
136
+ self.constantValue = val
137
+ end
138
+ end
139
+ end
data/lib/marcspec/map.rb CHANGED
@@ -45,6 +45,13 @@ module MARCSpec
45
45
  raise e
46
46
  end
47
47
 
48
+ # Derive a name if there isn't one
49
+ unless rawmap[:mapname]
50
+ name = File.basename(filename)
51
+ name.gsub! /\..*$/, '' # remove the extension
52
+ rawmap[:mapname] = name
53
+ end
54
+
48
55
  case rawmap[:maptype]
49
56
  when :kv
50
57
  return KVMap.new(rawmap[:mapname], rawmap[:map])
@@ -19,24 +19,56 @@ module MARCSpec
19
19
  # Again, note that if several keys are === to the passed argument, all the values will be returned.
20
20
 
21
21
  class MultiValueMap < Map
22
+
23
+ # Override initialize and map= so we can do some optimization
24
+
25
+ def initialize *args
26
+ super(*args)
27
+ self.optimize
28
+ end
29
+
30
+ def map= map
31
+ @map = map
32
+ self.optimize
33
+ end
22
34
 
23
- attr_accessor :mapname,:map
35
+
36
+ def optimize
37
+ @super_regexp = Regexp.union @map.map{|pv| pv[0]}
38
+ inverted = {}
39
+ @map.each do |pv|
40
+ inverted[pv[1]] ||= []
41
+ inverted[pv[1]] << pv[0]
42
+ end
43
+ inverted.each_pair do |vals, patterns|
44
+ next unless patterns.size > 1
45
+ newpat = Regexp.union patterns
46
+ patterns.each do |p|
47
+ @map.delete_if{|pv| p == pv[0]}
48
+ end
49
+ @map << [newpat, vals]
50
+ end
51
+ end
24
52
 
25
53
  # Given a passed_in_key (and optional default) return the set of values that match, as described
26
54
  # above.
27
55
  def [] key, default=nil
28
56
  rv = []
29
- @map.each do |pv|
30
- if pv[1].is_a? Proc
31
- match = pv[0].match key
32
- rv << pv[1].call(match) if match
33
- else
34
- rv << pv[1] if pv[0] === key
57
+
58
+ if @super_regexp.match key # do *any* of them match?
59
+ @map.each do |pv|
60
+ if pv[1].is_a? Proc
61
+ match = pv[0].match key
62
+ rv << pv[1].call(match) if match
63
+ else
64
+ rv << pv[1] if pv[0] === key
65
+ end
35
66
  end
67
+ rv.flatten!
68
+ rv.compact!
69
+ rv.uniq!
36
70
  end
37
- rv.flatten!
38
- rv.compact!
39
- rv.uniq!
71
+
40
72
  if rv.size > 0
41
73
  return rv
42
74
  else
@@ -3,12 +3,13 @@ require 'marc4j4r/controlfield'
3
3
 
4
4
  module MARCSpec
5
5
  class SolrFieldSpec
6
- attr_accessor :solrField, :first, :map, :noMapKeyDefault, :marcfieldspecs, :default, :arity
6
+ attr_accessor :solrField, :first, :map, :noMapKeyDefault, :marcfieldspecs, :defaultValue, :_mapname
7
+ attr_reader :arity
7
8
 
8
9
  def initialize(opts)
9
10
  @solrField = opts[:solrField]
10
11
  @first = opts[:firstOnly] || false
11
- @default = opts[:default] || nil
12
+ @defaultValue = opts[:default] || nil
12
13
  @map = opts[:map] || nil
13
14
  @noMapKeyDefault = opts[:noMapKeyDefault] || nil
14
15
  @arity = 1
@@ -42,10 +43,10 @@ module MARCSpec
42
43
  # If we got nothing, just return either nothing or the defualt,
43
44
  # if there is one. Don't screw around with mapping.
44
45
  if vals.size == 0
45
- if @default.nil? # unless there's a default value, just return nothing
46
+ if @defaultValue.nil? # unless there's a default value, just return nothing
46
47
  return []
47
48
  else
48
- return [@default]
49
+ return [@defaultValue]
49
50
  end
50
51
  end
51
52
 
@@ -68,6 +69,7 @@ module MARCSpec
68
69
  return ((other.solrField == self.solrField) and
69
70
  (other.first == self.first) and
70
71
  (other.map == self.map) and
72
+ (other.defaultValue == self.defaultValue) and
71
73
  (other.noMapKeyDefault == self.noMapKeyDefault) and
72
74
  (other.marcfieldspecs == self.marcfieldspecs))
73
75
  end
@@ -98,9 +100,9 @@ module MARCSpec
98
100
  PP.singleline_pp(@solrField, s)
99
101
  s.print(",\n ")
100
102
  s.print ":firstOnly => true,\n " if @first
101
- if @default
103
+ if @defaultValue
102
104
  s.print(":default => ")
103
- PP.singleline_pp(@default, s)
105
+ PP.singleline_pp(@defaultValue, s)
104
106
  s.print(",\n ")
105
107
  end
106
108
  if @map
@@ -24,6 +24,8 @@ module MARCSpec
24
24
  end
25
25
 
26
26
  end
27
+
28
+
27
29
 
28
30
  class SpecSet
29
31
  attr_accessor :tmaps, :solrfieldspecs, :benchmarks
@@ -54,6 +56,31 @@ module MARCSpec
54
56
  end
55
57
 
56
58
 
59
+ def buildSpecsFromDSLFile file
60
+ f = File.open(file)
61
+ $LOG.fatal("Can't open file #{file}") unless f
62
+ self.instance_eval(f.read)
63
+ self.check_and_fill_maps
64
+ end
65
+
66
+ def check_and_fill_maps
67
+ @solrfieldspecs.each do |sfs|
68
+ if sfs._mapname
69
+ map = self.map(sfs._mapname)
70
+ if map
71
+ $LOG.debug " Found map #{map.mapname} for solr field #{sfs.solrField}"
72
+ sfs.map = map
73
+ else
74
+ $LOG.error " Cannot find map #{sfs._mapname} for solr field #{sfs.solrField}"
75
+ STDERR.puts "FATAL Cannot find map #{sfs._mapname} for solr field #{sfs.solrField}"
76
+ Process.exit
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+
83
+
57
84
  def buildSpecsFromList speclist
58
85
  speclist.each do |spechash|
59
86
  if spechash[:module]
@@ -67,6 +94,7 @@ module MARCSpec
67
94
  map = self.map(spechash[:mapname])
68
95
  unless map
69
96
  $LOG.error " Cannot find map #{spechash[:mapname]} for field #{spechash[:solrField]}"
97
+ Process.exit
70
98
  else
71
99
  $LOG.debug " Found map #{spechash[:mapname]} for field #{spechash[:solrField]}"
72
100
  solrspec.map = map
@@ -80,7 +108,7 @@ module MARCSpec
80
108
 
81
109
  def add_spec solrfieldspec
82
110
  self.solrfieldspecs << solrfieldspec
83
- @benchmarks[solrfieldspec.solrField] = Benchmark::Tms.new(0,0,0,0, 0, solrfieldspec.solrField)
111
+ @benchmarks[solrfieldspec.solrField.to_s] = Benchmark::Tms.new(0,0,0,0, 0, solrfieldspec.solrField)
84
112
  end
85
113
 
86
114
  alias_method :<<, :add_spec
@@ -106,7 +134,7 @@ module MARCSpec
106
134
 
107
135
  def fill_hashlike_from_marc_benchmark r, hashlike
108
136
  @solrfieldspecs.each do |sfs|
109
- @benchmarks[sfs.solrField] += Benchmark.measure do
137
+ @benchmarks[sfs.solrField.to_s] += Benchmark.measure do
110
138
  if sfs.arity == 1
111
139
  hashlike.add(sfs.solrField,sfs.marc_values(r, hashlike))
112
140
  else
@@ -15,12 +15,13 @@ module MARCSpec
15
15
 
16
16
  class VariableFieldSpec
17
17
 
18
- attr_accessor :tag, :codes, :joiner, :ind1, :ind2
18
+ attr_accessor :tag, :codes, :joiner, :ind1, :ind2, :codehistory
19
19
 
20
20
  def initialize tag, codes=nil, joiner=' '
21
21
  @tag = tag
22
22
  @joiner = joiner || ' '
23
23
  self.codes = codes
24
+ @codehistory = []
24
25
  end
25
26
 
26
27
  def == other
@@ -30,6 +31,7 @@ module MARCSpec
30
31
  end
31
32
 
32
33
  def codes= c
34
+ @codehistory << @codes if @codes
33
35
  if c.nil?
34
36
  @codes = nil
35
37
  return nil
data/lib/marcspec.rb CHANGED
@@ -11,7 +11,7 @@ require "marcspec/kvmap"
11
11
  require "marcspec/multivaluemap"
12
12
  require "marcspec/specset"
13
13
  require "marcspec/marcfieldspec"
14
-
14
+ require "marcspec/dsl"
15
15
 
16
16
  # Build up a little module to include in MARC4J4R::Record that
17
17
  # gives us a way to cache computed values within the record itself
@@ -0,0 +1,14 @@
1
+ {
2
+ :maptype=>:kv,
3
+ :map => {"mdp"=>"University of Michigan",
4
+ "wu"=>"University of Wisconsin",
5
+ "gwla"=>"University of Michigan",
6
+ "miua"=>"University of Michigan",
7
+ "miun"=>"University of Michigan",
8
+ "inu"=>"Indiana University",
9
+ "uc1"=>"University of California",
10
+ "uc2"=>"University of California",
11
+ "pst"=>"Penn State University",
12
+ "umn"=>"University of Minnesota"}
13
+
14
+ }
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,370 @@
1
+ require 'spec_helper'
2
+
3
+ # The contents of @one
4
+ #
5
+ #
6
+ # LEADER 00734njm a2200217uu 4500
7
+ # 001 afc99990058366
8
+ # 003 DLC
9
+ # 005 20071104155141.9
10
+ # 007 sd ummunniauub
11
+ # 008 071103s1939 xxufmnne||||||||| u eng||
12
+ # 010 $a afc99990058366
13
+ # 040 $a DLC $c DLC
14
+ # 245 04 $a The Texas ranger $h [sound recording] / $c Sung by Beale D. Taylor.
15
+ # 260 $a Medina, Texas, $c 1939.
16
+ # 300 $a 1 sound disc : $b analog, 33 1/3 rpm, mono. ; $c 12 in.
17
+ # 651 0 $a Medina $z Texas $z United States of America.
18
+ # 700 1 $a Lomax, John Avery, 1867-1948 $e Recording engineer.
19
+ # 700 1 $a Lomax, Ruby T. (Ruby Terrill) $e Recording engineer.
20
+ # 700 1 $a Taylor, Beale D. $e Singer.
21
+ # 852 $a American Folklife Center, Library of Congress
22
+ # 852 $a DLC
23
+
24
+
25
+ # Create a helper for the custom functions
26
+
27
+ module SPECHelper
28
+ def self.test doc, r
29
+ return 'Hello'
30
+ end
31
+
32
+ def self.single doc, r, arg
33
+ return "Hello " + arg.to_s
34
+ end
35
+
36
+ def self.double doc, r, one, two
37
+ return ["Hello", one, two].join(' ')
38
+ end
39
+
40
+ def self.any doc, r, *args
41
+ return 'Hello ' + args.map{|s| s.to_s}.join(' ')
42
+ end
43
+
44
+ def self.multihead doc, r
45
+ return ['one', 'two']
46
+ end
47
+
48
+ end
49
+
50
+ describe "DSL" do
51
+ before do
52
+ @one = MARC4J4R::Reader.new("#{DIR}/data/one.dat").first
53
+ end
54
+
55
+ describe "constant DSL" do
56
+
57
+ it "can add a constant solrfieldspec" do
58
+ ss = MARCSpec.build do
59
+ constant('id') do
60
+ value "Bill"
61
+ end
62
+ end
63
+ ss.solrfieldspecs.size.should.equal 1
64
+ ss.hash_from_marc(@one)['id'].should.equal ['Bill']
65
+ end
66
+ end
67
+
68
+ describe "custom DSL" do
69
+
70
+ # before do
71
+ # @one = MARC4J4R::Reader.new("#{DIR}/data/one.dat").first
72
+ # end
73
+
74
+ it "builds a bare-bones custom" do
75
+ ss = MARCSpec.build do
76
+ custom('hello') do
77
+ function(:test) {
78
+ mod SPECHelper
79
+ }
80
+ end
81
+ end
82
+
83
+ ss.hash_from_marc(@one)['hello'].should.equal ['Hello']
84
+ end
85
+
86
+ it "builds a custom that can take a single argument" do
87
+ ss = MARCSpec.build do
88
+ custom('hello') do
89
+ function(:single) {
90
+ mod SPECHelper
91
+ args 'Bill'
92
+ }
93
+ end
94
+ end
95
+
96
+ ss.hash_from_marc(@one)['hello'].should.equal ['Hello Bill']
97
+ end
98
+
99
+ it "builds a custom that can take two arguments" do
100
+ ss = MARCSpec.build do
101
+ custom('hello') do
102
+ function(:double) {
103
+ mod SPECHelper
104
+ args 'Bill', 'Dueber'
105
+ }
106
+ end
107
+ end
108
+
109
+ ss.hash_from_marc(@one)['hello'].should.equal ['Hello Bill Dueber']
110
+ end
111
+
112
+ it "builds a custom that can take two arguments" do
113
+ ss = MARCSpec.build do
114
+ custom('hello') do
115
+ function(:any) {
116
+ mod SPECHelper
117
+ args 1,2,3,4,5
118
+ }
119
+ end
120
+ end
121
+
122
+ ss.hash_from_marc(@one)['hello'].should.equal ['Hello 1 2 3 4 5']
123
+ end
124
+
125
+ it "works with multihead" do
126
+ ss = MARCSpec.build do
127
+ custom(['a', 'b']) do
128
+ function(:multihead) {
129
+ mod SPECHelper
130
+ }
131
+ end
132
+ end
133
+
134
+ ss.hash_from_marc(@one)['a'].should.equal ['one']
135
+ ss.hash_from_marc(@one)['b'].should.equal ['two']
136
+ end
137
+ end
138
+
139
+
140
+ describe "control fields DSL" do
141
+ # before do
142
+ # @one = MARC4J4R::Reader.new("#{DIR}/data/one.dat").first
143
+ # end
144
+
145
+ it "should get a standard with a single whole control spec" do
146
+ ss = MARCSpec.build do
147
+ field("id") do
148
+ spec('001')
149
+ end
150
+ end
151
+ ss.hash_from_marc(@one)['id'].should.equal ['afc99990058366']
152
+ end
153
+
154
+ it "should allow integer tags" do
155
+ ss = MARCSpec.build do
156
+ field("id") do
157
+ spec(001)
158
+ end
159
+ end
160
+ ss.hash_from_marc(@one)['id'].should.equal ['afc99990058366']
161
+ end
162
+
163
+ it "can get a single char" do
164
+ ss = MARCSpec.build do
165
+ field("tst") do
166
+ spec(001) {
167
+ char 2
168
+ }
169
+ end
170
+ end
171
+ ss.hash_from_marc(@one)['tst'].should.equal ['c']
172
+ end
173
+
174
+ it "can get a range" do
175
+ ss = MARCSpec.build do
176
+ field("tst") do
177
+ spec(001) {
178
+ chars 2..6
179
+ }
180
+ end
181
+ end
182
+ ss.hash_from_marc(@one)['tst'].should.equal ['c9999']
183
+ end
184
+
185
+ it "allows multiple char/chars per control field" do
186
+ ss = MARCSpec.build do
187
+ field("tst") do
188
+ spec(001) {
189
+ char 2
190
+ chars 2..6
191
+ }
192
+ end
193
+ end
194
+ ss.hash_from_marc(@one)['tst'].should.equal ['c', 'c9999']
195
+ end
196
+
197
+
198
+ end
199
+
200
+
201
+ describe "variable fields DSL" do
202
+ # before do
203
+ # @one = MARC4J4R::Reader.new("#{DIR}/data/one.dat").first
204
+ # end
205
+
206
+ it "can get a whole variable field" do
207
+ ss = MARCSpec.build do
208
+ field("tst") do
209
+ spec(260)
210
+ end
211
+ end
212
+ ss.hash_from_marc(@one)['tst'].should.equal ['Medina, Texas, 1939.']
213
+ end
214
+
215
+ it "can get a single subfield" do
216
+ ss = MARCSpec.build do
217
+ field("tst") do
218
+ spec(260) {
219
+ sub 'a'
220
+ }
221
+ end
222
+ end
223
+ ss.hash_from_marc(@one)['tst'].should.equal ['Medina, Texas,']
224
+ end
225
+
226
+
227
+ it "can get multiple subfields" do
228
+ ss = MARCSpec.build do
229
+ field("tst") do
230
+ spec(245) {
231
+ sub 'ac'
232
+ }
233
+ end
234
+ end
235
+ ss.hash_from_marc(@one)['tst'].should.equal ['The Texas ranger Sung by Beale D. Taylor.']
236
+ end
237
+
238
+ it "can get multiple subfields as array" do
239
+ ss = MARCSpec.build do
240
+ field("tst") do
241
+ spec(245) {
242
+ subs ['a', 'c']
243
+ }
244
+ end
245
+ end
246
+ ss.hash_from_marc(@one)['tst'].should.equal ['The Texas ranger Sung by Beale D. Taylor.']
247
+ end
248
+
249
+ it "can get multiple different subfields from the same field" do
250
+ ss = MARCSpec.build do
251
+ field("tst") do
252
+ spec(245) {
253
+ sub 'a'
254
+ sub 'c'
255
+ }
256
+ end
257
+ end
258
+ ss.hash_from_marc(@one)['tst'].should.equal ['The Texas ranger', 'Sung by Beale D. Taylor.']
259
+ end
260
+
261
+ it "can handle multiple specs" do
262
+ ss = MARCSpec.build do
263
+ field('tst') do
264
+ spec(245) {
265
+ sub 'a'
266
+ }
267
+ spec(245) {
268
+ sub 'c'
269
+ }
270
+ end
271
+ end
272
+ ss.hash_from_marc(@one)['tst'].should.equal ['The Texas ranger', 'Sung by Beale D. Taylor.']
273
+ end
274
+ end
275
+
276
+ describe "SolrFieldSpec modifiers DSL" do
277
+ it "works with firstOnly" do
278
+ ss = MARCSpec.build do
279
+ field('tst') do
280
+ firstOnly
281
+
282
+ spec(700) {
283
+ sub 'a'
284
+ }
285
+ end
286
+ end
287
+
288
+ ss.hash_from_marc(@one)['tst'].should.equal ['Lomax, John Avery, 1867-1948']
289
+ end
290
+
291
+ it "works with default" do
292
+ ss = MARCSpec.build do
293
+ field('tst') do
294
+ default 'Default value'
295
+
296
+ spec(777) {
297
+ sub 'a'
298
+ }
299
+ end
300
+ end
301
+
302
+ ss.hash_from_marc(@one)['tst'].should.equal ['Default value']
303
+ end
304
+ end
305
+
306
+ describe "use as config file" do
307
+ it "works in practice" do
308
+ string = %q|
309
+ field('tst') do
310
+ default 'Default'
311
+ spec(999)
312
+ end
313
+
314
+ field('id') do
315
+ spec(001) {
316
+ chars 2..6
317
+ }
318
+ end
319
+ |
320
+
321
+ ss = MARCSpec::SpecSet.new
322
+ ss.instance_eval(string)
323
+ ss.hash_from_marc(@one)['tst'].should.equal ['Default']
324
+ ss.hash_from_marc(@one)['id'].should.equal ['c9999']
325
+ end
326
+
327
+ it "works in compact form" do
328
+ string = %q|
329
+ field('tst') do
330
+ default 'Default'
331
+ spec(999)
332
+ end
333
+
334
+ field('id') do
335
+ spec(001) {chars 2..6}
336
+ end
337
+ |
338
+
339
+ ss = MARCSpec::SpecSet.new
340
+ ss.instance_eval(string)
341
+ ss.hash_from_marc(@one)['tst'].should.equal ['Default']
342
+ ss.hash_from_marc(@one)['id'].should.equal ['c9999']
343
+ end
344
+
345
+ it "bails on a missing map" do
346
+ string = %q|
347
+ field('tst') do
348
+ default 'Default'
349
+ mapname 'nosuchmap'
350
+ spec(999)
351
+ end
352
+
353
+ field('id') do
354
+ spec(001) {chars 2..6}
355
+ end
356
+ |
357
+ f = Tempfile.new('ss')
358
+ f.puts string
359
+ path = f.path
360
+ f.close
361
+ ss = MARCSpec::SpecSet.new
362
+ lambda{ss.buildSpecsFromDSLFile(path)}.should.raise SystemExit
363
+ f.unlink
364
+
365
+ end
366
+
367
+ end
368
+
369
+
370
+ end
data/spec/maps_spec.rb CHANGED
@@ -1,21 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Maps" do
3
+ describe "KV Maps" do
4
4
  before do
5
5
  @kvmap = MARCSpec::KVMap.new('kvmap', {'one' => '1', 'two' => ['2', 'zwei']})
6
- @mvmap = MARCSpec::MultiValueMap.new('mvmap', [
7
- [/bi/, 'Bill'],
8
- [/mo/i, 'Molly'],
9
- [/ll/, 'Bill'],
10
- [/lly/i, ['One', 'Two']],
11
- [/^.*?\s+(.*)$/, Proc.new{|m| m[1]}]
12
- ]
13
- )
14
6
  end
15
7
 
16
8
  it "knows its name" do
17
9
  @kvmap.mapname.should.equal 'kvmap'
18
- @mvmap.mapname.should.equal 'mvmap'
19
10
  end
20
11
 
21
12
  it "gets simple value from a kv map" do
@@ -30,21 +21,90 @@ describe "Maps" do
30
21
  @kvmap['ddd'].should.equal nil
31
22
  end
32
23
 
33
- it "gets nothing on nonmatches for mvmap" do
34
- @mvmap['ddd'].should.equal nil
24
+ it "gets default if set for nonmatches with KVMap" do
25
+ @kvmap['ddd', 'default'].should.equal 'default'
26
+ end
27
+
28
+ it "gets key if default is :passthrough for nonmatches with KVMap" do
29
+ @kvmap['ddd', :passthrough].should.equal 'ddd'
35
30
  end
31
+
36
32
 
37
- it "gets default if set for nonmatches with KVMap" do
33
+ it "correctly uses default value" do
38
34
  @kvmap['ddd', 'default'].should.equal 'default'
35
+ @kvmap['one', 'default'].should.equal '1'
36
+ end
37
+
38
+ it "should round-trip a kvmap" do
39
+ s = @kvmap.asPPString
40
+ newkvmap = MARCSpec::KVMap.fromPPString s
41
+ newkvmap.should.equal @kvmap
42
+ end
43
+
44
+
45
+ it "should read a kv solrmarc file" do
46
+ map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/country_map.properties"
47
+ map.mapname.should.equal 'country_map'
48
+ map["nl"].should.equal "New Caledonia"
49
+ end
50
+
51
+ it "should correctly deal with solrmarc files with escaped chars (via \\)" do
52
+ map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/location_map.properties"
53
+ map['AAEL'].should.equal 'AAEL'
54
+ map['AAEL MICE'].should.equal 'AAEL MICE'
55
+ map['BUHR AAEL'].should.equal 'BUHR'
56
+ end
57
+
58
+
59
+ it "can dump/load a kv map via generic map interface" do
60
+ map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/country_map.properties"
61
+ f = Tempfile.new('kvmap')
62
+ f.puts map.asPPString
63
+ path = f.path
64
+ f.close
65
+ map2 = MARCSpec::Map.fromFile(path)
66
+ f.unlink
67
+ map.class.should.equal MARCSpec::KVMap
68
+ map.should.equal map2
69
+ end
70
+
71
+ it "can name a map based on the filename when using fromFile(path)" do
72
+ map = MARCSpec::Map.fromFile("#{DIR}/data/simplemap.rb")
73
+ map.mapname.should.equal 'simplemap'
39
74
  end
40
75
 
76
+ end
77
+
78
+ describe "MVMaps" do
79
+ before do
80
+ @mvmap = MARCSpec::MultiValueMap.new('mvmap', [
81
+ [/bi/, 'Bill'],
82
+ [/mo/i, 'Molly'],
83
+ [/ll/, 'Bill'],
84
+ [/lly/i, ['One', 'Two']],
85
+ [/^.*?\s+(.*)$/, Proc.new{|m| m[1]}]
86
+ ]
87
+ )
88
+ @mvmapCollapse = MARCSpec::MultiValueMap.new('mvmap', [
89
+ [/^bill/i, 'William'],
90
+ [/^will.*/i, 'William'],
91
+ [/dueber/i, 'Dueber'],
92
+ [/duebs/i, 'Dueber']
93
+ ])
94
+ end
95
+
96
+ it "knows its name" do
97
+ @mvmap.mapname.should.equal 'mvmap'
98
+ end
99
+
100
+ it "gets nothing on nonmatches for mvmap" do
101
+ @mvmap['ddd'].should.equal nil
102
+ end
103
+
41
104
  it "gets default if set for nonmatches with MVMap" do
42
105
  @mvmap['ddd', 'default'].should.equal 'default'
43
106
  end
44
107
 
45
- it "gets key if default is :passthrough for nonmatches with KVMap" do
46
- @kvmap['ddd', :passthrough].should.equal 'ddd'
47
- end
48
108
 
49
109
  it "gets key if default is :passthrough for nonmatches with KVMap" do
50
110
  @mvmap['ddd', :passthrough].should.equal 'ddd'
@@ -57,18 +117,10 @@ describe "Maps" do
57
117
  @mvmap['mobi'].sort.should.equal ['Bill', 'Molly'].sort
58
118
  @mvmap['Molly'].sort.should.equal ['Molly', 'Bill', 'One', 'Two'].sort
59
119
  end
60
-
120
+
61
121
  it "correctly uses default value" do
62
122
  @mvmap['bi', 'default'].should.equal ['Bill']
63
123
  @mvmap['ddd', 'default'].should.equal 'default'
64
- @kvmap['ddd', 'default'].should.equal 'default'
65
- @kvmap['one', 'default'].should.equal '1'
66
- end
67
-
68
- it "should round-trip a kvmap" do
69
- s = @kvmap.asPPString
70
- newkvmap = MARCSpec::KVMap.fromPPString s
71
- newkvmap.should.equal @kvmap
72
124
  end
73
125
 
74
126
  it "should round trip a multivaluemap without a Proc" do
@@ -98,38 +150,13 @@ describe "Maps" do
98
150
  @mvmap['one'].should.equal ['one']
99
151
  @mvmap['two'].should.equal ['two']
100
152
  end
101
-
102
- it "should read a kv solrmarc file" do
103
- map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/country_map.properties"
104
- map.mapname.should.equal 'country_map'
105
- map["nl"].should.equal "New Caledonia"
106
- end
107
-
108
- it "should correctly deal with solrmarc files with escaped chars (via \\)" do
109
- map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/location_map.properties"
110
- map['AAEL'].should.equal 'AAEL'
111
- map['AAEL MICE'].should.equal 'AAEL MICE'
112
- map['BUHR AAEL'].should.equal 'BUHR'
113
- end
114
-
153
+
115
154
  it "should read a pattern solrmarc file" do
116
155
  map = MARCSpec::MultiValueMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/library_map.properties"
117
156
  map.mapname.should.equal 'library_map'
118
157
  map['UMTRI Stuff'].should.equal ['Transportation Research Institute Library (UMTRI)']
119
158
  map['HATCH DOCS'].should.equal ['Hatcher Graduate', 'Hatcher Graduate Documents Center']
120
159
  end
121
-
122
- it "can dump/load a kv map via generic map interface" do
123
- map = MARCSpec::KVMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/country_map.properties"
124
- f = Tempfile.new('kvmap')
125
- f.puts map.asPPString
126
- path = f.path
127
- f.close
128
- map2 = MARCSpec::Map.fromFile(path)
129
- f.unlink
130
- map.class.should.equal MARCSpec::KVMap
131
- map.should.equal map2
132
- end
133
160
 
134
161
  it "can dump/load a multivalue map via generic map interface" do
135
162
  map = MARCSpec::MultiValueMap.from_solrmarc_file "#{DIR}/data/umich/translation_maps/library_map.properties"
@@ -143,5 +170,13 @@ describe "Maps" do
143
170
  map.should.equal map2
144
171
  end
145
172
 
173
+ it "collapses ok" do
174
+ @mvmapCollapse['bill'].should.equal ['William']
175
+ @mvmapCollapse['william'].should.equal ['William']
176
+ @mvmapCollapse['bill dueber'].sort.should.equal ['William', 'Dueber'].sort
177
+ @mvmapCollapse['Will "duebes" Dueber'].sort.should.equal ['William', 'Dueber'].sort
178
+ @mvmapCollapse['notinthere'].should.equal nil
179
+ end
180
+
146
181
 
147
- end
182
+ end
@@ -189,17 +189,6 @@ describe "CustomSolrSpec" do
189
189
  @map = MARCSpec::KVMap.new('nameOfTheMap', {@titleACValue.upcase => @mapValue, @default=>@mapValueForDefault})
190
190
  end
191
191
 
192
- it "requires solrfield, module, and function" do
193
- lambda{
194
- css = MARCSpec::CustomSolrSpec.new(:solrField=>'solrField')
195
- }.should.raise ArgumentError
196
- lambda{
197
- css = MARCSpec::CustomSolrSpec.new(:solrField=>'solrField', :module=>A::B)
198
- }.should.raise ArgumentError
199
- lambda{
200
- css = MARCSpec::CustomSolrSpec.new(:module=>A::B, :functionSymbol=>:titleUp)
201
- }.should.raise ArgumentError
202
- end
203
192
 
204
193
  it "works with no args or map" do
205
194
  css = MARCSpec::CustomSolrSpec.new(:solrField=>'solrField', :module=>A::B, :functionSymbol=>:titleUp)
data/spec/specset_spec.rb CHANGED
@@ -118,6 +118,12 @@ describe "SpecSet Basics" do
118
118
  h['two'].should.equal [2]
119
119
  h['letters'].should.equal ['a', 'b']
120
120
  end
121
+
122
+ it "bails if it can't find a map" do
123
+ @speclist << {:solrField => 'tst', :mapname=>'nosuch', :specs => [['245']]}
124
+ lambda{@ss.buildSpecsFromList(@speclist)}.should.raise SystemExit
125
+ end
126
+
121
127
  end
122
128
 
123
129
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marcspec
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 3
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 1
9
- - 1
10
- version: 1.1.1
8
+ - 5
9
+ - 0
10
+ version: 1.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - BillDueber
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-15 00:00:00 -04:00
18
+ date: 2010-09-20 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -99,6 +99,7 @@ files:
99
99
  - lib/marcspec/constantspec.rb
100
100
  - lib/marcspec/controlfieldspec.rb
101
101
  - lib/marcspec/customspec.rb
102
+ - lib/marcspec/dsl.rb
102
103
  - lib/marcspec/kvmap.rb
103
104
  - lib/marcspec/leaderspec.rb
104
105
  - lib/marcspec/map.rb
@@ -111,6 +112,7 @@ files:
111
112
  - spec/controlfieldspec_spec.rb
112
113
  - spec/data/batch.dat
113
114
  - spec/data/one.dat
115
+ - spec/data/simplemap.rb
114
116
  - spec/data/umich/translation_maps/area_map.properties
115
117
  - spec/data/umich/translation_maps/availability_map_ht.properties
116
118
  - spec/data/umich/translation_maps/availability_map_umich.properties
@@ -125,6 +127,7 @@ files:
125
127
  - spec/data/umich/translation_maps/library_map.properties
126
128
  - spec/data/umich/translation_maps/location_map.properties
127
129
  - spec/data/umich/umich_index.properties
130
+ - spec/dsl_spec.rb
128
131
  - spec/leaderspec_spec.rb
129
132
  - spec/maps_spec.rb
130
133
  - spec/marcfieldspecs_spec.rb
@@ -169,6 +172,8 @@ summary: Extract data from MARC records and send to Solr
169
172
  test_files:
170
173
  - spec/cachespot_spec.rb
171
174
  - spec/controlfieldspec_spec.rb
175
+ - spec/data/simplemap.rb
176
+ - spec/dsl_spec.rb
172
177
  - spec/leaderspec_spec.rb
173
178
  - spec/maps_spec.rb
174
179
  - spec/marcfieldspecs_spec.rb