ocfl 0.6.0 → 0.8.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,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)