bagit 0.3.1 → 0.3.2.pre

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,11 @@
1
+ .DS_Store
2
+ *~
3
+ .#*
4
+ \#*#
5
+ *.gem
6
+ tags
7
+ vendor
8
+ Gemfile.lock
9
+ .bundle
10
+ .project
11
+ .idea
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3@bagit --create
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - "1.9.3"
3
+ - "1.9.2"
4
+ - "1.8.7"
5
+
6
+ branches:
7
+ only:
8
+ - master
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -1,16 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bagit/version'
1
5
 
2
- BAGIT_SPEC = Gem::Specification.new do |spec|
6
+ Gem::Specification.new do |spec|
3
7
  spec.name = "bagit"
4
- spec.version = '0.3.1'
8
+ spec.version = BagIt::VERSION
5
9
  spec.summary = "BagIt package generation and validation"
6
10
  spec.description = "Ruby Library and Command Line tools for bagit"
7
11
  spec.email = "johnson.tom@gmail.com"
8
12
  spec.homepage = 'http://github.com/tipr/bagit'
9
13
  spec.authors = ["Tom Johnson, Francesco Lazzarino"]
14
+ spec.license = "MIT"
10
15
 
11
16
  spec.add_dependency 'validatable', '~> 1.6'
12
17
  spec.add_dependency 'docopt', '~> 0.5.0'
13
18
 
14
- spec.files = %w(Rakefile README.md LICENSE.txt bagit.gemspec) + Dir["lib/**/*.rb"]
15
- spec.executables << 'bagit'
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency 'rake'
21
+ spec.add_development_dependency 'rspec'
22
+
23
+ spec.files = `git ls-files`.split($/)
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
+ spec.require_paths = ["lib"]
16
27
  end
data/bin/bagit CHANGED
@@ -36,7 +36,7 @@ DOCOPT
36
36
  # [--bag-info-entry <label> <value>] [-f <file>...] [-t <tagfile>...] BAGPATH
37
37
 
38
38
  begin
39
- opts = Docopt::docopt(doc, version: 'BagIt 0.3.1')
39
+ opts = Docopt::docopt(doc, version: BagIt::VERSION)
40
40
 
41
41
  bag = BagIt::Bag.new(opts['BAGPATH'])
42
42
 
@@ -4,6 +4,7 @@
4
4
  # http://www.cdlib.org/inside/diglib/bagit/bagitspec.html
5
5
 
6
6
  require 'bagit/bag'
7
+ require 'bagit/version'
7
8
  require 'fileutils'
8
9
  require 'date'
9
10
 
@@ -62,33 +62,33 @@ module BagIt
62
62
  FileUtils::mkdir_p File.dirname(path)
63
63
 
64
64
  if src_path.nil?
65
- f = open(path, 'w') { |io| yield io }
65
+ f = File.open(path, 'w') { |io| yield io }
66
66
  else
67
67
  f = FileUtils::cp src_path, path
68
68
  end
69
69
  write_bag_info
70
70
  return f
71
71
  end
72
-
72
+
73
73
  # Remove a bag file
74
74
  def remove_file(base_path)
75
75
  path = File.join(data_dir, base_path)
76
76
  raise "Bag file does not exist: #{base_path}" unless File.exist? path
77
77
  FileUtils::rm path
78
78
  end
79
-
79
+
80
80
  # Retrieve the IO handle for a file in the bag
81
81
  def get(base_path)
82
82
  path = File.join(data_dir, base_path)
83
83
  return nil unless File.exist?(path)
84
84
  File.open(path)
85
85
  end
86
-
86
+
87
87
  # Test if this bag is empty (no files)
88
88
  def empty?
89
89
  self.bag_files.empty?
90
90
  end
91
-
91
+
92
92
  # Get all bag file paths relative to the data dir
93
93
  def paths
94
94
  self.bag_files.collect { |f| f.sub(data_dir + '/', '') }
@@ -103,7 +103,7 @@ module BagIt
103
103
  end
104
104
  return bytes.to_s + '.' + bag_files.count.to_s
105
105
  end
106
-
106
+
107
107
  # Remove all empty directory trees from the bag
108
108
  def gc!
109
109
  Dir.entries(data_dir).each do |f|
@@ -4,7 +4,7 @@ module BagIt
4
4
 
5
5
  module Info
6
6
 
