rpairtree 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
@@ -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/
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ script: bundle exec rake
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in test.gemspec
4
+ gemspec
5
+
6
+
7
+ gem 'rcov', :platform => :mri_18
8
+ gem 'simplecov', :platforms => [:mri_19, :mri_20]
9
+ gem 'simplecov-rcov', :platforms => [:mri_19, :mri_20]
@@ -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.
@@ -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
+
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+ 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: