ocfl 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b32346d0bd14e71b28385a1e34ad4f4af73335ab64345fa374c890ccf07e4f83
4
- data.tar.gz: 4f9f6bc929cdee4f24e3b8b4bca167d69219d34fb74de00ff75414a68ee66ad2
3
+ metadata.gz: 9b1b7553fa1d862a3aa1f6a7f63b148f424f1448c321f7b534776c857ce932f2
4
+ data.tar.gz: 8408cf71f4a08fc2c44485ea69e6f77eb5e49366a80d460d20e7ab7961ef9442
5
5
  SHA512:
6
- metadata.gz: e9a520d6bf056649a3b4c261ea27fb6d5a994d8f0bd274f5a8aca19751e24fa4c1ca08de57ee7971b927081f4dcb941a514570deba8088580579613123a1d1f1
7
- data.tar.gz: a3df7b8ae6913eee74a0bea12474ee4c7ebe937bd1b92c28c8918e2a00c46d82191e99a360dacd780b1be65f1bdab25dbca7edfa47f3d03b8d4cf8e3ef534ebe
6
+ metadata.gz: 5420543eb4d07840970600fbc1f4a475e2ae3f09dea57de76087e626ed87a99ec9b02cf4dd38598398fb75aaa1e8eb61fb20d2af4d4b2b9939a269cf811d7b13
7
+ data.tar.gz: f3f6b84156e307897c3be76ed247043ce5d5f161536267b2ee5cfb8a91b50c6080da57ba6fc5c063349603784a13689ef4bbc74c624cbf302ec3f50810b53307
data/.rubocop.yml CHANGED
@@ -28,6 +28,9 @@ RSpec/MultipleExpectations:
28
28
  RSpec/ExampleLength:
29
29
  Max: 10
30
30
 
31
+ RSpec/MultipleMemoizedHelpers:
32
+ Max: 8
33
+
31
34
  RSpec/NestedGroups:
32
35
  Max: 4
33
36
 
data/README.md CHANGED
@@ -20,25 +20,54 @@ directory = OCFL::Object::Directory.new(object_root: '/files/[object_root]')
20
20
  directory.exists?
21
21
  # => false
22
22
  builder = OCFL::Object::DirectoryBuilder.new(object_root: 'spec/abc123', id: 'http://example.com/abc123')
23
- builder.copy_file('sig/ocfl.rbs')
23
+ builder.copy_file('sig/ocfl.rbs', destination_path: 'ocfl/types/generated.rbs')
24
24
 
25
25
  directory = builder.save
26
26
  directory.exists?
27
27
  # => true
28
28
  directory.valid?
29
29
  # => true
30
+ ```
31
+
32
+ ### Versions
33
+
34
+ There are three ways to get a version with an existing object directory.
30
35
 
36
+ #### Start a new version
37
+ ```
31
38
  new_version = directory.begin_new_version
32
39
  new_version.copy_file('sig/ocfl.rbs')
33
40
  new_version.save
34
41
 
35
42
  directory.head
36
43
  # => 'v2'
44
+ ```
45
+
46
+ #### Modify the existing head version
47
+ ```
48
+ new_version = directory.head_version
49
+ new_version.delete_file('sample.txt')
50
+ new_version.copy_file('sig/ocfl.rbs')
51
+ new_version.save
52
+ ```
53
+
54
+ #### Overwrite the existing head version
55
+ ```
56
+ new_version = directory.overwrite_current_version
57
+ new_version.copy_file('sig/ocfl.rbs')
58
+ new_version.save
59
+ ```
37
60
 
61
+ ### File paths
62
+ ```
38
63
  # List file names that were part of a given version
39
64
  directory.versions['v2'].file_names
40
65
  # => ["ocfl.rbs"]
41
66
 
67
+ # Or on the head version
68
+ directory.head_version.file_names
69
+ # => ["ocfl.rbs"]
70
+
42
71
  # Get the path of a file in a given version
43
72
  directory.path(filepath: "ocfl.rbs", version: "v2")
44
73
  # => <Pathname:/files/[object_root]/v2/content/ocfl.rbs>
@@ -47,9 +76,6 @@ directory.path(filepath: "ocfl.rbs", version: "v2")
47
76
  directory.path(filepath: "ocfl.rbs")
48
77
  # => <Pathname:/files/[object_root]/v2/content/ocfl.rbs>
49
78
 
