assembly-objectfile 1.8.3 → 1.8.4

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: 9fe45aaa147dfaac9e82820d38af8de6247116031979ff68f711954af0ff9bc8
4
- data.tar.gz: 4dca70b7bf315d8520b523cfd516d78211201c5092dd36b44d8cf855ccf6247f
3
+ metadata.gz: 55d4581c113766a963826ca6e77b15edcab495dfe1a58d029515ac63f71dab61
4
+ data.tar.gz: 6345774179c1af7b1bb179dcb079c79f0c48d15418d15b6c9a8010d2a81d66a8
5
5
  SHA512:
6
- metadata.gz: 4573713a41500722359af8a99e4972fcd6e7e566a864ddbc277bfecf6228bae9b2ea7194550ce43278189b13480e0e380ee72c1d7690305367f497442c87bf02
7
- data.tar.gz: 5c09f8e544d3479c87caf0793bb632389bcb01a8b7c22ce369344ad99f43bd0f1f78f0b055dfe121a1f63c430c3ba8b82f1793c006e8a7d8306bb89306add142
6
+ metadata.gz: 834d7062c0f3ffa547d5c88e051a5148598cbd6ec0debc9b0318da8c97725e9b136ee5cacbd64b49d1cf5e0bd7481ae4eaa5061e9b247772915b821f2af1c0d3
7
+ data.tar.gz: 3c2b9d1496af4400a57b383de90df51b02775e6f15afb6ca869607a4c890dd489cd932f99f473fb346de7fea81121e731ab1e04fb7c58dd4f6ee77bedda37ae3
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Bug report
3
+ about: File a bug report
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **User Impact**
14
+ A description of the impact of this bug on an end-user (or your own work).
15
+
16
+ **To Reproduce**
17
+ Steps to reproduce the behavior:
18
+ 1. Go to '...'
19
+ 2. Click on '....'
20
+ 3. Scroll down to '....'
21
+ 4. See error
22
+
23
+ **Expected behavior**
24
+ A clear and concise description of what you expected to happen.
25
+
26
+ **Screenshots**
27
+ If applicable, add screenshots to help explain your problem.
28
+
29
+ **Additional context**
30
+ Add any other context about the problem here.
@@ -0,0 +1,5 @@
1
+ ## Why was this change made?
2
+
3
+
4
+
5
+ ## Was the documentation updated?
@@ -1,10 +1,6 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
  require: rubocop-rspec
3
3
 
4
- # Configuration parameters: AllowURI, URISchemes.
5
- Metrics/LineLength:
6
- Max: 200
7
-
8
4
  RSpec/ContextWording:
9
5
  Enabled: false # too dogmatic
10
6
 
@@ -15,8 +11,5 @@ RSpec/ExampleLength:
15
11
  RSpec/MessageSpies:
16
12
  Enabled: false
17
13
 
18
- RSpec/MultipleExpectations:
19
- Max: 5
20
-
21
14
  RSpec/NestedGroups:
22
15
  Max: 4 # default: 3
@@ -1,78 +1,62 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-09-13 12:44:21 -0700 using RuboCop version 0.59.0.
3
+ # on 2019-10-17 23:28:35 -0500 using RuboCop version 0.75.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
9
  # Offense count: 1
10
- # Cop supports --auto-correct.
11
- # Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity.
12
- # SupportedStylesAlignWith: keyword, variable, start_of_line
13
- Layout/EndAlignment:
10
+ Lint/AmbiguousBlockAssociation:
14
11
  Exclude:
15
- - 'lib/assembly-objectfile/content_metadata.rb'
12
+ - 'spec/content_metadata_spec.rb'
16
13
 
17
14
  # Offense count: 1
18
15
  Lint/UselessAssignment:
19
16
  Exclude:
20
17
  - 'config/boot.rb'
21
18
 
22
- # Offense count: 5
23
- # Configuration parameters: CheckForMethodsWithNoSideEffects.
24
- Lint/Void:
25
- Exclude:
26
- - 'spec/content_metadata_spec.rb'
27
-
28
19
  # Offense count: 3
29
20
  Metrics/AbcSize:
30
- Max: 170
21
+ Max: 51
31
22
 
32
- # Offense count: 15
23
+ # Offense count: 11
33
24
  # Configuration parameters: CountComments, ExcludedMethods.
34
25
  # ExcludedMethods: refine
35
26
  Metrics/BlockLength:
36
- Max: 549
27
+ Max: 577
37
28
 
38
- # Offense count: 1
39
- # Configuration parameters: CountComments.
40
- Metrics/ClassLength:
41
- Max: 137
42
-
43
- # Offense count: 1
29
+ # Offense count: 3
44
30
  Metrics/CyclomaticComplexity:
45
- Max: 63
46
-
47
- # Offense count: 26
48
- # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
49
- # URISchemes: http, https
50
- Metrics/LineLength:
51
- Max: 304
31
+ Max: 14
52
32
 
53
- # Offense count: 1
54
- # Configuration parameters: CountComments.
33
+ # Offense count: 5
34
+ # Configuration parameters: CountComments, ExcludedMethods.
55
35
  Metrics/MethodLength:
56
- Max: 131
36
+ Max: 30
57
37
 
58
38
  # Offense count: 1
59
39
  # Configuration parameters: CountComments.
60
40
  Metrics/ModuleLength:
61
- Max: 106
41
+ Max: 107
62
42
 
63
43
  # Offense count: 1
