berkeley_library-tind 0.4.0 → 0.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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +1 -1
- data/.idea/inspectionProfiles/Project_Default.xml +18 -0
- data/.idea/tind.iml +91 -91
- data/.ruby-version +1 -1
- data/CHANGES.md +33 -1
- data/README.md +15 -1
- data/berkeley_library-tind.gemspec +3 -2
- data/lib/berkeley_library/tind/api/api.rb +17 -11
- data/lib/berkeley_library/tind/api/collection.rb +1 -1
- data/lib/berkeley_library/tind/api/search.rb +2 -2
- data/lib/berkeley_library/tind/export/exporter.rb +1 -1
- data/lib/berkeley_library/tind/export/table.rb +1 -1
- data/lib/berkeley_library/tind/export/table_metrics.rb +1 -1
- data/lib/berkeley_library/tind/marc/xml_builder.rb +62 -0
- data/lib/berkeley_library/tind/marc/xml_reader.rb +32 -19
- data/lib/berkeley_library/tind/marc/xml_writer.rb +152 -0
- data/lib/berkeley_library/tind/module_info.rb +1 -1
- data/lib/berkeley_library/util/files.rb +39 -0
- data/lib/berkeley_library/util/ods/spreadsheet.rb +1 -1
- data/lib/berkeley_library/util/ods/xml/element_node.rb +1 -1
- data/spec/berkeley_library/tind/export/export_spec.rb +3 -1
- data/spec/berkeley_library/tind/export/table_spec.rb +2 -0
- data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +42 -1
- data/spec/berkeley_library/tind/marc/xml_writer_spec.rb +156 -0
- data/spec/data/new-records.xml +46 -0
- metadata +36 -39
- data/Jenkinsfile +0 -18
- data/lib/berkeley_library/util/arrays.rb +0 -178
- data/lib/berkeley_library/util/logging.rb +0 -1
- data/lib/berkeley_library/util/paths.rb +0 -111
- data/lib/berkeley_library/util/stringios.rb +0 -30
- data/lib/berkeley_library/util/strings.rb +0 -42
- data/lib/berkeley_library/util/sys_exits.rb +0 -15
- data/lib/berkeley_library/util/times.rb +0 -22
- data/lib/berkeley_library/util/uris/appender.rb +0 -162
- data/lib/berkeley_library/util/uris/requester.rb +0 -62
- data/lib/berkeley_library/util/uris/validator.rb +0 -32
- data/lib/berkeley_library/util/uris.rb +0 -44
- data/spec/berkeley_library/util/arrays_spec.rb +0 -340
- data/spec/berkeley_library/util/paths_spec.rb +0 -90
- data/spec/berkeley_library/util/stringios_spec.rb +0 -34
- data/spec/berkeley_library/util/strings_spec.rb +0 -27
- data/spec/berkeley_library/util/times_spec.rb +0 -39
- data/spec/berkeley_library/util/uris_spec.rb +0 -118
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: berkeley_library-tind
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - David Moles
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2022-02-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: berkeley_library-logging
         | 
| @@ -30,14 +30,34 @@ dependencies: | |
| 30 30 | 
             
                requirements:
         | 
| 31 31 | 
             
                - - "~>"
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version:  | 
| 33 | 
            +
                    version: 0.3.0
         | 
| 34 | 
            +
                - - ">="
         | 
| 35 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 36 | 
            +
                    version: 0.3.1
         | 
| 34 37 | 
             
              type: :runtime
         | 
| 35 38 | 
             
              prerelease: false
         | 
| 36 39 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 40 | 
             
                requirements:
         | 
| 38 41 | 
             
                - - "~>"
         | 
| 39 42 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version:  | 
| 43 | 
            +
                    version: 0.3.0
         | 
| 44 | 
            +
                - - ">="
         | 
| 45 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 46 | 
            +
                    version: 0.3.1
         | 
| 47 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 48 | 
            +
              name: berkeley_library-util
         | 
| 49 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 50 | 
            +
                requirements:
         | 
| 51 | 
            +
                - - "~>"
         | 
| 52 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            +
                    version: '0.1'
         | 
| 54 | 
            +
              type: :runtime
         | 
| 55 | 
            +
              prerelease: false
         | 
| 56 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 | 
            +
                requirements:
         | 
| 58 | 
            +
                - - "~>"
         | 
| 59 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 60 | 
            +
                    version: '0.1'
         | 
| 41 61 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 62 | 
             
              name: ice_nine
         | 
| 43 63 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -179,25 +199,19 @@ dependencies: | |
| 179 199 | 
             
                  - !ruby/object:Gem::Version
         | 
| 180 200 | 
             
                    version: '2.7'
         | 
| 181 201 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 182 | 
            -
              name:  | 
