hydra-works 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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