druid-tools 0.3.3 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cf9c519783ef2847427e51eaa3f35cb768d9406c
4
- data.tar.gz: ae57ca611f4fc4edd01ddaa35d6a366697e715dc
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWYwNmM0OTExYjMwZDEyZTI4OTlmMGIzMGE5NzQ2NWY0NTlmNGVlOQ==
5
+ data.tar.gz: !binary |-
6
+ MDllNDUwYWUzMzJiZDUzZDUxZTA3NmQyYjFkMDQ1Y2FkMDQ3OWFhNg==
5
7
  SHA512:
6
- metadata.gz: 2bbdd81ce14eb76a1c65d2b94b483dc69b7148ea197f49a28e3395f9682e61d179d220a81bd407562bf744c671a6c4101c7ea64c111cefa515f2a08972fe95e3
7
- data.tar.gz: 75c06f9555897382202c5997571cfb4e15555e85a43deefba0702121e8392c33f7a5d7799d2a714c5d9397562ad651b1f4f3ad5553a1833b7209b6345234fdef
8
+ metadata.gz: !binary |-
9
+ YWQ4NzY4ODNiYWI3NjZmZTQ0MDE3NDQzYTE5OWNiMjJhZTk3N2Y0ZTlhM2U5
10
+ ZDI3NDc2NTU4NDNlOTBiMWRmOWFiNjM5NDAzNzBlOWY3NGU5MDI5NTU3ZmJk
11
+ NTgxMThjYTlmODU0NTJhYTBhNDYzZDk5NTg4ODU2NmNlNTZlZWU=
12
+ data.tar.gz: !binary |-
13
+ N2E4ODdiZGEwMzI1YTU5ODE5ZDA1MjIxMmVjNmNhODZmZmQ0YTE1ZGY4Yzhl
14
+ ODYwOTY3NWEwOTU1ZWE4NDcwYzdhODcwYTI1YzliMmRkZTNkNDFjMjE1ZmI2
15
+ YzAwYTdlZjJkNTA3NDQxM2QyMzRmZWJkNGEyYjUzNTRjNDZhNzA=
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.4.0
@@ -1,7 +1,9 @@
1
1
  require 'pathname'
2
+ require 'fileutils'
2
3
 
3
4
  module DruidTools
4
5
  class Druid
6
+ @@deletes_directory_name = '.deletes'
5
7
  attr_accessor :druid, :base
6
8
 
7
9
  class << self
@@ -136,16 +138,84 @@ module DruidTools
136
138
  parent = this_path.parent
137
139
  parent.rmtree if parent.exist? && parent != base_pathname
138
140
  prune_ancestors parent.parent
141
+ creates_delete_record
139
142
  end