| 202 | 
            +
              name: equivalent-xml
         | 
| 183 203 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 184 204 | 
             
                requirements:
         | 
| 185 | 
            -
                - - " | 
| 186 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 187 | 
            -
                    version: 3.0.5
         | 
| 188 | 
            -
                - - "<"
         | 
| 205 | 
            +
                - - "~>"
         | 
| 189 206 | 
             
                  - !ruby/object:Gem::Version
         | 
| 190 | 
            -
                    version: ' | 
| 207 | 
            +
                    version: '0.6'
         | 
| 191 208 | 
             
              type: :development
         | 
| 192 209 | 
             
              prerelease: false
         | 
| 193 210 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 194 211 | 
             
                requirements:
         | 
| 195 | 
            -
                - - " | 
| 196 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 197 | 
            -
                    version: 3.0.5
         | 
| 198 | 
            -
                - - "<"
         | 
| 212 | 
            +
                - - "~>"
         | 
| 199 213 | 
             
                  - !ruby/object:Gem::Version
         | 
| 200 | 
            -
                    version: ' | 
| 214 | 
            +
                    version: '0.6'
         | 
| 201 215 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 202 216 | 
             
              name: rake
         | 
| 203 217 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -360,7 +374,6 @@ files: | |
| 360 374 | 
             
            - CHANGES.md
         | 
| 361 375 | 
             
            - Dockerfile
         | 
| 362 376 | 
             
            - Gemfile
         | 
| 363 | 
            -
            - Jenkinsfile
         | 
| 364 377 | 
             
            - LICENSE.md
         | 
| 365 378 | 
             
            - README.md
         | 
| 366 379 | 
             
            - Rakefile
         | 
| @@ -396,10 +409,11 @@ files: | |
| 396 409 | 
             
            - lib/berkeley_library/tind/export/table.rb
         | 
| 397 410 | 
             
            - lib/berkeley_library/tind/export/table_metrics.rb
         | 
| 398 411 | 
             
            - lib/berkeley_library/tind/marc.rb
         | 
| 412 | 
            +
            - lib/berkeley_library/tind/marc/xml_builder.rb
         | 
| 399 413 | 
             
            - lib/berkeley_library/tind/marc/xml_reader.rb
         | 
| 414 | 
            +
            - lib/berkeley_library/tind/marc/xml_writer.rb
         | 
| 400 415 | 
             
            - lib/berkeley_library/tind/module_info.rb
         | 
| 401 | 
            -
            - lib/berkeley_library/util/ | 
| 402 | 
            -
            - lib/berkeley_library/util/logging.rb
         | 
| 416 | 
            +
            - lib/berkeley_library/util/files.rb
         | 
| 403 417 | 
             
            - lib/berkeley_library/util/ods/spreadsheet.rb
         | 
| 404 418 | 
             
            - lib/berkeley_library/util/ods/xml/content_doc.rb
         | 
| 405 419 | 
             
            - lib/berkeley_library/util/ods/xml/document_node.rb
         | 
| @@ -439,15 +453,6 @@ files: | |
| 439 453 | 
             
            - lib/berkeley_library/util/ods/xml/table/table_column.rb
         | 
| 440 454 | 
             
            - lib/berkeley_library/util/ods/xml/table/table_row.rb
         | 
| 441 455 | 
             
            - lib/berkeley_library/util/ods/xml/text/p.rb
         | 
| 442 | 
            -
            - lib/berkeley_library/util/paths.rb
         | 
| 443 | 
            -
            - lib/berkeley_library/util/stringios.rb
         | 
| 444 | 
            -
            - lib/berkeley_library/util/strings.rb
         | 
| 445 | 
            -
            - lib/berkeley_library/util/sys_exits.rb
         | 
| 446 | 
            -
            - lib/berkeley_library/util/times.rb
         | 
| 447 | 
            -
            - lib/berkeley_library/util/uris.rb
         | 
| 448 | 
            -
            - lib/berkeley_library/util/uris/appender.rb
         | 
| 449 | 
            -
            - lib/berkeley_library/util/uris/requester.rb
         | 
| 450 | 
            -
            - lib/berkeley_library/util/uris/validator.rb
         | 
| 451 456 | 
             
            - rakelib/bundle.rake
         | 
| 452 457 | 
             
            - rakelib/coverage.rake
         | 
| 453 458 | 
             
            - rakelib/gem.rake
         | 
| @@ -472,7 +477,7 @@ files: | |
| 472 477 | 
             
            - spec/berkeley_library/tind/export/row_spec.rb
         | 
| 473 478 | 
             
            - spec/berkeley_library/tind/export/table_spec.rb
         | 
| 474 479 | 
             
            - spec/berkeley_library/tind/marc/xml_reader_spec.rb
         | 
| 475 | 
            -
            - spec/berkeley_library/ | 