50
- new_version = directory.overwrite_current_version
51
- new_version.copy_file('sig/ocfl.rbs')
52
- new_version.save
53
79
  ```
54
80
 
55
81
  ## Development
@@ -64,10 +64,17 @@ module OCFL
64
64
  true
65
65
  end
66
66
 
67
+ # Start a completely new version
67
68
  def begin_new_version
68
69
  DraftVersion.new(object_directory: self, state: head_inventory.state)
69
70
  end
70
71
 
72
+ # Get a handle for the head version
73
+ def head_version
74
+ DraftVersion.new(object_directory: self, overwrite_head: true, state: head_inventory.state)
75
+ end
76
+
77
+ # Get a handle that will replace the existing head version
71
78
  def overwrite_current_version
72
79
  DraftVersion.new(object_directory: self, overwrite_head: true)
73
80
  end
@@ -33,12 +33,12 @@ module OCFL
33
33
 
34
34
  def create_object_directory
35
35
  FileUtils.mkdir_p(object_root)
36
+ FileUtils.touch(object_directory.namaste_file) unless File.exist?(object_directory.namaste_file)
36
37
  end
37
38
 
38
39
  # @return [Directory]
39
40
  def save
40
- FileUtils.mkdir_p(object_root)
41
- FileUtils.touch(object_directory.namaste_file)
41
+ create_object_directory
42
42
  write_inventory
43
43
  object_directory
44
44
  end
@@ -17,6 +17,8 @@ module OCFL
17
17
 
18
18
  attr_reader :object_directory, :manifest, :state, :version_number
19
19
 
20
+ delegate :file_names, to: :to_version_struct
21
+
20
22
  def move_file(incoming_path)
21
23
  prepare_content_directory
22
24
  add(incoming_path)
@@ -25,7 +27,24 @@ module OCFL
25
27
 
26
28
  def copy_file(incoming_path, destination_path: "")
27
29
  prepare_content_directory
28
- copy_one(File.basename(incoming_path), incoming_path, destination_path)
30
+ copy_one(destination_path.presence || File.basename(incoming_path), incoming_path)
31
+ end
32
+
33
+ def digest_for_filename(filename)
34
+ state.find { |_, filenames| filenames.include?(filename) }&.first
35
+ end
36
+
37
+ # Note, this only removes the file from this version. Previous versions may still use it.
38
+ def delete_file(filename)
39
+ sha512_digest = digest_for_filename(filename)
40
+ raise "Unknown file: #{filename}" unless sha512_digest
41
+
42
+ state.delete(sha512_digest)
43
+ # If the manifest points at the current content directory, then we can delete it.
44
+ file_paths = manifest[sha512_digest]
45
+ return unless file_paths.all? { |path| path.start_with?("#{version_number}/") }
46
+
47
+ File.unlink (object_directory.object_root + file_paths.first).to_s
29
48
  end
30
49
 
31
50
  # Copies files into the object and preserves their relative paths as logical directories in the object
@@ -33,7 +52,10 @@ module OCFL
33
52
  prepare_content_directory
34
53
  incoming_path = incoming_path.delete_suffix("/")
35
54
  Dir.glob("#{incoming_path}/**/*").reject { |fn| File.directory?(fn) }.each do |file|
36
- copy_one(file.delete_prefix(incoming_path).delete_prefix("/"), file, destination_path)
55
+ logical_file_path = file.delete_prefix(incoming_path).delete_prefix("/")
56
+ logical_file_path = File.join(destination_path, logical_file_path) unless destination_path.empty?
57
+
58
+ copy_one(logical_file_path, file)
37
59
  end
38
60
  end
39
61
 
@@ -43,6 +65,10 @@ module OCFL
43
65
  object_directory.reload
44
66
  end
45
67
 
68
+ def to_version_struct
69
+ Version.new(state:, created: Time.now.utc.iso8601)
70
+ end
71
+
46
72
  private
47
73
 
48
74
  def write_inventory(inventory)
@@ -51,8 +77,9 @@ module OCFL
51
77
  FileUtils.cp(path / "inventory.json.sha512", object_directory.object_root)
52
78
  end
53
79
 
54
- def copy_one(logical_file_path, incoming_path, destination_path)
55
- logical_file_path = File.join(destination_path, logical_file_path) unless destination_path.empty?
80
+ # @param [String] logical_file_path where we're going to store the file (e.g. 'object/directory_builder_spec.rb')
81
+ # @param [String] incoming_path where's this file from (e.g. 'spec/ocfl/object/directory_builder_spec.rb')
82
+ def copy_one(logical_file_path, incoming_path)
56
83
  add(incoming_path, logical_file_path:)
57
84
  parent_dir = (content_path / logical_file_path).parent
58
85
  FileUtils.mkdir_p(parent_dir) unless parent_dir == content_path
@@ -93,8 +120,8 @@ module OCFL
93
120
  def build_inventory
94
121
  old_data = object_directory.inventory.data
95
122
  versions = versions(old_data.versions)
96
- # Prune items from manifest if they are not part of any version
97
123
 
124
+ # Prune items from manifest if they are not part of any version
98
125
  Inventory::InventoryStruct.new(old_data.to_h.merge(manifest: filtered_manifest(versions),
99
126
  head: version_number, versions:))
100
127
  end
@@ -102,13 +129,14 @@ module OCFL
102
129
  # This gives the update list of versions. The old list plus this new one.
103
130
  # @param [Hash] old_versions the versions prior to this one.
104
131
  def versions(old_versions)
105
- old_versions.merge(version_number => Version.new(created: Time.now.utc.iso8601, state: @state))
132
+ old_versions.merge(version_number => to_version_struct)
106
133
  end
107
134
 
108
135
  # The manifest after unused SHAs have been filtered out.
109
136
  def filtered_manifest(versions)
110
137
  shas_in_versions = versions.values.flat_map { |v| v.state.keys }.uniq
111
- manifest.slice(*shas_in_versions)
138
+ manifest.slice!(*shas_in_versions)
139
+ manifest
112
140
  end
113
141
  end
114
142
  end
data/lib/ocfl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OCFL
4
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0"
5
5
  end
data/lib/ocfl.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "zeitwerk"
4
4
  require "active_support"
5
5
  require "active_support/core_ext/module/delegation"
6
+ require "active_support/core_ext/object/blank"
6
7
  require "digest"
7
8
  require "dry/monads"
8
9
  require "dry-schema"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ocfl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-24 00:00:00.000000000 Z
11
+ date: 2024-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 3.5.10
128
+ rubygems_version: 3.4.19
129
129
  signing_key:
130
130
  specification_version: 4
131
131
  summary: A ruby library for interacting with the Oxford Common File Layout (OCFL)