assembly-objectfile 1.8.2 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,18 @@ 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.required_ruby_version = '~> 2.5'
24
+
25
+ s.add_dependency 'activesupport', '>= 5.2.0'
26
+ s.add_dependency 'deprecation'
27
+ s.add_dependency 'dry-struct', '~> 1.0'
28
+ s.add_dependency 'dry-types', '~> 1.1'
23
29
  s.add_dependency 'mime-types', '> 3'
24
30
  s.add_dependency 'mini_exiftool'
25
31
  s.add_dependency 'nokogiri'
@@ -29,5 +35,5 @@ Gem::Specification.new do |s|
29
35
  s.add_development_dependency 'rspec', '~> 3.0'
30
36
  s.add_development_dependency 'rubocop'
31
37
  s.add_development_dependency 'rubocop-rspec'
32
- s.add_development_dependency 'simplecov'
38
+ s.add_development_dependency 'simplecov', '~> 0.17.0' # CodeClimate cannot use SimpleCov >= 0.18.0 for generating test coverage
33
39
  end
data/config/boot.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
 
3
5
  environment = ENV['ENVIRONMENT'] ||= 'development'
4
- project_root = File.expand_path(File.dirname(__FILE__) + '/..')
6
+ project_root = File.expand_path("#{File.dirname(__FILE__)}/..")
5
7
 
6
8
  # Load config for current environment.
7
- $LOAD_PATH.unshift(project_root + '/lib')
9
+ $LOAD_PATH.unshift("#{project_root}/lib")
8
10
 
9
11
  require 'assembly-objectfile'
@@ -1,36 +1,19 @@
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
- PATH_TO_GEM = File.expand_path(File.dirname(__FILE__) + '/..')
5
+ PATH_TO_GEM = File.expand_path("#{File.dirname(__FILE__)}/..")
4
6
 
5
7
  # if input image is not one of these mime types, it will not be regarded as a valid image for the purpose of generating a JP2 derivative
6
8
  VALID_IMAGE_MIMETYPES = ['image/jpeg', 'image/tiff', 'image/tif', 'image/png'].freeze
7
9
 
8
10
  # if input file has one of these extensions in a 3D object, it will get the 3d resource type
9
- VALID_THREE_DIMENSION_EXTENTIONS = ['.obj', '.ply', '.threejs', '.gltf'].freeze
11
+ VALID_THREE_DIMENSION_EXTENTIONS = ['.obj'].freeze
10
12
 
11
13
  # the list of mimetypes that will be "trusted" by the unix file command; if a mimetype other than one of these is returned
12
14
  # by the file command, then a check will be made to see if exif data exists...if so, the mimetype returned by the exif data will be used
13
15
  # if no exif data exists, then the mimetype returned by the unix file command will be used
14
16
  TRUSTED_MIMETYPES = ['text/plain', 'plain/text', 'application/pdf', 'text/html', 'application/xml'].freeze
15
-
16
- # default publish/preserve/shelve attributes used in content metadata
17
- FILE_ATTRIBUTES = {}
18
- # 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' }
34
17
  end
35
18
 
36
19
  require 'assembly-objectfile/content_metadata'
@@ -1,10 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'nokogiri'
4
+ require 'deprecation'
5
+ require 'active_support'
6
+ require 'assembly-objectfile/content_metadata/file'
7
+ require 'assembly-objectfile/content_metadata/file_set'
8
+ require 'assembly-objectfile/content_metadata/file_set_builder'
9
+ require 'assembly-objectfile/content_metadata/config'
10
+ require 'assembly-objectfile/content_metadata/nokogiri_builder'
2
11
 
3
12
  module Assembly
4
13
  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
5
14
  # these are used when :bundle=>:dpg only
6
15
 
7
16
  DEPRECATED_STYLES = %i[book_with_pdf book_as_image].freeze
17
+ VALID_STYLES = %i[simple_image simple_book file map document 3d webarchive-seed].freeze
8
18
 
9
19
  # This class generates content metadata for image files
10
20
  class ContentMetadata
