marcspec 1.5.0 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +32 -0
- data/Rakefile +21 -11
- data/VERSION +1 -1
- data/lib/marcspec.rb +9 -7
- data/lib/marcspec/constantspec.rb +16 -1
- data/lib/marcspec/controlfieldspec.rb +22 -1
- data/lib/marcspec/customspec.rb +37 -1
- data/lib/marcspec/dsl.rb +20 -5
- data/lib/marcspec/map.rb +8 -7
- data/lib/marcspec/multivaluemap.rb +1 -1
- data/lib/marcspec/solrfieldspec.rb +66 -2
- data/lib/marcspec/specset.rb +79 -11
- data/lib/marcspec/variablefieldspec.rb +52 -4
- data/spec/cachespot_spec.rb +1 -1
- data/spec/controlfieldspec_spec.rb +4 -4
- data/spec/dsl_spec.rb +52 -30
- data/spec/leaderspec_spec.rb +2 -2
- data/spec/maps_spec.rb +42 -42
- data/spec/marcfieldspecs_spec.rb +22 -22
- data/spec/solrfieldspec_spec.rb +24 -24
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +3 -6
- data/spec/specset_spec.rb +20 -19
- data/spec/variablefieldspec_spec.rb +9 -9
- metadata +156 -144
data/CHANGES
CHANGED
@@ -1,45 +1,77 @@
|
|
1
|
+
1.6.1
|
2
|
+
* Change VariableField#asDSLString to reflect compact declaration of spec with subs
|
3
|
+
|
4
|
+
1.6.0
|
5
|
+
* Allow specs like spec('245ac'), with or without a block as well.
|
6
|
+
|
7
|
+
1.5.3
|
8
|
+
* Changed to use Logback::Simple
|
9
|
+
|
10
|
+
1.5.2
|
11
|
+
* Added more docs
|
12
|
+
* Put in actual @deprecated strings in YARD docs
|
13
|
+
|
14
|
+
1.5.1
|
15
|
+
* Fixed lots of DSL bugs
|
16
|
+
* Moved testing to RSpec in anticipation of using Hudson
|
17
|
+
|
1
18
|
1.5.0
|
2
19
|
* Added DSL; making it the preferred way to operate. Totally deprecating the aray-of-hashes stuff.
|
20
|
+
|
3
21
|
1.1.1
|
4
22
|
* Fixed bug with :noMapKeyDefault => :passthrough for MVMap
|
23
|
+
|
5
24
|
1.1.0
|
6
25
|
* Added support for Proc objects as the value in a MVMap
|
26
|
+
|
7
27
|
1.0.0
|
8
28
|
* Added constant specs (:constantValue=>'val' or :constantValue=>['array', 'of', 'values'])
|
9
29
|
* Arbitrarily decided this is version 1.0
|
30
|
+
|
10
31
|
0.9.0
|
11
32
|
* Added ability to benchmark by calling ss.doc_from_marc(r, true) instead of just ss.doc_from_marc(r)
|
33
|
+
|
12
34
|
0.8.1
|
13
35
|
* Added some specs, squashed some bugs. In particular, make sure the Range passed to a ControlField
|
14
36
|
makes sense (x..y, where x<=y and x > 0)
|
37
|
+
|
15
38
|
0.8.0
|
16
39
|
* Changed the syntax for a variable field from ['245', '*', '*', 'ab'] to just ['245', 'ab']. We'll worry
|
17
40
|
about indicators when that bridge gets crossed.
|
41
|
+
|
18
42
|
0.7.3
|
19
43
|
* Squashed a bug where I forgot to deal with escaping in java .properties files. Now I'm just using
|
20
44
|
a real Java Properties object so I don't have to worry about it.
|
45
|
+
|
21
46
|
0.7.2
|
22
47
|
* Also change methodArgs to functionArgs
|
48
|
+
|
23
49
|
0.7.1
|
24
50
|
* Forgot to update this CHANGES file.
|
51
|
+
|
25
52
|
0.7.0
|
26
53
|
* Change configuration for custom functions to use :functionSymbol instead of :methodSymbol. Because, you know, they're
|
27
54
|
module functions, not class methods.
|
28
55
|
* Split out marcspec stuff into multiple files (leader/control/variable fields).
|
56
|
+
|
29
57
|
0.6.0
|
30
58
|
* Allow custom functions to return values for multiple solr fields at once; should help avoid duplication of work.
|
59
|
+
|
31
60
|
0.5.0
|
32
61
|
* Allow solr field names to repeat in a spec set. This allows you to cumulatively add to a solr field based on "regular"
|
33
62
|
and custom (or, say, two custom) specs.
|
63
|
+
|
34
64
|
0.4.0
|
35
65
|
* MAJOR BACKWARD-INCOMPATIBLE CHANGE!! The signature for custom routines is now def function(doc, record, my, args), where
|
36
66
|
"doc" is a hashlike (usually a SolrDocument) that contains all the work that has happened up to this point. The idea is that you
|
37
67
|
can use previously-computed values to determine values for different fields. Use sparingly.
|
68
|
+
|
38
69
|
0.3.0
|
39
70
|
* Changed behavior with respect to repeated subfields. A spec such as '260 ac' returns the values of the 'a' and
|
40
71
|
'c' subfields concatenated together. A request for '631 z' would, similarly, return the values *of all the subfield
|
41
72
|
z's* concatenated together. I'm now treating this as a special case. If the request is for a single subfield code,
|
42
73
|
multiple values are returned separate from each other.
|
43
74
|
|
75
|
+
|
44
76
|
0.2.0
|
45
77
|
* First public release
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
+
gem 'ci_reporter'
|
4
|
+
require 'ci/reporter/rake/rspec'
|
3
5
|
|
4
6
|
begin
|
5
7
|
require 'jeweler'
|
@@ -10,11 +12,12 @@ begin
|
|
10
12
|
gem.email = "bill@dueber.com"
|
11
13
|
gem.homepage = "http://github.com/billdueber/marcspec"
|
12
14
|
gem.authors = ["BillDueber"]
|
13
|
-
gem.add_development_dependency "
|
15
|
+
gem.add_development_dependency "rspec"
|
14
16
|
gem.add_development_dependency "yard", ">= 0"
|
15
|
-
|
16
|
-
gem.add_dependency 'marc4j4r', '>=
|
17
|
-
gem.add_dependency '
|
17
|
+
gem.add_development_dependency 'ci_reporter'
|
18
|
+
gem.add_dependency 'marc4j4r', '>=1.1.0'
|
19
|
+
gem.add_dependency 'logback-simple'
|
20
|
+
gem.add_dependency 'jruby_streaming_update_solr_server', '>=0.5.0'
|
18
21
|
|
19
22
|
|
20
23
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
@@ -24,13 +27,20 @@ rescue LoadError
|
|
24
27
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
|
31
|
+
require 'spec/rake/spectask'
|
32
|
+
desc "Run rspec specs"
|
33
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
34
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Run Rspec tests with CI output in spec/reports"
|
38
|
+
Spec::Rake::SpecTask.new('cispec') do |t|
|
39
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
32
40
|
end
|
33
41
|
|
42
|
+
|
43
|
+
|
34
44
|
begin
|
35
45
|
require 'rcov/rcovtask'
|
36
46
|
Rcov::RcovTask.new do |spec|
|
@@ -40,12 +50,12 @@ begin
|
|
40
50
|
end
|
41
51
|
rescue LoadError
|
42
52
|
task :rcov do
|
43
|
-
abort "RCov is not available. In order to run rcov, you must:
|
53
|
+
abort "RCov is not available. In order to run rcov, you must: jgem install rcov-java"
|
44
54
|
end
|
45
55
|
end
|
46
56
|
|
47
57
|
task :spec => :check_dependencies
|
48
|
-
|
58
|
+
task :cispec => :"ci:setup:rspec"
|
49
59
|
task :default => :spec
|
50
60
|
|
51
61
|
begin
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.6.1
|
data/lib/marcspec.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require 'logger'
|
2
1
|
require 'marc4j4r'
|
2
|
+
require "logback-simple"
|
3
|
+
|
3
4
|
|
4
|
-
$LOG = Logger.new(STDOUT)
|
5
|
-
$LOG.level = Logger::WARN
|
6
5
|
|
7
6
|
require "marcspec/customspec"
|
8
7
|
require "marcspec/solrfieldspec"
|
@@ -13,11 +12,14 @@ require "marcspec/specset"
|
|
13
12
|
require "marcspec/marcfieldspec"
|
14
13
|
require "marcspec/dsl"
|
15
14
|
|
16
|
-
# Build up a little module to include in MARC4J4R::Record that
|
17
|
-
# gives us a way to cache computed values within the record itself
|
18
|
-
# It's just a hash.
|
19
|
-
|
20
15
|
module CacheSpot
|
16
|
+
# Build up a little module to include in MARC4J4R::Record that
|
17
|
+
# gives us a way to cache computed values within the record itself
|
18
|
+
# It's just a hash.
|
19
|
+
#
|
20
|
+
# Pretty sure this belongs somewhere else; not sure where.
|
21
|
+
#
|
22
|
+
# @return [Hash] the cachespot hash
|
21
23
|
def cachespot
|
22
24
|
@_cachespot ||= {}
|
23
25
|
return @_cachespot
|
@@ -2,10 +2,11 @@ require 'marcspec/solrfieldspec'
|
|
2
2
|
|
3
3
|
|
4
4
|
module MARCSpec
|
5
|
+
|
6
|
+
# A ConstantSolrSpec always returns the same value(s) no matter what's in the record.
|
5
7
|
class ConstantSolrSpec < SolrFieldSpec
|
6
8
|
attr_accessor :constantValue
|
7
9
|
|
8
|
-
#attr_accessor :solrField, :first, :map, :noMapKeyDefault, :marcfieldspecs, :default, :arity
|
9
10
|
|
10
11
|
def initialize opts = {}
|
11
12
|
@solrField = opts[:solrField]
|
@@ -20,18 +21,32 @@ module MARCSpec
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
# Return the constant value associated with this spec
|
25
|
+
# @param [MARC4J4R::Record] r The record. IGNORED. It's a constant ;-)
|
26
|
+
# @param [Hash, SolrInputDocument] doc The hash-like object that contains previously-generated content. IGNORED
|
27
|
+
# @return [String, Array] The constant value(s) associated with this object.
|
28
|
+
|
23
29
|
def marc_values r, doc = {}
|
24
30
|
return @constantValue
|
25
31
|
end
|
26
32
|
|
33
|
+
# Basic equality
|
27
34
|
def == other
|
28
35
|
return @constantValue == other.constantValue
|
29
36
|
end
|
30
37
|
|
38
|
+
# Build up from a ruby hash
|
39
|
+
# @deprecated Use the DSL
|
31
40
|
def self.fromHash h
|
32
41
|
return self.new(h)
|
33
42
|
end
|
34
43
|
|
44
|
+
def asDSLString
|
45
|
+
return "constant('#{@solrField}') do\n value #{@constantValue.inspect}\nend"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Used to round-trip to/from hash syntax
|
49
|
+
# @deprecated Use the DSL
|
35
50
|
def asPPString
|
36
51
|
s = StringIO.new
|
37
52
|
s.print "{\n :solrField=> "
|
@@ -17,6 +17,8 @@ module MARCSpec
|
|
17
17
|
# substrings are specified.
|
18
18
|
|
19
19
|
class ControlFieldSpec
|
20
|
+
include Logback::Simple
|
21
|
+
|
20
22
|
attr_accessor :tag, :range, :rangehistory
|
21
23
|
|
22
24
|
def initialize (tag, range=nil)
|
@@ -33,7 +35,8 @@ module MARCSpec
|
|
33
35
|
(self.range == other.range))
|
34
36
|
end
|
35
37
|
|
36
|
-
|
38
|
+
# Set the range of characters to use (nil for all)
|
39
|
+
#
|
37
40
|
# Always force a real range, since in Ruby 1.9 a string subscript with a single fixnum
|
38
41
|
# will return the character code of that character (e.g., "Bill"[0] => 66, wherease
|
39
42
|
# "Bill"[0..0] gives the expected 'B'
|
@@ -76,10 +79,25 @@ module MARCSpec
|
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
82
|
+
# Print it out has a ruby hash
|
83
|
+
# @deprecated Use the DSL
|
84
|
+
|
79
85
|
def pretty_print pp
|
80
86
|
pp.pp eval(self.asPPString)
|
81
87
|
end
|
82
88
|
|
89
|
+
# Print out as a DSL segment
|
90
|
+
def asDSLString
|
91
|
+
if (@range)
|
92
|
+
return "spec('#{@tag}') {chars #{@range}}"
|
93
|
+
else
|
94
|
+
return "spec('#{@tag}')"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Print out as a ruby hash.
|
99
|
+
# @deprecated Use the DSL
|
100
|
+
|
83
101
|
def asPPString
|
84
102
|
s = StringIO.new
|
85
103
|
if @range
|
@@ -90,6 +108,9 @@ module MARCSpec
|
|
90
108
|
return s.string
|
91
109
|
end
|
92
110
|
|
111
|
+
# Recreate from an asPPString call
|
112
|
+
# @deprecated Use the DSL
|
113
|
+
|
93
114
|
def self.fromPPString str
|
94
115
|
a = eval(str)
|
95
116
|
return self.new(*a)
|
data/lib/marcspec/customspec.rb
CHANGED
@@ -47,6 +47,8 @@ module MARCSpec
|
|
47
47
|
# no key in the map that matches the value.
|
48
48
|
#
|
49
49
|
# Note that the last four options don't make sense if multiple :solrFields are given, and are illegal in that case.
|
50
|
+
#
|
51
|
+
# Also note that using the DSL to produce these is easier from config file.
|
50
52
|
|
51
53
|
def initialize(opts)
|
52
54
|
@solrField = opts[:solrField]
|
@@ -85,11 +87,45 @@ module MARCSpec
|
|
85
87
|
end
|
86
88
|
|
87
89
|
|
90
|
+
# Produce one from the results of eval'ing a asPPString string
|
91
|
+
# @deprecated Use the DSL
|
92
|
+
|
88
93
|
def self.fromHash h
|
89
94
|
return self.new(h)
|
90
95
|
end
|
91
96
|
|
92
|
-
|
97
|
+
# Produce DSL code that will reproduce this object
|
98
|
+
def asDSLString
|
99
|
+
s = StringIO.new
|
100
|
+
s.puts "custom('#{@solrField}') do"
|
101
|
+
s.puts " firstOnly" if @first
|
102
|
+
if @defaultValue
|
103
|
+
s.puts " default " +
|
104
|
+
PP.singleline_pp(@defaultValue + "\n", s)
|
105
|
+
end
|
106
|
+
if @map
|
107
|
+
s.print " mapname "
|
108
|
+
PP.pp(@map.mapname, s)
|
109
|
+
end
|
110
|
+
if @noMapKeyDefault
|
111
|
+
s.print(" mapMissDefault ")
|
112
|
+
PP.singleline_pp(@noMapKeyDefault, s)
|
113
|
+
s.print("\n ")
|
114
|
+
end
|
115
|
+
|
116
|
+
s.puts(" function(:#{@functionSymbol}) {")
|
117
|
+
s.puts(" mod #{@module}")
|
118
|
+
if @functionArgs and @functionArgs.size > 0
|
119
|
+
args = @functionArgs.map{|a| a.inspect}.join(', ')
|
120
|
+
s.puts " args #{args}"
|
121
|
+
end
|
122
|
+
s.puts " }"
|
123
|
+
s.puts "end"
|
124
|
+
return s.string
|
125
|
+
end
|
126
|
+
|
127
|
+
# Print out as a ruby hash.
|
128
|
+
# @deprecated Use the DSL
|
93
129
|
def asPPString
|
94
130
|
s = StringIO.new
|
95
131
|
s.print "{\n :solrField=> "
|
data/lib/marcspec/dsl.rb
CHANGED
@@ -9,11 +9,11 @@ module MARCSpec
|
|
9
9
|
|
10
10
|
# Re-open SpecSet to add the necessary methods
|
11
11
|
|
12
|
-
class SpecSet
|
12
|
+
class SpecSet
|
13
13
|
|
14
14
|
# create a normal field
|
15
15
|
def field(name, &blk)
|
16
|
-
|
16
|
+
log.debug "Creating regular field #{name}"
|
17
17
|
sfs = SolrFieldSpec.new(:solrField=>name)
|
18
18
|
sfs.instance_eval(&blk)
|
19
19
|
self << sfs
|
@@ -22,7 +22,7 @@ module MARCSpec
|
|
22
22
|
|
23
23
|
# Create a constant field
|
24
24
|
def constant(name, &blk)
|
25
|
-
|
25
|
+
log.debug "Creating constant field #{name}"
|
26
26
|
|
27
27
|
constant = ConstantSolrSpec.new(:solrField=>name)
|
28
28
|
constant.instance_eval(&blk)
|
@@ -31,7 +31,7 @@ module MARCSpec
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def custom(name, &blk)
|
34
|
-
|
34
|
+
log.debug "Creating custom field #{name}"
|
35
35
|
custom = CustomSolrSpec.new(:solrField=>name)
|
36
36
|
custom.instance_eval(&blk)
|
37
37
|
|
@@ -45,17 +45,32 @@ module MARCSpec
|
|
45
45
|
|
46
46
|
class SolrFieldSpec
|
47
47
|
def spec(tag, &blk)
|
48
|
+
|
49
|
+
subs = nil
|
50
|
+
if tag =~ /^(...)(.+)$/
|
51
|
+
tag = $1
|
52
|
+
subs = $2
|
53
|
+
end
|
54
|
+
|
48
55
|
if tag.to_i == tag
|
49
56
|
tag = '%03d' % tag
|
50
57
|
end
|
51
58
|
|
52
59
|
marcfieldspec = nil
|
53
|
-
|
60
|
+
|
61
|
+
if tag == 'LDR'
|
62
|
+
marcfieldspec = MARCSpec::LeaderSpec.new('LDR')
|
63
|
+
elsif MARC4J4R::ControlField.control_tag? tag
|
54
64
|
marcfieldspec = MARCSpec::ControlFieldSpec.new(tag)
|
55
65
|
else
|
56
66
|
marcfieldspec = MARCSpec::VariableFieldSpec.new(tag)
|
57
67
|
end
|
58
68
|
|
69
|
+
# Did we get subs? If so, add them now.
|
70
|
+
if subs
|
71
|
+
marcfieldspec.codes = subs
|
72
|
+
end
|
73
|
+
|
59
74
|
marcfieldspec.instance_eval(&blk) if block_given?
|
60
75
|
|
61
76
|
# If we had multiple sub calls, get them from the codehistory
|
data/lib/marcspec/map.rb
CHANGED
@@ -10,14 +10,15 @@ module MARCSpec
|
|
10
10
|
# NOTE: THIS IS AN ABSTRACT SUPERCLASS. DO NOT INSTANTIATE IT DIRECTLY
|
11
11
|
|
12
12
|
class Map
|
13
|
+
include Logback::Simple
|
14
|
+
|
13
15
|
attr_accessor :mapname, :map
|
14
16
|
|
15
|
-
# Create a new map. The passed map is either
|
16
|
-
#
|
17
|
+
# Create a new map. The passed map is either a standard hash (KVMap) or a list of duples
|
18
|
+
# (for a MultiValueMap)
|
17
19
|
#
|
18
20
|
# @param [String] mapname The name of this map; can be used to find it later on.
|
19
|
-
# @param [Hash, Array
|
20
|
-
# array of duples (2-value arrays) for a MultiValueMap.
|
21
|
+
# @param [Hash, Array] map Either a normal key-value hash (for a KV Map) or an array of duples (2-value arrays) for a MultiValueMap.
|
21
22
|
def initialize(mapname, map)
|
22
23
|
@mapname = mapname
|
23
24
|
@map = map
|
@@ -34,14 +35,14 @@ module MARCSpec
|
|
34
35
|
begin
|
35
36
|
str = File.open(filename).read
|
36
37
|
rescue Exception => e
|
37
|
-
|
38
|
+
log.fatal "Problem opening #{filename}: #{e.message}"
|
38
39
|
raise e
|
39
40
|
end
|
40
41
|
|
41
42
|
begin
|
42
43
|
rawmap = eval(str)
|
43
44
|
rescue Exception => e
|
44
|
-
|
45
|
+
log.fatal "Problem evaluating (with 'eval') file #{filename}: #{e.message}"
|
45
46
|
raise e
|
46
47
|
end
|
47
48
|
|
@@ -58,7 +59,7 @@ module MARCSpec
|
|
58
59
|
when :multi
|
59
60
|
return MultiValueMap.new(rawmap[:mapname], rawmap[:map])
|
60
61
|
else
|
61
|
-
|
62
|
+
log.fatal "Map file #{filename} doesn't seem to be either a KV map or a MuliValueMap according to :maptype (#{rawmap[:maptype]})"
|
62
63
|
raise ArgumentError, "File #{filename} doesn't evaluate to a valid map"
|
63
64
|
end
|
64
65
|
|
@@ -91,7 +91,7 @@ module MARCSpec
|
|
91
91
|
prop.load(f.to_inputstream)
|
92
92
|
prop.each do |patstring,kv|
|
93
93
|
unless patstring =~ /^pattern/ and kv =~ /.+=>.+/
|
94
|
-
|
94
|
+
log.warn "MultiValueMap import skipping weird line in #{filename}\n #{l}"
|
95
95
|
next
|
96
96
|
end
|
97
97
|
match = /^\s*(.+?)\s*=>\s*(.+?)\s*$/.match(kv)
|