140
-
143
+
144
+ #This function checks for existance of a .deletes dir one level into the path (ex: stacks/.deletes or purl/.deletes).
145
+ #If the directory does not exist, it is created. If the directory exists, check to see if the current druid has an entry there, if it does delete it.
146
+ #This is done because a file might be deleted, then republishing, then deleted we again, and we want to log the most recent delete.
147
+ #
148
+ #@raises [Errno::EACCES] If write priveleges are denied
149
+ #
150
+ #@return [void]
151
+ def prep_deletes_dir
152
+ #Check for existences of deletes dir
153
+ create_deletes_dir if !deletes_dir_exists?
154
+ #In theory we could return true after this step (if it fires), since if there was no deletes dir then the file can't be present in the dir
155
+
156
+ #Check to see if this druid has been deleted before, meaning file currently exists
157
+ deletes_delete_record if deletes_record_exists?
158
+ end
159
+
160
+ #Provide the location for the .deletes directory in the tree
161
+ #
162
+ #@return [Pathname] the path to the directory, ex: "stacks/.deletes"
163
+ def deletes_dir_pathname
164
+ return Pathname(self.base.to_s + (File::SEPARATOR+@@deletes_directory_name))
165
+ end
166
+
167
+ def deletes_record_pathname
168
+ return Pathname(deletes_dir_pathname.to_s + File::SEPARATOR + self.id)
169
+ end
170
+
171
+ #Using the deletes directory path supplied by deletes_dir_pathname, this function determines if this directory exists
172
+ #
173
+ #@return [Boolean] true if if exists, false if it does not
174
+ def deletes_dir_exists?
175
+ return File.directory?(deletes_dir_pathname)
176
+ end
177
+
178
+ def deletes_record_exists?
179
+ return File.exists?(deletes_dir_pathname.to_s + File::SEPARATOR + self.id)
180
+ end
181
+
182
+ #Creates the deletes dir using the path supplied by deletes_dir_pathname
183
+ #
184
+ #@raises [Errno::EACCES] If write priveleges are denied
185
+ #
186
+ #@return [void]
187
+ def create_deletes_dir
188
+ FileUtils::mkdir_p deletes_dir_pathname
189
+ end
190
+
191
+ #Deletes the delete record if it currently exists. This is done to change the filed created, not just last modified time, on the system
192
+ #
193
+ #@raises [Errno::EACCES] If write priveleges are denied
194
+ #
195
+ #return [void]
196
+ def deletes_delete_record
197
+ FileUtils.rm(deletes_record_pathname) if deletes_record_exists? #thrown in to prevent an Errno::ENOENT if you call this on something without a delete record
198
+ end
199
+
200
+ #Creates an empty (pointer) file using the object's id in the .deletes dir
201
+ #
202
+ #@raises [Errno::EACCES] If write priveleges are denied
203
+ #
204
+ #@return [void]
205
+ def creates_delete_record
206
+ prep_deletes_dir
207
+ FileUtils.touch(deletes_record_pathname)
208
+ end
209
+
141
210
  # @param [Pathname] outermost_branch The branch at which pruning begins
142
211
  # @return [void] Ascend the druid tree and prune empty branches
143
212
  def prune_ancestors(outermost_branch)
144
- while outermost_branch.exist? && outermost_branch.children.size == 0
213
+ while outermost_branch.children.size == 0
145
214
  outermost_branch.rmdir
146
215
  outermost_branch = outermost_branch.parent
147
216
  break if outermost_branch == base_pathname
148
217
  end
218
+ rescue
149
219
  end
150
220
 
151
221
  end
@@ -16,7 +16,7 @@ describe DruidTools::Druid do
16
16
  FileUtils.rm_rf(File.join(@fixture_dir,'cd'))
17
17
  end
18
18
 
