pairtree 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +44 -0
- data/Gemfile +2 -11
- data/LICENSE.txt +12 -20
- data/README.textile +37 -0
- data/Rakefile +20 -45
- data/lib/pairtree.rb +72 -1
- data/lib/pairtree/identifier.rb +13 -0
- data/lib/pairtree/obj.rb +47 -0
- data/lib/pairtree/path.rb +41 -2
- data/lib/pairtree/root.rb +72 -26
- data/lib/tasks/rdoc.rake +32 -0
- data/pairtree.gemspec +24 -0
- data/spec/pairtree/pairtree_encoding_spec.rb +87 -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 +10 -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 +82 -100
- data/README.rdoc +0 -19
- data/VERSION +0 -1
- data/lib/pairtree/client.rb +0 -18
- data/test/helper.rb +0 -18
- data/test/test_pairtree.rb +0 -91
data/.gitignore
ADDED
@@ -0,0 +1,44 @@
|
|
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
|
data/Gemfile
CHANGED
@@ -1,13 +1,4 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
group :development do
|
9
|
-
gem "shoulda", ">= 0"
|
10
|
-
gem "bundler", "~> 1.0.0"
|
11
|
-
gem "jeweler", "~> 1.5.1"
|
12
|
-
gem "rcov", ">= 0"
|
13
|
-
end
|
3
|
+
gemspec
|
4
|
+
gem 'rcov', :platform => :mri_18
|
data/LICENSE.txt
CHANGED
@@ -1,20 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
the
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
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.textile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
h1. pairtree
|
2
|
+
|
3
|
+
Ruby implementation of the "Pairtree":https://confluence.ucop.edu/display/Curation/PairTree microservice specification from the California Digital Library
|
4
|
+
|
5
|
+
h2. Usage
|
6
|
+
|
7
|
+
<pre><code>
|
8
|
+
# Initiate a tree
|
9
|
+
pairtree = Pairtree.at('./data', :prefix => 'pfx:', :create => true)
|
10
|
+
|
11
|
+
# Create a ppath
|
12
|
+
obj = pairtree.mk('pfx:abc123def')
|
13
|
+
|
14
|
+
# Access an existing ppath
|
15
|
+
obj = pairtree['pfx:abc123def']
|
16
|
+
obj = pairtree.get('pfx:abc123def')
|
17
|
+
|
18
|
+
# ppaths are Dir instances with some File and Dir class methods mixed in
|
19
|
+
obj.read('content.xml')
|
20
|
+
=> "<content/>"
|
21
|
+
obj.open('my_file.txt','w') { |io| io.write("Write text to file") }
|
22
|
+
obj.entries
|
23
|
+
=> ["content.xml","my_file.txt"]
|
24
|
+
obj['*.xml']
|
25
|
+
=> ["content.xml"]
|
26
|
+
obj.each { |file| ... }
|
27
|
+
obj.unlink('my_file.txt')
|
28
|
+
|
29
|
+
# Delete a ppath and all its contents
|
30
|
+
pairtree.purge!('pfx:abc123def')
|
31
|
+
</code></pre>
|
32
|
+
|
33
|
+
h2. Copyright
|
34
|
+
|
35
|
+
Copyright (c) 2010 Chris Beer. See LICENSE.txt for
|
36
|
+
further details.
|
37
|
+
|
data/Rakefile
CHANGED
@@ -1,52 +1,27 @@
|
|
1
|
-
|
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
|
-
require 'rake'
|
1
|
+
Dir.glob('lib/tasks/*.rake').each { |r| import r }
|
11
2
|
|
12
|
-
require '
|
13
|
-
|
14
|
-
|
15
|
-
gem.name = "pairtree"
|
16
|
-
gem.homepage = "http://github.com/cbeer/pairtree"
|
17
|
-
gem.license = "MIT"
|
18
|
-
gem.summary = %Q{Ruby Pairtree implementation}
|
19
|
-
gem.email = "chris@cbeer.info"
|
20
|
-
gem.authors = ["Chris Beer"]
|
21
|
-
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
22
|
-
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
23
|
-
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
24
|
-
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
25
|
-
end
|
26
|
-
Jeweler::RubygemsDotOrgTasks.new
|
27
|
-
|
28
|
-
require 'rake/testtask'
|
29
|
-
Rake::TestTask.new(:test) do |test|
|
30
|
-
test.libs << 'lib' << 'test'
|
31
|
-
test.pattern = 'test/**/test_*.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake'
|
5
|
+
require 'rspec/core/rake_task'
|
34
6
|
|
7
|
+
begin
|
8
|
+
if RUBY_VERSION < "1.9"
|
35
9
|
require 'rcov/rcovtask'
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
10
|
+
desc "Generate code coverage"
|
11
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
12
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
13
|
+
t.rcov = true
|
14
|
+
t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
rescue
|
40
18
|
end
|
41
19
|
|
42
|
-
|
43
|
-
|
44
|
-
require 'rake/rdoctask'
|
45
|
-
Rake::RDocTask.new do |rdoc|
|
46
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
20
|
+
RSpec::Core::RakeTask.new(:spec)
|
47
21
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
task :clean do
|
23
|
+
puts 'Cleaning old coverage.data'
|
24
|
+
FileUtils.rm('coverage.data') if(File.exists? 'coverage.data')
|
52
25
|
end
|
26
|
+
|
27
|
+
task :default => [:rcov, :doc]
|
data/lib/pairtree.rb
CHANGED
@@ -2,6 +2,77 @@ require 'pairtree/identifier'
|
|
2
2
|
require 'pairtree/path'
|
3
3
|
require 'pairtree/obj'
|
4
4
|
require 'pairtree/root'
|
5
|
-
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
|
6
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
|
7
78
|
end
|
data/lib/pairtree/identifier.rb
CHANGED
@@ -2,18 +2,31 @@ module Pairtree
|
|
2
2
|
class Identifier
|
3
3
|
ENCODE_REGEX = Regexp.compile("[\"*+,<=>?\\\\^|]|[^\x21-\x7e]", nil, 'u')
|
4
4
|
DECODE_REGEX = Regexp.compile("\\^(..)", nil, 'u')
|
5
|
+
|
6
|
+
##
|
7
|
+
# Encode special characters within an identifier
|
8
|
+
# @param [String] id The identifier
|
5
9
|
def self.encode id
|
6
10
|
id.gsub(ENCODE_REGEX) { |c| char2hex(c) }.tr('/:.', '=+,')
|
7
11
|
end
|
8
12
|
|
13
|
+
##
|
14
|
+
# Decode special characters within an identifier
|
15
|
+
# @param [String] id The identifier
|
9
16
|
def self.decode id
|
10
17
|
id.tr('=+,', '/:.').gsub(DECODE_REGEX) { |h| hex2char(h) }
|
11
18
|
end
|
12
19
|
|
20
|
+
##
|
21
|
+
# Convert a character to its pairtree hexidecimal representation
|
22
|
+
# @param [Char] c The character to convert
|
13
23
|
def self.char2hex c
|
14
24
|
c.unpack('H*')[0].scan(/../).map { |x| "^#{x}"}
|
15
25
|
end
|
16
26
|
|
27
|
+
##
|
28
|
+
# Convert a pairtree hexidecimal string to its character representation
|
29
|
+
# @param [String] h The hexidecimal string to convert
|
17
30
|
def self.hex2char h
|
18
31
|
'' << h.delete('^').hex
|
19
32
|
end
|
data/lib/pairtree/obj.rb
CHANGED
@@ -1,5 +1,52 @@
|
|
1
1
|
module Pairtree
|
2
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
|
3
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
|
+
|
4
51
|
end
|
5
52
|
end
|
data/lib/pairtree/path.rb
CHANGED
@@ -1,11 +1,50 @@
|
|
1
1
|
module Pairtree
|
2
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
|
+
|
3
25
|
def self.id_to_path id
|
4
|
-
File.join(Pairtree::Identifier.encode(id).scan(/..?/))
|
26
|
+
path = File.join(Pairtree::Identifier.encode(id).scan(/..?/),self.leaf(id))
|
27
|
+
path.sub(%r{#{File::SEPARATOR}+$},'')
|
5
28
|
end
|
6
29
|
|
7
30
|
def self.path_to_id ppath
|
8
|
-
|
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
|
9
48
|
end
|
10
49
|
end
|
11
50
|
end
|
data/lib/pairtree/root.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
require 'find'
|
2
1
|
require 'fileutils'
|
3
2
|
module Pairtree
|
4
3
|
class Root
|
5
4
|
SHORTY_LENGTH = 2
|
6
5
|
|
7
|
-
attr_reader :root
|
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
|
8
13
|
def initialize root, args = {}
|
9
14
|
@root = root
|
10
15
|
|
@@ -14,44 +19,85 @@ module Pairtree
|
|
14
19
|
@options = args
|
15
20
|
end
|
16
21
|
|
22
|
+
##
|
23
|
+
# Get a list of valid existing identifiers within the pairtree
|
24
|
+
# @return [Array]
|
17
25
|
def list
|
18
26
|
objects = []
|
19
|
-
return [] unless
|
27
|
+
return [] unless File.directory? @root
|
20
28
|
|
21
29
|
Dir.chdir(@root) do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
+
}
|
27
35
|
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
36
|
objects.map { |x| @prefix + Pairtree::Path.path_to_id(x) }
|
32
37
|
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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))
|
39
56
|
end
|
40
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
|
41
70
|
def get id
|
42
|
-
|
43
|
-
path = File.join(@root, Pairtree::Path.id_to_path(id))
|
44
|
-
Pairtree::Obj.new path if File.directory? path
|
71
|
+
Pairtree::Obj.new path_for(id)
|
45
72
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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)
|
51
93
|
end
|
52
94
|
|
53
|
-
|
54
|
-
|
95
|
+
##
|
96
|
+
# Get the version of the pairtree spec that this pairtree conforms to
|
97
|
+
# @return [String]
|
98
|
+
def pairtree_version
|
99
|
+
@options[:version]
|
55
100
|
end
|
101
|
+
|
56
102
|
end
|
57
103
|
end
|