hydra-works 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +2 -5
  4. data/Gemfile +1 -3
  5. data/README.md +28 -13
  6. data/hydra-works.gemspec +4 -3
  7. data/lib/hydra/works.rb +7 -57
  8. data/lib/hydra/works/models/concerns/block_child_objects.rb +16 -0
  9. data/lib/hydra/works/models/concerns/collection_behavior.rb +27 -2
  10. data/lib/hydra/works/models/concerns/generic_file/contained_files.rb +5 -13
  11. data/lib/hydra/works/models/concerns/generic_file/derivatives.rb +9 -13
  12. data/lib/hydra/works/models/concerns/generic_file/mime_types.rb +14 -9
  13. data/lib/hydra/works/models/concerns/generic_file/versioned_content.rb +18 -0
  14. data/lib/hydra/works/models/concerns/generic_file/virus_check.rb +48 -0
  15. data/lib/hydra/works/models/concerns/generic_file_behavior.rb +24 -1
  16. data/lib/hydra/works/models/concerns/generic_work_behavior.rb +32 -3
  17. data/lib/hydra/works/models/generic_file.rb +3 -0
  18. data/lib/hydra/works/services/generic_file/add_file_to_generic_file.rb +127 -0
  19. data/lib/hydra/works/services/generic_file/generate/thumbnail.rb +1 -2
  20. data/lib/hydra/works/services/generic_file/persist_derivative.rb +22 -0
  21. data/lib/hydra/works/services/generic_file/upload_file.rb +11 -19
  22. data/lib/hydra/works/version.rb +1 -1
  23. data/lib/hydra/works/vocab/works_terms.rb +1 -1
  24. data/spec/fixtures/Example.ogg +0 -0
  25. data/spec/fixtures/charter.docx +0 -0
  26. data/spec/fixtures/countdown.avi +0 -0
  27. data/spec/fixtures/image.jp2 +0 -0
  28. data/spec/fixtures/piano_note.wav +0 -0
  29. data/spec/fixtures/test5.mp3 +0 -0
  30. data/spec/fixtures/world.png +0 -0
  31. data/spec/hydra/works/models/collection_spec.rb +499 -16
  32. data/spec/hydra/works/models/concerns/block_child_objects_spec.rb +19 -0
  33. data/spec/hydra/works/models/concerns/{file → generic_file}/contained_files_spec.rb +20 -17
  34. data/spec/hydra/works/models/concerns/generic_file/mime_types_spec.rb +76 -0
  35. data/spec/hydra/works/models/concerns/generic_file/versioned_content_spec.rb +32 -0
  36. data/spec/hydra/works/models/concerns/generic_file/virus_check_spec.rb +50 -0
  37. data/spec/hydra/works/models/concerns/generic_file_behavior_spec.rb +1 -1
  38. data/spec/hydra/works/models/generic_file_spec.rb +201 -14
  39. data/spec/hydra/works/models/generic_work_spec.rb +530 -14
  40. data/spec/hydra/works/services/generic_file/add_file_to_generic_file_spec.rb +110 -0
  41. data/spec/hydra/works/services/generic_file/upload_file_spec.rb +45 -28
  42. data/spec/hydra/works/services/persist_derivatives_spec.rb +93 -0
  43. data/spec/hydra/works_spec.rb +35 -35
  44. data/spec/spec_helper.rb +6 -0
  45. metadata +56 -95
  46. data/lib/hydra/works/models/concerns/aggregates_collections.rb +0 -16
  47. data/lib/hydra/works/models/concerns/aggregates_generic_files.rb +0 -20
  48. data/lib/hydra/works/models/concerns/aggregates_generic_works.rb +0 -20
  49. data/lib/hydra/works/processor.rb +0 -9
  50. data/lib/hydra/works/services/collection/add_collection.rb +0 -19
  51. data/lib/hydra/works/services/collection/add_generic_work.rb +0 -19
  52. data/lib/hydra/works/services/collection/add_related_object.rb +0 -19
  53. data/lib/hydra/works/services/collection/get_collections.rb +0 -17
  54. data/lib/hydra/works/services/collection/get_generic_works.rb +0 -17
  55. data/lib/hydra/works/services/collection/get_related_objects.rb +0 -17
  56. data/lib/hydra/works/services/collection/remove_collection.rb +0 -19
  57. data/lib/hydra/works/services/collection/remove_generic_work.rb +0 -20
  58. data/lib/hydra/works/services/collection/remove_related_object.rb +0 -19
  59. data/lib/hydra/works/services/generic_file/add_file.rb +0 -55
  60. data/lib/hydra/works/services/generic_file/add_generic_file.rb +0 -19
  61. data/lib/hydra/works/services/generic_file/add_original_file.rb +0 -11
  62. data/lib/hydra/works/services/generic_file/add_related_object.rb +0 -19
  63. data/lib/hydra/works/services/generic_file/add_versioned_original_file.rb +0 -10
  64. data/lib/hydra/works/services/generic_file/get_generic_files.rb +0 -17
  65. data/lib/hydra/works/services/generic_file/get_related_objects.rb +0 -17
  66. data/lib/hydra/works/services/generic_file/remove_generic_file.rb +0 -20
  67. data/lib/hydra/works/services/generic_file/remove_related_object.rb +0 -19
  68. data/lib/hydra/works/services/generic_work/add_generic_file.rb +0 -19
  69. data/lib/hydra/works/services/generic_work/add_generic_work.rb +0 -19
  70. data/lib/hydra/works/services/generic_work/add_related_object.rb +0 -19
  71. data/lib/hydra/works/services/generic_work/get_generic_files.rb +0 -17
  72. data/lib/hydra/works/services/generic_work/get_generic_works.rb +0 -17
  73. data/lib/hydra/works/services/generic_work/get_related_objects.rb +0 -17
  74. data/lib/hydra/works/services/generic_work/move_generic_file.rb +0 -19
  75. data/lib/hydra/works/services/generic_work/remove_generic_file.rb +0 -20
  76. data/lib/hydra/works/services/generic_work/remove_generic_work.rb +0 -20
  77. data/lib/hydra/works/services/generic_work/remove_related_object.rb +0 -19
  78. data/spec/hydra/works/services/collection/add_collection_spec.rb +0 -166
  79. data/spec/hydra/works/services/collection/add_generic_work_spec.rb +0 -155
  80. data/spec/hydra/works/services/collection/add_related_object_spec.rb +0 -149
  81. data/spec/hydra/works/services/collection/get_collections_spec.rb +0 -35
  82. data/spec/hydra/works/services/collection/get_generic_works_spec.rb +0 -35
  83. data/spec/hydra/works/services/collection/get_related_objects_spec.rb +0 -49
  84. data/spec/hydra/works/services/collection/remove_collection_spec.rb +0 -133
  85. data/spec/hydra/works/services/collection/remove_generic_work_spec.rb +0 -133
  86. data/spec/hydra/works/services/collection/remove_related_object_spec.rb +0 -128
  87. data/spec/hydra/works/services/generic_file/add_file_spec.rb +0 -28
  88. data/spec/hydra/works/services/generic_file/add_generic_file_spec.rb +0 -174
  89. data/spec/hydra/works/services/generic_file/add_related_object_spec.rb +0 -150
  90. data/spec/hydra/works/services/generic_file/get_generic_files_spec.rb +0 -19
  91. data/spec/hydra/works/services/generic_file/get_related_objects_spec.rb +0 -45
  92. data/spec/hydra/works/services/generic_file/remove_generic_file_spec.rb +0 -124
  93. data/spec/hydra/works/services/generic_file/remove_related_object_spec.rb +0 -123
  94. data/spec/hydra/works/services/generic_work/add_generic_file_spec.rb +0 -156
  95. data/spec/hydra/works/services/generic_work/add_generic_work_spec.rb +0 -157
  96. data/spec/hydra/works/services/generic_work/add_related_object_spec.rb +0 -148
  97. data/spec/hydra/works/services/generic_work/get_generic_files_spec.rb +0 -35
  98. data/spec/hydra/works/services/generic_work/get_generic_works_spec.rb +0 -35
  99. data/spec/hydra/works/services/generic_work/get_related_objects_spec.rb +0 -46
  100. data/spec/hydra/works/services/generic_work/move_generic_file_spec.rb +0 -21
  101. data/spec/hydra/works/services/generic_work/remove_generic_file_spec.rb +0 -132
  102. data/spec/hydra/works/services/generic_work/remove_generic_work_spec.rb +0 -133
  103. data/spec/hydra/works/services/generic_work/remove_related_object_spec.rb +0 -128
