marcspec 1.1.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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