| 480 | 
            +
            - spec/berkeley_library/tind/marc/xml_writer_spec.rb
         | 
| 476 481 | 
             
            - spec/berkeley_library/util/ods/spreadsheet_spec.rb
         | 
| 477 482 | 
             
            - spec/berkeley_library/util/ods/xml/content_doc_spec.rb
         | 
| 478 483 | 
             
            - spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb
         | 
| @@ -482,14 +487,10 @@ files: | |
| 482 487 | 
             
            - spec/berkeley_library/util/ods/xml/style/family_spec.rb
         | 
| 483 488 | 
             
            - spec/berkeley_library/util/ods/xml/table/table_row_spec.rb
         | 
| 484 489 | 
             
            - spec/berkeley_library/util/ods/xml/table/table_spec.rb
         | 
| 485 | 
            -
            - spec/berkeley_library/util/paths_spec.rb
         | 
| 486 | 
            -
            - spec/berkeley_library/util/stringios_spec.rb
         | 
| 487 | 
            -
            - spec/berkeley_library/util/strings_spec.rb
         | 
| 488 | 
            -
            - spec/berkeley_library/util/times_spec.rb
         | 
| 489 | 
            -
            - spec/berkeley_library/util/uris_spec.rb
         | 
| 490 490 | 
             
            - spec/data/collection-names.txt
         | 
| 491 491 | 
             
            - spec/data/collections.json
         | 
| 492 492 | 
             
            - spec/data/disjoint-records.xml
         | 
| 493 | 
            +
            - spec/data/new-records.xml
         | 
| 493 494 | 
             
            - spec/data/record-184453.xml
         | 
| 494 495 | 
             
            - spec/data/record-184458.xml
         | 
| 495 496 | 
             
            - spec/data/record-187888.xml
         | 
| @@ -549,7 +550,7 @@ test_files: | |
| 549 550 | 
             
            - spec/berkeley_library/tind/export/row_spec.rb
         | 
| 550 551 | 
             
            - spec/berkeley_library/tind/export/table_spec.rb
         | 
| 551 552 | 
             
            - spec/berkeley_library/tind/marc/xml_reader_spec.rb
         | 
| 552 | 
            -
            - spec/berkeley_library/ | 
| 553 | 
            +
            - spec/berkeley_library/tind/marc/xml_writer_spec.rb
         | 
| 553 554 | 
             
            - spec/berkeley_library/util/ods/spreadsheet_spec.rb
         | 
| 554 555 | 
             
            - spec/berkeley_library/util/ods/xml/content_doc_spec.rb
         | 
| 555 556 | 
             
            - spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb
         | 
| @@ -559,14 +560,10 @@ test_files: | |
| 559 560 | 
             
            - spec/berkeley_library/util/ods/xml/style/family_spec.rb
         | 
| 560 561 | 
             
            - spec/berkeley_library/util/ods/xml/table/table_row_spec.rb
         | 
| 561 562 | 
             
            - spec/berkeley_library/util/ods/xml/table/table_spec.rb
         | 
| 562 | 
            -
            - spec/berkeley_library/util/paths_spec.rb
         | 
| 563 | 
            -
            - spec/berkeley_library/util/stringios_spec.rb
         | 
| 564 | 
            -
            - spec/berkeley_library/util/strings_spec.rb
         | 
| 565 | 
            -
            - spec/berkeley_library/util/times_spec.rb
         | 
| 566 | 
            -
            - spec/berkeley_library/util/uris_spec.rb
         | 
| 567 563 | 
             
            - spec/data/collection-names.txt
         | 
| 568 564 | 
             
            - spec/data/collections.json
         | 
| 569 565 | 
             
            - spec/data/disjoint-records.xml
         | 
| 566 | 
            +
            - spec/data/new-records.xml
         | 
| 570 567 | 
             
            - spec/data/record-184453.xml
         | 
| 571 568 | 
             
            - spec/data/record-184458.xml
         | 
| 572 569 | 
             
            - spec/data/record-187888.xml
         | 
    
        data/Jenkinsfile
    DELETED
    
    | @@ -1,18 +0,0 @@ | |
| 1 | 
            -
            #!/usr/bin/env groovy
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            dockerComposePipeline(
         | 
| 4 | 
            -
                commands: [
         | 
| 5 | 
            -
                    'bundle exec rake coverage',
         | 
| 6 | 
            -
                    'bundle exec rake rubocop',
         | 
| 7 | 
            -
                    'bundle exec rake bundle:audit',
         | 
| 8 | 
            -
                    'bundle exec rake gem'
         | 
| 9 | 
            -
                ],
         | 
| 10 | 
            -
                artifacts: [
         | 
| 11 | 
            -
                    junit: 'artifacts/rspec/**/*.xml',
         | 
| 12 | 
            -
                    html : [
         | 
| 13 | 
            -
                        'Code Coverage': 'artifacts/rcov',
         | 
| 14 | 
            -
                        'RuboCop'      : 'artifacts/rubocop'
         | 
| 15 | 
            -
                    ],
         | 
| 16 | 
            -
                    raw  : ['artifacts/**/*.gem']
         | 
| 17 | 
            -
                ]
         | 
| 18 | 
            -
            )
         | 