@@ -18,13 +18,42 @@ module Hydra::Works
18
18
 
19
19
  included do
20
20
  type [RDFVocabularies::PCDMTerms.Object,WorksVocabularies::WorksTerms.GenericWork]
21
- include Hydra::Works::AggregatesGenericFiles
22
- include Hydra::Works::AggregatesGenericWorks
21
+ include Hydra::Works::BlockChildObjects
22
+
23
+ filters_association :members, as: :child_generic_works, condition: :works_generic_work?
24
+ filters_association :members, as: :generic_files, condition: :works_generic_file?
23
25
  end
24
26
 
25
27
  def contains= files
26
28
  raise NoMethodError, "works can not directly contain files. You must add a GenericFile to the work's members and add files to that GenericFile."
27
29
  end
28
30
 
31
+ # @return [Boolean] whether this instance is a Hydra::Works Collection.
32
+ def works_collection?
33
+ false
34
+ end
35
+
36
+ # @return [Boolean] whether this instance is a Hydra::Works Generic Work.
37
+ def works_generic_work?
38
+ true
39
+ end
40
+
41
+ # @return [Boolean] whether this instance is a Hydra::Works Generic File.
42
+ def works_generic_file?
43
+ false
44
+ end
45
+
46
+ def parents
47
+ aggregated_by
48
+ end
49
+
50
+ def parent_generic_works
51
+ aggregated_by.select { |parent| parent.class.included_modules.include?(Hydra::Works::GenericWorkBehavior) }
52
+ end
53
+
54
+ def parent_collections
55
+ aggregated_by.select { |parent| parent.class.included_modules.include?(Hydra::Works::CollectionBehavior) }
56
+ end
57
+
29
58
  end
30
- end
59
+ end
@@ -7,6 +7,9 @@ module Hydra::Works
7
7
  autoload :Derivatives, 'hydra/works/models/concerns/generic_file/derivatives'
8
8
  autoload :MimeTypes, 'hydra/works/models/concerns/generic_file/mime_types'
9
9
  autoload :ContainedFiles, 'hydra/works/models/concerns/generic_file/contained_files'
10
+ autoload :VersionedContent, 'hydra/works/models/concerns/generic_file/versioned_content'
11
+ autoload :VirusCheck, 'hydra/works/models/concerns/generic_file/virus_check'
12
+
10
13
 
11
14
  # Base class for creating objects that behave like Hydra::Works::GenericFiles
12
15
  class Base < ActiveFedora::Base