64
- Metrics/PerceivedComplexity:
65
- Max: 59
44
+ # Configuration parameters: CountKeywordArgs.
45
+ Metrics/ParameterLists:
46
+ Max: 11
66
47
 
67
48
  # Offense count: 2
49
+ Metrics/PerceivedComplexity:
50
+ Max: 12
51
+
52
+ # Offense count: 1
68
53
  # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
69
54
  # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
70
55
  Naming/FileName:
71
56
  Exclude:
72
- - 'assembly-objectfile.gemspec'
73
57
  - 'lib/assembly-objectfile.rb'
74
58
 
75
- # Offense count: 2
59
+ # Offense count: 1
76
60
  # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
77
61
  # NamePrefix: is_, has_, have_
78
62
  # NamePrefixBlacklist: is_, has_, have_
@@ -81,7 +65,6 @@ Naming/FileName:
81
65
  Naming/PredicateName:
82
66
  Exclude:
83
67
  - 'spec/**/*'
84
- - 'lib/assembly-objectfile/content_metadata.rb'
85
68
  - 'lib/assembly-objectfile/object_fileable.rb'
86
69
 
87
70
  # Offense count: 8
@@ -97,13 +80,13 @@ RSpec/FilePath:
97
80
  - 'spec/content_metadata_spec.rb'
98
81
  - 'spec/object_file_spec.rb'
99
82
 
100
- # Offense count: 60
83
+ # Offense count: 67
101
84
  # Configuration parameters: AssignmentOnly.
102
85
  RSpec/InstanceVariable:
103
86
  Exclude:
104
87
  - 'spec/object_file_spec.rb'
105
88
 
106
- # Offense count: 28
89
+ # Offense count: 38
107
90
  # Configuration parameters: AggregateFailuresByDefault.
108
91
  RSpec/MultipleExpectations:
109
92
  Max: 29
@@ -113,20 +96,25 @@ RSpec/RepeatedDescription:
113
96
  Exclude:
114
97
  - 'spec/object_file_spec.rb'
115
98
 
116
- # Offense count: 6
117
- Style/CommentedKeyword:
99
+ # Offense count: 2
100
+ RSpec/RepeatedExample:
118
101
  Exclude:
119
- - 'lib/assembly-objectfile/content_metadata.rb'
102
+ - 'spec/object_file_spec.rb'
120
103
 
121
- # Offense count: 1
122
- Style/Documentation:
104
+ # Offense count: 2
105
+ Style/CommentedKeyword:
123
106
  Exclude:
124
- - 'spec/**/*'
125
- - 'test/**/*'
126
- - 'lib/assembly-objectfile.rb'
107
+ - 'lib/assembly-objectfile/content_metadata.rb'
127
108
 
128
109
  # Offense count: 1
129
110
  # Configuration parameters: MinBodyLength.
130
111
  Style/GuardClause:
131
112
  Exclude:
132
113
  - 'lib/assembly-objectfile/object_file.rb'
114
+
115
+ # Offense count: 346
116
+ # Cop supports --auto-correct.
117
+ # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
118
+ # URISchemes: http, https
119
+ Metrics/LineLength:
120
+ Max: 304
@@ -1,9 +1,7 @@
1
-
2
1
  language: ruby
3
2
  rvm:
4
- - 2.2.5
5
- - 2.3.1
6
- - 2.5.3
3
+ - 2.5.7
4
+ - 2.6.5
7
5
  addons:
8
6
  apt:
9
7
  packages:
@@ -13,6 +11,7 @@ before_script:
13
11
  - chmod +x ./cc-test-reporter
14
12
  - ./cc-test-reporter before-build
15
13
  script:
14
+ - bundle exec rubocop
16
15
  - bundle exec rake
17
16
  after_script:
18
17
  - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'http://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in .gemspec
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
 
3
5
  desc 'Run console with irb (default), pry, etc.'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.push File.expand_path('lib', __dir__)
2
4
  require 'assembly-objectfile/version'
3
5
 
@@ -12,14 +14,15 @@ Gem::Specification.new do |s|
12
14
  s.description = 'Get exif data, file sizes and more.'
13
15
  s.license = 'ALv2'
14
16
 
