assembly-objectfile 1.8.3 → 1.8.4

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: 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