| @@ -1,178 +0,0 @@ | |
| 1 | 
            -
            module BerkeleyLibrary
         | 
| 2 | 
            -
              module Util
         | 
| 3 | 
            -
                module Arrays
         | 
| 4 | 
            -
                  class << self
         | 
| 5 | 
            -
                    # Clients can chose to call class methods directly, or include the module
         | 
| 6 | 
            -
                    include Arrays
         | 
| 7 | 
            -
                  end
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  # Recursively checks whether the specified list contains, in the
         | 
| 10 | 
            -
                  # same order, all values in the other specified list (additional codes
         | 
| 11 | 
            -
                  # in between are fine)
         | 
| 12 | 
            -
                  #
         | 
| 13 | 
            -
                  # @param subset [Array] the values to look for
         | 
| 14 | 
            -
                  # @param superset [Array] the list of values to look in
         | 
| 15 | 
            -
                  # @return boolean True if all values were found, false otherwise
         | 
| 16 | 
            -
                  def ordered_superset?(superset:, subset:)
         | 
| 17 | 
            -
                    !find_indices(in_array: superset, for_array: subset).nil?
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                  # Counts how many contiguous elements from the start of an
         | 
| 21 | 
            -
                  # sequence of values satisfy the given block.
         | 
| 22 | 
            -
                  #
         | 
| 23 | 
            -
                  # @overload count_while(arr:)
         | 
| 24 | 
            -
                  #   Returns an enumerator.
         | 
| 25 | 
            -
                  #   @param values [Enumerable] the values
         | 
| 26 | 
            -
                  #   @return [Enumerator] the enumerator.
         | 
| 27 | 
            -
                  # @overload count_while(arr:, &block)
         | 
| 28 | 
            -
                  #   Passes elements to the block until the block returns nil or false,
         | 
| 29 | 
            -
                  #   then stops iterating and returns the count of matching elements.
         | 
| 30 | 
            -
                  #   @param values [Enumerable] the values
         | 
| 31 | 
            -
                  #   @return [Integer] the count
         | 
| 32 | 
            -
                  def count_while(values:)
         | 
| 33 | 
            -
                    return to_enum(:count_while, values: values) unless block_given?
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                    values.inject(0) do |count, x|
         | 
| 36 | 
            -
                      matched = yield x
         | 
| 37 | 
            -
                      break count unless matched
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                      count + 1
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                  # Given two lists, one of which is a superset of the other, with elements
         | 
| 44 | 
            -
                  # in the same order (but possibly with additional elements in the superset),
         | 
| 45 | 
            -
                  # returns an array the length of the subset, containing for each element in
         | 
| 46 | 
            -
                  # the subset the index of the corresponding element in the superset.
         | 
| 47 | 
            -
                  #
         | 
| 48 | 
            -
                  # @overload find_matching_indices(for_array:, in_array:)
         | 
| 49 | 
            -
                  #   For each value in `for_array`, finds the index of the first equal value
         | 
| 50 | 
            -
                  #   in `in_array` after the previously matched value.
         | 
| 51 | 
            -
                  #   @param in_array [Array] the list of values to look in
         | 
| 52 | 
            -
                  #   @param for_array [Array] the values to look for
         | 
| 53 | 
            -
                  #   @return [Array<Integer>, nil] the indices in `in_array` of each value in `for_array`,
         | 
| 54 | 
            -
                  #     or `nil` if not all values could be found
         | 
| 55 | 
            -
                  #
         | 
| 56 | 
            -
                  # @overload find_matching_indices(for_array:, in_array:)
         | 
| 57 | 
            -
                  #   For each value in `for_array`, finds the index of the first value
         | 
| 58 | 
            -
                  #   in `in_array` after the previously matched value that matches
         | 
| 59 | 
            -
                  #   the specified match function.
         | 
| 60 | 
            -
                  #   @param in_array [Array] the list of values to look in
         | 
| 61 | 
            -
                  #   @param for_array [Array] the values to look for
         | 
| 62 | 
            -
                  #   @yieldparam source [Object] the value to compare
         | 
| 63 | 
            -
                  #   @yieldparam target [Object] the value to compare against
         | 
| 64 | 
            -
                  #   @return [Array<Integer>, nil] the indices in `in_array` of each value in `for_array`,
         | 
| 65 | 
            -
                  #     or `nil` if not all values could be found
         | 
| 66 | 
            -
                  def find_indices(for_array:, in_array:, &block)
         | 
| 67 | 
            -
                    return find_indices_matching(for_array, in_array, &block) if block_given?
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                    find_all_indices(for_array, in_array)
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                  # Given a block or a value, finds the index of the first matching value
         | 
| 73 | 
            -
                  # at or after the specified start index.
         | 
| 74 | 
            -
                  #
         | 
| 75 | 
            -
                  # @overload find_index(value, in_array:, start_index:)
         | 
| 76 | 
            -
                  #   Finds the first index of the specified value.
         | 
| 77 | 
            -
                  #   @param value [Object] the value to find
         | 
| 78 | 
            -
                  #   @param in_array [Array] the array to search
         | 
| 79 | 
            -
                  #   @param start_index [Integer] the index to start with
         | 
| 80 | 
            -
                  #   @return [Integer, nil] the index, or `nil` if no value matches
         | 
| 81 | 
            -
                  # @overload find_index(&block)
         | 
| 82 | 
            -
                  #   Finds the index of the first value matching
         | 
| 83 | 
            -
                  #   the specified block.
         | 
| 84 | 
            -
                  #   @param in_array [Array] the array to search
         | 
| 85 | 
            -
                  #   @param start_index [Integer] the index to start with
         | 
| 86 | 
            -
                  #   @yieldreturn [Boolean] whether the element matches
         | 
| 87 | 
            -
                  #   @return [Integer, nil] the index, or `nil` if no value matches
         | 
| 88 | 
            -
                  # @overload find_index
         | 
| 89 | 
            -
                  #   @param in_array [Array] the array to search
         | 
| 90 | 
            -
                  #   @param start_index [Integer] the index to start with
         | 
| 91 | 
            -
                  #   @return [Enumerator] a new enumerator
         | 
| 92 | 
            -
                  def find_index(*args, in_array:, start_index: 0, &block)
         | 
| 93 | 
            -
                    raise ArgumentError, "wrong number of arguments (given #{value.length}, expected 0..1" if args.size > 1
         | 
| 94 | 
            -
                    return Enumerator.new { |y| find_index(in_array: in_array, start_index: start_index, &y) } if args.empty? && !block_given?
         | 
| 95 | 
            -
                    return unless (relative_index = in_array[start_index..].find_index(*args, &block))
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                    relative_index + start_index
         | 
| 98 | 
            -
                  end
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                  # Given an array of unique integers _a<sub>1</sub>_, returns a new array
         | 
| 101 | 
            -
                  # _a<sub>2</sub>_ in which the value at each index _i<sub>2</sub>_ is the
         | 
| 102 | 
            -
                  # index _i<sub>1</sub>_ at which that value was found in _a<sub>1</sub>_.
         | 
| 103 | 
            -
                  # E.g., given `[0, 2, 3]`, returns `[0, nil, 1, 2]`. The indices need
         | 
| 104 | 
            -
                  # not be in order but must be unique.
         | 
| 105 | 
            -
                  #
         | 
| 106 | 
            -
                  # @param arr [Array<Integer>, nil] the array to invert.
         | 
| 107 | 
            -
                  # @return [Array<Integer, nil>, nil] the inverted array, or nil if the input array is nil
         | 
| 108 | 
            -
                  # @raise TypeError if `arr` is not an array of integers
         | 
| 109 | 
            -
                  # @raise ArgumentError if `arr` contains duplicate values
         | 
| 110 | 
            -
                  def invert(arr)
         | 
| 111 | 
            -
                    return unless arr
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                    # noinspection RubyNilAnalysis
         | 
| 114 | 
            -
                    Array.new(arr.size).tap do |inv|
         | 
| 115 | 
            -
                      arr.each_with_index do |v, i|
         | 
| 116 | 
            -
                        next inv[v] = i unless (prev_index = inv[v])
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                        raise ArgumentError, "Duplicate value #{v} at index #{i} already found at #{prev_index}"
         | 
| 119 | 
            -
                      end
         | 
| 120 | 
            -
                    end
         | 
| 121 | 
            -
                  end
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                  # Merges two arrays in an order-preserving manner.
         | 
| 124 | 
            -
                  # @param a1 [Array] the first array
         | 
| 125 | 
            -
                  # @param a2 [Array] the second array
         | 
| 126 | 
            -
                  # @return [Array] a merged array that is an ordered superset of both `a1` and `a2`
         | 
| 127 | 
            -
                  # @see Arrays#ordered_superset?
         | 
| 128 | 
            -
                  def merge(a1, a2)
         | 
| 129 | 
            -
                    return a1 if a2.empty?
         | 
