Skip to content

Commit

Permalink
Massive, non-atomic refactor.
Browse files Browse the repository at this point in the history
* Change from Jeweler gem format to bundler gem format
* Change from Test::Unit to RSpec
* More object manipulation from Pairtree::Root (Root#purge!, Root#[], Root#exists?)
* Much more file manipulation from Pairtree::Obj (Dir and File methods invoked relative to ppath)
  • Loading branch information
mbklein committed Jun 9, 2011
1 parent b546de9 commit 10d0a34
Show file tree
Hide file tree
Showing 16 changed files with 319 additions and 255 deletions.
12 changes: 1 addition & 11 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
source "http://rubygems.org"
# Add dependencies required to use your gem here.
# Example:
# gem "activesupport", ">= 2.3.5"

# Add dependencies to develop your gem here.
# Include everything needed to run rake, tests, features, etc.
group :development do
gem "shoulda", ">= 0"
gem "bundler", "~> 1.0.0"
gem "jeweler", "~> 1.5.1"
gem "rcov", ">= 0"
end
gemspec
37 changes: 27 additions & 10 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
= pairtree

Ruby Pairtre implementation
Ruby Pairtree implementation

== Contributing to pairtree
== Usage

* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
* Fork the project
* Start a feature/bugfix branch
* Commit and push until you are happy with your contribution
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

# Initiate a tree
pt = Pairtree::Client.new('./data', :prefix => 'pfx:')
root = pt.root

# Create a ppath
obj = root.mk('pfx:abc123def')

# Access an existing ppath
obj = root['pfx:abc123def']
obj = root.get('pfx:abc123def')

# ppaths are Dir instances with some File and Dir class methods mixed in
obj.read('content.xml')
=> "<content/>"
obj.open('my_file.txt','w') { |io| io.write("Write text to file") }
obj.entries
=> ["content.xml","my_file.txt"]
obj['*.xml']
=> ["content.xml"]
obj.each { |file| ... }
obj.unlink('my_file.txt')

# Delete a ppath and all its contents
root.purge!('pfx:abc123def')

== Copyright

Copyright (c) 2010 Chris Beer. See LICENSE.txt for
Expand Down
57 changes: 11 additions & 46 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,52 +1,17 @@
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"
exit e.status_code
end
require 'rake'

require 'jeweler'
Jeweler::Tasks.new do |gem|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
gem.name = "pairtree"
gem.homepage = "http://github.com/cbeer/pairtree"
gem.license = "MIT"
gem.summary = %Q{Ruby Pairtree implementation}
gem.email = "[email protected]"
gem.authors = ["Chris Beer"]
# Include your dependencies below. Runtime dependencies are required when using your gem,
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
# gem.add_development_dependency 'rspec', '> 1.2.3'
end
Jeweler::RubygemsDotOrgTasks.new
require 'bundler/gem_tasks'

require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
end
Dir.glob('lib/tasks/*.rake').each { |r| import r }

require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
end

task :default => :test
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)

require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
task :rcov => ["functional"] do
end

rdoc.rdoc_dir = 'rdoc'
rdoc.title = "pairtree #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
task :clean do
puts 'Cleaning old coverage.data'
FileUtils.rm('coverage.data') if(File.exists? 'coverage.data')
end

task :default => [:rcov, :doc]
1 change: 0 additions & 1 deletion VERSION

This file was deleted.

1 change: 1 addition & 0 deletions lib/pairtree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
require 'pairtree/root'
require 'pairtree/client'
module Pairtree
class IdentifierException < Exception; end
end
57 changes: 57 additions & 0 deletions lib/pairtree/obj.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,62 @@
module Pairtree
class Obj < ::Dir

# Alias certain class methods from File to instance methods of Pairtree::Obj
# Methods that require a filename, 0..n additional args, and maybe a block
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)
end
end

# Methods that require multiple filenames
def delete *args
File.delete(*(prepend_filenames(args)))
end
alias_method :unlink, :delete

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

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

# Methods that require non-filename args, followed by filenames
def utime atime, mtime, *args
File.utime(atime, mtime, *(prepend_filenames(args)))
end

# Alias File.open to Pairtree::Obj#file
def file fname, *args, &block
File.open(File.join(self.path, fname), *args, &block)
end

