rpairtree 0.2.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 +7 -0
- data/.document +5 -0
- data/.gitignore +46 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +12 -0
- data/README.md +54 -0
- data/Rakefile +45 -0
- data/lib/pairtree.rb +78 -0
- data/lib/pairtree/identifier.rb +52 -0
- data/lib/pairtree/obj.rb +52 -0
- data/lib/pairtree/path.rb +50 -0
- data/lib/pairtree/root.rb +103 -0
- data/lib/tasks/rdoc.rake +32 -0
- data/pairtree.gemspec +21 -0
- data/spec/pairtree/pairtree_encoding_spec.rb +94 -0
- data/spec/pairtree/pairtree_obj_spec.rb +69 -0
- data/spec/pairtree/pairtree_path_spec.rb +35 -0
- data/spec/pairtree/pairtree_root_spec.rb +63 -0
- data/spec/pairtree/pairtree_spec.rb +69 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/test_data/fixtures/pairtree_root_spec/pairtree_prefix +1 -0
- data/spec/test_data/fixtures/pairtree_root_spec/pairtree_root/ab/c1/23/de/f/abc123def/content.xml +1 -0
- data/spec/test_data/fixtures/pairtree_root_spec/pairtree_version0_1 +1 -0
- metadata +125 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: a4d0513966c46f689157aed52b75c1b10731fc2a
         | 
| 4 | 
            +
              data.tar.gz: 18ecfb0525a2102152303d4c957e54fa62550a15
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 20de254a99e42de2341801c3be0a39dd23130311dfcfb094c337ccee1c2713c68e17e805fde7d98b7e59d236c38bf5ff1820298c503f56a48c5553c1233244f5
         | 
| 7 | 
            +
              data.tar.gz: 90de343158f62be17ae7b617d389fc865e86aa0142ac46316e1dfd7dee8b2d7d6826f47c08bf0fdcc43beb02f64e63696e44232dd7344e28679f890ea485168b
         | 
    
        data/.document
    ADDED
    
    
    
        data/.gitignore
    ADDED
    
    | @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            # rcov generated
         | 
| 2 | 
            +
            coverage
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # rdoc generated
         | 
| 5 | 
            +
            rdoc
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # yard generated
         | 
| 8 | 
            +
            doc
         | 
| 9 | 
            +
            .yardoc
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # bundler
         | 
| 12 | 
            +
            .bundle
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            # jeweler generated
         | 
| 15 | 
            +
            pkg
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            # * Create a file at ~/.gitignore
         | 
| 20 | 
            +
            # * Include files you want ignored
         | 
| 21 | 
            +
            # * Run: git config --global core.excludesfile ~/.gitignore
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            # After doing this, these files will be ignored in all your git projects,
         | 
| 24 | 
            +
            # saving you from having to 'pollute' every project you touch with them
         | 
| 25 | 
            +
            #
         | 
| 26 | 
            +
            # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
         | 
| 27 | 
            +
            #
         | 
| 28 | 
            +
            # For MacOS:
         | 
| 29 | 
            +
            #
         | 
| 30 | 
            +
            #.DS_Store
         | 
| 31 | 
            +
            #
         | 
| 32 | 
            +
            # For TextMate
         | 
| 33 | 
            +
            #*.tmproj
         | 
| 34 | 
            +
            #tmtags
         | 
| 35 | 
            +
            #
         | 
| 36 | 
            +
            # For emacs:
         | 
| 37 | 
            +
            #*~
         | 
| 38 | 
            +
            #\#*
         | 
| 39 | 
            +
            #.\#*
         | 
| 40 | 
            +
            #
         | 
| 41 | 
            +
            # For vim:
         | 
| 42 | 
            +
            #*.swp
         | 
| 43 | 
            +
            #
         | 
| 44 | 
            +
            Gemfile.lock
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            .idea/
         | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            ###########################################################################
         | 
| 2 | 
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 3 | 
            +
            # you may not use this file except in compliance with the License.
         | 
| 4 | 
            +
            # You may obtain a copy of the License at
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            #     http://www.apache.org/licenses/LICENSE-2.0
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            # Unless required by applicable law or agreed to in writing, software
         | 
| 9 | 
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 10 | 
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 11 | 
            +
            # See the License for the specific language governing permissions and
         | 
