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