| 130 | 
            -
                    return a2 if a1.empty?
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                    shorter, longer = a1.size > a2.size ? [a2, a1] : [a1, a2]
         | 
| 133 | 
            -
                    do_merge(shorter, longer)
         | 
| 134 | 
            -
                  end
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                  private
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                  def do_merge(shorter, longer)
         | 
| 139 | 
            -
                    shorter.each_with_index do |v, ix_s|
         | 
| 140 | 
            -
                      next unless (ix_l = longer.find_index(v))
         | 
| 141 | 
            -
             | 
| 142 | 
            -
                      shorter_unmatched = shorter[0...ix_s]
         | 
| 143 | 
            -
                      longer_unmatched = longer[0...ix_l]
         | 
| 144 | 
            -
                      all_unmatched = sort_by_first_and_flatten(shorter_unmatched, longer_unmatched)
         | 
| 145 | 
            -
                      return (all_unmatched << v) + merge(shorter[ix_s + 1..], longer[ix_l + 1..])
         | 
| 146 | 
            -
                    end
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                    sort_by_first_and_flatten(longer, shorter)
         | 
| 149 | 
            -
                  end
         | 
| 150 | 
            -
             | 
| 151 | 
            -
                  def sort_by_first_and_flatten(a1, a2)
         | 
| 152 | 
            -
                    return a1 if a2.empty?
         | 
| 153 | 
            -
                    return a2 if a1.empty?
         | 
| 154 | 
            -
                    return a2 + a1 if a1.first.respond_to?(:>) && a1.first > a2.first
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                    a1 + a2
         | 
| 157 | 
            -
                  end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                  def find_all_indices(source, target)
         | 
| 160 | 
            -
                    source.each_with_object([]) do |src, target_indices|
         | 
| 161 | 
            -
                      target_offset = (target_indices.last&.+ 1) || 0
         | 
| 162 | 
            -
                      return nil unless (target_index = find_index(src, in_array: target, start_index: target_offset))
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                      target_indices << target_index
         | 
| 165 | 
            -
                    end
         | 
| 166 | 
            -
                  end
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                  def find_indices_matching(source, target)
         | 
| 169 | 
            -
                    source.each_with_object([]) do |src, target_indices|
         | 
| 170 | 
            -
                      target_offset = (target_indices.last&.+ 1) || 0
         | 
| 171 | 
            -
                      return nil unless (target_index = find_index(in_array: target, start_index: target_offset) { |tgt| yield src, tgt })
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                      target_indices << target_index
         | 
| 174 | 
            -
                    end
         | 
| 175 | 
            -
                  end
         | 
| 176 | 
            -
                end
         | 
| 177 | 
            -
              end
         | 
| 178 | 
            -
            end
         | 
| @@ -1 +0,0 @@ | |
| 1 | 
            -
            require 'berkeley_library/logging'
         | 
| @@ -1,111 +0,0 @@ | |
| 1 | 
            -
            require 'berkeley_library/util/stringios'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module BerkeleyLibrary
         | 
| 4 | 
            -
              module Util
         | 
| 5 | 
            -
                # This module, modeled on the {https://golang.org/pkg/path/ Go `path` package},
         | 
| 6 | 
            -
                # provides utility routines for modifying paths separated by forward slashes,
         | 
| 7 | 
            -
                # such as URL paths. For system-dependent file paths, use
         | 
| 8 | 
            -
                # {https://ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Pathname.html `Pathname`}
         | 
| 9 | 
            -
                # instead.
         | 
| 10 | 
            -
                module Paths
         | 
| 11 | 
            -
                  include BerkeleyLibrary::Util::StringIOs
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  class << self
         | 
| 14 | 
            -
                    include Paths
         | 
| 15 | 
            -
                  end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                  # Returns the shortest path name equivalent to `path` by purely lexical
         | 
| 18 | 
            -
                  # processing by:
         | 
| 19 | 
            -
                  #
         | 
| 20 | 
            -
                  # 1. replacing runs of multiple `/` with a single `/`
         | 
| 21 | 
            -
                  # 2. eliminating all `.` (current directory) elements
         | 
| 22 | 
            -
                  # 3. eliminating all `<child>/..` in favor of directly
         | 
| 23 | 
            -
                  #    referencing the parent directory
         | 
| 24 | 
            -
                  # 4. replaing all `/..` at the beginning of the path
         | 
| 25 | 
            -
                  #    with a single leading `/`
         | 
| 26 | 
            -
                  #
         | 
| 27 | 
            -
                  # The returned path ends in a slash only if it is the root `/`.
         | 
| 28 | 
            -
                  # @see https://9p.io/sys/doc/lexnames.html Rob Pike, "Lexical File Names in Plan 9 or Getting Dot-Dot Right"
         | 
| 29 | 
            -
                  #
         | 