@@ -24,6 +34,7 @@ module Assembly
24
34
  # :book_as_image, as simple_book, but with contentMetadata type="book", resource type="image" (same rule applies for resources with non images) - NOTE: THIS IS DEPRECATED
25
35
  # :map, like simple_image, but with contentMetadata type="map", resource type="image"
26
36
  # :3d, contentMetadata type="3d", ".obj" and other configured 3d extension files go into resource_type="3d", everything else into resource_type="file"
37
+ # :webarchive-seed, contentMetadata type="webarchive-seed", resource type="image"
27
38
  # :bundle = optional - a symbol containing the method of bundling files into resources, allowed values are
28
39
  # :default = all files get their own resources (default)
29
40
  # :filename = files with the same filename but different extensions get bundled together in a single resource
@@ -43,195 +54,61 @@ module Assembly
43
54
  # :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
55
  # Example:
45
56
  # 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
57
+ def self.create_content_metadata(druid:, objects:, auto_labels: true,
58
+ add_exif: false, bundle: :default, style: :simple_image,
59
+ add_file_attributes: false, file_attributes: {},
60
+ preserve_common_paths: false, flatten_folder_structure: false,
61
+ include_root_xml: nil)
62
+
63
+ common_path = find_common_path(objects) unless preserve_common_paths # find common paths to all files provided if needed
64
+
65
+ filesets = FileSetBuilder.build(bundle: bundle, objects: objects, style: style)
66
+ config = Config.new(auto_labels: auto_labels,
67
+ flatten_folder_structure: flatten_folder_structure,
68
+ add_file_attributes: add_file_attributes,
69
+ file_attributes: file_attributes,
70
+ add_exif: add_exif,
71
+ type: object_level_type(style))
72
+
73
+ builder = NokogiriBuilder.build(druid: druid,
74
+ filesets: filesets,
75
+ common_path: common_path,
76
+ config: config)
77
+
78
+ if include_root_xml == false
79
+ builder.doc.root.to_xml
80
+ else
81
+ builder.to_xml
82
+ end
83
+ end
53
84
 
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]
85
+ def self.special_dpg_folder?(folder)
86
+ SPECIAL_DPG_FOLDERS.include?(folder)
87
+ end
63
88
 
64
- all_paths = []
65
- objects.flatten.each do |obj|
89
+ def self.find_common_path(objects)
90
+ all_paths = objects.flatten.map do |obj|
66
91
  raise "File '#{obj.path}' not found" unless obj.file_exists?
67
92
 
68
- all_paths << obj.path unless preserve_common_paths # collect all of the filenames into an array
93
+ obj.path # collect all of the filenames into an array
69
94
  end
70
95
 
71
- common_path = Assembly::ObjectFile.common_path(all_paths) unless preserve_common_paths # find common paths to all files provided if needed
72
-
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' }
76
-
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
+ Deprecation.warn(self, "the style #{style} is now deprecated and should not be used. This will be removed in assembly-objectfile 2.0") if DEPRECATED_STYLES.include? style
102
+ raise "Supplied style (#{style}) not valid" unless (VALID_STYLES + DEPRECATED_STYLES).include? style
82
103
 
83
- # set the object level content type id
84
104
  case style
85
105
  when :simple_image
86
- content_type_description = content_type_descriptions[:image]
87
- when :file
88
- content_type_description = content_type_descriptions[:file]
106
+ 'image'
89
107
  when :simple_book, :book_with_pdf, :book_as_image
90
- content_type_description = content_type_descriptions[:book]
91
- when :map
92
- content_type_description = content_type_descriptions[:map]
93
- when :'3d'
94
- content_type_description = content_type_descriptions[:'3d']
95
- else
96
- raise "Supplied style (#{style}) not valid"
97
- 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
108
+ 'book'
124
109
  else
125
- raise 'Invalid bundle method'
110
+ style.to_s
126
111
  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
112
  end
236
113
  end # class