| 12 | 
            +
            # limitations under the License.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            # (r)pairtree
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Ruby implementation of the [Pairtree](https://wiki.ucop.edu/display/Curation/PairTree) specification from the California Digital Library.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Description
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            A fork of the seemingly-abandoned  [pairtree](https://github.com/microservices/pairtree).
         | 
| 8 | 
            +
             | 
| 9 | 
            +
             | 
| 10 | 
            +
            ## Installation
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Add this line to your application's Gemfile:
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ```ruby
         | 
| 15 | 
            +
            gem 'rpairtree'
         | 
| 16 | 
            +
            ```
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            And then execute:
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                $ bundle
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            Or install it yourself as:
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                $ gem install rpairtree
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## Usage
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ```ruby
         | 
| 29 | 
            +
              # Initiate a tree
         | 
| 30 | 
            +
              pairtree = Pairtree.at('./data', :prefix => 'pfx:', :create => true)
         | 
| 31 | 
            +
              
         | 
| 32 | 
            +
              # Create a ppath
         | 
| 33 | 
            +
              obj = pairtree.mk('pfx:abc123def')
         | 
| 34 | 
            +
              
         | 
| 35 | 
            +
              # Access an existing ppath
         | 
| 36 | 
            +
              obj = pairtree['pfx:abc123def']
         | 
| 37 | 
            +
              obj = pairtree.get('pfx:abc123def')
         | 
| 38 | 
            +
              
         | 
| 39 | 
            +
              # ppaths are Dir instances with some File and Dir class methods mixed in
         | 
| 40 | 
            +
              obj.read('content.xml')
         | 
| 41 | 
            +
              => "<content/>"
         | 
| 42 | 
            +
              obj.open('my_file.txt','w') { |io| io.write("Write text to file") }
         | 
| 43 | 
            +
              obj.entries
         | 
| 44 | 
            +
              => ["content.xml","my_file.txt"]
         | 
| 45 | 
            +
              obj['*.xml']
         | 
| 46 | 
            +
              => ["content.xml"]
         | 
| 47 | 
            +
              obj.each { |file| ... }
         | 
| 48 | 
            +
              obj.unlink('my_file.txt')
         | 
| 49 | 
            +
              
         | 
| 50 | 
            +
              # Delete a ppath and all its contents
         | 
| 51 | 
            +
              pairtree.purge!('pfx:abc123def')
         | 
| 52 | 
            +
            ```
         | 
| 53 | 
            +
             | 
| 54 | 
            +
             | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'bundler'
         | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              Bundler.setup(:default, :development)
         | 
| 5 | 
            +
            rescue Bundler::BundlerError => e
         | 
| 6 | 
            +
              $stderr.puts e.message
         | 
| 7 | 
            +
              $stderr.puts "Run `bundle install` to install missing gems"
         | 
| 8 | 
            +
              exit e.status_code
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Bundler::GemHelper.install_tasks
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            require 'rake'
         | 
| 14 | 
            +
            require 'rspec'
         | 
| 15 | 
            +
            require 'rspec/core/rake_task'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            desc 'Default: run specs.'
         | 
| 18 | 
            +
            task :default => :spec
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            RSpec::Core::RakeTask.new do |t|
         | 
| 21 | 
            +
              if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.8/
         | 
| 22 | 
            +
                t.rcov = true
         | 
| 23 | 
            +
                t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            # Use yard to build docs
         | 
| 28 | 
            +
            begin
         | 
| 29 | 
            +
              require 'yard'
         | 
| 30 | 
            +
              require 'yard/rake/yardoc_task'
         | 
| 31 | 
            +
              project_root = File.expand_path(File.dirname(__FILE__))
         | 
| 32 | 
            +
              doc_destination = File.join(project_root, 'doc')
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              YARD::Rake::YardocTask.new(:doc) do |yt|
         | 
| 35 | 
            +
                yt.files   = Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) + 
         | 
| 36 | 
            +
                             [ File.join(project_root, 'README.md') ]
         | 
| 37 | 
            +
                yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            rescue LoadError
         | 
| 40 | 
            +
              desc "Generate YARD Documentation"
         | 
| 41 | 
            +
              task :doc do
         | 
| 42 | 
            +
                abort "Please install the YARD gem to generate rdoc."
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
             | 
    
        data/lib/pairtree.rb
    ADDED
    
    | @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            require 'pairtree/identifier'
         | 
| 2 | 
            +
            require 'pairtree/path'
         | 
| 3 | 
            +
            require 'pairtree/obj'
         | 
| 4 | 
            +
            require 'pairtree/root'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'fileutils'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Pairtree
         | 
| 9 | 
            +
              class IdentifierError < Exception; end
         | 
| 10 | 
            +
              class PathError < Exception; end
         | 
| 11 | 
            +
              class VersionMismatch < Exception; end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              SPEC_VERSION = 0.1
         | 
| 14 | 
            +
              
         | 
| 15 | 
            +
              ##
         | 
| 16 | 
            +
              # Instantiate a pairtree at a given path location
         | 
| 17 | 
            +
              # @param [String] path The path in which the pairtree resides
         | 
| 18 | 
            +
              # @param [Hash] args Pairtree options
         | 
| 19 | 
            +
              # @option args [String] :prefix (nil) the identifier prefix used throughout the pairtree
         | 
| 20 | 
            +
              # @option args [String] :version (Pairtree::SPEC_VERSION) the version of the pairtree spec that this tree conforms to
         | 
| 21 | 
            +
              # @option args [Boolean] :create (false) if true, create the pairtree and its directory structure if it doesn't already exist
         | 
| 22 | 
            +
              def self.at path, args = {}
         | 
| 23 | 
            +
                args = { :prefix => nil, :version => nil, :create => false }.merge(args)
         | 
| 24 | 
            +
                args[:version] ||= SPEC_VERSION
         | 
| 25 | 
            +
                args[:version] = args[:version].to_f
         | 
| 26 | 
            +
                
         | 
| 27 | 
            +
                root_path = File.join(path, 'pairtree_root')
         | 
| 28 | 
            +
                prefix_file = File.join(path, 'pairtree_prefix')
         | 
| 29 | 
            +
                version_file = File.join(path, pairtree_version_filename(args[:version]))
         | 
| 30 | 
            +
                existing_version_file = Dir[File.join(path, "pairtree_version*")].sort.last
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                if args.delete(:create)
         | 
| 33 | 
            +
                  if File.exists?(path) and not File.directory?(path)
         | 
| 34 | 
            +
                    raise PathError, "#{path} exists, but is not a valid pairtree root"
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  FileUtils.mkdir_p(root_path)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  unless File.exists? prefix_file
         | 
| 39 | 
            +
                    File.open(prefix_file, 'w') { |f| f.write(args[:prefix].to_s) }
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                  
         | 
| 42 | 
            +
                  if existing_version_file
         | 
| 43 | 
            +
                    if existing_version_file != version_file
         | 
| 44 | 
            +
                      stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join('.').to_f
         | 
| 45 | 
            +
                      raise VersionMismatch, "Version #{args[:version]} specified, but #{stored_version} found."
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  else
         | 
| 48 | 
            +
                    args[:version] ||= SPEC_VERSION
         | 
| 49 | 
            +
                    version_file = File.join(path, pairtree_version_filename(args[:version]))
         | 
| 50 | 
            +
                    File.open(version_file, 'w') { |f| f.write %{This directory conforms to Pairtree Version #{args[:version]}. Updated spec: http://www.cdlib.org/inside/diglib/pairtree/pairtreespec.html} }
         | 
| 51 | 
            +
                    existing_version_file = version_file
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                else
         | 
| 54 | 
            +
                  unless File.directory? root_path
         | 
| 55 | 
            +
                    raise PathError, "#{path} does not point to an existing pairtree"
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                stored_prefix = File.read(prefix_file)
         | 
| 60 | 
            +
                unless args[:prefix].nil? or args[:prefix].to_s == stored_prefix
         | 
| 61 | 
            +
                  raise IdentifierError, "Specified prefix #{args[:prefix].inspect} does not match stored prefix #{stored_prefix.inspect}"
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
                args[:prefix] = stored_prefix
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join('.').to_f
         | 
| 66 | 
            +
                args[:version] ||= stored_version
         | 
| 67 | 
            +
                unless args[:version] == stored_version
         | 
| 68 | 
            +
                  raise VersionMismatch, "Version #{args[:version]} specified, but #{stored_version} found."
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
                    
         | 
| 71 | 
            +
                Pairtree::Root.new(File.join(path, 'pairtree_root'), args)
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              private
         | 
| 75 | 
            +
              def self.pairtree_version_filename(version)
         | 
| 76 | 
            +
                "pairtree_version#{version.to_s.gsub(/\./,'_')}"
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
| @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            module Pairtree
         | 
| 3 | 
            +
              class Identifier
         | 
| 4 | 
            +
                ENCODE_REGEX = Regexp.compile("[\"*+,<=>?\\\\^|]|[^\x21-\x7e]", nil)
         | 
| 5 | 
            +
                DECODE_REGEX = Regexp.compile("\\^(..)", nil)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                ##
         | 
| 8 | 
            +
                # Encode special characters within an identifier
         | 
| 9 | 
            +
                # @param [String] id The identifier
         | 
| 10 | 
            +
                def self.encode id
         | 
| 11 | 
            +
                  id.gsub(ENCODE_REGEX) { |c| char2hex(c) }.tr('/:.', '=+,')
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                ##
         | 
| 15 | 
            +
                # Decode special characters within an identifier
         | 
| 16 | 
            +
                # @param [String] id The identifier
         | 
| 17 | 
            +
                def self.decode id
         | 
| 18 | 
            +
                  input = id.tr('=+,', '/:.').bytes.to_a
         | 
| 19 | 
            +
                  intermediate = []
         | 
| 20 | 
            +
                  while input.first
         | 
| 21 | 
            +
                    if input.first == 94
         | 
| 22 | 
            +
                      h = []
         | 
| 23 | 
            +
                      input.shift
         | 
| 24 | 
            +
                      h << input.shift
         | 
| 25 | 
            +
                      h << input.shift
         | 
| 26 | 
            +
                      intermediate << h.pack('c*').hex
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      intermediate << input.shift
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  result = intermediate.pack('c*')
         | 
| 32 | 
            +
                  if result.respond_to? :force_encoding
         | 
| 33 | 
            +
                    result.force_encoding('UTF-8')
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  result
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                ##
         | 
| 39 | 
            +
                # Convert a character to its pairtree hexidecimal representation
         | 
| 40 | 
            +
                # @param [Char] c The character to convert
         | 
| 41 | 
            +
                def self.char2hex c
         | 
| 42 | 
            +
                  c.unpack('H*')[0].scan(/../).map { |x| "^#{x}"}.join('')
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                ##
         | 
| 46 | 
            +
                # Convert a pairtree hexidecimal string to its character representation
         | 
| 47 | 
            +
                # @param [String] h The hexidecimal string to convert
         | 
| 48 | 
            +
                def self.hex2char h
         | 
| 49 | 
            +
                   '' << h.delete('^').hex
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
            end
         | 
    
        data/lib/pairtree/obj.rb
    ADDED
    
    | @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            module Pairtree  
         | 
| 2 | 
            +
              class Obj < ::Dir
         | 
| 3 | 
            +
                
         | 
| 4 | 
            +
                FILE_METHODS = [:atime, :open, :read, :file?, :directory?, :exist?, :exists?, :file?, :ftype, :lstat, 
         | 
| 5 | 
            +
                  :mtime, :readable?, :size, :stat, :truncate, :writable?, :zero?]
         | 
| 6 | 
            +
                FILE_METHODS.each do |file_method|
         | 
| 7 | 
            +
                  define_method file_method do |fname,*args,&block|
         | 
| 8 | 
            +
                    File.send(file_method, File.join(self.path, fname), *args, &block)
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def delete *args
         | 
| 13 | 
            +
                  File.delete(*(prepend_filenames(args)))
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                alias_method :unlink, :delete
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def link *args
         | 
| 18 | 
            +
                  File.link(*(prepend_filenames(args)))
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def rename *args
         | 
| 22 | 
            +
                  File.rename(*(prepend_filenames(args)))
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                def utime atime, mtime, *args
         | 
| 26 | 
            +
                  File.utime(atime, mtime, *(prepend_filenames(args)))
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                
         | 
| 29 | 
            +
                def entries
         | 
| 30 | 
            +
                  super - ['.','..']
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                def each &block
         | 
| 34 | 
            +
                  super { |entry| yield(entry) unless entry =~ /^\.{1,2}$/ }
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                def glob(string, flags = 0)
         | 
| 38 | 
            +
                  result = Dir.glob(File.join(self.path, string), flags) - ['.','..']
         | 
| 39 | 
            +
                  result.collect { |f| f.sub(%r{^#{self.path}/},'') }
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
                
         | 
| 42 | 
            +
                def [](string)
         | 
| 43 | 
            +
                  glob(string, 0)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                private
         | 
| 47 | 
            +
                def prepend_filenames(files)
         | 
| 48 | 
            +
                  files.collect { |fname| File.join(self.path, fname) }
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            module Pairtree
         | 
| 2 | 
            +
              class Path
         | 
| 3 | 
            +
                @@leaf_proc = lambda { |id| id }
         | 
| 4 | 
            +
                
         | 
| 5 | 
            +
                def self.set_leaf value = nil, &block
         | 
| 6 | 
            +
                  if value.nil?
         | 
| 7 | 
            +
                    @@leaf_proc = block
         | 
| 8 | 
            +
                  else
         | 
| 9 | 
            +
                    if value.is_a?(Proc)
         | 
| 10 | 
            +
                      @@leaf_proc = value
         | 
| 11 | 
            +
                    else
         | 
| 12 | 
            +
                      @@leaf_proc = lambda { |id| value }
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                def self.leaf id
         | 
| 18 | 
            +
                  if @@leaf_proc
         | 
| 19 | 
            +
                    Pairtree::Identifier.encode(@@leaf_proc.call(id))
         | 
| 20 | 
            +
                  else
         | 
| 21 | 
            +
                    ''
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                def self.id_to_path id
         | 
| 26 | 
            +
                  path = File.join(Pairtree::Identifier.encode(id).scan(/..?/),self.leaf(id))
         | 
| 27 | 
            +
                  path.sub(%r{#{File::SEPARATOR}+$},'')
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.path_to_id ppath
         | 
| 31 | 
            +
                  parts = ppath.split(File::SEPARATOR)
         | 
| 32 | 
            +
                  parts.pop if @@leaf_proc and parts.last.length > Root::SHORTY_LENGTH
         | 
| 33 | 
            +
                  Pairtree::Identifier.decode(parts.join)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                
         | 
| 36 | 
            +
                def self.remove! path
         | 
| 37 | 
            +
                  FileUtils.remove_dir(path, true)
         | 
| 38 | 
            +
                  parts = path.split(File::SEPARATOR)
         | 
| 39 | 
            +
                  parts.pop
         | 
| 40 | 
            +
                  while parts.length > 0 and parts.last != 'pairtree_root'
         | 
| 41 | 
            +
                    begin
         | 
| 42 | 
            +
                      FileUtils.rmdir(parts.join(File::SEPARATOR))
         | 
| 43 | 
            +
                      parts.pop
         | 
| 44 | 
            +
                    rescue SystemCallError
         | 
| 45 | 
            +
                      break
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,103 @@ | |
| 1 | 
            +
            require 'fileutils'
         | 
| 2 | 
            +
            module Pairtree
         | 
| 3 | 
            +
              class Root
         | 
| 4 | 
            +
                SHORTY_LENGTH = 2
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                attr_reader :root, :prefix
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                ##
         | 
| 9 | 
            +
                # @param [String] root The pairtree_root directory within the pairtree home
         | 
| 10 | 
            +
                # @param [Hash] args Pairtree options
         | 
| 11 | 
            +
                # @option args [String] :prefix (nil) the identifier prefix used throughout the pairtree
         | 
| 12 | 
            +
                # @option args [String] :version (Pairtree::SPEC_VERSION) the version of the pairtree spec that this tree conforms to
         | 
| 13 | 
            +
                def initialize root, args = {}
         | 
| 14 | 
            +
                  @root = root
         | 
| 15 | 
            +
                  
         | 
| 16 | 
            +
                  @shorty_length = args.delete(:shorty_length) || SHORTY_LENGTH
         | 
| 17 | 
            +
                  @prefix = args.delete(:prefix) || ''
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  @options = args
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                ##
         | 
| 23 | 
            +
                # Get a list of valid existing identifiers within the pairtree
         | 
| 24 | 
            +
                # @return [Array]
         | 
| 25 | 
            +
                def list          
         | 
| 26 | 
            +
                  objects = []
         | 
| 27 | 
            +
                  return [] unless File.directory? @root
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  Dir.chdir(@root) do
         | 
| 30 | 
            +
                    possibles = Dir['**/?'] + Dir['**/??']
         | 
| 31 | 
            +
                    possibles.each { |path|
         | 
| 32 | 
            +
                      contents = Dir.entries(path).reject { |x| x =~ /^\./ }
         | 
| 33 | 
            +
                      objects << path unless contents.all? { |f| f.length <= @shorty_length and File.directory?(File.join(path, f)) }
         | 
| 34 | 
            +
                    }
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  objects.map { |x| @prefix + Pairtree::Path.path_to_id(x) }
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                ##
         | 
| 40 | 
            +
                # Get the path containing the pairtree_root
         | 
| 41 | 
            +
                # @return [String]
         | 
| 42 | 
            +
                def path
         | 
| 43 | 
            +
                  File.dirname(root)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                ##
         | 
| 47 | 
            +
                # Get the full path for a given identifier (whether it exists or not)
         | 
| 48 | 
            +
                # @param [String] id The full, prefixed identifier
         | 
| 49 | 
            +
                # @return [String]
         | 
| 50 | 
            +
                def path_for id
         | 
| 51 | 
            +
                  unless id.start_with? @prefix
         | 
| 52 | 
            +
                    raise IdentifierError, "Identifier must start with #{@prefix}"
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                  path_id = id[@prefix.length..-1]
         | 
| 55 | 
            +
                  File.join(@root, Pairtree::Path.id_to_path(path_id))
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                ##
         | 
| 59 | 
            +
                # Determine if a given identifier exists within the pairtree
         | 
| 60 | 
            +
                # @param [String] id The full, prefixed identifier
         | 
| 61 | 
            +
                # @return [Boolean]
         | 
| 62 | 
            +
                def exists? id
         | 
| 63 | 
            +
                  File.directory?(path_for(id))
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
                
         | 
| 66 | 
            +
                ##
         | 
| 67 | 
            +
                # Get an existing ppath
         | 
| 68 | 
            +
                # @param [String] id The full, prefixed identifier
         | 
| 69 | 
            +
                # @return [Pairtree::Obj] The object encapsulating the identifier's ppath
         | 
| 70 | 
            +
                def get id
         | 
| 71 | 
            +
                  Pairtree::Obj.new path_for(id)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
                alias_method :[], :get
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                ##
         | 
| 76 | 
            +
                # Create a new ppath
         | 
| 77 | 
            +
                # @param [String] id The full, prefixed identifier
         | 
| 78 | 
            +
                # @return [Pairtree::Obj] The object encapsulating the newly created ppath
         | 
| 79 | 
            +
                def mk id
         | 
| 80 | 
            +
                  FileUtils.mkdir_p path_for(id)
         | 
| 81 | 
            +
                  get(id)
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
                
         | 
| 84 | 
            +
                ##
         | 
| 85 | 
            +
                # Delete a ppath
         | 
| 86 | 
            +
                # @param [String] id The full, prefixed identifier
         | 
| 87 | 
            +
                # @return [Boolean]
         | 
| 88 | 
            +
                def purge! id
         | 
| 89 | 
            +
                  if exists?(id)
         | 
| 90 | 
            +
                    Pairtree::Path.remove!(path_for(id))
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                  not exists?(id)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                ##
         | 
| 96 | 
            +
                # Get the version of the pairtree spec that this pairtree conforms to
         | 
| 97 | 
            +
                # @return [String]
         | 
| 98 | 
            +
                def pairtree_version
         | 
| 99 | 
            +
                  @options[:version]
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
                
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
            end
         | 
    
        data/lib/tasks/rdoc.rake
    ADDED
    
    | @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            desc "Generate RDoc"
         | 
| 2 | 
            +
            task :doc => ['doc:generate']
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            namespace :doc do
         | 
| 5 | 
            +
              project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
         | 
| 6 | 
            +
              doc_destination = File.join(project_root, 'rdoc')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              begin
         | 
| 9 | 
            +
                require 'yard'
         | 
| 10 | 
            +
                require 'yard/rake/yardoc_task'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                YARD::Rake::YardocTask.new(:generate) do |yt|
         | 
| 13 | 
            +
                  yt.files   =  Dir.glob(File.join(project_root, 'lib', '*.rb')) + 
         | 
| 14 | 
            +
                                Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
         | 
| 15 | 
            +
                               [ File.join(project_root, 'README.rdoc') ] +
         | 
| 16 | 
            +
                               [ File.join(project_root, 'LICENSE') ]
         | 
| 17 | 
            +
                               
         | 
| 18 | 
            +
                  yt.options = ['--output-dir', doc_destination, '--readme', 'README.rdoc']
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              rescue LoadError
         | 
| 21 | 
            +
                desc "Generate YARD Documentation"
         | 
| 22 | 
            +
                task :generate do
         | 
| 23 | 
            +
                  abort "Please install the YARD gem to generate rdoc."
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              desc "Remove generated documenation"
         | 
| 28 | 
            +
              task :clean do
         | 
| 29 | 
            +
                rm_r doc_destination if File.exists?(doc_destination)
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            end
         | 
    
        data/pairtree.gemspec
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            Gem::Specification.new do |s|
         | 
| 2 | 
            +
              s.name             = %q{rpairtree}
         | 
| 3 | 
            +
              s.summary          = %q{Ruby Pairtree implementation, forked from pairtree which is abandoned.}
         | 
| 4 | 
            +
              s.version          = "0.2.0"
         | 
| 5 | 
            +
              s.homepage         = %q{http://github.com/mlibrary/pairtree}
         | 
| 6 | 
            +
              s.licenses         = ["Apache2"]
         | 
| 7 | 
            +
              s.authors          = ["Chris Beer, Bryan Hockey, Michael Slone"]
         | 
| 8 | 
            +
              s.date             = %q{2015-05-05}
         | 
| 9 | 
            +
              s.files            = `git ls-files`.split("\n")
         | 
| 10 | 
            +
              s.test_files       = `git ls-files -- {test,spec,features}/*`.split("\n")
         | 
| 11 | 
            +
              s.extra_rdoc_files = ["LICENSE.txt", "README.md"]
         | 
| 12 | 
            +
              s.executables      = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 13 | 
            +
              s.require_paths    = ["lib"]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
              s.add_development_dependency "bundler"
         | 
| 17 | 
            +
              s.add_development_dependency "rspec", ">= 2.0"
         | 
| 18 | 
            +
              s.add_development_dependency "yard"
         | 
| 19 | 
            +
              s.add_development_dependency "rake"
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            require 'spec_helper'
         | 
| 3 | 
            +
            require 'pairtree'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe "Pairtree encoding" do
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def roundtrip(id, expected_encoded=nil, expected_path=nil)
         | 
| 8 | 
            +
                encoded = Pairtree::Identifier.encode(id)
         | 
| 9 | 
            +
                unless expected_encoded.nil?
         | 
| 10 | 
            +
                  expect(encoded).to eql(expected_encoded)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
                unless expected_path.nil?
         | 
| 13 | 
            +
                  path = Pairtree::Path.id_to_path(id)
         | 
| 14 | 
            +
                  expect(path).to eql(expected_path)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                str = Pairtree::Identifier.decode(encoded)
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                if str.respond_to? :force_encoding
         | 
| 19 | 
            +
                  str.force_encoding("UTF-8")
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                expect(str).to eql(id)
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
              
         | 
| 25 | 
            +
              it "should handle a" do
         | 
| 26 | 
            +
                roundtrip('a', 'a', 'a/a')
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              it "should handle ab" do
         | 
| 30 | 
            +
                roundtrip('ab', 'ab', 'ab/ab')
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              it "should handle abc" do
         | 
| 34 | 
            +
                roundtrip('abc', 'abc', 'ab/c/abc')
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              it "should handle abcd" do
         | 
| 38 | 
            +
                roundtrip('abcd', 'abcd', 'ab/cd/abcd')
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              it "should handle space" do
         | 
| 42 | 
            +
                roundtrip('hello world', 'hello^20world', 'he/ll/o^/20/wo/rl/d/hello^20world')
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              it "should handle slash" do
         | 
| 46 | 
            +
                roundtrip("/","=",'=/=')
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              it "should handle urn" do
         | 
| 50 | 
            +
                roundtrip('http://n2t.info/urn:nbn:se:kb:repos-1','http+==n2t,info=urn+nbn+se+kb+repos-1','ht/tp/+=/=n/2t/,i/nf/o=/ur/n+/nb/n+/se/+k/b+/re/po/s-/1/http+==n2t,info=urn+nbn+se+kb+repos-1')
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
              
         | 
| 53 | 
            +
              it "should handle wtf" do
         | 
| 54 | 
            +
                roundtrip('what-the-*@?#!^!?', "what-the-^2a@^3f#!^5e!^3f", "wh/at/-t/he/-^/2a/@^/3f/#!/^5/e!/^3/f/what-the-^2a@^3f#!^5e!^3f")
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              it "should handle special characters" do
         | 
| 58 | 
            +
                roundtrip('\\"*+,<=>?^|', "^5c^22^2a^2b^2c^3c^3d^3e^3f^5e^7c")
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
              
         | 
| 61 | 
            +
              it "should roundtrip hardcore Unicode" do
         | 
| 62 | 
            +
                roundtrip(%{
         | 
| 63 | 
            +
                   1. Euro Symbol: €.
         | 
| 64 | 
            +
                   2. Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
         | 
| 65 | 
            +
                   3. Íslenska / Icelandic: Ég get etið gler án þess að meiða mig.
         | 
| 66 | 
            +
                   4. Polish: Mogę jeść szkło, i mi nie szkodzi.
         | 
| 67 | 
            +
                   5. Romanian: Pot să mănânc sticlă și ea nu mă rănește.
         | 
| 68 | 
            +
                   6. Ukrainian: Я можу їсти шкло, й воно мені не пошкодить.
         | 
| 69 | 
            +
                   7. Armenian: Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ։
         | 
| 70 | 
            +
                   8. Georgian: მინას ვჭამ და არა მტკივა.
         | 
| 71 | 
            +
                   9. Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.
         | 
| 72 | 
            +
                  10. Hebrew(2): אני יכול לאכול זכוכית וזה לא מזיק לי.
         | 
| 73 | 
            +
                  11. Yiddish(2): איך קען עסן גלאָז און עס טוט מיר נישט װײ.
         | 
| 74 | 
            +
                  12. Arabic(2): أنا قادر على أكل الزجاج و هذا لا يؤلمني.
         | 
| 75 | 
            +
                  13. Japanese: 私はガラスを食べられます。それは私を傷つけません。
         | 
| 76 | 
            +
                  14. Thai: ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ "
         | 
| 77 | 
            +
                })
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
              
         | 
| 80 | 
            +
              it "should roundtrip French" do
         | 
| 81 | 
            +
                roundtrip('Années de Pèlerinage', 'Ann^c3^a9es^20de^20P^c3^a8lerinage', 'An/n^/c3/^a/9e/s^/20/de/^2/0P/^c/3^/a8/le/ri/na/ge/Ann^c3^a9es^20de^20P^c3^a8lerinage')
         | 
| 82 | 
            +
                roundtrip(%{
         | 
| 83 | 
            +
                  Années de Pèlerinage (Years of Pilgrimage) (S.160, S.161,
         | 
| 84 | 
            +
                  S.163) is a set of three suites by Franz Liszt for solo piano. Liszt's
         | 
| 85 | 
            +
                  complete musical style is evident in this masterwork, which ranges from
         | 
| 86 | 
            +
                  virtuosic fireworks to sincerely moving emotional statements. His musical
         | 
| 87 | 
            +
                  maturity can be seen evolving through his experience and travel. The
         | 
| 88 | 
            +
                  third volume is especially notable as an example of his later style: it
         | 
| 89 | 
            +
                  was composed well after the first two volumes and often displays less
         | 
| 90 | 
            +
                  showy virtuosity and more harmonic experimentation.
         | 
| 91 | 
            +
                })
         | 
| 92 | 
            +
             end
         | 
| 93 | 
            +
              
         | 
| 94 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            +
            require 'pairtree'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Pairtree::Obj" do
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              before(:all) do
         | 
| 7 | 
            +
                @base_path = File.join(File.dirname(__FILE__), "../test_data/working")
         | 
| 8 | 
            +
                Dir.chdir(File.join(File.dirname(__FILE__), "../test_data")) do
         | 
| 9 | 
            +
                  FileUtils.cp_r('fixtures/pairtree_root_spec', './working')
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
                @root = Pairtree.at(@base_path)
         | 
| 12 | 
            +
                @obj = @root.get('pfx:abc123def')
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
              
         | 
| 15 | 
            +
              after(:all) do
         | 
| 16 | 
            +
                FileUtils.rm_rf(@base_path)
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
              it "should read a file" do
         | 
| 20 | 
            +
                expect(@obj.read('content.xml')).to eql('<content/>')
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              it "should have entries" do
         | 
| 24 | 
            +
                expect(@obj.entries).to eql(['content.xml'])
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
              
         | 
| 27 | 
            +
              it "should glob" do
         | 
| 28 | 
            +
                expect(@obj['*.xml']).to eql(['content.xml'])
         | 
| 29 | 
            +
                expect(@obj['*.txt']).to eql([])
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
              
         | 
| 32 | 
            +
              it "should be enumerable" do
         | 
| 33 | 
            +
                block_body = double('block_body')
         | 
| 34 | 
            +
                expect(block_body).to receive(:yielded).with('content.xml')
         | 
| 35 | 
            +
                @obj.each { |file| block_body.yielded(file) }
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
              
         | 
| 38 | 
            +
              describe "Call a bunch of File methods" do
         | 
| 39 | 
            +
                before(:each) do
         | 
| 40 | 
            +
                  @target = File.join(@base_path, 'pairtree_root/ab/c1/23/de/f/abc123def/content.xml')
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
                
         | 
| 43 | 
            +
                it "should open a file" do
         | 
| 44 | 
            +
                  expect(File).to receive(:open).with(@target,'r')
         | 
| 45 | 
            +
                  @obj.open('content.xml','r')
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                it "should call delete" do
         | 
| 49 | 
            +
                  expect(File).to receive(:delete).with(@target)
         | 
| 50 | 
            +
                  @obj.delete('content.xml')
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                it "should call link" do
         | 
| 54 | 
            +
                  expect(File).to receive(:link).with(@target,@target + '.2')
         | 
| 55 | 
            +
                  @obj.link('content.xml','content.xml.2')
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it "should call rename" do
         | 
| 59 | 
            +
                  expect(File).to receive(:rename).with(@target,@target + '.new')
         | 
| 60 | 
            +
                  @obj.rename('content.xml','content.xml.new')
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                it "should call utime" do
         | 
| 64 | 
            +
                  expect(File).to receive(:utime).with(0,1,@target)
         | 
| 65 | 
            +
                  @obj.utime(0,1,'content.xml')
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
              
         | 
| 69 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            +
            require 'pairtree'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Pairtree::Path" do
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              after(:each) do
         | 
| 7 | 
            +
                Pairtree::Path.set_leaf { |id| id }
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
              
         | 
| 10 | 
            +
              it "should generate an encoded id as the leaf path by default" do
         | 
| 11 | 
            +
                expect(Pairtree::Path.leaf('abc/def')).to eql("abc=def")
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              it "should accept a nil override" do
         | 
| 15 | 
            +
                Pairtree::Path.set_leaf nil
         | 
| 16 | 
            +
                expect(Pairtree::Path.leaf('abc/def')).to eql("")
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
              it "should accept a scalar override" do
         | 
| 20 | 
            +
                Pairtree::Path.set_leaf 'obj'
         | 
| 21 | 
            +
                expect(Pairtree::Path.leaf('abc/def')).to eql("obj")
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
              
         | 
| 24 | 
            +
              it "should accept a Proc override" do
         | 
| 25 | 
            +
                lp = Proc.new { |id| id.reverse }
         | 
| 26 | 
            +
                Pairtree::Path.set_leaf(lp)
         | 
| 27 | 
            +
                expect(Pairtree::Path.leaf('abc/def')).to eql("fed=cba")
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
              it "should accept a block override" do
         | 
| 31 | 
            +
                Pairtree::Path.set_leaf { |id| id.reverse }
         | 
| 32 | 
            +
                expect(Pairtree::Path.leaf('abc/def')).to eql("fed=cba")
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
              
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,63 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            +
            require 'pairtree'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Pairtree::Root" do
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              before(:all) do
         | 
| 7 | 
            +
                @base_path = File.join(File.dirname(__FILE__), "../test_data/working")
         | 
| 8 | 
            +
                Dir.chdir(File.join(File.dirname(__FILE__), "../test_data")) do
         | 
| 9 | 
            +
                  FileUtils.cp_r('fixtures/pairtree_root_spec', './working')
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
                @root = Pairtree.at(@base_path)
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              
         | 
| 14 | 
            +
              after(:all) do
         | 
| 15 | 
            +
                FileUtils.rm_rf(@base_path)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
              
         | 
| 18 | 
            +
              it "should have the correct prefix" do
         | 
| 19 | 
            +
                expect(@root.prefix).to eql('pfx:')
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
              
         | 
| 22 | 
            +
              it "should be in the correct location" do
         | 
| 23 | 
            +
                expect(File.expand_path(@root.path)).to eql(File.expand_path(@base_path))
         | 
| 24 | 
            +
                expect(File.expand_path(@root.root)).to eql(File.expand_path(File.join(@base_path, "pairtree_root")))
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
              
         | 
| 27 | 
            +
              it "should list identifiers" do
         | 
| 28 | 
            +
                expect(@root.list).to eql(['pfx:abc123def'])
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
              
         | 
| 31 | 
            +
              it "should verify whether an identifier exists" do
         | 
| 32 | 
            +
                expect(@root.exists?('pfx:abc123def')).to be true
         | 
| 33 | 
            +
                expect(@root.exists?('pfx:abc123jkl')).to be false
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
              
         | 
| 36 | 
            +
              it "should raise an exception if an invalid prefix is used" do
         | 
| 37 | 
            +
                expect { @root.exists?('xfp:abc123def') }.to raise_error(Pairtree::IdentifierError)
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
              
         | 
| 40 | 
            +
              it "should get a ppath for a valid ID" do
         | 
| 41 | 
            +
                obj = @root.get 'pfx:abc123def'
         | 
| 42 | 
            +
                expect(obj.class).to eql(Pairtree::Obj)
         | 
| 43 | 
            +
                expect(File.expand_path(obj.path)).to eql(File.expand_path(File.join(@base_path, "pairtree_root/ab/c1/23/de/f/abc123def/")))
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
              
         | 
| 46 | 
            +
              it "should raise an exception when attempting to get a ppath for an invalid ID" do
         | 
| 47 | 
            +
                expect { @root.get 'pfx:abc123jkl' }.to raise_error(Errno::ENOENT)
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
              
         | 
| 50 | 
            +
              it "should create a new ppath" do
         | 
| 51 | 
            +
                obj = @root.mk 'pfx:abc123jkl'
         | 
| 52 | 
            +
                expect(obj.class).to eql(Pairtree::Obj)
         | 
| 53 | 
            +
                expect(File.expand_path(obj.path)).to eql(File.expand_path(File.join(@base_path, "pairtree_root/ab/c1/23/jk/l/abc123jkl/")))
         | 
| 54 | 
            +
                expect(@root.exists?('pfx:abc123jkl')).to be true
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
              
         | 
| 57 | 
            +
              it "should delete a ppath" do
         | 
| 58 | 
            +
                expect(@root.exists?('pfx:abc123jkl')).to be true
         | 
| 59 | 
            +
                @root.purge!('pfx:abc123jkl')
         | 
| 60 | 
            +
                expect(@root.exists?('pfx:abc123jkl')).to be false
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
              
         | 
| 63 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
         | 
| 2 | 
            +
            require 'pairtree'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Pairtree" do
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              before(:all) do
         | 
| 7 | 
            +
                @base_path = File.join(File.dirname(__FILE__), "../test_data/working")
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
              
         | 
| 10 | 
            +
              it "should raise an error if a non-existent is specified without :create" do
         | 
| 11 | 
            +
                expect { Pairtree.at(@base_path) }.to raise_error(Pairtree::PathError)
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              describe "new pairtree" do
         | 
| 15 | 
            +
                after(:all) do
         | 
| 16 | 
            +
                  FileUtils.rm_rf(@base_path)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                it "should create a new pairtree" do
         | 
| 20 | 
            +
                  prefix = 'my_prefix:'
         | 
| 21 | 
            +
                  pt = Pairtree.at(@base_path, :prefix => prefix, :create => true)
         | 
| 22 | 
            +
                  expect(pt.prefix).to eql(prefix)
         | 
| 23 | 
            +
                  expect(File.read(File.join(@base_path,'pairtree_prefix'))).to eql(prefix)
         | 
| 24 | 
            +
                  expect(pt.root).to eql(File.join(@base_path, 'pairtree_root'))
         | 
| 25 | 
            +
                  expect(pt.pairtree_version).to eql(Pairtree::SPEC_VERSION)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
              describe "existing pairtree" do
         | 
| 31 | 
            +
                before(:all) do
         | 
| 32 | 
            +
                  Dir.chdir(File.join(File.dirname(__FILE__), "../test_data")) do
         | 
| 33 | 
            +
                    FileUtils.cp_r('fixtures/pairtree_root_spec', './working')
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                after(:all) do
         | 
| 38 | 
            +
                  FileUtils.rm_rf(@base_path)
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                it "should raise an error if a regular file is specified as a root" do
         | 
| 42 | 
            +
                  expect { Pairtree.at(File.join(@base_path, "pairtree_prefix"), :create => true) }.to raise_error(Pairtree::PathError)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                it "should read the prefix if none is specified" do
         | 
| 46 | 
            +
                  expect(Pairtree.at(@base_path).prefix).to eql(File.read(File.join(@base_path, 'pairtree_prefix')))
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                it "should not complain if the given prefix matches the saved prefix" do
         | 
| 50 | 
            +
                  expect(Pairtree.at(@base_path, :prefix => 'pfx:').prefix).to eql(File.read(File.join(@base_path, 'pairtree_prefix')))
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                
         | 
| 53 | 
            +
                it "should raise an error if the given prefix does not match the saved prefix" do
         | 
| 54 | 
            +
                  expect { Pairtree.at(@base_path, :prefix => 'wrong-prefix:') }.to raise_error(Pairtree::IdentifierError)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                
         | 
| 57 | 
            +
                it "should not complain if the given version matches the saved version" do
         | 
| 58 | 
            +
                  expect(Pairtree.at(@base_path, :version => Pairtree::SPEC_VERSION).pairtree_version).to eql(Pairtree::SPEC_VERSION)
         | 
| 59 | 
            +
                  expect(Pairtree.at(@base_path, :version => Pairtree::SPEC_VERSION, :create => true).pairtree_version).to eql(Pairtree::SPEC_VERSION)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                it "should raise an error if the given version does not match the saved version" do
         | 
| 63 | 
            +
                  expect { Pairtree.at(@base_path, :version => 0.2) }.to raise_error(Pairtree::VersionMismatch)
         | 
| 64 | 
            +
                  expect { Pairtree.at(@base_path, :version => 0.2, :create => true) }.to raise_error(Pairtree::VersionMismatch)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
                
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
              
         | 
| 69 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 2 | 
            +
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'bundler/setup'
         | 
| 5 | 
            +
            require 'rspec'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.9/
         | 
| 8 | 
            +
              require 'simplecov'
         | 
| 9 | 
            +
              SimpleCov.start
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            require 'pairtree'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            RSpec.configure do |config|
         | 
| 15 | 
            +
              
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            pfx:
         | 
    
        data/spec/test_data/fixtures/pairtree_root_spec/pairtree_root/ab/c1/23/de/f/abc123def/content.xml
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            <content/>
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            This directory conforms to Pairtree Version 0.1. Updated spec: http://www.cdlib.org/inside/diglib/pairtree/pairtreespec.html
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,125 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: rpairtree
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Chris Beer, Bryan Hockey, Michael Slone
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2015-05-05 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: bundler
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :development
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rspec
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '2.0'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '2.0'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: yard
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ">="
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0'
         | 
| 48 | 
            +
              type: :development
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ">="
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: rake
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ">="
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '0'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '0'
         | 
| 69 | 
            +
            description: 
         | 
| 70 | 
            +
            email: 
         | 
| 71 | 
            +
            executables: []
         | 
| 72 | 
            +
            extensions: []
         | 
| 73 | 
            +
            extra_rdoc_files:
         | 
| 74 | 
            +
            - LICENSE.txt
         | 
| 75 | 
            +
            - README.md
         | 
| 76 | 
            +
            files:
         | 
| 77 | 
            +
            - ".document"
         | 
| 78 | 
            +
            - ".gitignore"
         | 
| 79 | 
            +
            - ".travis.yml"
         | 
| 80 | 
            +
            - Gemfile
         | 
| 81 | 
            +
            - LICENSE.txt
         | 
| 82 | 
            +
            - README.md
         | 
| 83 | 
            +
            - Rakefile
         | 
| 84 | 
            +
            - lib/pairtree.rb
         | 
| 85 | 
            +
            - lib/pairtree/identifier.rb
         | 
| 86 | 
            +
            - lib/pairtree/obj.rb
         | 
| 87 | 
            +
            - lib/pairtree/path.rb
         | 
| 88 | 
            +
            - lib/pairtree/root.rb
         | 
| 89 | 
            +
            - lib/tasks/rdoc.rake
         | 
| 90 | 
            +
            - pairtree.gemspec
         | 
| 91 | 
            +
            - spec/pairtree/pairtree_encoding_spec.rb
         | 
| 92 | 
            +
            - spec/pairtree/pairtree_obj_spec.rb
         | 
| 93 | 
            +
            - spec/pairtree/pairtree_path_spec.rb
         | 
| 94 | 
            +
            - spec/pairtree/pairtree_root_spec.rb
         | 
| 95 | 
            +
            - spec/pairtree/pairtree_spec.rb
         | 
| 96 | 
            +
            - spec/spec_helper.rb
         | 
| 97 | 
            +
            - spec/test_data/fixtures/pairtree_root_spec/pairtree_prefix
         | 
| 98 | 
            +
            - spec/test_data/fixtures/pairtree_root_spec/pairtree_root/ab/c1/23/de/f/abc123def/content.xml
         | 
| 99 | 
            +
            - spec/test_data/fixtures/pairtree_root_spec/pairtree_version0_1
         | 
| 100 | 
            +
            homepage: http://github.com/mlibrary/pairtree
         | 
| 101 | 
            +
            licenses:
         | 
| 102 | 
            +
            - Apache2
         | 
| 103 | 
            +
            metadata: {}
         | 
| 104 | 
            +
            post_install_message: 
         | 
| 105 | 
            +
            rdoc_options: []
         | 
| 106 | 
            +
            require_paths:
         | 
| 107 | 
            +
            - lib
         | 
| 108 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 109 | 
            +
              requirements:
         | 
| 110 | 
            +
              - - ">="
         | 
| 111 | 
            +
                - !ruby/object:Gem::Version
         | 
| 112 | 
            +
                  version: '0'
         | 
| 113 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
              requirements:
         | 
| 115 | 
            +
              - - ">="
         | 
| 116 | 
            +
                - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                  version: '0'
         | 
| 118 | 
            +
            requirements: []
         | 
| 119 | 
            +
            rubyforge_project: 
         | 
| 120 | 
            +
            rubygems_version: 2.4.5
         | 
| 121 | 
            +
            signing_key: 
         | 
| 122 | 
            +
            specification_version: 4
         | 
| 123 | 
            +
            summary: Ruby Pairtree implementation, forked from pairtree which is abandoned.
         | 
| 124 | 
            +
            test_files: []
         | 
| 125 | 
            +
            has_rdoc: 
         |