7
- @@bag_info_headers = {
7
+ @@bag_info_headers = {
8
8
  :agent => 'Bag-Software-Agent',
9
9
  :org => 'Source-Organization',
10
10
  :org_addr => 'Organization-Address',
@@ -35,7 +35,7 @@ module BagIt
35
35
  end
36
36
 
37
37
  def write_bag_info(hash={})
38
- hash = bag_info.merge(hash)
38
+ hash = bag_info.merge(hash)
39
39
  hash[@@bag_info_headers[:agent]] = "BagIt Ruby Gem (http://bagit.rubyforge.org)" if hash[@@bag_info_headers[:agent]].nil?
40
40
  hash[@@bag_info_headers[:date]] = Date.today.strftime('%Y-%m-%d') if hash[@@bag_info_headers[:date]].nil?
41
41
  hash[@@bag_info_headers[:oxum]] = payload_oxum
@@ -49,7 +49,7 @@ module BagIt
49
49
  def bagit
50
50
  read_info_file bagit_txt_file
51
51
  end
52
-
52
+
53
53
  def write_bagit(hash)
54
54
  write_info_file bagit_txt_file, hash
55
55
  end
@@ -62,18 +62,18 @@ module BagIt
62
62
  protected
63
63
 
64
64
  def read_info_file(file)
65
-
66
- open(file) do |io|
67
-
65
+
66
+ File.open(file) do |io|
67
+
68
68
  entries = io.read.split /\n(?=[^\s])/
69
-
69
+
70
70
  entries.inject({}) do |hash, line|
71
71
  name, value = line.chomp.split /\s*:\s*/, 2
72
72
  hash.merge({name => value})
73
73
  end
74
-
74
+
75
75
  end
76
-
76
+
77
77
  end
78
78
 
79
79
  def write_info_file(file, hash)
@@ -82,25 +82,25 @@ module BagIt
82
82
  a = hash.keys.grep(/#{key}/i)
83
83
  acc + (a.size > 1 ? a : [])
84
84
  end
85
-
85
+
86
86
  raise "Multiple labels (#{dups.to_a.join ', '}) in #{file}" unless dups.empty?
87
-
88
- open(file, 'w') do |io|
89
-
87
+
88
+ File.open(file, 'w') do |io|
89
+
90
90
  hash.each do |name, value|
91
91
  simple_entry = "#{name}: #{value.gsub /\s+/, ' '}"
92
-
92
+
93
93
  entry = if simple_entry.length > 79
94
94
  simple_entry.wrap(77).indent(2)
95
95
  else
96
96
  simple_entry
97
97
  end
98
-
98
+
99
99
  io.puts entry
100
100
  end
101
-
101
+
102
102
  end
103
-
103
+
104
104
  end
105
105
 
106
106
  end
@@ -10,7 +10,7 @@ module BagIt
10
10
  # All tag files that are bag manifest files (manifest-[algorithm].txt)
11
11
  def manifest_files
12
12
  files = Dir[File.join(@bag_dir, '*')].select { |f|
13
- File.file? f and File.basename(f) =~ /^manifest-.*.txt/
13
+ File.file? f and File.basename(f) =~ /^manifest-.*.txt/
14
14
  }
15
15
  files
16
16
  end
@@ -32,19 +32,19 @@ module BagIt
32
32
 
33
33
  # sha1
34
34
  sha1 = Digest::SHA1.file f
35
- open(manifest_file(:sha1), 'a') { |io| io.puts "#{sha1} #{rel_path}" }
35
+ File.open(manifest_file(:sha1), 'a') { |io| io.puts "#{sha1} #{rel_path}" }
36
36
 
37
37
  # md5
38
38
  md5 = Digest::MD5.file f
39
- open(manifest_file(:md5), 'a') { |io| io.puts "#{md5} #{rel_path}" }
39
+ File.open(manifest_file(:md5), 'a') { |io| io.puts "#{md5} #{rel_path}" }
40
40
  end
41
-
41
+ tagmanifest!
42
42
  end
43
43
 
44
44
  # All tag files that are bag manifest files (tagmanifest-[algorithm].txt)
45
45
  def tagmanifest_files
46
46
  files = Dir[File.join(@bag_dir, '*')].select { |f|
47
- File.file? f and File.basename(f) =~ /^tagmanifest-.*.txt/
47
+ File.file? f and File.basename(f) =~ /^tagmanifest-.*.txt/
48
48
  }
49
49
  files
50
50
  end
@@ -57,12 +57,12 @@ module BagIt
57
57
  # Generate manifest files for all the tag files (except the tag
58
58
  # manifest files)
59
59
  def tagmanifest!(tags=nil)
60
-
60
+
61
61
  tags = tag_files if tags == nil
62
62
 
63
63
  # nuke all the existing tagmanifest files
64
64
  tagmanifest_files.each { |f| FileUtils::rm f }
65
-
65
+
66
66
  # ensure presence of manfiest files
67
67
  manifest_files.each do |manifest|
68
68
  tags << manifest unless tags.include?(manifest)
@@ -81,36 +81,36 @@ module BagIt
81
81
 
82
82
  def add_tag_file(path, src_path=nil)
83
83
 
84
- f = File.join(@bag_dir, path)
84
+ f = File.join(@bag_dir, path)
85
85
  raise "Tag file already in manifest: #{path}" if tag_files.include?(f)
86
-
86
+
87
87
  if not File.exist? f
88
88
  FileUtils::mkdir_p File.dirname(f)
89
89
 
90
90
  # write file
91
91
  if src_path.nil?
92
- open(f, 'w') { |io| yield io }
92
+ File.open(f, 'w') { |io| yield io }
93
93
  else
94
94
  FileUtils::cp src_path, f
95
95
  end
96
96
  # this adds the manifest and bag info files on initial creation
97
- # it must only run when the manifest doesn't already exist or it will
97
+ # it must only run when the manifest doesn't already exist or it will
98
98
  # infinitely recall add_tag_file. Better way of doing this?
99
- tagmanifest!
100
- elsif not src_path.nil?
99
+ tagmanifest!
100
+ elsif not src_path.nil?
101
101
  raise "Tag file already exists, will not overwrite: #{path}\n Use add_tag_file(path) to add an existing tag file."
102
102
  end
103
103
 
104
- data = open(f) { |io| io.read }
104
+ data = File.open(f) { |io| io.read }
105
105
  rel_path = Pathname.new(f).relative_path_from(Pathname.new(bag_dir)).to_s
106
106
 
107
107
  # sha1
108
108
  sha1 = Digest::SHA1.hexdigest data
109
- open(tagmanifest_file(:sha1), 'a') { |io| io.puts "#{sha1} #{rel_path}" }
109
+ File.open(tagmanifest_file(:sha1), 'a') { |io| io.puts "#{sha1} #{rel_path}" }
110
110
 
111
111
  # md5
112
112
  md5 = Digest::MD5.hexdigest data
113
- open(tagmanifest_file(:md5), 'a') { |io| io.puts "#{md5} #{rel_path}" }
113
+ File.open(tagmanifest_file(:md5), 'a') { |io| io.puts "#{md5} #{rel_path}" }
114
114
  tag_files
115
115
  end
116
116
 
@@ -146,11 +146,11 @@ module BagIt
146
146
 
147
147
  # check it, an unknown algorithm is always true
148
148
  unless algo == :unknown
149
- lines = open(mf) { |io| io.readlines }
149
+ lines = File.open(mf) { |io| io.readlines }
150
150
 
151
151
  lines.all? do |line|
152
152
  manifested_digest, path = line.chomp.split /\s+/, 2
153
- actual_digest = open(File.join(@bag_dir, path)) { |io| algo.hexdigest io.read }
153
+ actual_digest = File.open(File.join(@bag_dir, path)) { |io| algo.hexdigest io.read }
154
154
  actual_digest == manifested_digest
155
155
  end
156
156
 
@@ -4,8 +4,8 @@ module BagIt
4
4
 
5
5
  class Bag
6
6
  include Validatable
7
- validates_true_for :consistency, :logic => Proc.new { complete? }
8
- validates_true_for :completeness, :logic => Proc.new { consistent? }
7
+ validates_true_for :consistency, :logic => Proc.new { consistent? }
8
+ validates_true_for :completeness, :logic => Proc.new { complete? }
9
9
  end
10
10
 
11
11
  module Validity
@@ -24,7 +24,7 @@ module BagIt
24
24
  tag_empty_manifests.each do |file|
25
25
  errors.add :completeness, "#{file} is a manifested tag but not present"
26
26
  end
27
-
27
+
28
28
  errors.on(:completeness).nil?
29
29
  end
30
30
 
@@ -42,7 +42,7 @@ module BagIt
42
42
  :unknown
43
43
  end
44
44
  # Check every file in the manifest
45
- open(mf) do |io|
45
+ File.open(mf) do |io|
46
46
  io.each_line do |line|
47
47
  expected, path = line.chomp.split /\s+/, 2
48
48
  file = File.join(bag_dir, path)
@@ -55,7 +55,7 @@ module BagIt
55
55
  end
56
56
  end
57
57
  end
58
-
58
+
59
59
 
60
60
  errors.on(:consistency).nil?
61
61
  end
@@ -93,7 +93,7 @@ module BagIt
93
93
 
94
94
  manifest_files.inject([]) do |acc, mf|
95
95
 
96
- files = open(mf) do |io|
96
+ files = File.open(mf) do |io|
97
97
 
98
98
  io.readlines.map do |line|
99
99
  digest, path = line.chomp.split /\s+/, 2
@@ -109,7 +109,7 @@ module BagIt
109
109
  # Returns a list of all files in the tag manifest files
110
110
  def tag_manifested_files
111
111
  tagmanifest_files.inject([]) do |acc, mf|
112
- files = open(mf) do |io|
112
+ files = File.open(mf) do |io|
113
113
  io.readlines.map do |line|
114
114
  digest, path = line.chomp.split /\s+/, 2
115
115
  path
@@ -0,0 +1,3 @@
1
+ module BagIt
2
+ VERSION = "0.3.2.pre"
3
+ end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ # based on v0.96 http://www.cdlib.org/inside/diglib/bagit/bagitspec.html
4
+ describe BagIt::Bag do
5
+ describe 'empty bag' do
6
+ before(:each) do
7
+ @sandbox = Sandbox.new
8
+ # make the bag
9
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
10
+ @bag = BagIt::Bag.new @bag_path
11
+ end
12
+
13
+ after(:each) do
14
+ @sandbox.cleanup!
15
+ end
16
+
17
+ it "should be empty" do
18
+ @bag.should be_empty
19
+ end
20
+ end
21
+
22
+
23
+ describe 'bag with files' do
24
+ before(:each) do
25
+ @sandbox = Sandbox.new
26
+
27
+ # make the bag
28
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
29
+ @bag = BagIt::Bag.new @bag_path
30
+
31
+ # add some files
32
+ File.open('/dev/urandom') do |rio|
33
+ 10.times do |n|
34
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
35
+ end
36
+ end
37
+ end
38
+
39
+ after(:each) do
40
+ @sandbox.cleanup!
41
+ end
42
+
43
+ it "should be a directory" do
44
+ File.directory?(@bag_path).should be_true
45
+ end
46
+
47
+ it "should not be empty" do
48
+ @bag.should_not be_empty
49
+ end
50
+
51
+ it "should have a sub-directory called data" do
52
+ data_path = File.join @bag_path, 'data'
53
+ File.directory?(data_path).should be_true
54
+ end
55
+
56
+ describe "#add_file" do
57
+ it "should allow addition of files via io" do
58
+ @bag.add_file("foo") { |io| io.puts 'all alone' }
59
+ File.join(@bag_path, "data", "foo").should exist_on_fs
60
+ end
61
+
62
+ it "should allow addition of files via copy" do
63
+ src_path = File.join @sandbox.to_s, 'somefile'
64
+ File.open(src_path, 'w') { |io| io.puts "something" }
65
+ @bag.add_file("foo", src_path) { |io| io.puts 'all alone' }
66
+ File.join(@bag_path, "data", "foo").should exist_on_fs
67
+ end
68
+
69
+ it "should allow addition of files with deep paths" do
70
+ @bag.add_file("deep/dir/structure/file") { |io| io.puts 'all alone' }
71
+ File.join(@bag_path, "data", "deep/dir/structure/file").should exist_on_fs
72
+ end
73
+
74
+ it "should not allow overwriting of files" do
75
+ lambda { @bag.add_file("file-0") { |io| io.puts 'overwrite!' } }.should raise_error
76
+ end
77
+
78
+ it "should update payload oxum" do
79
+ oxum_count = @bag.bag_info["Payload-Oxum"].split('.')[1].to_i
80
+ @bag.add_file("foo") { |io| io.puts 'all alone' }
81
+ @bag.bag_info["Payload-Oxum"].split('.')[1].to_i.should == oxum_count + 1
82
+ end
83
+ end
84
+
85
+ describe "#remove_file" do
86
+ it "should raise an error when deleing non existant files" do
87
+ lambda { @bag.remove_file("file-x") }.should raise_error
88
+ end
89
+ end
90
+
91
+ describe "#get" do
92
+ describe "file not in bag" do
93
+ it "should return nil" do
94
+ @bag.get('foobar').should be_nil
95
+ end
96
+ end
97
+
98
+ describe "file in bag" do
99
+ before do
100
+ @contents = 'all alone'
101
+ @bag.add_file("foo") { |io| io << 'all alone' }
102
+ @file = @bag.get("foo")
103
+ end
104
+
105
+ it "should return an IO object for the given path" do
106
+ @file.should be_a_kind_of(IO)
107
+ end
108
+
109
+ it "should have the same content as the file added" do
110
+ @file.read.should == @contents
111
+ end
112
+
113
+ it "should accept an optional leading slash or ./" do
114
+ @bag.get("/foo").read.should == @contents
115
+ @bag.get("./foo").read.should == @contents
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#paths" do
121
+ before do
122
+ @paths = @bag.paths
123
+ end
124
+
125
+ it "should return a non-empty Array of Strings" do
126
+ @paths.should be_a_kind_of(Array)
127
+ @paths.should_not be_empty
128
+ @paths.each do |p|
129
+ p.should be_a_kind_of(String)
130
+ end
131
+ end
132
+
133
+ it "should return relative paths to all files in the data directory" do
134
+ @paths.should =~ (0..9).collect { |x| "file-#{x}" }
135
+ end
136
+ end
137
+
138
+ describe "#payload-oxum" do
139
+ it "should return a valid oxum" do
140
+ @bag.payload_oxum.should =~ /^[0-9]+\.[0-9]+$/
141
+ end
142
+
143
+ it "should accurately specify the number of payload files" do
144
+ @bag.add_tag_file('non-payload') { |f| f.puts "I shouldn't count in the oxum" }
145
+ @bag.payload_oxum.split('.')[1] == @bag.bag_files.count
146
+ end
147
+ end
148
+
149
+ describe "#gc!" do
150
+ it "should clean up empty directories" do
151
+ f = File.join "1", "2", "3", "file"
152
+ @bag.add_file(f) { |io| io.puts 'all alone' }
153
+ @bag.remove_file f
154
+ File.exist?(File.dirname(File.join(@bag_path, 'data', f))).should be_true
155
+ @bag.gc!
156
+ File.exist?(File.dirname(File.join(@bag_path, 'data', f))).should be_false
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe "fetch.txt" do
4
+
5
+ before(:each) do
6
+
7
+ @sandbox = Sandbox.new
8
+
9
+ # make the bag
10
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
11
+ @bag = BagIt::Bag.new @bag_path
12
+
13
+ # add some files
14
+ File.open('/dev/urandom') do |rio|
15
+
16
+ 10.times do |n|
17
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ after(:each) do
25
+ @sandbox.cleanup!
26
+ end
27
+
28
+ before(:each) do
29
+ @bag.add_remote_file('http://www.gnu.org/graphics/heckert_gnu.small.png', 'gnu.png', 6322,
30
+ '390c0a30976f899cbdf951eab5cce60fe9743ac9',
31
+ 'a3bd7ab2442028bb91b51d9f6722ec98')
32
+
33
+ path = File.join @bag_path, 'fetch.txt'
34
+ @lines = File.open(path) { |io| io.readlines }
35
+ end
36
+
37
+ it "should not be empty" do
38
+ @lines.should_not be_empty
39
+ end
40
+
41
+ it "should only contain lines of the format URL LENGTH FILENAME" do
42
+ @lines.each { |line| line.chomp.should =~ /^[^\s]+\s+(\d+|\-)\s+[^\s]+$/ }
43
+ end
44
+
45
+ it "should contain manifested files" do
46
+ path = File.join @bag_path, 'manifest-sha1.txt'
47
+ data = File.open(path) { |io| io.read }
48
+ data.should include('gnu.png')
49
+ end
50
+
51
+ it "should be gone when fetch is complete" do
52
+ @bag.fetch!
53
+ File.exist?(File.join(@bag_path, 'fetch.txt')).should_not be_true
54
+ end
55
+
56
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ describe "BagIt Manifests" do
4
+
5
+ before(:each) do
6
+
7
+ @sandbox = Sandbox.new
8
+
9
+ # make the bag
10
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
11
+ @bag = BagIt::Bag.new @bag_path
12
+
13
+ # add some files
14
+ File.open('/dev/urandom') do |rio|
15
+
16
+ 10.times do |n|
17
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
18
+ @bag.add_tag_file("tag-#{n}") { |io| io.write rio.read(16) }
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ after(:each) do
26
+ @sandbox.cleanup!
27
+ end
28
+
29
+ shared_examples_for "a manifest file" do
30
+
31
+ before do
32
+ pattern = File.join @bag_path, '*manifest-*.txt'
33
+ @manifest_files = Dir.glob pattern
34
+ end
35
+
36
+ it "should have valid algorithm in the name (at least md5 or sha1)" do
37
+ algorithms = @manifest_files.map { |mf| mf =~ /manifest-(.*).txt$/; $1 }
38
+ algorithms.each { |a| a.should be_in('md5', 'sha1') }
39
+ end
40
+
41
+ it "should not be an empty file" do
42
+ @manifest_files.each { |mf| File.size(mf).should_not == 0 }
43
+ end
44
+
45
+ it "should only contain lines of the format CHECKSUM FILENAME" do
46
+ @manifest_files.each do |file|
47
+ File.open(file) do |io|
48
+ io.each_line { |line| line.chomp.should =~ /^[a-fA-F0-9]+\s+[^\s].+$/ }
49
+ end
50
+ end
51
+ end
52
+
53
+ it "should validate after adding a file and remanifesting" do
54
+ @bag.add_file('newfile.txt') { |io| io.puts("new file to remanifest") }
55
+ @bag.manifest!
56
+ @bag.should be_valid
57
+ end
58
+
59
+ end
60
+
61
+ describe "bag manifest files" do
62
+
63
+ before do
64
+ @bag.manifest!
65
+ end
66
+
67
+ it_behaves_like "a manifest file"
68
+
69
+ it "should have a manifest file" do
70
+ @bag.manifest_files.should_not be_empty
71
+ end
72
+
73
+ it "should only contain bag files" do
74
+ @bag.manifest_files.each do |mf|
75
+ File.open(mf) do |io|
76
+ io.each_line do |line|
77
+ line.chomp.should =~ /^[a-f0-9]+\s+data\/[^\s].+$/
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ describe "tag manifest files" do
86
+
87
+ before do
88
+ @bag.add_tag_file("test-tag") { |f| f.puts "all alone" }
89
+ end
90
+
91
+ it_should_behave_like "a manifest file"
92
+
93
+ it "should have a tag manifest file" do
94
+ @bag.tagmanifest_files.should_not be_empty
95
+ end
96
+ it "should only contain tag files" do
97
+ @bag.tagmanifest_files.each do |mf|
98
+ File.open(mf) do |io|
99
+ io.each_line do |line|
100
+ line.chomp.should =~ /^[a-fA-F0-9]+\s+(?!data\/)[^\s].+$/
101
+ end
102
+ end
103
+ end
104
+ end
105
+ it "should contain manifest and bag info files" do
106
+ @bag.tagmanifest_files.each do |mf|
107
+ File.open(mf).read.should include(File.basename(@bag.bag_info_txt_file))
108
+ File.open(mf).read.should include(File.basename(@bag.bagit_txt_file))
109
+ @bag.manifest_files.each do |man|
110
+ File.open(mf).read.should include(man)
111
+ end
112
+ end
113
+ end
114
+ it "should not contain the untracked tag file" do
115
+ @bag.tagmanifest_files.each do |mf|
116
+ File.open(mf) do |io|
117
+ io.read.should_not include "tag-notrack"
118
+ end
119
+ end
120
+ end
121
+ describe "removing tracked files" do
122
+ before(:each) do
123
+ @bag.remove_tag_file "tag-1"
124
+ @bag.delete_tag_file "tag-2"
125
+ end
126
+ it "should still have the untracked tag file on the file system" do
127
+ File.join(@bag_path, "tag-1").should exist_on_fs
128
+ end
129
+ it "should not have the deleted tag file on the file system" do
130
+ File.join(@bag_path, "tag-2").should_not exist_on_fs
131
+ end
132
+ it "should not have the removed or deleted tag files in the manifest" do
133
+ @bag.tagmanifest_files.each do |mf|
134
+ File.open(mf) do |io|
135
+ io.read.should_not include "tag-1"
136
+ io.read.should_not include "tag-2"
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+
144
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.require(:default, :test)
4
+
5
+ require File.expand_path('./util/bagit_matchers', File.dirname(__FILE__))
6
+
7
+ RSpec.configure do |config|
8
+ config.include(BagitMatchers)
9
+ end
10
+
11
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
12
+ require 'bagit'
13
+
14
+ require 'tempfile'
15
+
16
+ class Sandbox
17
+
18
+ def initialize
19
+ tf = Tempfile.open 'sandbox'
20
+ @path = tf.path
21
+ tf.close!
22
+ FileUtils::mkdir @path
23
+ end
24
+
25
+ def cleanup!
26
+ FileUtils::rm_rf @path
27
+ end
28
+
29
+ def to_s
30
+ @path
31
+ end
32
+
33
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Tag Info Files" do
4
+
5
+ before(:each) do
6
+
7
+ @sandbox = Sandbox.new
8
+
9
+ # make the bag
10
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
11
+ @bag = BagIt::Bag.new @bag_path
12
+
13
+ # add some files
14
+ File.open('/dev/urandom') do |rio|
15
+ 10.times do |n|
16
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ after(:each) do
23
+ @sandbox.cleanup!
24
+ end
25
+
26
+ describe "bagit.txt" do
27
+
28
+ before do
29
+ path = File.join @bag_path, 'bagit.txt'
30
+ @lines = File.open(path) { |io| io.readlines }
31
+ end
32
+
33
+ it "should create a file bagit.txt on bag initialization" do
34
+ File.join(@bag_path, 'bagit.txt').should exist_on_fs
35
+ end
36
+
37
+ it "should have exactly two lines" do
38
+ @lines.size.should == 2
39
+ end
40
+
41
+ it "should have a bagit version" do
42
+ a = @lines.select { |line| line.chomp =~ /BagIt-Version:\s*\d+\.\d+/ }
43
+ a.should_not be_empty
44
+ end
45
+
46
+ it "should have a tag file encoding" do
47
+ a = @lines.select { |line| line.chomp =~ /Tag-File-Character-Encoding:\s*.+/ }
48
+ a.should_not be_empty
49
+ end
50
+
51
+ end
52
+
53
+ describe "bag-info.txt" do
54
+
55
+ before(:each) do
56
+ path = File.join @bag_path, 'bag-info.txt'
57
+ @lines = File.open(path) { |io| io.readlines }
58
+ end
59
+
60
+ it "should not be empty" do
61
+ @lines.should_not be_empty
62
+ end
63
+
64
+ it "should contain lines of the format LABEL: VALUE (like an email header)" do
65
+ @lines.each { |line| line.chomp.should =~ /^[^\s]+\s*:\s+.*$/ }
66
+ end
67
+
68
+ it "should be case insensitive with respect to LABELs" do
69
+ path = File.join @bag_path, 'bag-info.txt'
70
+ lambda { @bag.write_bag_info 'foo' => 'lowercase', 'Foo' => 'capital' }.should raise_error(/Multiple labels/)
71
+ end
72
+
73
+ it "should fold long VALUEs" do
74
+ longline = <<LOREM
75
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
76
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enimad
77
+ minim veniam, quis nostrud exercitation ullamco laboris nisi ut
78
+ aliquip ex ea commodo consequat. Duis aute irure dolor in
79
+ reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
80
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
81
+ culpa qui officia deserunt mollit anim id est laborum.
82
+ LOREM
83
+ @bag.write_bag_info 'Lorem' => longline
84
+ @bag.bag_info.keys.length.should == 4 # this isn't a great test. Changed it from 1 to 4 because unrelated changes caused failure.
85
+ end
86
+
87
+ it "should specify a bag software agent" do
88
+ @bag.bag_info.keys.should include("Bag-Software-Agent")
89
+ end
90
+
91
+ it "should contain a valid bagging date" do
92
+ @bag.bag_info.keys.should include("Bagging-Date")
93
+ @bag.bag_info["Bagging-Date"] =~ /^^[0-9]{4}-[0-9]{2}-[0-9]{2}$/
94
+ end
95
+
96
+ it "should contain a payload oxum" do
97
+ @bag.bag_info.keys.should include("Payload-Oxum")
98
+ end
99
+ it "should not override any previous values" do
100
+ path = File.join @bag_path, 'bag-info.txt'
101
+ @bag.write_bag_info 'Bag-Software-Agent' => 'Some Other Agent'
102
+ @bag.write_bag_info 'Source-Organization' => 'Awesome Inc.'
103
+ @bag.write_bag_info 'Bagging-Date' => '1901-01-01'
104
+ @bag.write_bag_info
105
+ contents = File.open(path).read
106
+ contents.should include "Some Other Agent"
107
+ contents.should include "Awesome Inc."
108
+ contents.should include "1901-01-01"
109
+ end
110
+ it "should override previous tags when they collide with new ones" do
111
+ path = File.join @bag_path, 'bag-info.txt'
112
+ @bag.write_bag_info 'Source-Organization' => 'Awesome Inc.'
113
+ @bag.write_bag_info 'Source-Organization' => 'Awesome LLC.'
114
+ contents = File.open(path).read
115
+ contents.should include "Awesome LLC."
116
+ contents.should_not include "Awesome Inc."
117
+ end
118
+ it "should contain values passed to bag" do
119
+ hash = {"Bag-Software-Agent" => "rspec",
120
+ "Bagging-Date" => "2012-11-21",
121
+ "Contact-Name" => "Willis Corto",
122
+ "Some-Tag" => "Some Value"
123
+ }
124
+ bag_with_info = BagIt::Bag.new(@bag_path + '2', hash)
125
+ hash.each do |key, value|
126
+ bag_with_info.bag_info[key].should == value
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Tag Specs" do
4
+
5
+ before(:each) do
6
+
7
+ @sandbox = Sandbox.new
8
+
9
+ # make the bag
10
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
11
+ @bag = BagIt::Bag.new @bag_path
12
+
13
+ # add some files
14
+ File.open('/dev/urandom') do |rio|
15
+
16
+ 10.times do |n|
17
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
18
+ @bag.add_tag_file("tag-#{n}") { |io| io.write rio.read(16)}
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ after(:each) do
26
+ @sandbox.cleanup!
27
+ end
28
+ describe "#add_tag_file" do
29
+ it "should allow addition of tag files via io" do
30
+ @bag.add_tag_file("foo") { |io| io.puts 'all alone' }
31
+ File.join(@bag_path, "foo").should exist_on_fs
32
+ end
33
+ it "should allow addition of bag files within directories using io" do
34
+ @bag.add_tag_file("fedora/foo") { |io| io.puts 'all alone' }
35
+ File.join(@bag_path, "fedora","foo").should exist_on_fs
36
+ end
37
+ it "should allow addition of deep tag files" do
38
+ @bag.add_tag_file("fedora/foo/newfoo/deep") {|io| io.puts "woah that's deep"}
39
+ File.join(@bag_path,"fedora","foo","newfoo","deep").should exist_on_fs
40
+ end
41
+ it "should not allow overwriting of tag files" do
42
+ lambda { @bag.add_tag_file("tag-0") { |io| io.puts 'overwrite!' } }.should raise_error
43
+ end
44
+ it "should allow addition of tag files via copy" do
45
+ src_path = File.join @sandbox.to_s, 'somefile'
46
+ File.open(src_path, 'w') { |io| io.puts "something" }
47
+ @bag.add_tag_file("foo", src_path) { |io| io.puts 'all alone' }
48
+ File.join(@bag_path, "foo").should exist_on_fs
49
+ end
50
+ end
51
+ describe "#remove_tag_file" do
52
+ it "should raise an error when removing non existant files" do
53
+ lambda { @bag.remove_tag_file("file-x") }.should raise_error
54
+ end
55
+ end
56
+ describe "#delete_tag_file" do
57
+ it "should raise an error when deleting non existant tag files" do
58
+ lambda { @bag.delete_tag_file("file-x") }.should raise_error
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,49 @@
1
+ module BagitMatchers
2
+
3
+ class BeIn
4
+
5
+ def initialize(*expected_collection)
6
+ @expected = expected_collection
7
+ end
8
+
9
+ def matches?(target)
10
+ @target = target
11
+ @expected.include? @target
12
+ end
13
+
14
+ def failure_message
15
+ "expected <#{@target}> to be in collection <#{@expected}>"
16
+ end
17
+
18
+ def negative_failure_message
19
+ "expected <#{@target}> to not be in collection <#{@expected}>"
20
+ end
21
+
22
+ end
23
+
24
+ def be_in(*expected_collection)
25
+ BeIn.new(*expected_collection)
26
+ end
27
+
28
+ class ExistOnFS
29
+
30
+ def matches?(target)
31
+ @target = target
32
+ File.exist? target
33
+ end
34
+
35
+ def failure_message
36
+ "expected <#{@target}> to exist, but it doesn't"
37
+ end
38
+
39
+ def negative_failure_message
40
+ "expected <#{@target}> to not exist but it does"
41
+ end
42
+
43
+ end
44
+
45
+ def exist_on_fs
46
+ ExistOnFS.new
47
+ end
48
+
49
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ describe "a valid bag" do
4
+
5
+ before(:each) do
6
+
7
+ @sandbox = Sandbox.new
8
+
9
+ # make the bag
10
+ @bag_path = File.join @sandbox.to_s, 'the_bag'
11
+ @bag = BagIt::Bag.new @bag_path
12
+
13
+ # add some files
14
+ File.open('/dev/urandom') do |rio|
15
+
16
+ 10.times do |n|
17
+ @bag.add_file("file-#{n}") { |io| io.write rio.read(16) }
18
+ @bag.add_tag_file("tag-#{n}") { |io| io.write rio.read(16) }
19
+ end
20
+
21
+ end
22
+
23
+ @bag.manifest!
24
+ end
25
+
26
+ after(:each) do
27
+ @sandbox.cleanup!
28
+ end
29
+
30
+ it "should validate with no errors" do
31
+ @bag.should be_valid
32
+ end
33
+
34
+ it "should not be lewd (some file is not covered by the manifest)" do
35
+ # add a file into the bag through the back door
36
+ File.open(File.join(@bag.data_dir, 'not-manifested'), 'w') do |io|
37
+ io.puts 'nothing to see here, move along'
38
+ end
39
+
40
+ @bag.validate_only('true_for/completeness')
41
+ @bag.errors.on(:completeness).should_not be_empty
42
+ @bag.should_not be_valid
43
+ end
44
+
45
+ it "should not be prude (the manifest covers files that do not exist)" do
46
+ # add a file and then remove it through the back door
47
+ @bag.add_file("file-k") { |io| io.puts 'time to go' }
48
+ @bag.manifest!
49
+
50
+ FileUtils::rm File.join(@bag.bag_dir, 'data', 'file-k')
51
+
52
+ @bag.validate_only('true_for/completeness')
53
+ @bag.errors.on(:completeness).should_not be_empty
54
+ @bag.should_not be_valid
55
+ end
56
+
57
+ it "should be consistent (fixity)" do
58
+ # tweak a file through the back door
59
+ File.open(@bag.bag_files[0], 'a') { |io| io.puts 'oops!' }
60
+
61
+ @bag.validate_only('true_for/consistency')
62
+ @bag.errors.on(:consistency).should_not be_empty
63
+ @bag.should_not be_valid
64
+ end
65
+
66
+ it "should calculate sha1 correctly for a big file" do
67
+ @bag.add_file 'big-data-file' do |fh|
68
+ count = 0
69
+ while count < 1024 * 512 do
70
+ fh.write "1" * 1024
71
+ count += 1
72
+ end
73
+ end
74
+ @bag.manifest!
75
+ sha1_manifest = File.join @bag_path, 'manifest-sha1.txt'
76
+ checksums = {}
77
+ File.open(sha1_manifest).each_line do |line|
78
+ fixity, path = line.split(' ')
79
+ checksums[path] = fixity
80
+ end
81
+ expected = checksums['data/big-data-file']
82
+ expected.should == '12be64c30968bb90136ee695dc58f4b2276968c6'
83
+ end
84
+
85
+ it "should validate by oxum when needed" do
86
+ @bag.valid_oxum?.should == true
87
+ end
88
+
89
+ it "should validate false by oxum when file count is incorrect" do
90
+ # tweak oxum through backdoor
91
+ File.open(@bag.bag_info_txt_file, 'a') { |f| f.write "Payload-Oxum: " + @bag.bag_info["Payload-Oxum"].split('.')[0] + '.0' }
92
+ @bag.valid_oxum?.should == false
93
+ end
94
+
95
+ it "should validate false by oxum when octetstream size is incorrect" do
96
+ # tweak oxum through backdoor
97
+ File.open(@bag.bag_info_txt_file, 'a') { |f| f.write "Payload-Oxum: 1." + @bag.bag_info["Payload-Oxum"].split('.')[1] }
98
+ @bag.valid_oxum?.should == false
99
+ end
100
+
101
+ describe "tag manifest validation" do
102
+ it "should be invalid if listed tag file does not exist" do
103
+ # add a file and then remove it through the back door
104
+ @bag.add_tag_file("tag-k") { |io| io.puts 'time to go' }
105
+ @bag.tagmanifest!
106
+
107
+ FileUtils::rm File.join(@bag.bag_dir, 'tag-k')
108
+
109
+ # @bag.should_not be_valid
110
+ @bag.should_not be_valid
111
+ @bag.errors.on(:completeness).should_not be_empty
112
+ end
113
+ end
114
+
115
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bagit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
5
- prerelease:
4
+ version: 0.3.2.pre
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tom Johnson, Francesco Lazzarino
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-28 00:00:00.000000000 Z
12
+ date: 2013-08-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: validatable
@@ -43,6 +43,54 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 0.5.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
46
94
  description: Ruby Library and Command Line tools for bagit
47
95
  email: johnson.tom@gmail.com
48
96
  executables:
@@ -50,21 +98,35 @@ executables:
50
98
  extensions: []
51
99
  extra_rdoc_files: []
52
100
  files:
53
- - Rakefile
54
- - README.md
101
+ - .gitignore
102
+ - .rvmrc
103
+ - .travis.yml
104
+ - Gemfile
55
105
  - LICENSE.txt
106
+ - README.md
107
+ - Rakefile
56
108
  - bagit.gemspec
109
+ - bin/bagit
57
110
  - lib/bagit.rb
58
- - lib/bagit/string.rb
111
+ - lib/bagit/bag.rb
59
112
  - lib/bagit/fetch.rb
60
- - lib/bagit/manifest.rb
61
- - lib/bagit/info.rb
62
113
  - lib/bagit/file.rb
114
+ - lib/bagit/info.rb
115
+ - lib/bagit/manifest.rb
116
+ - lib/bagit/string.rb
63
117
  - lib/bagit/valid.rb
64
- - lib/bagit/bag.rb
65
- - bin/bagit
118
+ - lib/bagit/version.rb
119
+ - spec/bagit_spec.rb
120
+ - spec/fetch_spec.rb
121
+ - spec/manifest_spec.rb
122
+ - spec/spec_helper.rb
123
+ - spec/tag_info_spec.rb
124
+ - spec/tag_spec.rb
125
+ - spec/util/bagit_matchers.rb
126
+ - spec/validation_spec.rb
66
127
  homepage: http://github.com/tipr/bagit
67
- licenses: []
128
+ licenses:
129
+ - MIT
68
130
  post_install_message:
69
131
  rdoc_options: []
70
132
  require_paths:
@@ -78,13 +140,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
140
  required_rubygems_version: !ruby/object:Gem::Requirement
79
141
  none: false
80
142
  requirements:
81
- - - ! '>='
143
+ - - ! '>'
82
144
  - !ruby/object:Gem::Version
83
- version: '0'
145
+ version: 1.3.1
84
146
  requirements: []
85
147
  rubyforge_project:
86
148
  rubygems_version: 1.8.25
87
149
  signing_key:
88
150
  specification_version: 3
89
151
  summary: BagIt package generation and validation
90
- test_files: []
152
+ test_files:
153
+ - spec/bagit_spec.rb
154
+ - spec/fetch_spec.rb
155
+ - spec/manifest_spec.rb
156
+ - spec/spec_helper.rb
157
+ - spec/tag_info_spec.rb
158
+ - spec/tag_spec.rb
159
+ - spec/util/bagit_matchers.rb
160
+ - spec/validation_spec.rb