@@ -0,0 +1,127 @@
1
+ module Hydra::Works
2
+ class AddFileToGenericFile
3
+
4
+ # Adds a file to the generic_file
5
+ # @param [Hydra::PCDM::GenericFile::Base] generic_file the file will be added to
6
+ # @param [IO,File,Rack::Multipart::UploadedFile, #read] object that will be the contents. If file responds to :mime_type, :content_type, :original_name, or :original_filename, those will be called to provide metadata.
7
+ # @param [RDF::URI or String] type URI for the RDF.type that identifies the file's role within the generic_file
8
+ # @param [Boolean] update_existing whether to update an existing file if there is one. When set to true, performs a create_or_update. When set to false, always creates a new file within generic_file.files.
9
+ # @param [Boolean] versioning whether to create new version entries (only applicable if +type+ corresponds to a versionable file)
10
+
11
+ def self.call(generic_file, file, type, update_existing: true, versioning: true)
12
+ raise ArgumentError, "supplied object must be a generic file" unless generic_file.works_generic_file?
13
+ raise ArgumentError, "supplied file must respond to read" unless file.respond_to? :read
14
+
15
+ # TODO required as a workaround for https://github.com/projecthydra/active_fedora/pull/858
16
+ generic_file.save unless generic_file.persisted?
17
+
18
+ updater_class = versioning ? VersioningUpdater : Updater
19
+ updater = updater_class.new(generic_file, type, update_existing)
20
+ status = updater.update(file)
21
+ status ? generic_file : false
22
+ end
23
+
24
+ class Updater
25
+ attr_reader :generic_file, :current_file
26
+
27
+ def initialize(generic_file, type, update_existing)
28
+ @generic_file = generic_file
29
+ @current_file = find_or_create_file(type, update_existing)
30
+ end
31
+
32
+ # @param [#read] file object that will be interrogated using the methods: :path, :original_name, :original_filename, :mime_type, :content_type
33
+ # None of the attribute description methods are required.
34
+ def update(file)
35
+ attach_attributes(file)
36
+ persist
37
+ end
38
+
39
+ private
40
+
41
+ def persist
42
+ if current_file.new_record?
43
+ # persist current_file and its membership in generic_file.files container
44
+ generic_file.save
45
+ else
46
+ # we updated the content of an existing file, so we need to save the file explicitly
47
+ current_file.save
48
+ end
49
+ end
50
+
51
+ def attach_attributes(file)
52
+ current_file.content = file
53
+ current_file.original_name = determine_original_name(file)
54
+ current_file.mime_type = determine_mime_type(file)
55
+ end
56
+
57
+ # Return mime_type based on methods available to file
58
+ # @param object for mimetype to be determined. Attempts to use methods: :mime_type, :content_type, and :path.
59
+ def determine_mime_type(file)
60
+ if file.respond_to? :mime_type
61
+ return file.mime_type
62
+ elsif file.respond_to? :content_type
63
+ return file.content_type
64
+ elsif file.respond_to? :path
65
+ return Hydra::PCDM::GetMimeTypeForFile.call(file.path)
66
+ else
67
+ return "application/octet-stream"
68
+ end
69
+ end
70
+
71
+ # Return original_name based on methods available to file
72
+ # @param object for original name to be determined. Attempts to use methods: :original_name, :original_filename, and :path.
73
+ def determine_original_name(file)
74
+ if file.respond_to? :original_name
75
+ return file.original_name
76
+ elsif file.respond_to? :original_filename
77
+ return file.original_filename
78
+ elsif file.respond_to? :path
79
+ return ::File.basename(file.path)
80
+ else
81
+ return ""
82
+ end
83
+ end
84
+
85
+ # @param [Symbol, RDF::URI] the type of association or filter to use
86
+ # @param [true, false] update_existing when true, try to retrieve existing element before building one
87
+ def find_or_create_file(type, update_existing)
88
+ if type.instance_of? Symbol
89
+ association = generic_file.association(type)
90
+ raise ArgumentError, "you're attempting to add a file to a generic_file using '#{type}' association but the generic_file does not have an association called '#{type}''" unless association
91
+
92
+ current_file = association.reader if update_existing
93
+ current_file ||= association.build
94
+ else
95
+ current_file = generic_file.filter_files_by_type(type_to_uri(type)).first if update_existing
96
+ unless current_file
97
+ generic_file.files.build
98
+ current_file = generic_file.files.last
99
+ Hydra::PCDM::AddTypeToFile.call(current_file, type_to_uri(type))
100
+ end
101
+ end
102
+ end
103
+
104
+ # Returns appropriate URI for the requested type
105
+ # * Converts supported symbols to corresponding URIs
106
+ # * Converts URI strings to RDF::URI
107
+ # * Returns RDF::URI objects as-is
108
+ def type_to_uri(type)
109
+ case type
110
+ when ::RDF::URI
111
+ type
112
+ when String
113
+ ::RDF::URI(type)
114
+ else
115
+ raise ArgumentError, "Invalid file type. You must submit a URI or a symbol."
116
+ end
117
+ end
118
+ end
119
+
120
+ class VersioningUpdater < Updater
121
+ def update(*)
122
+ super && current_file.create_version
123
+ end
124
+ end
125
+ end
126
+ end
127
+
@@ -7,8 +7,7 @@ module Hydra::Works
7
7
 
8
8
  # Always replace the thumbnail with whatever is from the original file
9
9
  if object.thumbnail.nil?
10
- object.files.build
11
- AddTypeToFile.call(object.files.last, ::RDF::URI("http://pcdm.org/ThumbnailImage"))
10
+ object.build_thumbnail
12
11
  end