# Override Dir#entries and Dir#each to remove the dotfiles
def entries
super - ['.','..']
end

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

# Basic globbing
def glob(string, flags = 0)
entries.select { |entry| File.fnmatch(string, entry, flags) }
end

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

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

end
end
43 changes: 41 additions & 2 deletions lib/pairtree/path.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
module Pairtree
class Path
@@leaf_proc = lambda { |id| Pairtree::Identifier.encode(id) }

def self.leaf_proc value = nil, &block
if value.nil?
@@leaf_proc = block
else
if value.is_a?(Proc)
@@leaf_proc = value
else
@@leaf_proc = lambda { |id| value }
end
end
end

def self.leaf id
if @@leaf_proc
@@leaf_proc.call(id)
else
''
end
end

def self.id_to_path id
File.join(Pairtree::Identifier.encode(id).scan(/..?/))
path = File.join(Pairtree::Identifier.encode(id).scan(/..?/),self.leaf(id))
path.sub(%r{#{File::SEPARATOR}+$},'')
end

def self.path_to_id ppath
Pairtree::Identifier.decode(ppath.split(File::SEPARATOR).join)
parts = ppath.split(File::SEPARATOR)
parts.pop if @@leaf_proc
Pairtree::Identifier.decode(parts.join)
end

def self.remove! path
FileUtils.remove_dir(path, true)
parts = path.split(File::SEPARATOR)
parts.pop
while parts.length > 0 and parts.last != 'pairtree_root'
begin
FileUtils.rmdir(parts.join(File::SEPARATOR))
parts.pop
rescue SystemCallError
break
end
end
end
end
end
48 changes: 32 additions & 16 deletions lib/pairtree/root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,45 @@ def list
return [] unless pairtree_root? @root

Dir.chdir(@root) do
Find.find(*Dir.entries('.').reject { |x| x =~ /^\./ }) do |path|
if File.directory? path
Find.prune if File.basename(path).length > @shorty_length
objects << path if Dir.entries(path).any? { |f| f.length > @shorty_length or File.file? File.join(path, f) }
next
end
end
Find.find(*Dir.entries('.').reject { |x| x =~ /^\./ }) do |path|
if File.directory? path
Find.prune if File.basename(path).length > @shorty_length
objects << path if Dir.entries(path).any? { |f| f.length > @shorty_length or File.file? File.join(path, f) }
next
end
end
end

objects.map { |x| @prefix + Pairtree::Path.path_to_id(x) }
end

def mk id
id.sub! @prefix, ''
path = File.join(@root, Pairtree::Path.id_to_path(id))
FileUtils.mkdir_p path
Pairtree::Obj.new path
def path_for id
unless id.start_with? @prefix
raise IdentifierException, "Identifier must start with #{@prefix}"
end
path_id = id[@prefix.length..-1]
File.join(@root, Pairtree::Path.id_to_path(path_id))
end


def exists? id
File.directory?(path_for(id))
end

def get id
id.sub! @prefix, ''
path = File.join(@root, Pairtree::Path.id_to_path(id))
Pairtree::Obj.new path if File.directory? path
Pairtree::Obj.new path_for(id)
end
alias_method :[], :get

def mk id
FileUtils.mkdir_p path_for(id)
get(id)
end

def purge! id
if exists?
Pairtree::Path.remove!(path_for(id))
end
not exists?
end

private
Expand Down
32 changes: 32 additions & 0 deletions lib/tasks/rdoc.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
desc "Generate RDoc"
task :doc => ['doc:generate']

namespace :doc do
project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
doc_destination = File.join(project_root, 'rdoc')

begin
require 'yard'
require 'yard/rake/yardoc_task'

YARD::Rake::YardocTask.new(:generate) do |yt|
yt.files = Dir.glob(File.join(project_root, 'lib', '*.rb')) +
Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
[ File.join(project_root, 'README.rdoc') ] +
[ File.join(project_root, 'LICENSE') ]

yt.options = ['--output-dir', doc_destination, '--readme', 'README.rdoc']
end
rescue LoadError
desc "Generate YARD Documentation"
task :generate do
abort "Please install the YARD gem to generate rdoc."
end
end

desc "Remove generated documenation"
task :clean do
rm_r doc_destination if File.exists?(doc_destination)
end

end
Loading

0 comments on commit 10d0a34

Please sign in to comment.