Skip to content

Commit

Permalink
Merge pull request #5 from mlibrary/update-upstream
Browse files Browse the repository at this point in the history
Updates for current ruby
  • Loading branch information
aelkiss authored Mar 28, 2023
2 parents cd133a9 + 62de3ff commit 8667d00
Show file tree
Hide file tree
Showing 19 changed files with 330 additions and 298 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Run Tests

on: push

jobs:
test:
runs-on: ubuntu-latest
name: Ruby ${{ matrix.ruby }}
strategy:
matrix:
ruby: [2.7, 3.0, 3.1, 3.2]
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run linter for Ruby
run: bundle exec standardrb
- name: Run tests
run: bundle exec rspec
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ doc

# bundler
.bundle
vendor/

# jeweler generated
pkg
Expand Down Expand Up @@ -42,3 +43,5 @@ pkg
#*.swp
#
Gemfile.lock

.idea/
7 changes: 0 additions & 7 deletions .travis.yml

This file was deleted.

11 changes: 6 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
source "http://rubygems.org"
source "https://rubygems.org"

# Specify your gem's dependencies in test.gemspec
gemspec


gem 'rcov', :platform => :mri_18
gem 'simplecov', :platform => :mri_19
gem 'simplecov-rcov', :platform => :mri_19
group :development, :test do
gem "simplecov"
gem "standardrb"
gem "simplecov-lcov"
end
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
[![Tests](https://github.com/ruby-microservices/pairtree/actions/workflows/tests.yml/badge.svg)](https://github.com/ruby-microservices/pairtree/actions/workflows/tests.yml)
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)

# pairtree

Ruby implementation of the [Pairtree](https://confluence.ucop.edu/display/Curation/PairTree microservice specification from the California Digital Librar)
Ruby implementation of the [Pairtree](https://www.ietf.org/archive/id/draft-kunze-pairtree-01.txt) specification from the California Digital Library.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'pairtree'
```

And then execute:

# Usage
$ bundle

Or install it yourself as:

$ gem install pairtree

## Usage

```ruby
require 'pairtree'

# Initiate a tree
pairtree = Pairtree.at('./data', :prefix => 'pfx:', :create => true)

Expand All @@ -29,9 +50,7 @@ Ruby implementation of the [Pairtree](https://confluence.ucop.edu/display/Curati
# Delete a ppath and all its contents
pairtree.purge!('pfx:abc123def')
```

## Copyright

Copyright (c) 2010 Chris Beer. See LICENSE.txt for
further details.
## Copyright

Copyright (c) 2010 Chris Beer. See LICENSE.txt for further details.
40 changes: 16 additions & 24 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,45 +1,37 @@
require 'rubygems'
require 'bundler'
require "rubygems"
require "bundler"
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
warn e.message
warn "Run `bundle install` to install missing gems"
exit e.status_code
end

Bundler::GemHelper.install_tasks

require 'rake'
require 'rspec'
require 'rspec/core/rake_task'
require "rake"
require "rspec"
require "rspec/core/rake_task"

desc 'Default: run specs.'
task :default => :spec

RSpec::Core::RakeTask.new do |t|
if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.8/
t.rcov = true
t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
end
end
desc "Default: run specs."
task default: :spec

# Use yard to build docs
begin
require 'yard'
require 'yard/rake/yardoc_task'
project_root = File.expand_path(File.dirname(__FILE__))
doc_destination = File.join(project_root, 'doc')
require "yard"
require "yard/rake/yardoc_task"
project_root = __dir__
doc_destination = File.join(project_root, "doc")

YARD::Rake::YardocTask.new(:doc) do |yt|
yt.files = Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
[ File.join(project_root, 'README.md') ]
yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
yt.files = Dir.glob(File.join(project_root, "lib", "**", "*.rb")) +
[File.join(project_root, "README.md")]
yt.options = ["--output-dir", doc_destination, "--readme", "README.md"]
end
rescue LoadError
desc "Generate YARD Documentation"
task :doc do
abort "Please install the YARD gem to generate rdoc."
end
end

61 changes: 33 additions & 28 deletions lib/pairtree.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
require 'pairtree/identifier'
require 'pairtree/path'
require 'pairtree/obj'
require 'pairtree/root'
require "pairtree/identifier"
require "pairtree/path"
require "pairtree/obj"
require "pairtree/root"

require 'fileutils'
require "fileutils"

module Pairtree
class IdentifierError < Exception; end
class PathError < Exception; end
class VersionMismatch < Exception; end
class IdentifierError < RuntimeError; end

class PathError < RuntimeError; end

class VersionMismatch < RuntimeError; end

SPEC_VERSION = 0.1

##
# Instantiate a pairtree at a given path location
# @param [String] path The path in which the pairtree resides
Expand All @@ -20,34 +22,34 @@ class VersionMismatch < Exception; end
# @option args [String] :version (Pairtree::SPEC_VERSION) the version of the pairtree spec that this tree conforms to
# @option args [Boolean] :create (false) if true, create the pairtree and its directory structure if it doesn't already exist
def self.at path, args = {}
args = { :prefix => nil, :version => nil, :create => false }.merge(args)
args = {prefix: nil, version: nil, create: false}.merge(args)
args[:version] ||= SPEC_VERSION
args[:version] = args[:version].to_f
root_path = File.join(path, 'pairtree_root')
prefix_file = File.join(path, 'pairtree_prefix')

root_path = File.join(path, "pairtree_root")
prefix_file = File.join(path, "pairtree_prefix")
version_file = File.join(path, pairtree_version_filename(args[:version]))
existing_version_file = Dir[File.join(path, "pairtree_version*")].sort.last
existing_version_file = Dir[File.join(path, "pairtree_version*")].max

if args.delete(:create)
if File.exists?(path) and not File.directory?(path)
if File.exist?(path) && !File.directory?(path)
raise PathError, "#{path} exists, but is not a valid pairtree root"
end
FileUtils.mkdir_p(root_path)

unless File.exists? prefix_file
File.open(prefix_file, 'w') { |f| f.write(args[:prefix].to_s) }
unless File.exist? prefix_file
File.write(prefix_file, args[:prefix].to_s)
end

if existing_version_file
if existing_version_file != version_file
stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join('.').to_f
stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join(".").to_f
raise VersionMismatch, "Version #{args[:version]} specified, but #{stored_version} found."
end
else
args[:version] ||= SPEC_VERSION
version_file = File.join(path, pairtree_version_filename(args[:version]))
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} }
File.write(version_file, %(This directory conforms to Pairtree Version #{args[:version]}. Updated spec: http://www.cdlib.org/inside/diglib/pairtree/pairtreespec.html))
existing_version_file = version_file
end
else
Expand All @@ -57,22 +59,25 @@ def self.at path, args = {}
end

stored_prefix = File.read(prefix_file)
unless args[:prefix].nil? or args[:prefix].to_s == stored_prefix
unless args[:prefix].nil? || (args[:prefix].to_s == stored_prefix)
raise IdentifierError, "Specified prefix #{args[:prefix].inspect} does not match stored prefix #{stored_prefix.inspect}"
end
args[:prefix] = stored_prefix

stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join('.').to_f
stored_version = existing_version_file.scan(/([0-9]+)_([0-9]+)/).flatten.join(".").to_f
args[:version] ||= stored_version
unless args[:version] == stored_version
raise VersionMismatch, "Version #{args[:version]} specified, but #{stored_version} found."
end
Pairtree::Root.new(File.join(path, 'pairtree_root'), args)

Pairtree::Root.new(File.join(path, "pairtree_root"), args)
end

private
def self.pairtree_version_filename(version)
"pairtree_version#{version.to_s.gsub(/\./,'_')}"
class << self
private

def pairtree_version_filename(version)
"pairtree_version#{version.to_s.tr(".", "_")}"
end
end
end
25 changes: 21 additions & 4 deletions lib/pairtree/identifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,45 @@ class Identifier
# Encode special characters within an identifier
# @param [String] id The identifier
def self.encode id
id.gsub(ENCODE_REGEX) { |c| char2hex(c) }.tr('/:.', '=+,')
id.gsub(ENCODE_REGEX) { |c| char2hex(c) }.tr("/:.", "=+,")
end

##
# Decode special characters within an identifier
# @param [String] id The identifier
def self.decode id
id.tr('=+,', '/:.').gsub(DECODE_REGEX) { |h| hex2char(h) }
input = id.tr("=+,", "/:.").bytes.to_a
intermediate = []
while input.first
if input.first == 94
h = []
input.shift
h << input.shift
h << input.shift
intermediate << h.pack("c*").hex
else
intermediate << input.shift
end
end
result = intermediate.pack("c*")
if result.respond_to? :force_encoding
result.force_encoding("UTF-8")
end
result
end

##
# Convert a character to its pairtree hexidecimal representation
# @param [Char] c The character to convert
def self.char2hex c
c.unpack('H*')[0].scan(/../).map { |x| "^#{x}"}.join('')
c.unpack1("H*").scan(/../).map { |x| "^#{x}" }.join("")
end

##
# Convert a pairtree hexidecimal string to its character representation
# @param [String] h The hexidecimal string to convert
def self.hex2char h
'' << h.delete('^').hex
"" << h.delete("^").hex
end
end
end
Loading

0 comments on commit 8667d00

Please sign in to comment.