13
12
 
14
13
  object.create_derivatives
@@ -0,0 +1,22 @@
1
+ require 'hydra/derivatives'
2
+
3
+ module Hydra::Works
4
+ class PersistDerivative < Hydra::Derivatives::PersistOutputFileService
5
+
6
+ ##
7
+ # Persists a derivative to a GenericFile
8
+ # This Service conforms to the signature of `Hydra::Derivatives::PersistOutputFileService`.
9
+ # The purpose of this Service is for use as an alternative to the default Hydra::Derivatives::PersistOutputFileService. It's necessary because the default behavior in Hydra::Derivatives assumes that you're using LDP Basic Containment. Hydra::Works::GenericFiles use IndirectContainment. This Service handles that case.
10
+ # This service will always update existing and does not do versioning of persisted files.
11
+ #
12
+ # @param [Hydra::Works::GenericFile::Base] object the file will be added to
13
+ # @param [#read or String] file the derivative filestream optionally responds to :mime_type
14
+ # @param [String] extract file type symbol (e.g. :thumbnail) from Hydra::Derivatives created destination_name
15
+
16
+ def self.call(object, file, destination_name)
17
+ type = destination_name.gsub(/^original_file_/, '').to_sym
18
+ Hydra::Works::AddFileToGenericFile.call(object, file, type, update_existing: true, versioning: false)
19
+ end
20
+
21
+ end
22
+ end
@@ -1,31 +1,23 @@
1
1
  module Hydra::Works
2
2
  class UploadFileToGenericFile
3
3
 
4
- def self.call(object, path, additional_services=[], replace=false)
5
- raise ArgumentError, "supplied object must be a generic file" unless Hydra::Works.generic_file?(object)
6
- raise ArgumentError, "supplied path must be a string" unless path.is_a?(String)
7
- raise ArgumentError, "supplied path to file does not exist" unless ::File.exists?(path)
8
-
9
- if object.original_file.nil? || object.original_file.new_record?
10
- Hydra::Works::AddOriginalFile.call(object, path)
11
- else
12
- if replace
13
- Hydra::Works::AddOriginalFile.call(object, path, replace)
14
- else
15
- Hydra::Works::AddVersionedOriginalFile.call(object, path)
16
- end
17
- end
4
+ # Sets a file as the primary file (original_file) of the generic_file
5
+ # @param [Hydra::PCDM::GenericFile::Base] generic_file the file will be added to
6
+ # @param [IO,File,Rack::Multipart::UploadedFile, #read] object that will be the contents. If file responds to :mime_type or :original_name, those will be called to provide technical metadata.
7
+ # @param [Array] additional_services (ie Generating Thumbnails) to call with generic_file after adding the file as its original_file
8
+ # @param [Boolean] update_existing whether to update an existing file if there is one. When set to true, performs a create_or_update. When set to false, always creates a new file within generic_file.files.
9
+ # @param [Boolean] versioning whether to create new version entries (only applicable if +type+ corresponds to a versionable file)
18
10
 
19
- object.save
20
- object.reload
11
+ def self.call(generic_file, file, additional_services: [], update_existing: true, versioning: true)
12
+ Hydra::Works::AddFileToGenericFile.call(generic_file, file, :original_file, update_existing: update_existing, versioning: versioning)
21
13
 
22
14
  # Call any additional services
23
15
  additional_services.each do |service|
24
- service.call(object)
16
+ service.call(generic_file)
25
17
  end
26
18
 
27
- object.save
28
- object
19
+ generic_file.save
20
+ generic_file
29
21
  end
30
22
 
31
23
  end
@@ -1,5 +1,5 @@
1
1
  module Hydra
2
2
  module Works
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  require 'rdf'
2
2
  module WorksVocabularies
3
- class WorksTerms < RDF::Vocabulary("http://hydra.org/works/models#")
3
+ class WorksTerms < RDF::Vocabulary("http://projecthydra.org/works/models#")
4
4
 
5
5
  # Class definitions
6
6
  term :Collection
Binary file
Binary file
Binary file
Binary file
@@ -2,40 +2,523 @@ require 'spec_helper'
2
2
 
3
3
  describe Hydra::Works::Collection do
4
4
 
5
- let(:collection1) { Hydra::Works::Collection.create }
6
- let(:collection2) { Hydra::Works::Collection.create }
7
- let(:collection3) { Hydra::Works::Collection.create }
5
+ subject { Hydra::Works::Collection.new }
8
6
 