15
- s.rubyforge_project = 'assembly-objectfile'
16
-
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- spec/*`.split("\n")
19
19
  s.bindir = 'exe'
20
20
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  s.require_paths = ['lib']
22
22
 
23
+ s.add_dependency 'activesupport', '>= 5.2.0'
24
+ s.add_dependency 'dry-struct', '~> 1.0'
25
+ s.add_dependency 'dry-types', '~> 1.1'
23
26
  s.add_dependency 'mime-types', '> 3'
24
27
  s.add_dependency 'mini_exiftool'
25
28
  s.add_dependency 'nokogiri'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
 
3
5
  environment = ENV['ENVIRONMENT'] ||= 'development'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Assembly
2
4
  # the path to the gem, used to access profiles stored with the gem
3
5
  PATH_TO_GEM = File.expand_path(File.dirname(__FILE__) + '/..')
@@ -14,23 +16,24 @@ module Assembly
14
16
  TRUSTED_MIMETYPES = ['text/plain', 'plain/text', 'application/pdf', 'text/html', 'application/xml'].freeze
15
17
 
16
18
  # default publish/preserve/shelve attributes used in content metadata
17
- FILE_ATTRIBUTES = {}
18
19
  # if no mimetype specific attributes are specified for a given file, define some defaults, and override for specific mimetypes below
19
- FILE_ATTRIBUTES['default'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
20
- FILE_ATTRIBUTES['image/tif'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
21
- FILE_ATTRIBUTES['image/tiff'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
22
- FILE_ATTRIBUTES['image/jp2'] = { preserve: 'no', shelve: 'yes', publish: 'yes' }
23
- FILE_ATTRIBUTES['image/jpeg'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
24
- FILE_ATTRIBUTES['audio/wav'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
25
- FILE_ATTRIBUTES['audio/x-wav'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
26
- FILE_ATTRIBUTES['audio/mp3'] = { preserve: 'no', shelve: 'yes', publish: 'yes' }
27
- FILE_ATTRIBUTES['audio/mpeg'] = { preserve: 'no', shelve: 'yes', publish: 'yes' }
28
- FILE_ATTRIBUTES['application/pdf'] = { preserve: 'yes', shelve: 'yes', publish: 'yes' }
29
- FILE_ATTRIBUTES['plain/text'] = { preserve: 'yes', shelve: 'yes', publish: 'yes' }
30
- FILE_ATTRIBUTES['text/plain'] = { preserve: 'yes', shelve: 'yes', publish: 'yes' }
31
- FILE_ATTRIBUTES['image/png'] = { preserve: 'yes', shelve: 'yes', publish: 'no'}
32
- FILE_ATTRIBUTES['application/zip'] = { preserve: 'yes', shelve: 'no', publish: 'no' }
33
- FILE_ATTRIBUTES['application/json'] = { preserve: 'yes', shelve: 'yes', publish: 'yes' }
20
+ FILE_ATTRIBUTES = {
21
+ 'default' => { preserve: 'yes', shelve: 'no', publish: 'no' },
22
+ 'image/tif' => { preserve: 'yes', shelve: 'no', publish: 'no' },
23
+ 'image/tiff' => { preserve: 'yes', shelve: 'no', publish: 'no' },
24
+ 'image/jp2' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
25
+ 'image/jpeg' => { preserve: 'yes', shelve: 'no', publish: 'no' },
26
+ 'audio/wav' => { preserve: 'yes', shelve: 'no', publish: 'no' },
27
+ 'audio/x-wav' => { preserve: 'yes', shelve: 'no', publish: 'no' },
28
+ 'audio/mp3' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
29
+ 'audio/mpeg' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
30
+ 'application/pdf' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
31
+ 'plain/text' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
32
+ 'text/plain' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
33
+ 'image/png' => { preserve: 'yes', shelve: 'yes', publish: 'no' },
34
+ 'application/zip' => { preserve: 'yes', shelve: 'no', publish: 'no' },
35
+ 'application/json' => { preserve: 'yes', shelve: 'yes', publish: 'yes' }
36
+ }.freeze
34
37
  end
35
38
 
36
39
  require 'assembly-objectfile/content_metadata'
@@ -1,4 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'nokogiri'
4
+ require 'active_support'
5
+ require 'assembly-objectfile/content_metadata/file'
6
+ require 'assembly-objectfile/content_metadata/file_set'
7
+ require 'assembly-objectfile/content_metadata/file_set_builder'
8
+ require 'assembly-objectfile/content_metadata/config'
9
+ require 'assembly-objectfile/content_metadata/nokogiri_builder'
2
10
 
3
11
  module Assembly
4
12
  SPECIAL_DPG_FOLDERS = %w[31 44 50].freeze # these special dpg folders will force any files contained in them into their own resources, regardless of filenaming convention
@@ -43,195 +51,69 @@ module Assembly
43
51
  # :auto_labels = optional - Will add automated resource labels (e.g. "File 1") when labels are not provided by the user. The default is true.
44
52
  # Example:
45
53
  # Assembly::ContentMetadata.create_content_metadata(:druid=>'druid:nx288wh8889',:style=>:simple_image,:objects=>object_files,:add_file_attributes=>false)
46
- def self.create_content_metadata(params = {})
47
- druid = params[:druid]
48
- objects = params[:objects]
49
-
50
- raise 'No objects and/or druid supplied' if druid.nil? || objects.nil?
51
-
52
- pid = druid.gsub('druid:', '') # remove druid prefix when creating IDs
53
-
54
- style = params[:style] || :simple_image
55
- bundle = params[:bundle] || :default
56
- add_exif = params[:add_exif] || false
57
- auto_labels = (params[:auto_labels].nil? ? true : params[:auto_labels])
58
- add_file_attributes = params[:add_file_attributes] || false
59
- file_attributes = params[:file_attributes] || {}
60
- preserve_common_paths = params[:preserve_common_paths] || false
61
- flatten_folder_structure = params[:flatten_folder_structure] || false
62
- include_root_xml = params[:include_root_xml]
63
-
64
- all_paths = []
65
- objects.flatten.each do |obj|
66
- raise "File '#{obj.path}' not found" unless obj.file_exists?
54
+ def self.create_content_metadata(druid:, objects:, auto_labels: true,
55
+ add_exif: false, bundle: :default, style: :simple_image,
56
+ add_file_attributes: false, file_attributes: {},
57
+ preserve_common_paths: false, flatten_folder_structure: false,
58
+ include_root_xml: nil)
67
59
 
68
- all_paths << obj.path unless preserve_common_paths # collect all of the filenames into an array
69
- end
60
+ common_path = find_common_path(objects) unless preserve_common_paths # find common paths to all files provided if needed
61
+
62
+ filesets = FileSetBuilder.build(bundle: bundle, objects: objects, style: style)
63
+
64
+ config = Config.new(auto_labels: auto_labels,
65
+ flatten_folder_structure: flatten_folder_structure,
66
+ add_file_attributes: add_file_attributes,
67
+ file_attributes: file_attributes,
68
+ add_exif: add_exif,
69
+ type: object_level_type(style))
70
+
71
+ builder = NokogiriBuilder.build(druid: druid,
72
+ filesets: filesets,
73
+ common_path: common_path,
74
+ config: config)
75
+
76
+ result = if include_root_xml == false
77
+ builder.doc.root.to_xml
78
+ else
79
+ builder.to_xml
80
+ end
81
+
82
+ result
83
+ end
70
84
 
71
- common_path = Assembly::ObjectFile.common_path(all_paths) unless preserve_common_paths # find common paths to all files provided if needed
85
+ def self.special_dpg_folder?(folder)
86
+ SPECIAL_DPG_FOLDERS.include?(folder)
87
+ end
72
88
 
73
- # these are the valid strings for each type of document to be use contentMetadata type and resourceType
74
- content_type_descriptions = { file: 'file', image: 'image', book: 'book', map: 'map', '3d': '3d' }
75
- resource_type_descriptions = { object: 'object', file: 'file', image: 'image', book: 'page', map: 'image', '3d': '3d' }
89
+ def self.find_common_path(objects)
90
+ all_paths = objects.flatten.map do |obj|
91
+ raise "File '#{obj.path}' not found" unless obj.file_exists?
92
+
93
+ obj.path # collect all of the filenames into an array
94
+ end
76
95
 
77
- # global sequence for resource IDs
78
- sequence = 0
96
+ Assembly::ObjectFile.common_path(all_paths) # find common paths to all files provided if needed
97
+ end
98
+ private_class_method :find_common_path
79
99
 
80
- # a counter to use when creating auto-labels for resources, with incremenets for each type
81
- resource_type_counters = Hash.new(0)
100
+ def self.object_level_type(style)
101
+ puts "WARNING - the style #{style} is now deprecated and should not be used." if DEPRECATED_STYLES.include? style
82
102
 
83
- # set the object level content type id
84
103
  case style
85
104
  when :simple_image
86
- content_type_description = content_type_descriptions[:image]
105
+ 'image'
87
106
  when :file
88
- content_type_description = content_type_descriptions[:file]
107
+ 'file'
89
108
  when :simple_book, :book_with_pdf, :book_as_image
90
- content_type_description = content_type_descriptions[:book]
109
+ 'book'
91
110
  when :map
92
- content_type_description = content_type_descriptions[:map]
111
+ 'map'
93
112
  when :'3d'
94
- content_type_description = content_type_descriptions[:'3d']
113
+ '3d'
95
114
  else
96
115
  raise "Supplied style (#{style}) not valid"
97
116
  end
98
-
99
- puts "WARNING - the style #{style} is now deprecated and should not be used." if DEPRECATED_STYLES.include? style
100
-
101
- # determine how many resources to create
102
- # setup an array of arrays, where the first array is the number of resources, and the second array is the object files containined in that resource
103
- case bundle
104
- when :default # one resource per object
105
- resources = objects.collect { |obj| [obj] }
106
- when :filename # one resource per distinct filename (excluding extension)
107
- # loop over distinct filenames, this determines how many resources we will have and
108
- # create one resource node per distinct filename, collecting the relevant objects with the distinct filename into that resource
109
- resources = []
110
- distinct_filenames = objects.collect(&:filename_without_ext).uniq # find all the unique filenames in the set of objects, leaving off extensions and base paths
111
- distinct_filenames.each { |distinct_filename| resources << objects.collect { |obj| obj if obj.filename_without_ext == distinct_filename }.compact }
112
- when :dpg # group by DPG filename
113
- # loop over distinct dpg base names, this determines how many resources we will have and
114
- # create one resource node per distinct dpg base name, collecting the relevant objects with the distinct names into that resource
115
- resources = []
116
- distinct_filenames = objects.collect(&:dpg_basename).uniq # find all the unique DPG filenames in the set of objects
117
- distinct_filenames.each do |distinct_filename|
118
- resources << objects.collect { |obj| obj if obj.dpg_basename == distinct_filename && !is_special_dpg_folder?(obj.dpg_folder) }.compact
119
- end
120
- objects.each { |obj| resources << [obj] if is_special_dpg_folder?(obj.dpg_folder) } # certain subfolders require individual resources for files within them regardless of file-naming convention
121
- when :prebundled
122
- # if the user specifies this method, they will pass in an array of arrays, indicating resources, so we don't need to bundle in the gem
123
- resources = objects
124
- else
125
- raise 'Invalid bundle method'
126
- end
127
-
128
- resources.delete([]) # delete any empty elements
129
-
130
- builder = Nokogiri::XML::Builder.new do |xml|
131
- xml.contentMetadata(objectId: druid.to_s, type: content_type_description) do
132
- resources.each do |resource_files| # iterate over all the resources
133
- # start a new resource element
134
- sequence += 1
135
- resource_id = "#{pid}_#{sequence}"
136
-
137
- # grab all of the file types within a resource into an array so we can decide what the resource type should be
138
- resource_file_types = resource_files.collect(&:object_type)
139
- resource_has_non_images = !(resource_file_types - [:image]).empty?
140
- resource_from_special_dpg_folder = resource_files.collect { |obj| is_special_dpg_folder?(obj.dpg_folder) }.uniq
141
-
142
- if bundle == :dpg && resource_from_special_dpg_folder.include?(true) # objects in the special DPG folders are always type=object when we using :bundle=>:dpg
143
- resource_type_description = resource_type_descriptions[:object]
144
- else # otherwise look at the style to determine the resource_type_description
145
- case style
146
- when :simple_image
147
- resource_type_description = resource_type_descriptions[:image]
148
- when :file
149
- resource_type_description = resource_type_descriptions[:file]
150
- when :simple_book # in a simple book project, all resources are pages unless they are *all* non-images -- if so, switch the type to object
151
- resource_type_description = resource_has_non_images && resource_file_types.include?(:image) == false ? resource_type_descriptions[:object] : resource_type_descriptions[:book]
152
- when :book_as_image # same as simple book, but all resources are images instead of pages, unless we need to switch them to object type
153
- resource_type_description = resource_has_non_images && resource_file_types.include?(:image) == false ? resource_type_descriptions[:object] : resource_type_descriptions[:image]
154
- when :book_with_pdf # in book with PDF type, if we find a resource with *any* non images, switch it's type from book to object
155
- resource_type_description = resource_has_non_images ? resource_type_descriptions[:object] : resource_type_descriptions[:book]
156
- when :map
157
- resource_type_description = resource_type_descriptions[:map]
158
- when :'3d'
159
- resource_extensions = resource_files.collect {|obj| obj.ext}
160
- if (resource_extensions & VALID_THREE_DIMENSION_EXTENTIONS).empty? # if this resource contains no known 3D file extensions, the resource type is file
161
- resource_type_description = resource_type_descriptions[:file]
162
- else # otherwise the resource type is 3d
163
- resource_type_description = resource_type_descriptions[:'3d']
164
- end
165
- end
166
- end
167
-
168
- resource_type_counters[resource_type_description.to_sym] += 1 # each resource type description gets its own incrementing counter
169
-
170
- xml.resource(id: resource_id, sequence: sequence, type: resource_type_description) do
171
- # create a generic resource label if needed
172
- resource_label = (auto_labels == true ? "#{resource_type_description.capitalize} #{resource_type_counters[resource_type_description.to_sym]}" : '')
173
-
174
- # but if one of the files has a label, use it instead
175
- resource_files.each { |obj| resource_label = obj.label unless obj.label.nil? || obj.label.empty? }
176
-
177
- xml.label(resource_label) unless resource_label.empty?
178
-
179
- resource_files.each do |obj| # iterate over all the files in a resource
180
- mimetype = obj.mimetype if add_file_attributes || add_exif # we only need to compute the mimetype if we are adding file attributes or exif info, otherwise skip it for performance reasons
181
-
182
- # set file id attribute, first check the relative_path parameter on the object, and if it is set, just use that
183
- if obj.relative_path
184
- file_id = obj.relative_path
185
- else
186
- # if the relative_path attribute is not set, then use the path attribute and check to see if we need to remove the common part of the path
187
- file_id = preserve_common_paths ? obj.path : obj.path.gsub(common_path, '')
188
- file_id = File.basename(file_id) if flatten_folder_structure
189
- end
190
-
191
- xml_file_params = { id: file_id }
192
-
193
- if add_file_attributes
194
- file_attributes_hash = obj.file_attributes || file_attributes[mimetype] || file_attributes['default'] || Assembly::FILE_ATTRIBUTES[mimetype] || Assembly::FILE_ATTRIBUTES['default']
195
- xml_file_params.merge!(
196
- preserve: file_attributes_hash[:preserve],
197
- publish: file_attributes_hash[:publish],
198
- shelve: file_attributes_hash[:shelve],
199
- role: file_attributes_hash[:role]
200
- )
201
- xml_file_params.reject! { |_k, v| v.nil? || v.empty? }
202
- end
203
-
204
- if add_exif
205
- xml_file_params[:mimetype] = mimetype
206
- xml_file_params[:size] = obj.filesize
207
- end
208
- xml.file(xml_file_params) do
209
- if add_exif # add exif info if the user requested it
210
- xml.checksum(obj.sha1, type: 'sha1')
211
- xml.checksum(obj.md5, type: 'md5')
212
- xml.imageData(height: obj.exif.imageheight, width: obj.exif.imagewidth) if obj.image? # add image data for an image
213
- elsif obj.provider_md5 || obj.provider_sha1 # if we did not add exif info, see if there are user supplied checksums to add
214
- xml.checksum(obj.provider_sha1, type: 'sha1') if obj.provider_sha1
215
- xml.checksum(obj.provider_md5, type: 'md5') if obj.provider_md5
216
- end # add_exif
217
- end
218
- end # end resource_files.each
219
- end
220
- end # resources.each
221
- end
222
- end
223
-
224
- result = if include_root_xml == false
225
- builder.doc.root.to_xml
226
- else
227
- builder.to_xml
228
- end
229
-
230
- result
231
- end # create_content_metadata
232
-
233
- def self.is_special_dpg_folder?(folder)
234
- SPECIAL_DPG_FOLDERS.include?(folder)
235
117
  end
236
118
  end # class
237
119
  end # module
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'dry-types'
5
+
6
+ module Assembly
7
+ class ContentMetadata
8
+ # Types for the configuration
9
+ module Types
10
+ include Dry.Types()
11
+ end
12
+
13
+ # Represents a configuration for generating the content metadata
14
+ class Config < Dry::Struct
15
+ STYLES = %w[image file book map 3d].freeze
16
+ attribute :auto_labels, Types::Strict::Bool.default(true)
17
+ attribute :flatten_folder_structure, Types::Strict::Bool.default(false)
18
+ attribute :add_file_attributes, Types::Strict::Bool.default(false)
19
+ attribute :add_exif, Types::Strict::Bool.default(false)
20
+ attribute :file_attributes, Types::Strict::Hash.default({}.freeze)
21
+ attribute :type, Types::Strict::String.enum(*STYLES)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module/delegation'
4
+
5
+ module Assembly
6
+ class ContentMetadata
7
+ # Represents a single File
8
+ class File
9
+ # @param [Symbol] bundle
10
+ # @param [Assembly::ObjectFile] file
11
+ # @param style
12
+ def initialize(bundle: nil, file:, style: nil)
13
+ @bundle = bundle
14
+ @file = file
15
+ @style = style
16
+ end
17
+
18
+ delegate :sha1, :md5, :provider_md5, :provider_sha1, :mimetype, :filesize, :image?, to: :file
19
+
20
+ def file_id(common_path:, flatten_folder_structure:)
21
+ # set file id attribute, first check the relative_path parameter on the object, and if it is set, just use that
22
+ return file.relative_path if file.relative_path
23
+
24
+ # if the relative_path attribute is not set, then use the path attribute and check to see if we need to remove the common part of the path
25
+ file_id = common_path ? file.path.gsub(common_path, '') : file.path
26
+ file_id = ::File.basename(file_id) if flatten_folder_structure
27
+ file_id
28
+ end
29
+
30
+ def file_attributes(provided_file_attributes)
31
+ file.file_attributes || provided_file_attributes[mimetype] || provided_file_attributes['default'] || Assembly::FILE_ATTRIBUTES[mimetype] || Assembly::FILE_ATTRIBUTES['default']
32
+ end
33
+
34
+ def image_data
35
+ { height: file.exif.imageheight, width: file.exif.imagewidth }
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :file
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ module Assembly
6
+ class ContentMetadata
7
+ # Represents a groups of related Files, such as a single master file and the derivatives
8
+ class FileSet
9
+ # @param [Boolean] dpg (false) is it a dpg bundle?
10
+ # @param [Array] resource_files
11
+ # @param style
12
+ def initialize(dpg: false, resource_files:, style:)
13
+ @dpg = dpg
14
+ @resource_files = resource_files
15
+ @style = style
16
+ end
17
+
18
+ # objects in the special DPG folders are always type=object when we using :bundle=>:dpg
19
+ # otherwise look at the style to determine the resource_type_description
20
+ def resource_type_description
21
+ @resource_type_description ||= special_dpg_resource? ? 'object' : resource_type_descriptions
22
+ end
23
+
24
+ def label_from_file(default:)
25
+ resource_files.find { |obj| obj.label.present? }&.label || default
26
+ end
27
+
28
+ def files
29
+ resource_files.map { |file| File.new(file: file) }
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :dpg, :resource_files, :style
35
+
36
+ def special_dpg_resource?
37
+ return false unless dpg
38
+
39
+ resource_files.collect { |obj| ContentMetadata.special_dpg_folder?(obj.dpg_folder) }.include?(true)
40
+ end
41
+
42
+ def resource_type_descriptions
43
+ # grab all of the file types within a resource into an array so we can decide what the resource type should be
44
+ resource_file_types = resource_files.collect(&:object_type)
45
+ resource_has_non_images = !(resource_file_types - [:image]).empty?
46
+
47
+ case style
48
+ when :simple_image
49
+ 'image'
50
+ when :file
51
+ 'file'
52
+ when :simple_book # in a simple book project, all resources are pages unless they are *all* non-images -- if so, switch the type to object
53
+ resource_has_non_images && resource_file_types.include?(:image) == false ? 'object' : 'page'
54
+ when :book_as_image # same as simple book, but all resources are images instead of pages, unless we need to switch them to object type
55
+ resource_has_non_images && resource_file_types.include?(:image) == false ? 'object' : 'image'
56
+ when :book_with_pdf # in book with PDF type, if we find a resource with *any* non images, switch it's type from book to object
57
+ resource_has_non_images ? 'object' : 'page'
58
+ when :map
59
+ 'image'
60
+ when :'3d'
61
+ resource_extensions = resource_files.collect(&:ext)
62
+ if (resource_extensions & VALID_THREE_DIMENSION_EXTENTIONS).empty? # if this resource contains no known 3D file extensions, the resource type is file
63
+ 'file'
64
+ else # otherwise the resource type is 3d
65
+ '3d'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Assembly
4
+ class ContentMetadata
5
+ # Builds a groups of related Files, based on bundle
6
+ class FileSetBuilder
7
+ def self.build(bundle:, objects:, style:)
8
+ new(bundle: bundle, objects: objects, style: style).build
9
+ end
10
+
11
+ def initialize(bundle:, objects:, style:)
12
+ @bundle = bundle
13
+ @objects = objects
14
+ @style = style
15
+ end
16
+
17
+ # @return [Array<FileSet>] a list of filesets in the object
18
+ def build
19
+ case bundle
20
+ when :default # one resource per object
21
+ objects.collect { |obj| FileSet.new(resource_files: [obj], style: style) }
22
+ when :filename # one resource per distinct filename (excluding extension)
23
+ build_for_filename
24
+ when :dpg # group by DPG filename
25
+ build_for_dpg
26
+ when :prebundled
27
+ # if the user specifies this method, they will pass in an array of arrays, indicating resources, so we don't need to bundle in the gem
28
+ objects.map { |inner| FileSet.new(resource_files: inner, style: style) }
29
+ else
30
+ raise 'Invalid bundle method'
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :bundle, :objects, :style
37
+
38
+ def build_for_filename
39
+ # loop over distinct filenames, this determines how many resources we will have and
40
+ # create one resource node per distinct filename, collecting the relevant objects with the distinct filename into that resource
41
+ distinct_filenames = objects.collect(&:filename_without_ext).uniq # find all the unique filenames in the set of objects, leaving off extensions and base paths
42
+ distinct_filenames.map do |distinct_filename|
43
+ FileSet.new(resource_files: objects.collect { |obj| obj if obj.filename_without_ext == distinct_filename }.compact,
44
+ style: style)
45
+ end
46
+ end
47
+
48
+ def build_for_dpg
49
+ # loop over distinct dpg base names, this determines how many resources we will have and
50
+ # create one resource node per distinct dpg base name, collecting the relevant objects with the distinct names into that resource
51
+
52
+ distinct_filenames = objects.collect(&:dpg_basename).uniq # find all the unique DPG filenames in the set of objects
53
+ resources = distinct_filenames.map do |distinct_filename|
54
+ FileSet.new(dpg: true, resource_files: objects.collect { |obj| obj if obj.dpg_basename == distinct_filename && !ContentMetadata.special_dpg_folder?(obj.dpg_folder) }.compact, style: style)
55
+ end
56
+ objects.each { |obj| resources << FileSet.new(dpg: true, resource_files: [obj], style: style) if ContentMetadata.special_dpg_folder?(obj.dpg_folder) } # certain subfolders require individual resources for files within them regardless of file-naming convention
57
+ resources
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Assembly
4
+ class ContentMetadata
5
+ # Builds a nokogiri representation of the content metadata
6
+ class NokogiriBuilder
7
+ # @param [Array<Fileset>] filesets
8
+ # @param [String] druid
9
+ # @param [String] common_path
10
+ # @param [Config] config
11
+ def self.build(filesets:, druid:, common_path:, config:)
12
+ # a counter to use when creating auto-labels for resources, with incremenets for each type
13
+ resource_type_counters = Hash.new(0)
14
+ pid = druid.gsub('druid:', '') # remove druid prefix when creating IDs
15
+
16
+ Nokogiri::XML::Builder.new do |xml|
17
+ xml.contentMetadata(objectId: druid.to_s, type: config.type) do
18
+ filesets.each_with_index do |fileset, index| # iterate over all the resources
19
+ # start a new resource element
20
+ sequence = index + 1
21
+
22
+ resource_type_counters[fileset.resource_type_description] += 1 # each resource type description gets its own incrementing counter
23
+
24
+ xml.resource(id: "#{pid}_#{sequence}", sequence: sequence, type: fileset.resource_type_description) do
25
+ # create a generic resource label if needed
26
+ default_label = config.auto_labels ? "#{fileset.resource_type_description.capitalize} #{resource_type_counters[fileset.resource_type_description]}" : ''
27
+
28
+ # but if one of the files has a label, use it instead
29
+ resource_label = fileset.label_from_file(default: default_label)
30
+
31
+ xml.label(resource_label) unless resource_label.empty?
32
+ fileset.files.each do |obj| # iterate over all the files in a resource
33
+ xml_file_params = { id: obj.file_id(common_path: common_path, flatten_folder_structure: config.flatten_folder_structure) }
34
+ xml_file_params.merge!(obj.file_attributes(config.file_attributes)) if config.add_file_attributes
35
+ xml_file_params.merge!(mimetype: obj.mimetype, size: obj.filesize) if config.add_exif
36
+
37
+ xml.file(xml_file_params) do
38
+ if config.add_exif # add exif info if the user requested it
39
+ xml.checksum(obj.sha1, type: 'sha1')
40
+ xml.checksum(obj.md5, type: 'md5')
41
+ xml.imageData(obj.image_data) if obj.image? # add image data for an image
42
+ elsif obj.provider_md5 || obj.provider_sha1 # if we did not add exif info, see if there are user supplied checksums to add
43
+ xml.checksum(obj.provider_sha1, type: 'sha1') if obj.provider_sha1
44
+ xml.checksum(obj.provider_md5, type: 'md5') if obj.provider_md5
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Assembly
2
4
  # This class contains generic methods to operate on any file.
3
5
  class ObjectFile
@@ -18,9 +20,9 @@ module Assembly
18
20
  n += 1 while strings.all? { |s| s[n] && (s[n] == x[n]) }
19
21
  common_prefix = x[0...n]
20
22
  if common_prefix[-1, 1] != '/' # check if last element of the common string is the end of a directory
21
- return common_prefix.split('/')[0..-2].join('/') + '/' # if not, split string along directories, and reject last one
23
+ common_prefix.split('/')[0..-2].join('/') + '/' # if not, split string along directories, and reject last one
22
24
  else
23
- return common_prefix # if it was, then return the common prefix directly
25
+ common_prefix # if it was, then return the common prefix directly
24
26
  end
25
27
  end
26
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mini_exiftool'
2
4
  require 'mime/types'
3
5
  # require 'checksum-tools'
@@ -120,7 +122,7 @@ module Assembly
120
122
  exif_mimetype
121
123
  elsif file_mimetype # next, try exif/unix file system command
122
124
  file_mimetype
123
- else # finally, get it from the mime-types gem (using the file extension) if both of those failed for some reason
125
+ else # finally, get it from the mime-types gem (using the file extension) if both of those failed for some reason
124
126
  mtype = MIME::Types.type_for(path).first
125
127
  mtype ? mtype.content_type : ''
126
128
  end
@@ -148,7 +150,8 @@ module Assembly
148
150
  @exif_mimetype ||= begin
149
151
  check_for_file
150
152
  prefer_exif = !Assembly::TRUSTED_MIMETYPES.include?(file_mimetype) # if it's not a "trusted" mimetype and there is exif data; get the mimetype from the exif
151
- exif.mimetype if exif && exif.mimetype && prefer_exif
153
+ exif.mimetype if
154
+ exif&.mimetype && prefer_exif
152
155
  end
153
156
  end
154
157
 
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Main Assembly namespace
2
4
  module Assembly
3
5
  class ObjectFile
4
6
  # Project version number
5
- VERSION = '1.8.3'.freeze
7
+ VERSION = '1.8.4'
6
8
  end
7
9
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Assembly::ContentMetadata do
@@ -512,8 +514,8 @@ describe Assembly::ContentMetadata do
512
514
  end
513
515
 
514
516
  it 'generates valid content metadata for a 3d object with one 3d type files and three other supporting files (where one supporting file is a non-viewable but downloadable 3d file)' do
515
- objects=[Assembly::ObjectFile.new(TEST_OBJ_FILE),Assembly::ObjectFile.new(TEST_PLY_FILE),Assembly::ObjectFile.new(TEST_TIF_INPUT_FILE),Assembly::ObjectFile.new(TEST_PDF_FILE)]
516
- result = Assembly::ContentMetadata.create_content_metadata(:druid=>TEST_DRUID,:style=>:'3d',:objects=>objects)
517
+ objects = [Assembly::ObjectFile.new(TEST_OBJ_FILE), Assembly::ObjectFile.new(TEST_PLY_FILE), Assembly::ObjectFile.new(TEST_TIF_INPUT_FILE), Assembly::ObjectFile.new(TEST_PDF_FILE)]
518
+ result = described_class.create_content_metadata(druid: TEST_DRUID, style: :'3d', objects: objects)
517
519
  expect(result.class).to be String
518
520
  xml = Nokogiri::XML(result)
519
521
  expect(xml.errors.size).to be 0
@@ -600,9 +602,9 @@ describe Assembly::ContentMetadata do
600
602
 
601
603
  it 'generates an error message when an unknown style is passed in' do
602
604
  objects = []
603
- expect {
605
+ expect do
604
606
  described_class.create_content_metadata(druid: TEST_DRUID, bundle: :prebundled, style: :borked, objects: objects)
605
- }.to raise_error { |error|
607
+ end.to raise_error { |error|
606
608
  expect(error.message).to eq('Supplied style (borked) not valid')
607
609
  }
608
610
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Assembly::ObjectFile do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
2
4
  SimpleCov.start
3
5
 
@@ -52,7 +54,7 @@ TEST_RES3_TEI = File.join(TEST_INPUT_DIR, 'res3_teifile.txt')
52
54
 
53
55
  TEST_FILE_NO_EXIF = File.join(TEST_INPUT_DIR, 'file_with_no_exif.xml')
54
56
 
55
- TEST_OBJ_FILE=File.join(TEST_INPUT_DIR,'someobject.obj')
56
- TEST_PLY_FILE=File.join(TEST_INPUT_DIR,'someobject.ply')
57
+ TEST_OBJ_FILE = File.join(TEST_INPUT_DIR, 'someobject.obj')
58
+ TEST_PLY_FILE = File.join(TEST_INPUT_DIR, 'someobject.ply')
57
59
 
58
- TEST_DRUID = 'nx288wh8889'.freeze
60
+ TEST_DRUID = 'nx288wh8889'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assembly-objectfile
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.3
4
+ version: 1.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Mangiafico
@@ -11,8 +11,50 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2019-08-29 00:00:00.000000000 Z
14
+ date: 2020-01-21 00:00:00.000000000 Z
15
15
  dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.2.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 5.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: dry-struct
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '1.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: dry-types
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '1.1'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '1.1'
16
58
  - !ruby/object:Gem::Dependency
17
59
  name: mime-types
18
60
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +188,8 @@ executables: []
146
188
  extensions: []
147
189
  extra_rdoc_files: []
148
190
  files:
191
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
192
+ - ".github/pull_request_template.md"
149
193
  - ".gitignore"
150
194
  - ".rubocop.yml"
151
195
  - ".rubocop_todo.yml"
@@ -161,6 +205,11 @@ files:
161
205
  - config/boot.rb
162
206
  - lib/assembly-objectfile.rb
163
207
  - lib/assembly-objectfile/content_metadata.rb
208
+ - lib/assembly-objectfile/content_metadata/config.rb
209
+ - lib/assembly-objectfile/content_metadata/file.rb
210
+ - lib/assembly-objectfile/content_metadata/file_set.rb
211
+ - lib/assembly-objectfile/content_metadata/file_set_builder.rb
212
+ - lib/assembly-objectfile/content_metadata/nokogiri_builder.rb
164
213
  - lib/assembly-objectfile/object_file.rb
165
214
  - lib/assembly-objectfile/object_fileable.rb
166
215
  - lib/assembly-objectfile/version.rb
@@ -224,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
273
  - !ruby/object:Gem::Version
225
274
  version: '0'
226
275
  requirements: []
227
- rubygems_version: 3.0.4
276
+ rubygems_version: 3.1.2
228
277
  signing_key:
229
278
  specification_version: 4
230
279
  summary: Ruby immplementation of file services needed to prepare objects to be accessioned