| 30 | 
            -
                  # @param path [String, nil] the path to clean
         | 
| 31 | 
            -
                  # @return [String, nil] the cleaned path, or `nil` for a nil path.
         | 
| 32 | 
            -
                  def clean(path)
         | 
| 33 | 
            -
                    return unless path
         | 
| 34 | 
            -
                    return '.' if ['', '.'].include?(path)
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                    StringIO.new.tap do |out|
         | 
| 37 | 
            -
                      out << '/' if path[0] == '/'
         | 
| 38 | 
            -
                      dotdot = (r = out.size)
         | 
| 39 | 
            -
                      r, dotdot = process_next(r, dotdot, path, out) while r < path.size
         | 
| 40 | 
            -
                      out << '.' if out.pos == 0
         | 
| 41 | 
            -
                    end.string
         | 
| 42 | 
            -
                  end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                  # Joins any number of path elements into a single path, separating
         | 
| 45 | 
            -
                  # them with slashes, ignoring empty elements and passing the result
         | 
| 46 | 
            -
                  # to {Paths#clean}.
         | 
| 47 | 
            -
                  #
         | 
| 48 | 
            -
                  # @param elements [Array<String>] the elements to join
         | 
| 49 | 
            -
                  # @return [String] the joined path
         | 
| 50 | 
            -
                  def join(*elements)
         | 
| 51 | 
            -
                    elements = elements.reject { |e| [nil, ''].include?(e) }
         | 
| 52 | 
            -
                    joined_raw = elements.join('/')
         | 
| 53 | 
            -
                    return '' if joined_raw == ''
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    clean(joined_raw)
         | 
| 56 | 
            -
                  end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                  private
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                  def process_next(r, dotdot, path, out)
         | 
| 61 | 
            -
                    # empty path element, or .
         | 
| 62 | 
            -
                    return r + 1, dotdot if empty_or_dot?(r, path)
         | 
| 63 | 
            -
                    # .. element: remove to last /
         | 
| 64 | 
            -
                    return handle_dotdot(r, dotdot, path, out) if dotdot?(r, path)
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                    # real path element
         | 
| 67 | 
            -
                    [append_from(r, path, out), dotdot]
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  def handle_dotdot(r, dotdot, path, out)
         | 
| 71 | 
            -
                    if out.pos > dotdot
         | 
| 72 | 
            -
                      backtrack_to_dotdot(out, dotdot)
         | 
| 73 | 
            -
                    elsif path[0] != '/'
         | 
| 74 | 
            -
                      dotdot = append_dotdot(out)
         | 
| 75 | 
            -
                    end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                    [r + 2, dotdot]
         | 
| 78 | 
            -
                  end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                  def dotdot?(r, path)
         | 
| 81 | 
            -
                    path[r] == '.' && (r + 2 == path.size || path[r + 2] == '/')
         | 
| 82 | 
            -
                  end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                  def empty_or_dot?(r, path)
         | 
| 85 | 
            -
                    path[r] == '/' || (path[r] == '.' && (r + 1 == path.size || path[r + 1] == '/'))
         | 
| 86 | 
            -
                  end
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                  def append_from(r, path, out)
         | 
| 89 | 
            -
                    out << '/' if (path[0] == '/' && out.pos != 1) || (path[0] != '/' && out.pos != 0)
         | 
| 90 | 
            -
                    while r < path.size && path[r] != '/'
         | 
| 91 | 
            -
                      out << path[r]
         | 
| 92 | 
            -
                      r += 1
         | 
| 93 | 
            -
                    end
         | 
| 94 | 
            -
                    r
         | 
| 95 | 
            -
                  end
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                  def append_dotdot(out)
         | 
| 98 | 
            -
                    out << '/' if out.pos > 1
         | 
| 99 | 
            -
                    out << '..'
         | 
| 100 | 
            -
                    out.pos
         | 
| 101 | 
            -
                  end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                  def backtrack_to_dotdot(out, dotdot)
         | 
| 104 | 
            -
                    out.seek(-1, IO::SEEK_CUR)
         | 
| 105 | 
            -
                    out.seek(-1, IO::SEEK_CUR) while out.pos > dotdot && getbyte(out, out.pos) != 47 # '/' is ASCII 37
         | 
| 106 | 
            -
                    out.truncate(out.pos)
         | 
| 107 | 
            -
                  end
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                end
         | 
| 110 | 
            -
              end
         | 
| 111 | 
            -
            end
         | 
| @@ -1,30 +0,0 @@ | |
| 1 | 
            -
            require 'stringio'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module BerkeleyLibrary
         | 
| 4 | 
            -
              module Util
         | 
| 5 | 
            -
                module StringIOs
         | 
| 6 | 
            -
                  class << self
         | 