9
- let(:generic_work1) { Hydra::Works::GenericWork::Base.create }
10
- let(:generic_work2) { Hydra::Works::GenericWork::Base.create }
7
+ let(:collection1) { Hydra::Works::Collection.new }
8
+ let(:collection2) { Hydra::Works::Collection.new }
9
+ let(:collection3) { Hydra::Works::Collection.new }
10
+
11
+ let(:generic_work1) { Hydra::Works::GenericWork::Base.new }
12
+ let(:generic_work2) { Hydra::Works::GenericWork::Base.new }
13
+ let(:generic_work3) { Hydra::Works::GenericWork::Base.new }
14
+
15
+ describe '#child_collections' do
16
+ it 'should return empty array when only generic_works are aggregated' do
17
+ subject.child_generic_works << generic_work1
18
+ subject.child_generic_works << generic_work2
19
+ expect(subject.child_collections ).to eq []
20
+ end
21
+
22
+ context 'with other collections & generic_works' do
23
+ before do
24
+ subject.child_collections << collection1
25
+ subject.child_collections << collection2
26
+ subject.child_generic_works << generic_work1
27
+ subject.child_generic_works << generic_work2
28
+ end
29
+
30
+ it 'should only return collections' do
31
+ expect(subject.child_collections ).to eq [collection1,collection2]
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#child_collections <<' do
37
+ context 'with acceptable collections' do
38
+ context 'with collections and generic_works' do
39
+ before do
40
+ subject.child_collections << collection1
41
+ subject.child_collections << collection2
42
+ subject.child_generic_works << generic_work1
43
+ subject.child_generic_works << generic_work2
44
+ end
45
+
46
+ it 'should add an generic_work to collection with collections and generic_works' do
47
+ subject.child_collections << collection3
48
+ expect( subject.child_collections ).to eq [collection1,collection2,collection3]
49
+ end
50
+ end
51
+
52
+ describe 'aggregates collections that implement Hydra::Works' do
53
+ before do
54
+ class Kollection < ActiveFedora::Base
55
+ include Hydra::Works::CollectionBehavior
56
+ end
57
+ end
58
+ after { Object.send(:remove_const, :Kollection) }
59
+ let(:kollection1) { Kollection.new }
60
+
61
+ it 'should accept implementing collection as a child' do
62
+ subject.child_collections << kollection1
63
+ expect( subject.child_collections ).to eq [kollection1]
64
+ end
65
+
66
+ it 'should accept implementing collection as a parent' do
67
+ subject.child_collections << collection1
68
+ expect( subject.child_collections ).to eq [collection1]
69
+ end
70
+ end
71
+
72
+ describe 'aggregates collections that extend Hydra::Works' do
73
+ before do
74
+ class Cullection < Hydra::Works::Collection
75
+ end
76
+ end
77
+ after { Object.send(:remove_const, :Cullection) }
78
+ let(:cullection1) { Cullection.new }
79
+
80
+ it 'should accept extending collection as a child' do
81
+ subject.child_collections << cullection1
82
+ expect( subject.child_collections ).to eq [cullection1]
83
+ end
84
+
85
+ it 'should accept extending collection as a parent' do
86
+ subject.child_collections << collection1
87
+ expect( subject.child_collections ).to eq [collection1]
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'with unacceptable inputs' do
93
+ before(:all) do
94
+ @works_collection101 = Hydra::Works::Collection.new
95
+ @generic_work101 = Hydra::Works::GenericWork::Base.new
96
+ @generic_file101 = Hydra::Works::GenericFile::Base.new
97
+ @pcdm_collection101 = Hydra::PCDM::Collection.new
98
+ @pcdm_object101 = Hydra::PCDM::Object.new
99
+ @pcdm_file101 = Hydra::PCDM::File.new
100
+ @non_PCDM_object = "I'm not a PCDM object"
101
+ @af_base_object = ActiveFedora::Base.new
102
+ end
103
+
104
+ context 'that are unacceptable child collections' do
105
+
106
+ let(:error_type1) { ArgumentError }
107
+ let(:error_message1) { /Hydra::Works::Generic(Work|File)::Base with ID: was expected to works_collection\?, but it was false/ }
108
+ let(:error_type2) { NoMethodError }
109
+ let(:error_message2) { /undefined method `works_collection\?' for .*/ }
110
+
111
+ it 'should NOT aggregate Hydra::Works::GenericWork in collections aggregation' do
112
+ expect{ subject.child_collections << @generic_work101 }.to raise_error(error_type1,error_message1)
113
+ end
114
+
115
+ it 'should NOT aggregate Hydra::Works::GenericFile in collections aggregation' do
116
+ expect{ subject.child_collections << @generic_file101 }.to raise_error(error_type1,error_message1)
117
+ end
118
+
119
+ it 'should NOT aggregate Hydra::PCDM::Collections in collections aggregation' do
120
+ expect{ subject.child_collections << @pcdm_collection101 }.to raise_error(error_type2,error_message2)
121
+ end
122
+
123
+ it 'should NOT aggregate Hydra::PCDM::Objects in collections aggregation' do
124
+ expect{ subject.child_collections << @pcdm_object101 }.to raise_error(error_type2,error_message2)
125
+ end
126
+
127
+ it 'should NOT aggregate Hydra::PCDM::Files in collections aggregation' do
128
+ expect{ subject.child_collections << @pcdm_file101 }.to raise_error(error_type2,error_message2)
129
+ end
130
+
131
+ it 'should NOT aggregate non-PCDM objects in collections aggregation' do
132
+ expect{ subject.child_collections << @non_PCDM_object }.to raise_error(error_type2,error_message2)
133
+ end
134
+
135
+ it 'should NOT aggregate AF::Base objects in collections aggregation' do
136
+ expect{ subject.child_collections << @af_base_object }.to raise_error(error_type2,error_message2)
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ describe 'child_collections.delete' do
143
+ context 'when multiple collections' do
144
+ before do
145
+ subject.child_collections << collection1
146
+ subject.child_collections << collection2
147
+ subject.child_generic_works << generic_work2
148
+ subject.child_collections << collection3
149
+ subject.child_generic_works << generic_work1
150
+ expect( subject.child_collections ).to eq [collection1,collection2,collection3]
151
+ end
152
+
153
+ it 'should remove first collection' do
154
+ expect( subject.child_collections.delete collection1 ).to eq [collection1]
155
+ expect( subject.child_collections ).to eq [collection2,collection3]
156
+ expect( subject.child_generic_works ).to eq [generic_work2,generic_work1]
157
+ end
158
+
159
+ it 'should remove last collection' do
160
+ expect( subject.child_collections.delete collection3 ).to eq [collection3]
161
+ expect( subject.child_collections ).to eq [collection1,collection2]
162
+ expect( subject.child_generic_works). to eq [generic_work2,generic_work1]
163
+ end
164
+
165
+ it 'should remove middle collection' do
166
+ expect( subject.child_collections.delete collection2 ).to eq [collection2]
167
+ expect( subject.child_collections ).to eq [collection1,collection3]
168
+ expect( subject.child_generic_works). to eq [generic_work2,generic_work1]
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '#child_generic_works' do
174
+ it 'should return empty array when only collections are aggregated' do
175
+ subject.child_collections << collection1
176
+ subject.child_collections << collection2
177
+ expect(subject.child_generic_works). to eq []
178
+ end
179
+
180
+ context 'with collections and generic works' do
181
+ before do
182
+ subject.child_collections << collection1
183
+ subject.child_collections << collection2
184
+ subject.child_generic_works << generic_work1
185
+ subject.child_generic_works << generic_work2
186
+ end
187
+
188
+ it 'should only return generic works' do
189
+ expect(subject.child_generic_works). to eq [generic_work1,generic_work2]
190
+ end
191
+ end
192
+ end
193
+
194
+ describe '#child_generic_works <<' do
195
+
196
+ context 'with acceptable generic_works' do
197
+ context 'with collections and generic_works' do
198
+ before do
199
+ subject.child_collections << collection1
200
+ subject.child_collections << collection2
201
+ subject.child_generic_works << generic_work1
202
+ subject.child_generic_works << generic_work2
203
+ end
204
+
205
+ it 'should add generic_work to collection with collections and generic_works' do
206
+ subject.child_generic_works << generic_work3
207
+ expect( subject.child_generic_works ).to eq [generic_work1,generic_work2,generic_work3]
208
+ end
209
+ end
210
+
211
+ describe 'aggregates generic_works that implement Hydra::Works' do
212
+ before do
213
+ class DummyIncWork < ActiveFedora::Base
214
+ include Hydra::Works::GenericWorkBehavior
215
+ end
216
+ end
217
+ after { Object.send(:remove_const, :DummyIncWork) }
218
+ let(:iwork1) { DummyIncWork.new }
219
+
220
+ it 'should accept implementing generic_work as a child' do
221
+ subject.child_generic_works << iwork1
222
+ expect( subject.child_generic_works ).to eq [iwork1]
223
+ end
224
+ end
225
+
226
+ describe 'aggregates generic_works that extend Hydra::Works' do
227
+ before do
228
+ class DummyExtWork < Hydra::Works::GenericWork::Base
229
+ end
230
+ end
231
+ after { Object.send(:remove_const, :DummyExtWork) }
232
+ let(:ework1) { DummyExtWork.new }
233
+
234
+ it 'should accept extending generic_work as a child' do
235
+ subject.child_generic_works << ework1
236
+ expect( subject.child_generic_works ).to eq [ework1]
237
+ end
238
+ end
239
+ end
240
+
241
+ context 'with unacceptable inputs' do
242
+ before(:all) do
243
+ @works_collection101 = Hydra::Works::Collection.new
244
+ @works_collection102 = Hydra::Works::Collection.new
245
+ @generic_file101 = Hydra::Works::GenericFile::Base.new
246
+ @pcdm_collection101 = Hydra::PCDM::Collection.new
247
+ @pcdm_object101 = Hydra::PCDM::Object.new
248
+ @pcdm_file101 = Hydra::PCDM::File.new
249
+ @non_PCDM_object = "I'm not a PCDM object"
250
+ @af_base_object = ActiveFedora::Base.new
251
+ end
252
+
253
+ context 'that are unacceptable child generic works' do
254
+
255
+ let(:error_type1) { ArgumentError }
256
+ let(:error_message1) { /Hydra::Works::(GenericFile::Base|Collection) with ID: was expected to works_generic_work\?, but it was false/ }
257
+ let(:error_type2) { NoMethodError }
258
+ let(:error_message2) { /undefined method `works_generic_work\?' for .*/ }
259
+
260
+ it 'should NOT aggregate Hydra::Works::Collection in generic works aggregation' do
261
+ expect{ subject.child_generic_works << @works_collection101 }.to raise_error(error_type1,error_message1)
262
+ end
263
+
264
+ it 'should NOT aggregate Hydra::Works::GenericFile in generic works aggregation' do
265
+ expect{ subject.child_generic_works << @generic_file101 }.to raise_error(error_type1,error_message1)
266
+ end
267
+
268
+ it 'should NOT aggregate Hydra::PCDM::Collections in generic works aggregation' do
269
+ expect{ subject.child_generic_works << @pcdm_collection101 }.to raise_error(error_type2,error_message2)
270
+ end
271
+
272
+ it 'should NOT aggregate Hydra::PCDM::Objects in generic works aggregation' do
273
+ expect{ subject.child_generic_works << @pcdm_object101 }.to raise_error(error_type2,error_message2)
274
+ end
275
+
276
+ it 'should NOT aggregate Hydra::PCDM::Files in generic works aggregation' do
277
+ expect{ subject.child_generic_works << @pcdm_file101 }.to raise_error(error_type2,error_message2)
278
+ end
279
+
280
+ it 'should NOT aggregate non-PCDM objects in generic works aggregation' do
281
+ expect{ subject.child_generic_works << @non_PCDM_object }.to raise_error(error_type2,error_message2)
282
+ end
283
+
284
+ it 'should NOT aggregate AF::Base objects in generic works aggregation' do
285
+ expect{ subject.child_generic_works << @af_base_object }.to raise_error(error_type2,error_message2)
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ describe '#child_generic_works.delete' do
292
+ context 'when multiple generic works' do
293
+ before do
294
+ subject.child_generic_works << generic_work1
295
+ subject.child_generic_works << generic_work2
296
+ subject.child_collections << collection2
297
+ subject.child_generic_works << generic_work3
298
+ subject.child_collections << collection1
299
+ expect( subject.child_generic_works). to eq [generic_work1,generic_work2,generic_work3]
300
+ end
301
+
302
+ it 'should remove first generic work' do
303
+ expect( subject.child_generic_works.delete generic_work1 ).to eq [generic_work1]
304
+ expect( subject.child_generic_works). to eq [generic_work2,generic_work3]
305
+ expect( subject.child_collections ).to eq [collection2,collection1]
306
+ end
307
+
308
+ it 'should remove last generic work' do
309
+ expect( subject.child_generic_works.delete generic_work3 ).to eq [generic_work3]
310
+ expect( subject.child_generic_works). to eq [generic_work1,generic_work2]
311
+ expect( subject.child_collections ).to eq [collection2,collection1]
312
+ end
313
+
314
+ it 'should remove middle generic work' do
315
+ expect( subject.child_generic_works.delete generic_work2 ).to eq [generic_work2]
316
+ expect( subject.child_generic_works). to eq [generic_work1,generic_work3]
317
+ expect( subject.child_collections ).to eq [collection2,collection1]
318
+ end
319
+ end
320
+ end
321
+
322
+ describe '#related_objects' do
323
+ let(:generic_file1) { Hydra::Works::GenericFile::Base.new }
324
+ let(:object1) { Hydra::PCDM::Object.new }
325
+ let(:object2) { Hydra::PCDM::Object.new }
326
+
327
+ context 'with collections and generic works' do
328
+ before do
329
+ subject.child_collections << collection1
330
+ subject.child_collections << collection2
331
+ subject.child_generic_works << generic_work1
332
+ end
333
+
334
+ it 'should return empty array when only collections and generic works are aggregated' do
335
+ expect(subject.related_objects ).to eq []
336
+ end
337
+
338
+ it 'should only return related objects' do
339
+ subject.related_objects << object2
340
+ expect(subject.related_objects ).to eq [object2]
341
+ end
342
+
343
+ it 'should return related objects of various types' do
344
+ subject.related_objects << generic_work2
345
+ subject.related_objects << generic_file1
346
+ subject.related_objects << object1
347
+ subject.save
348
+ subject.reload
349
+ expect( subject.related_objects.include? object1 ).to be true
350
+ expect( subject.related_objects.include? generic_work2 ).to be true
351
+ expect( subject.related_objects.include? generic_file1 ).to be true
352
+ expect( subject.related_objects.size ).to eq 3
353
+ end
354
+ end
355
+ end
356
+
357
+ describe '#related_objects <<' do
358
+
359
+ context 'with acceptable related objects' do
360
+ let(:object1) { Hydra::PCDM::Object.new }
361
+ let(:object2) { Hydra::PCDM::Object.new }
362
+ let(:generic_file1) { Hydra::Works::GenericFile::Base.new }
363
+
364
+ it 'should add various types of related objects to collection' do
365
+ subject.related_objects << generic_work1
366
+ subject.related_objects << generic_file1
367
+ subject.related_objects << object1
368
+ subject.save
369
+ subject.reload
370
+ expect( subject.related_objects.include? generic_work1 ).to be true
371
+ expect( subject.related_objects.include? generic_file1 ).to be true
372
+ expect( subject.related_objects.include? object1 ).to be true
373
+ expect( subject.related_objects.size ).to eq 3
374
+ end
375
+
376
+ context 'with collections and generic_works' do
377
+ before do
378
+ subject.child_collections << collection1
379
+ subject.child_collections << collection2
380
+ subject.child_generic_works << generic_work1
381
+ subject.child_generic_works << generic_work2
382
+ subject.related_objects << object1
383
+ end
384
+
385
+ it 'should add a related object to collection with collections and generic_works' do
386
+ subject.related_objects << object2
387
+ subject.save
388
+ subject.reload
389
+ expect( subject.related_objects.include? object1 ).to be true
390
+ expect( subject.related_objects.include? object2 ).to be true
391
+ expect( subject.related_objects.size ).to eq 2
392
+ end
393
+ end
394
+ end
395
+
396
+ context 'with unacceptable child related objects' do
397
+ let(:pcdm_collection1) { Hydra::PCDM::Collection.new }
398
+ let(:pcdm_file1) { Hydra::PCDM::File.new }
399
+ let(:non_PCDM_object) { "I'm not a PCDM object" }
400
+ let(:af_base_object) { ActiveFedora::Base.new }
401
+
402
+ it 'should NOT aggregate Hydra::Works::Collection in related objects aggregation' do
403
+ expect{ subject.related_objects << collection1 }.to raise_error(ActiveFedora::AssociationTypeMismatch, /Hydra::Works::Collection:.*> is not a PCDM object./)
404
+ end
405
+
406
+ it 'should NOT aggregate Hydra::PCDM::Collections in related objects aggregation' do
407
+ expect{ subject.related_objects << pcdm_collection1 }.to raise_error(ActiveFedora::AssociationTypeMismatch, /Hydra::PCDM::Collection:.* is not a PCDM object./)
408
+ end
409
+
410
+ it 'should NOT aggregate Hydra::PCDM::Files in related objects aggregation' do
411
+ expect{ subject.related_objects << pcdm_file1 }.to raise_error(ActiveFedora::AssociationTypeMismatch, /ActiveFedora::Base.* expected, got Hydra::PCDM::File.*/)
412
+ end
413
+
414
+ it 'should NOT aggregate non-PCDM objects in related objects aggregation' do
415
+ expect{ subject.related_objects << non_PCDM_object }.to raise_error(ActiveFedora::AssociationTypeMismatch, /ActiveFedora::Base.* expected, got String.*/)
416
+ end
417
+
418
+ it 'should NOT aggregate AF::Base objects in related objects aggregation' do
419
+ expect{ subject.related_objects << af_base_object }.to raise_error(ActiveFedora::AssociationTypeMismatch, /ActiveFedora::Base.* is not a PCDM object./)
420
+ end
421
+ end
422
+
423
+ context 'with invalid behaviors' do
424
+ let(:object1) { Hydra::PCDM::Object.new }
425
+ let(:object2) { Hydra::PCDM::Object.new }
426
+
427
+ it 'should NOT allow related objects to repeat' do
428
+ skip 'skipping this test because issue pcdm#92 needs to be addressed' do
429
+ subject.related_objects << object1
430
+ subject.related_objects << object2
431
+ subject.related_objects << object1
432
+ related_objects = subject.related_objects
433
+ expect( related_objects.include? object1 ).to be true
434
+ expect( related_objects.include? object2 ).to be true
435
+ expect( related_objects.size ).to eq 2
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ describe '#related_objects.delete' do
442
+ let(:related_object1) { Hydra::PCDM::Object.new }
443
+ let(:related_work2) { Hydra::Works::GenericWork::Base.new }
444
+ let(:related_file3) { Hydra::Works::GenericFile::Base.new }
445
+ let(:related_object4) { Hydra::PCDM::Object.new }
446
+ let(:related_work5) { Hydra::Works::GenericWork::Base.new }
447
+
448
+ context 'when multiple related objects' do
449
+ before do
450
+ subject.related_objects << related_object1
451
+ subject.related_objects << related_work2
452
+ subject.child_collections << collection2
453
+ subject.child_generic_works << generic_work1
454
+ subject.related_objects << related_file3
455
+ subject.related_objects << related_object4
456
+ subject.child_collections << collection1
457
+ subject.related_objects << related_work5
458
+ expect( subject.related_objects ).to eq [related_object1,related_work2,related_file3,related_object4,related_work5]
459
+ end
460
+
461
+ it 'should remove first related object' do
462
+ expect( subject.related_objects.delete related_object1 ).to eq [related_object1]
463
+ expect( subject.related_objects ).to eq [related_work2,related_file3,related_object4,related_work5]
464
+ expect( subject.child_collections ).to eq [collection2,collection1]
465
+ expect( subject.child_generic_works). to eq [generic_work1]
466
+ end
467
+
468
+ it 'should remove last related object' do
469
+ expect( subject.related_objects.delete related_work5 ).to eq [related_work5]
470
+ expect( subject.related_objects ).to eq [related_object1,related_work2,related_file3,related_object4]
471
+ expect( subject.child_collections ).to eq [collection2,collection1]
472
+ expect( subject.child_generic_works). to eq [generic_work1]
473
+ end
474
+
475
+ it 'should remove middle related object' do
476
+ expect( subject.related_objects.delete related_file3 ).to eq [related_file3]
477
+ expect( subject.related_objects ).to eq [related_object1,related_work2,related_object4,related_work5]
478
+ expect( subject.child_collections ).to eq [collection2,collection1]
479
+ expect( subject.child_generic_works). to eq [generic_work1]
480
+ end
481
+ end
482
+ end
11
483
 
12
484
  describe '#collections=' do
13
485
  it 'should aggregate collections' do
14
- collection1.collections = [collection2, collection3]
15
- collection1.save
16
- expect(collection1.collections).to eq [collection2, collection3]
486
+ collection1.child_collections = [collection2, collection3]
487
+ expect(collection1.child_collections).to eq [collection2, collection3]
17
488
  end
18
489
  end
19
490
 
20
- describe '#generic_works=' do
491
+ describe '#child_generic_works=' do
21
492
  it 'should aggregate generic_works' do
22
- collection1.generic_works = [generic_work1,generic_work2]
23
- collection1.save
24
- expect(collection1.generic_works).to eq [generic_work1,generic_work2]
493
+ collection1.child_generic_works = [generic_work1,generic_work2]
494
+ expect(collection1.child_generic_works).to eq [generic_work1,generic_work2]
25
495
  end
26
496
  end
27
497
 
28
498
  describe 'Related objects' do
29
- let(:object) { Hydra::PCDM::Object.create }
30
- let(:collection) { Hydra::Works::Collection.create }
499
+ let(:object) { Hydra::PCDM::Object.new }
500
+ let(:collection) { Hydra::Works::Collection.new }
31
501
 
32
502
  before do
33
503
  collection.related_objects = [object]
34
- collection.save
35
504
  end
36
505
 
37
506
  it 'persists' do
38
- expect(collection.reload.related_objects).to eq [object]
507
+ expect(collection.related_objects).to eq [object]
508
+ end
509
+ end
510
+
511
+ describe "should have parent collection accessors" do
512
+ before do
513
+ collection1.child_collections << collection2
514
+ collection1.save
515
+ end
516
+
517
+ it 'should have parents' do
518
+ expect(collection2.parents).to eq [collection1]
519
+ end
520
+ it 'should have a parent collection' do
521
+ expect(collection2.parent_collections).to eq [collection1]
39
522
  end
40
523
  end
41
524
  end