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:
|