| 7 | 
            -
                    include StringIOs
         | 
| 8 | 
            -
                  end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  # Returns the byte (**not** character) at the specified byte index
         | 
| 11 | 
            -
                  # in the specified `StringIO`.
         | 
| 12 | 
            -
                  #
         | 
| 13 | 
            -
                  # @param s [StringIO] the StringIO to search in
         | 
| 14 | 
            -
                  # @param i [Integer] the byte index
         | 
| 15 | 
            -
                  # @return [Integer, nil] the byte, or nil if the byte index is invalid.
         | 
| 16 | 
            -
                  def getbyte(s, i)
         | 
| 17 | 
            -
                    return if i >= s.size
         | 
| 18 | 
            -
                    return if s.size + i < 0
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    pos_orig = s.pos
         | 
| 21 | 
            -
                    begin
         | 
| 22 | 
            -
                      s.seek(i >= 0 ? i : s.size + i)
         | 
| 23 | 
            -
                      s.getbyte
         | 
| 24 | 
            -
                    ensure
         | 
| 25 | 
            -
                      s.seek(pos_orig)
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
            end
         | 
| @@ -1,42 +0,0 @@ | |
| 1 | 
            -
            module BerkeleyLibrary
         | 
| 2 | 
            -
              module Util
         | 
| 3 | 
            -
                module Strings
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                  ASCII_0 = '0'.ord
         | 
| 6 | 
            -
                  ASCII_9 = '9'.ord
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def ascii_numeric?(s)
         | 
| 9 | 
            -
                    s.chars.all? do |c|
         | 
| 10 | 
            -
                      ord = c.ord
         | 
| 11 | 
            -
                      ord >= ASCII_0 && ord <= ASCII_9
         | 
| 12 | 
            -
                    end
         | 
| 13 | 
            -
                  end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                  # Locates the point at which two strings differ
         | 
| 16 | 
            -
                  #
         | 
| 17 | 
            -
                  # @return [Integer, nil] the index of the first character in either string
         | 
| 18 | 
            -
                  #   that differs from the other, or `nil` if the strings are identical,
         | 
| 19 | 
            -
                  #   or are not strings
         | 
| 20 | 
            -
                  def diff_index(s1, s2)
         | 
| 21 | 
            -
                    return unless string_like?(s1, s2)
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                    shorter, longer = s1.size > s2.size ? [s2, s1] : [s1, s2]
         | 
| 24 | 
            -
                    shorter.chars.each_with_index do |c, i|
         | 
| 25 | 
            -
                      return i if c != longer[i]
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
                    shorter.length if shorter.length < longer.length # otherwise they're equal
         | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  class << self
         | 
| 31 | 
            -
                    include Strings
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  private
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  def string_like?(*strs)
         | 
| 37 | 
            -
                    strs.all? { |s| s.respond_to?(:chars) && s.respond_to?(:size) }
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
            end
         | 
| @@ -1,15 +0,0 @@ | |
| 1 | 
            -
            module BerkeleyLibrary
         | 
| 2 | 
            -
              module Util
         | 
| 3 | 
            -
                # cf. BSD sysexits.h https://cgit.freebsd.org/src/tree/include/sysexits.h?h=releng/2.0
         | 
| 4 | 
            -
                module SysExits
         | 
| 5 | 
            -
                  # successful termination
         | 
| 6 | 
            -
                  EX_OK = 0
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  # command line usage error
         | 
| 9 | 
            -
                  EX_USAGE = 64
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  # internal software error
         | 
| 12 | 
            -
                  EX_SOFTWARE = 70 # command line usage error
         | 
| 13 | 
            -
                end
         | 
| 14 | 
            -
              end
         | 
| 15 | 
            -
            end
         | 
| @@ -1,22 +0,0 @@ | |
| 1 | 
            -
            require 'time'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module BerkeleyLibrary
         | 
| 4 | 
            -
              module Util
         | 
| 5 | 
            -
                module Times
         | 
| 6 | 
            -
                  class << self
         | 
| 7 | 
            -
                    include Times
         | 
| 8 | 
            -
                  end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  # @param time [Time, Date] the time
         | 
| 11 | 
            -
                  # @return the UTC time corresponding to `time`
         | 
| 12 | 
            -
                  def ensure_utc(time)
         | 
| 13 | 
            -
                    return unless time
         | 
| 14 | 
            -
                    return time if time.respond_to?(:utc?) && time.utc?
         | 
| 15 | 
            -
                    return time.getutc if time.respond_to?(:getutc)
         | 
| 16 | 
            -
                    return time.to_time.getutc if time.respond_to?(:to_time)
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    raise ArgumentError, "Not a date or time: #{time.inspect}"
         | 
| 19 | 
            -
                  end
         | 
| 20 | 
            -
                end
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
            end
         |