19
- it "should be able to validate druid strings using the valid? class method" do
19
+ it "validate druid strings using the valid? class method" do
20
20
  tests = [
21
21
  # Expected Input druid
22
22
  [true, 'druid:aa000bb0001'],
@@ -44,28 +44,28 @@ describe DruidTools::Druid do
44
44
  end
45
45
  end
46
46
 
47
- it "should provide the full druid including the prefix" do
47
+ it "provides the full druid including the prefix" do
48
48
  DruidTools::Druid.new('druid:cd456ef7890',@fixture_dir).druid.should == 'druid:cd456ef7890'
49
49
  DruidTools::Druid.new('cd456ef7890',@fixture_dir).druid.should == 'druid:cd456ef7890'
50
50
  end
51
51
 
52
- it "should extract the ID from the stem" do
52
+ it "extracts the ID from the stem" do
53
53
  DruidTools::Druid.new('druid:cd456ef7890',@fixture_dir).id.should == 'cd456ef7890'
54
54
  DruidTools::Druid.new('cd456ef7890',@fixture_dir).id.should == 'cd456ef7890'
55
55
  end
56
56
 
57
- it "should raise an exception if the druid is invalid" do
57
+ it "raises an exception if the druid is invalid" do
58
58
  lambda { DruidTools::Druid.new('nondruid:cd456ef7890',@fixture_dir) }.should raise_error(ArgumentError)
59
59
  lambda { DruidTools::Druid.new('druid:cd4567ef890',@fixture_dir) }.should raise_error(ArgumentError)
60
60
  end
61
61
 
62
- it "should build a druid tree from a druid" do
62
+ it "builds a druid tree from a druid" do
63
63
  druid = DruidTools::Druid.new(@druid_1,@fixture_dir)
64
64
  druid.tree.should == ['cd','456','ef','7890','cd456ef7890']
65
65
  druid.path.should == @tree_1
66
66
  end
67
67
 
68
- it "should create and destroy druid directories" do
68
+ it "creates and destroys druid directories" do
69
69
  File.exists?(@tree_1).should eq false
70
70
  File.exists?(@tree_2).should eq false
71
71
 
@@ -99,7 +99,7 @@ describe DruidTools::Druid do
99
99
  DruidTools::Druid.prefix = 'druid'
100
100
  end
101
101
 
102
- it "should handle alternate prefixes" do
102
+ it "handles alternate prefixes" do
103
103
  lambda { DruidTools::Druid.new('druid:cd456ef7890',@fixture_dir) }.should raise_error(ArgumentError)
104
104
  DruidTools::Druid.new('sulair:cd456ef7890',@fixture_dir).id.should == 'cd456ef7890'
105
105
  DruidTools::Druid.new('cd456ef7890',@fixture_dir).druid.should == 'sulair:cd456ef7890'
@@ -107,7 +107,7 @@ describe DruidTools::Druid do
107
107
  end
108
108
 
109
109
  describe "content directories" do
110
- it "should know where its content goes" do
110
+ it "knows where its content goes" do
111
111
  druid = DruidTools::Druid.new(@druid_1,@fixture_dir)
112
112
  druid.content_dir(false).should == File.join(@tree_1,'content')
113
113
  druid.metadata_dir(false).should == File.join(@tree_1,'metadata')
@@ -118,7 +118,7 @@ describe DruidTools::Druid do
118
118
  File.exists?(File.join(@tree_1,'temp')).should eq false
119
119
  end
120
120
 
121
- it "should create its content directories on the fly" do
121
+ it "creates its content directories on the fly" do
122
122
  druid = DruidTools::Druid.new(@druid_1,@fixture_dir)
123
123
  druid.content_dir.should == File.join(@tree_1,'content')
124
124
  druid.metadata_dir.should == File.join(@tree_1,'metadata')
@@ -129,7 +129,7 @@ describe DruidTools::Druid do
129
129
  File.exists?(File.join(@tree_1,'temp')).should eq true
130
130
  end
131
131
 
132
- it "should match glob" do
132
+ it "matches glob" do
133
133
  druid = DruidTools::Druid.new(@druid_1,@fixture_dir)
134
134
  druid.mkdir
135
135
  Dir.glob(File.join(File.dirname(druid.path), DruidTools::Druid::glob)).size.should == 1
@@ -142,37 +142,37 @@ describe DruidTools::Druid do
142
142
  @filelist = %w(1 2 3 4).collect { |num| "someFile#{num}" }
143
143
  end
144
144
 
145
- it "should find content in content directories" do
145
+ it "finds content in content directories" do
146
146
  location = @druid.content_dir
147
147
  File.open(File.join(location,'someContent'),'w') { |f| f.write 'This is the content' }
148
148
  @druid.find_content('someContent').should == File.join(location,'someContent')
149
149
  end
150
150
 
151
- it "should find content in the root directory" do
151
+ it "finds content in the root directory" do
152
152
  location = @druid.path(nil,true)
153
153
  File.open(File.join(location,'someContent'),'w') { |f| f.write 'This is the content' }
154
154
  @druid.find_content('someContent').should == File.join(location,'someContent')
155
155
  end
156
156
 
157
- it "should find content in the leaf directory" do
157
+ it "finds content in the leaf directory" do
158
158
  location = File.expand_path('..',@druid.path(nil,true))
159
159
  File.open(File.join(location,'someContent'),'w') { |f| f.write 'This is the content' }
160
160
  @druid.find_content('someContent').should == File.join(location,'someContent')
161
161
  end
162
162
 
163
- it "should not find content in the wrong content directory" do
163
+ it "does not find content in the wrong content directory" do
164
164
  location = @druid.metadata_dir
165
165
  File.open(File.join(location,'someContent'),'w') { |f| f.write 'This is the content' }
166
166
  @druid.find_content('someContent').should be_nil
167
167
  end
168
168
 
169
- it "should not find content in a higher-up directory" do
169
+ it "does not find content in a higher-up directory" do
170
170
  location = File.expand_path('../..',@druid.path(nil,true))
171
171
  File.open(File.join(location,'someContent'),'w') { |f| f.write 'This is the content' }
172
172
  @druid.find_content('someContent').should be_nil
173
173
  end
174
174
 
175
- it "should find a filelist in the content directory" do
175
+ it "finds a filelist in the content directory" do
176
176
  location = Pathname(@druid.content_dir)
177
177
  @filelist.each do |filename|
178
178
  location.join(filename).open('w') { |f| f.write "This is #{filename}" }
@@ -180,7 +180,7 @@ describe DruidTools::Druid do
180
180
  @druid.find_filelist_parent('content',@filelist).should == location
181
181
  end
182
182
 
183
- it "should find a filelist in the root directory" do
183
+ it "finds a filelist in the root directory" do
184
184
  location = Pathname(@druid.path(nil,true))
185
185
  @filelist.each do |filename|
186
186
  location.join(filename).open('w') { |f| f.write "This is #{filename}" }
@@ -188,7 +188,7 @@ describe DruidTools::Druid do
188
188
  @druid.find_filelist_parent('content',@filelist).should == location
189
189
  end
190
190
 
191
- it "should find a filelist in the leaf directory" do
191
+ it "finds a filelist in the leaf directory" do
192
192
  location = Pathname(File.expand_path('..',@druid.path(nil,true)))
193
193
  @filelist.each do |filename|
194
194
  location.join(filename).open('w') { |f| f.write "This is #{filename}" }
@@ -196,12 +196,12 @@ describe DruidTools::Druid do
196
196
  @druid.find_filelist_parent('content',@filelist).should == location
197
197
  end
198
198
 
199
- it "should raise an exception if the first file in the filelist is not found" do
199
+ it "raises an exception if the first file in the filelist is not found" do
200
200
  location = Pathname(@druid.content_dir)
201
201
  lambda{@druid.find_filelist_parent('content',@filelist)}.should raise_exception(/content dir not found for 'someFile1' when searching/)
202
202
  end
203
203
 
204
- it "should raise an exception if any other file in the filelist is not found" do
204
+ it "raises an exception if any other file in the filelist is not found" do
205
205
  location = Pathname(@druid.content_dir)
206
206
  location.join(@filelist.first).open('w') { |f| f.write "This is #{@filelist.first}" }
207
207
  lambda{@druid.find_filelist_parent('content',@filelist)}.should raise_exception(/File 'someFile2' not found/)
@@ -240,7 +240,7 @@ describe DruidTools::Druid do
240
240
  File.readlink(@tree_2).should == @source_dir
241
241
  end
242
242
 
243
- it "should not error out if the link to source already exists" do
243
+ it "does not error out if the link to source already exists" do
244
244
  @dr.mkdir_with_final_link(@source_dir)
245
245
  File.should be_symlink(@dr.path)
246
246
  File.readlink(@tree_2).should == @source_dir
@@ -250,6 +250,8 @@ describe DruidTools::Druid do
250
250
  @dr.mkdir(@fixture_dir)
251
251
  lambda { @dr.mkdir_with_final_link(@source_di) }.should raise_error(DruidTools::DifferentContentExistsError)
252
252
  end
253
+
254
+
253
255
  end
254
256
 
255
257
  describe "#prune!" do
@@ -259,7 +261,8 @@ describe DruidTools::Druid do
259
261
  let(:dr1) { DruidTools::Druid.new @druid_1, workspace }
260
262
  let(:dr2) { DruidTools::Druid.new @druid_2, workspace }
261
263
  let(:pathname1) { dr1.pathname }
262
-
264
+
265
+
263
266
  after(:each) do
264
267
  FileUtils.remove_entry workspace
265
268
  end
@@ -267,6 +270,8 @@ describe DruidTools::Druid do
267
270
  context "shared ancestor" do
268
271
 
269
272
  before(:each) do
273
+ #Nil the create records for this context because we're in a known read only one
274
+
270
275
  dr1.mkdir
271
276
  dr2.mkdir
272
277
  dr1.prune!
@@ -290,20 +295,123 @@ describe DruidTools::Druid do
290
295
  end
291
296
 
292
297
  it "removes all directories up to the base path when there are no common ancestors" do
298
+ #Make sure a delete record is not present
299
+ expect(dr1.deletes_record_exists?).to be_falsey
300
+
301
+ #Nil the create records for this test
293
302
  dr1.mkdir
294
303
  dr1.prune!
295
304
  expect(File).to_not exist(File.join(workspace, 'cd'))
296
305
  expect(File).to exist(workspace)
306
+
307
+ #Make sure a delete record was created
308
+ expect(dr1.deletes_dir_exists?).to be_truthy
309
+ expect(dr1.deletes_record_exists?).to be_truthy
297
310
  end
298
311
 
299
312
  it "removes directories with symlinks" do
313
+ #Make sure a delete record is not present
314
+ expect(dr2.deletes_record_exists?).to be_falsey
315
+
316
+ #Nil the create records for this test
300
317
  source_dir = File.join workspace, 'src_dir'
301
318
  FileUtils.mkdir_p(source_dir)
302
319
  dr2.mkdir_with_final_link(source_dir)
303
320
  dr2.prune!
304
321
  expect(File).to_not exist(dr2.path)
305
322
  expect(File).to_not exist(File.join(workspace, 'cd'))
323
+
324
+ #Make sure a delete record was created
325
+ expect(dr2.deletes_dir_exists?).to be_truthy
326
+ expect(dr2.deletes_record_exists?).to be_truthy
327
+ end
328
+
329
+ describe "logging deleted druids" do
330
+
331
+ #Purge any paths or delete records created in the test
332
+ after :each do
333
+ #Remove the .deletes dir to clean up
334
+ dr2.deletes_delete_record if dr2.deletes_record_exists?
335
+ FileUtils.rm_rf dr2.deletes_dir_pathname
336
+
337
+ end
338
+
339
+ it "returns the path to the .deletes directory as a Pathname" do
340
+ expect(dr2.deletes_dir_pathname.class).to eq(Pathname)
341
+ end
342
+
343
+ it "returns the path to the delete record for a druid as a Pathname" do
344
+ expect(dr2.deletes_record_pathname.class).to eq(Pathname)
345
+ end
346
+
347
+ it "returns the path to the delete record for a druid as top_level/.deletes/druid" do
348
+ expect(dr2.deletes_record_pathname.to_s).to eq("#{dr2.base}/.deletes/#{dr2.id}")
349
+ end
350
+
351
+ it "returns false when the .deletes dir is not present on the file system" do
352
+ expect(dr2.deletes_dir_exists?).to be_falsey
353
+ end
354
+
355
+ it "creates the .deletes dir and detect it exists" do
356
+
357
+ #Clean the .deletes dir if present
358
+ FileUtils.rm_rf dr2.deletes_dir_pathname
359
+
360
+ #Test for exists? and create
361
+ expect(dr2.deletes_dir_exists?).to be_falsey
362
+ dr2.create_deletes_dir
363
+ expect(dr2.deletes_dir_exists?).to be_truthy
364
+ end
365
+
366
+ it "returns false when the .deletes dir does not have a deleted record for a druid" do
367
+ expect(dr2.deletes_record_exists?).to be_falsey
368
+ end
369
+
370
+ it "creates a deleted record with a parent directory that has no .deletes directory and no deleted for the file and successfully create a delete record there" do
371
+ #Expect there not to be a .deletes dir or file (the file expectation is redundant I know)
372
+ expect(dr2.deletes_dir_exists?).to be_falsey
373
+ expect(dr2.deletes_record_exists?).to be_falsey
374
+
375
+ #Create the delete record
376
+ dr2.creates_delete_record
377
+
378
+ #Check to ensure items were created
379
+ expect(dr2.deletes_dir_exists?).to be_truthy
380
+ expect(dr2.deletes_record_exists?).to be_truthy
381
+ end
382
+
383
+ it "creates a delete record with a parent directory that has a .deletes directory that does not contain a delete record for this druid" do
384
+ #Expect there not to be a .deletes dir or file (the file expectation is redundant I know)
385
+ expect(dr2.deletes_dir_exists?).to be_falsey
386
+ expect(dr2.deletes_record_exists?).to be_falsey
387
+
388
+ #Creates the deletes dir and check
389
+ dr2.create_deletes_dir
390
+ expect(dr2.deletes_dir_exists?).to be_truthy
391
+ expect(dr2.deletes_record_exists?).to be_falsey
392
+
393
+ #Create the delete record
394
+ dr2.creates_delete_record
395
+
396
+ #Check to ensure items were created
397
+ expect(dr2.deletes_dir_exists?).to be_truthy
398
+ expect(dr2.deletes_record_exists?).to be_truthy
399
+ end
400
+
401
+ it "creates a delete record with a parent directory that does not have a .deletes directory and contains an older delete record" do
402
+ #Expect there not to be a .deletes dir or file (the file expectation is redundant I know)
403
+ expect(dr2.deletes_dir_exists?).to be_falsey
404
+ expect(dr2.deletes_record_exists?).to be_falsey
405
+
406
+ dr2.creates_delete_record
407
+ time = Time.now
408
+ expect(File.mtime(dr2.deletes_record_pathname)).to be <= time
409
+ sleep(1) #force a one second pause in case the machine is fast (as in not some old Commodore64), since mtime only goes down to the second
410
+
411
+ dr2.creates_delete_record
412
+ #Should have a new newer deleted record
413
+ expect(File.mtime(dr2.deletes_record_pathname)).to be > time
414
+ end
306
415
  end
307
416
  end
308
-
309
417
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: druid-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Klein
@@ -9,34 +9,34 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-04-18 00:00:00.000000000 Z
12
+ date: 2015-03-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
18
+ - - ! '>='
19
19
  - !ruby/object:Gem::Version
20
20
  version: 10.1.0
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ">="
25
+ - - ! '>='
26
26
  - !ruby/object:Gem::Version
27
27
  version: 10.1.0
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rspec
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ~>
33
33
  - !ruby/object:Gem::Version
34
34
  version: '3.0'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ~>
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.0'
42
42
  description: Tools to manipulate DRUID trees and content directories
@@ -46,7 +46,7 @@ executables: []
46
46
  extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
- - ".gitignore"
49
+ - .gitignore
50
50
  - Gemfile
51
51
  - LICENSE
52
52
  - README.md
@@ -103,17 +103,17 @@ require_paths:
103
103
  - lib
104
104
  required_ruby_version: !ruby/object:Gem::Requirement
105
105
  requirements:
106
- - - ">="
106
+ - - ! '>='
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  required_rubygems_version: !ruby/object:Gem::Requirement
110
110
  requirements:
111
- - - ">="
111
+ - - ! '>='
112
112
  - !ruby/object:Gem::Version
113
113
  version: '0'
114
114
  requirements: []
115
115
  rubyforge_project:
116
- rubygems_version: 2.4.5
116
+ rubygems_version: 2.1.2
117
117
  signing_key:
118
118
  specification_version: 4
119
119
  summary: Tools to manipulate DRUID trees and content directories