Skip to content

Commit

Permalink
Merge pull request #2 from mlibrary/ruby-3.x
Browse files Browse the repository at this point in the history
Support for ruby 3.x; use standardrb
  • Loading branch information
billdueber authored Mar 28, 2023
2 parents bd87706 + 180866d commit c971c6c
Show file tree
Hide file tree
Showing 18 changed files with 282 additions and 263 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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
- name: Report to Coveralls
uses: coverallsapp/[email protected]
with:
github-token: ${{ secrets.github_token }}
1 change: 1 addition & 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
9 changes: 5 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ source "https://rubygems.org"
# Specify your gem's dependencies in test.gemspec
gemspec


gem 'rcov', :platform => :mri_18
gem 'simplecov', :platforms => [:mri_19, :mri_20]
gem 'simplecov-rcov', :platforms => [:mri_19, :mri_20]
group :development, :test do
gem "simplecov"
gem "standardrb"
gem "simplecov-lcov"
end
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[![Tests](https://github.com/mlibrary/pairtree/actions/workflows/tests.yaml/badge.svg)](https://github.com/mlibrary/pairtree/actions/workflows/tests.yaml)
[![Coverage Status](https://coveralls.io/repos/github/mlibrary/pairtree/badge.svg?branch=main)](https://coveralls.io/github/mlibrary/pairtree?branch=main)
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)

# (r)pairtree

Ruby implementation of the [Pairtree](https://wiki.ucop.edu/display/Curation/PairTree) specification from the California Digital Library.
Expand Down
37 changes: 18 additions & 19 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
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
desc "Default: run specs."
task default: :spec

RSpec::Core::RakeTask.new do |t|
if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.8/
if ENV["COVERAGE"] && RUBY_VERSION =~ (/^1.8/)
t.rcov = true
t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
t.rcov_opts = ["--exclude", "spec", "--exclude", "gems"]
end
end

# 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
15 changes: 7 additions & 8 deletions lib/pairtree/identifier.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# encoding: utf-8
module Pairtree
class Identifier
ENCODE_REGEX = Regexp.compile("[\"*+,<=>?\\\\^|]|[^\x21-\x7e]", nil)
Expand All @@ -8,29 +7,29 @@ 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
input = id.tr('=+,', '/:.').bytes.to_a
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
intermediate << h.pack("c*").hex
else
intermediate << input.shift
end
end
result = intermediate.pack('c*')
result = intermediate.pack("c*")
if result.respond_to? :force_encoding
result.force_encoding('UTF-8')
result.force_encoding("UTF-8")
end
result
end
Expand All @@ -39,14 +38,14 @@ def self.decode id
# 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
41 changes: 20 additions & 21 deletions lib/pairtree/obj.rb
Original file line number Diff line number Diff line change
@@ -1,52 +1,51 @@
module Pairtree
module Pairtree
class Obj < ::Dir

FILE_METHODS = [:atime, :open, :read, :file?, :directory?, :exist?, :exists?, :file?, :ftype, :lstat,
FILE_METHODS = [:atime, :open, :read, :file?, :directory?, :exist?, :exists?, :file?, :ftype, :lstat,
:mtime, :readable?, :size, :stat, :truncate, :writable?, :zero?]
FILE_METHODS.each do |file_method|
define_method file_method do |fname,*args,&block|
File.send(file_method, File.join(self.path, fname), *args, &block)
define_method file_method do |fname, *args, &block|
File.send(file_method, File.join(path, fname), *args, &block)
end
end

def delete *args
File.delete(*(prepend_filenames(args)))
File.delete(*prepend_filenames(args))
end
alias_method :unlink, :delete

def link *args
File.link(*(prepend_filenames(args)))
File.link(*prepend_filenames(args))
end

def rename *args
File.rename(*(prepend_filenames(args)))
File.rename(*prepend_filenames(args))
end

def utime atime, mtime, *args
File.utime(atime, mtime, *(prepend_filenames(args)))
File.utime(atime, mtime, *prepend_filenames(args))
end

def entries
super - ['.','..']
super - [".", ".."]
end

def each &block
super { |entry| yield(entry) unless entry =~ /^\.{1,2}$/ }
super { |entry| yield(entry) unless /^\.{1,2}$/.match?(entry) }
end

def glob(string, flags = 0)
result = Dir.glob(File.join(self.path, string), flags) - ['.','..']
result.collect { |f| f.sub(%r{^#{self.path}/},'') }
result = Dir.glob(File.join(path, string), flags) - [".", ".."]
result.collect { |f| f.sub(%r{^#{path}/}, "") }
end

def [](string)
glob(string, 0)
end

private

def prepend_filenames(files)
files.collect { |fname| File.join(self.path, fname) }
files.collect { |fname| File.join(path, fname) }
end

end
end
Loading

0 comments on commit c971c6c

Please sign in to comment.