bagit 0.3.1 → 0.3.2.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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