237
114
  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 document webarchive-seed].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,63 @@
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
+ # default publish/preserve/shelve attributes used in content metadata
10
+ # if no mimetype specific attributes are specified for a given file, define some defaults, and override for specific mimetypes below
11
+ ATTRIBUTES_FOR_TYPE = {
12
+ 'default' => { preserve: 'yes', shelve: 'no', publish: 'no' },
13
+ 'image/tif' => { preserve: 'yes', shelve: 'no', publish: 'no' },
14
+ 'image/tiff' => { preserve: 'yes', shelve: 'no', publish: 'no' },
15
+ 'image/jp2' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
16
+ 'image/jpeg' => { preserve: 'yes', shelve: 'no', publish: 'no' },
17
+ 'audio/wav' => { preserve: 'yes', shelve: 'no', publish: 'no' },
18
+ 'audio/x-wav' => { preserve: 'yes', shelve: 'no', publish: 'no' },
19
+ 'audio/mp3' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
20
+ 'audio/mpeg' => { preserve: 'no', shelve: 'yes', publish: 'yes' },
21
+ 'application/pdf' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
22
+ 'plain/text' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
23
+ 'text/plain' => { preserve: 'yes', shelve: 'yes', publish: 'yes' },
24
+ 'image/png' => { preserve: 'yes', shelve: 'yes', publish: 'no' },
25
+ 'application/zip' => { preserve: 'yes', shelve: 'no', publish: 'no' },
26
+ 'application/json' => { preserve: 'yes', shelve: 'yes', publish: 'yes' }
27
+ }.freeze
28
+
29
+ # @param [Symbol] bundle
30
+ # @param [Assembly::ObjectFile] file
31
+ # @param style
32
+ def initialize(file:, bundle: nil, style: nil)
33
+ @bundle = bundle
34
+ @file = file
35
+ @style = style
36
+ end
37
+
38
+ delegate :sha1, :md5, :provider_md5, :provider_sha1, :mimetype, :filesize, :image?, to: :file
39
+
40
+ def file_id(common_path:, flatten_folder_structure:)
41
+ # set file id attribute, first check the relative_path parameter on the object, and if it is set, just use that
42
+ return file.relative_path if file.relative_path
43
+
44
+ # 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
45
+ file_id = common_path ? file.path.gsub(common_path, '') : file.path
46
+ file_id = ::File.basename(file_id) if flatten_folder_structure
47
+ file_id
48
+ end
49
+
50
+ def file_attributes(provided_file_attributes)
51
+ file.file_attributes || provided_file_attributes[mimetype] || provided_file_attributes['default'] || ATTRIBUTES_FOR_TYPE[mimetype] || ATTRIBUTES_FOR_TYPE['default']
52
+ end
53
+
54
+ def image_data
55
+ { height: file.exif.imageheight, width: file.exif.imagewidth }
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :file
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,73 @@
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<Assembly::ObjectFile>] resource_files
11
+ # @param style
12
+ def initialize(resource_files:, style:, dpg: false)
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
+ # rubocop:disable Metrics/CyclomaticComplexity
43
+ def resource_type_descriptions
44
+ # grab all of the file types within a resource into an array so we can decide what the resource type should be
45
+ resource_file_types = resource_files.collect(&:object_type)
46
+ resource_has_non_images = !(resource_file_types - [:image]).empty?
47
+
48
+ case style
49
+ when :simple_image, :map, :'webarchive-seed'
50
+ 'image'
51
+ when :file
52
+ 'file'
53
+ 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
54
+ resource_has_non_images && resource_file_types.include?(:image) == false ? 'object' : 'page'
55
+ 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
56
+ resource_has_non_images && resource_file_types.include?(:image) == false ? 'object' : 'image'
57
+ 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
58
+ resource_has_non_images ? 'object' : 'page'
59
+ when :document
60
+ 'document'
61
+ when :'3d'
62
+ resource_extensions = resource_files.collect(&:ext)
63
+ if (resource_extensions & VALID_THREE_DIMENSION_EXTENTIONS).empty? # if this resource contains no known 3D file extensions, the resource type is file
64
+ 'file'
65
+ else # otherwise the resource type is 3d
66
+ '3d'
67
+ end
68
+ end
69
+ end
70
+ # rubocop:enable Metrics/CyclomaticComplexity
71
+ end
72
+ end
73
+ end