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 +2 -0
- data/README.rdoc +4 -1
- data/VERSION +1 -1
- data/lib/marcspec/controlfieldspec.rb +4 -2
- data/lib/marcspec/customspec.rb +6 -11
- data/lib/marcspec/dsl.rb +139 -0
- data/lib/marcspec/map.rb +7 -0
- data/lib/marcspec/multivaluemap.rb +42 -10
- data/lib/marcspec/solrfieldspec.rb +8 -6
- data/lib/marcspec/specset.rb +30 -2
- data/lib/marcspec/variablefieldspec.rb +3 -1
- data/lib/marcspec.rb +1 -1
- data/spec/data/simplemap.rb +14 -0
- data/spec/dsl_spec.rb +370 -0
- data/spec/maps_spec.rb +87 -52
- data/spec/solrfieldspec_spec.rb +0 -11
- data/spec/specset_spec.rb +6 -0
- metadata +10 -5
    
        data/CHANGES
    CHANGED
    
    
    
        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 | 
| 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.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  | 
| 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
         | 
    
        data/lib/marcspec/customspec.rb
    CHANGED
    
    | @@ -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 | 
            -
                  @ | 
| 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 @ | 
| 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 @ | 
| 99 | 
            +
                  if @defaultValue
         | 
| 105 100 | 
             
                    s.print(":default => ")
         | 
| 106 | 
            -
                    PP.singleline_pp(@ | 
| 101 | 
            +
                    PP.singleline_pp(@defaultValue, s)
         | 
| 107 102 | 
             
                    s.print(",\n ")
         | 
| 108 103 | 
             
                  end
         | 
| 109 104 | 
             
                  if @map
         | 
    
        data/lib/marcspec/dsl.rb
    ADDED
    
    | @@ -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 | 
            -
                 | 
| 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 | 
            -
                   | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
                       | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 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 | 
            -
             | 
| 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, : | 
| 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 | 
            -
                  @ | 
| 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 @ | 
| 46 | 
            +
                    if @defaultValue.nil? # unless there's a default value, just return nothing
         | 
| 46 47 | 
             
                      return []
         | 
| 47 48 | 
             
                    else
         | 
| 48 | 
            -
                      return [@ | 
| 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 @ | 
| 103 | 
            +
                  if @defaultValue
         | 
| 102 104 | 
             
                    s.print(":default => ")
         | 
| 103 | 
            -
                    PP.singleline_pp(@ | 
| 105 | 
            +
                    PP.singleline_pp(@defaultValue, s)
         | 
| 104 106 | 
             
                    s.print(",\n ")
         | 
| 105 107 | 
             
                  end
         | 
| 106 108 | 
             
                  if @map
         | 
    
        data/lib/marcspec/specset.rb
    CHANGED
    
    | @@ -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  | 
| 34 | 
            -
                @ | 
| 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 " | 
| 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
         | 
    
        data/spec/solrfieldspec_spec.rb
    CHANGED
    
    | @@ -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:  | 
| 4 | 
            +
              hash: 3
         | 
| 5 5 | 
             
              prerelease: false
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 1
         | 
| 8 | 
            -
              -  | 
| 9 | 
            -
              -  | 
| 10 | 
            -
              version: 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- | 
| 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
         |