active-fedora 8.7.0 → 9.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +8 -15
  3. data/Gemfile +5 -5
  4. data/History.txt +0 -80
  5. data/README.md +1 -0
  6. data/Rakefile +0 -3
  7. data/active-fedora.gemspec +8 -7
  8. data/config/fedora.yml +5 -4
  9. data/config/predicate_mappings.yml +5 -5
  10. data/gemfiles/rails4.1.gemfile +10 -0
  11. data/gemfiles/rails4.2.beta.gemfile +10 -0
  12. data/lib/active_fedora.rb +151 -117
  13. data/lib/active_fedora/associations.rb +47 -15
  14. data/lib/active_fedora/associations/association.rb +29 -8
  15. data/lib/active_fedora/associations/association_scope.rb +5 -5
  16. data/lib/active_fedora/associations/belongs_to_association.rb +20 -63
  17. data/lib/active_fedora/associations/builder/association.rb +61 -25
  18. data/lib/active_fedora/associations/builder/belongs_to.rb +7 -94
  19. data/lib/active_fedora/associations/builder/collection_association.rb +11 -43
  20. data/lib/active_fedora/associations/builder/contains.rb +28 -0
  21. data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +13 -3
  22. data/lib/active_fedora/associations/builder/has_many.rb +16 -10
  23. data/lib/active_fedora/associations/builder/property.rb +14 -0
  24. data/lib/active_fedora/associations/builder/singular_association.rb +14 -18
  25. data/lib/active_fedora/associations/builder/singular_property.rb +12 -0
  26. data/lib/active_fedora/associations/collection_association.rb +57 -80
  27. data/lib/active_fedora/associations/contains_association.rb +50 -0
  28. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +44 -24
  29. data/lib/active_fedora/associations/has_many_association.rb +46 -14
  30. data/lib/active_fedora/associations/rdf.rb +86 -0
  31. data/lib/active_fedora/associations/singular_association.rb +4 -8
  32. data/lib/active_fedora/associations/singular_rdf.rb +15 -0
  33. data/lib/active_fedora/attached_files.rb +195 -0
  34. data/lib/active_fedora/attribute_methods.rb +122 -0
  35. data/lib/active_fedora/attribute_methods/dirty.rb +16 -0
  36. data/lib/active_fedora/attribute_methods/read.rb +61 -0
  37. data/lib/active_fedora/attribute_methods/write.rb +47 -0
  38. data/lib/active_fedora/attributes.rb +93 -44
  39. data/lib/active_fedora/attributes/primary_key.rb +12 -0
  40. data/lib/active_fedora/autosave_association.rb +2 -2
  41. data/lib/active_fedora/base.rb +16 -15
  42. data/lib/active_fedora/callbacks.rb +7 -7
  43. data/lib/active_fedora/change_set.rb +34 -0
  44. data/lib/active_fedora/cleaner.rb +62 -0
  45. data/lib/active_fedora/config.rb +4 -22
  46. data/lib/active_fedora/core.rb +173 -99
  47. data/lib/active_fedora/datastream.rb +4 -117
  48. data/lib/active_fedora/datastreams.rb +2 -263
  49. data/lib/active_fedora/datastreams/nokogiri_datastreams.rb +28 -51
  50. data/lib/active_fedora/{datastream_attribute.rb → delegated_attribute.rb} +57 -26
  51. data/lib/active_fedora/errors.rb +61 -0
  52. data/lib/active_fedora/fedora.rb +19 -0
  53. data/lib/active_fedora/fedora_attributes.rb +58 -26
  54. data/lib/active_fedora/file.rb +318 -0
  55. data/lib/active_fedora/file_configurator.rb +32 -32
  56. data/lib/active_fedora/file_path_builder.rb +24 -0
  57. data/lib/active_fedora/files_hash.rb +82 -0
  58. data/lib/active_fedora/fixity_service.rb +40 -0
  59. data/lib/active_fedora/indexing.rb +55 -82
  60. data/lib/active_fedora/indexing_service.rb +70 -0
  61. data/lib/active_fedora/ldp_resource.rb +26 -0
  62. data/lib/active_fedora/loadable_from_json.rb +112 -0
  63. data/lib/active_fedora/model.rb +5 -19
  64. data/lib/active_fedora/nested_attributes.rb +5 -6
  65. data/lib/active_fedora/nom_datastream.rb +15 -25
  66. data/lib/active_fedora/om_datastream.rb +26 -197
  67. data/lib/active_fedora/persistence.rb +95 -71
  68. data/lib/active_fedora/predicates.rb +4 -4
  69. data/lib/active_fedora/qualified_dublin_core_datastream.rb +17 -18
  70. data/lib/active_fedora/querying.rb +3 -4
  71. data/lib/active_fedora/railtie.rb +3 -6
  72. data/lib/active_fedora/rdf.rb +3 -1
  73. data/lib/active_fedora/rdf/datastream_indexing.rb +11 -0
  74. data/lib/active_fedora/rdf/fcrepo.rb +10 -324
  75. data/lib/active_fedora/rdf/indexing.rb +20 -16
  76. data/lib/active_fedora/rdf/ldp.rb +6 -0
  77. data/lib/active_fedora/rdf/ntriples_rdf_datastream.rb +1 -1
  78. data/lib/active_fedora/rdf/persistence.rb +5 -6
  79. data/lib/active_fedora/rdf/rdf_datastream.rb +44 -37
  80. data/lib/active_fedora/rdf/rdfxml_datastream.rb +13 -0
  81. data/lib/active_fedora/rdf/rels_ext.rb +26 -0
  82. data/lib/active_fedora/reflection.rb +256 -199
  83. data/lib/active_fedora/relation.rb +18 -6
  84. data/lib/active_fedora/relation/finder_methods.rb +69 -38
  85. data/lib/active_fedora/relation/query_methods.rb +7 -3
  86. data/lib/active_fedora/rspec_matchers/belong_to_associated_active_fedora_object_matcher.rb +7 -7
  87. data/lib/active_fedora/rspec_matchers/have_many_associated_active_fedora_objects_matcher.rb +8 -8
  88. data/lib/active_fedora/rspec_matchers/have_predicate_matcher.rb +9 -11
  89. data/lib/active_fedora/simple_datastream.rb +18 -13
  90. data/lib/active_fedora/solr_instance_loader.rb +18 -38
  91. data/lib/active_fedora/solr_service.rb +37 -20
  92. data/lib/active_fedora/sparql_insert.rb +45 -0
  93. data/lib/active_fedora/test_support.rb +1 -22
  94. data/lib/active_fedora/version.rb +1 -1
  95. data/lib/active_fedora/versionable.rb +90 -0
  96. data/lib/active_fedora/with_metadata.rb +37 -0
  97. data/lib/active_fedora/with_metadata/metadata_node.rb +70 -0
  98. data/lib/generators/active_fedora/config/config_generator.rb +0 -1
  99. data/lib/generators/active_fedora/config/solr/solr_generator.rb +1 -1
  100. data/lib/generators/active_fedora/model/model_generator.rb +5 -5
  101. data/lib/generators/active_fedora/model/templates/datastream_spec.rb.erb +1 -1
  102. data/lib/generators/active_fedora/model/templates/model_spec.rb.erb +2 -2
  103. data/lib/tasks/active_fedora_dev.rake +21 -27
  104. data/spec/config_helper.rb +1 -1
  105. data/spec/fixtures/mixed_rdf_descMetadata.nt +6 -6
  106. data/spec/fixtures/rails_root/config/predicate_mappings.yml +3 -19
  107. data/spec/fixtures/solr_rdf_descMetadata.nt +6 -6
  108. data/spec/integration/associations_spec.rb +133 -153
  109. data/spec/integration/attached_files_spec.rb +164 -0
  110. data/spec/integration/attributes_spec.rb +73 -12
  111. data/spec/integration/autosave_association_spec.rb +3 -3
  112. data/spec/integration/base_spec.rb +57 -351
  113. data/spec/integration/belongs_to_association_spec.rb +86 -76
  114. data/spec/integration/bug_spec.rb +3 -3
  115. data/spec/integration/collection_association_spec.rb +4 -4
  116. data/spec/integration/complex_rdf_datastream_spec.rb +54 -56
  117. data/spec/integration/delete_all_spec.rb +18 -15
  118. data/spec/integration/eradicate_spec.rb +54 -0
  119. data/spec/integration/fedora_solr_sync_spec.rb +7 -5
  120. data/spec/integration/field_to_solr_name_spec.rb +5 -5
  121. data/spec/integration/file_fixity_spec.rb +40 -0
  122. data/spec/integration/file_spec.rb +122 -0
  123. data/spec/integration/full_featured_model_spec.rb +53 -63
  124. data/spec/integration/has_and_belongs_to_many_associations_spec.rb +141 -114
  125. data/spec/integration/has_many_associations_spec.rb +142 -64
  126. data/spec/integration/json_serialization_spec.rb +50 -8
  127. data/spec/integration/model_spec.rb +12 -29
  128. data/spec/integration/nested_attribute_spec.rb +28 -20
  129. data/spec/integration/ntriples_datastream_spec.rb +60 -57
  130. data/spec/integration/om_datastream_spec.rb +51 -140
  131. data/spec/integration/rdf_nested_attributes_spec.rb +16 -14
  132. data/spec/integration/relation_delegation_spec.rb +7 -9
  133. data/spec/integration/relation_spec.rb +9 -7
  134. data/spec/integration/scoped_query_spec.rb +26 -26
  135. data/spec/integration/solr_instance_loader_spec.rb +69 -53
  136. data/spec/integration/solr_service_spec.rb +12 -73
  137. data/spec/integration/versionable_spec.rb +477 -0
  138. data/spec/integration/with_metadata_spec.rb +52 -0
  139. data/spec/samples/hydra-mods_article_datastream.rb +10 -6
  140. data/spec/samples/models/mods_article.rb +6 -2
  141. data/spec/samples/oral_history_sample.xml +1 -1
  142. data/spec/samples/oral_history_xml.xml +1 -1
  143. data/spec/samples/special_thing.rb +3 -3
  144. data/spec/spec_helper.rb +22 -12
  145. data/spec/support/an_active_model.rb +3 -7
  146. data/spec/unit/active_fedora_spec.rb +20 -17
  147. data/spec/unit/attached_files_spec.rb +203 -0
  148. data/spec/unit/attributes_spec.rb +286 -207
  149. data/spec/unit/base_active_model_spec.rb +8 -8
  150. data/spec/unit/base_datastream_management_spec.rb +11 -24
  151. data/spec/unit/base_extra_spec.rb +17 -67
  152. data/spec/unit/base_spec.rb +163 -428
  153. data/spec/unit/builder/has_and_belongs_to_many_spec.rb +2 -2
  154. data/spec/unit/callback_spec.rb +38 -23
  155. data/spec/unit/change_set_spec.rb +46 -0
  156. data/spec/unit/code_configurator_spec.rb +5 -5
  157. data/spec/unit/config_spec.rb +9 -14
  158. data/spec/unit/core_spec.rb +59 -8
  159. data/spec/unit/file_configurator_spec.rb +55 -53
  160. data/spec/unit/file_path_builder_spec.rb +18 -0
  161. data/spec/unit/file_spec.rb +221 -0
  162. data/spec/unit/files_hash_spec.rb +53 -0
  163. data/spec/unit/fixity_service_spec.rb +34 -0
  164. data/spec/unit/has_and_belongs_to_many_association_spec.rb +134 -0
  165. data/spec/unit/has_many_association_spec.rb +51 -0
  166. data/spec/unit/indexing_service_spec.rb +23 -0
  167. data/spec/unit/indexing_spec.rb +26 -0
  168. data/spec/unit/inheritance_spec.rb +9 -10
  169. data/spec/unit/model_spec.rb +15 -33
  170. data/spec/unit/nom_datastream_spec.rb +13 -10
  171. data/spec/unit/ntriples_datastream_spec.rb +81 -96
  172. data/spec/unit/om_datastream_spec.rb +137 -227
  173. data/spec/unit/persistence_spec.rb +28 -34
  174. data/spec/unit/predicates_spec.rb +29 -29
  175. data/spec/unit/property_spec.rb +1 -3
  176. data/spec/unit/qualified_dublin_core_datastream_spec.rb +27 -32
  177. data/spec/unit/query_spec.rb +116 -149
  178. data/spec/unit/rdf_datastream_spec.rb +25 -43
  179. data/spec/unit/rdf_resource_datastream_spec.rb +24 -123
  180. data/spec/unit/{rdfxml_rdf_datastream_spec.rb → rdfxml_datastream_spec.rb} +21 -25
  181. data/spec/unit/readonly_spec.rb +23 -0
  182. data/spec/unit/rspec_matchers/belong_to_associated_active_fedora_object_matcher_spec.rb +6 -6
  183. data/spec/unit/rspec_matchers/have_many_associated_active_fedora_objects_matcher_spec.rb +6 -6
  184. data/spec/unit/rspec_matchers/have_predicate_matcher_spec.rb +6 -6
  185. data/spec/unit/serializers_spec.rb +1 -1
  186. data/spec/unit/simple_datastream_spec.rb +12 -23
  187. data/spec/unit/solr_config_options_spec.rb +14 -17
  188. data/spec/unit/solr_service_spec.rb +38 -77
  189. data/spec/unit/sparql_insert_spec.rb +32 -0
  190. data/spec/unit/validations_spec.rb +8 -11
  191. metadata +96 -121
  192. data/lib/active_fedora/auditable.rb +0 -9
  193. data/lib/active_fedora/content_model.rb +0 -70
  194. data/lib/active_fedora/datastream_collections.rb +0 -302
  195. data/lib/active_fedora/datastream_hash.rb +0 -35
  196. data/lib/active_fedora/digital_object.rb +0 -55
  197. data/lib/active_fedora/fixture_exporter.rb +0 -33
  198. data/lib/active_fedora/fixture_loader.rb +0 -48
  199. data/lib/active_fedora/rdf/identifiable.rb +0 -66
  200. data/lib/active_fedora/rdf/project_hydra.rb +0 -12
  201. data/lib/active_fedora/rdf/rdfxml_rdf_datastream.rb +0 -13
  202. data/lib/active_fedora/rdf_xml_writer.rb +0 -49
  203. data/lib/active_fedora/relationship_graph.rb +0 -101
  204. data/lib/active_fedora/reload_on_save.rb +0 -16
  205. data/lib/active_fedora/rels_ext_datastream.rb +0 -100
  206. data/lib/active_fedora/rspec_matchers/match_fedora_datastream_matcher.rb +0 -41
  207. data/lib/active_fedora/rubydora_connection.rb +0 -35
  208. data/lib/active_fedora/semantic_node.rb +0 -164
  209. data/lib/active_fedora/service_definitions.rb +0 -88
  210. data/lib/active_fedora/sharding.rb +0 -58
  211. data/lib/active_fedora/solr_digital_object.rb +0 -68
  212. data/lib/active_fedora/unsaved_digital_object.rb +0 -58
  213. data/lib/generators/active_fedora/config/fedora/fedora_generator.rb +0 -12
  214. data/lib/generators/active_fedora/config/fedora/templates/fedora.yml +0 -38
  215. data/lib/generators/active_fedora/config/fedora/templates/fedora_conf/conf/development/fedora.fcfg +0 -953
  216. data/lib/generators/active_fedora/config/fedora/templates/fedora_conf/conf/test/fedora.fcfg +0 -953
  217. data/lib/tasks/active_fedora.rake +0 -83
  218. data/spec/fixtures/sharded_fedora.yml +0 -11
  219. data/spec/integration/auditable_spec.rb +0 -29
  220. data/spec/integration/datastream_collections_spec.rb +0 -127
  221. data/spec/integration/datastream_spec.rb +0 -90
  222. data/spec/integration/datastreams_spec.rb +0 -173
  223. data/spec/integration/load_from_solr_spec.rb +0 -66
  224. data/spec/integration/rels_ext_datastream_spec.rb +0 -82
  225. data/spec/support/mock_fedora.rb +0 -44
  226. data/spec/unit/content_model_spec.rb +0 -86
  227. data/spec/unit/datastream_collections_spec.rb +0 -420
  228. data/spec/unit/datastream_spec.rb +0 -83
  229. data/spec/unit/datastreams_spec.rb +0 -243
  230. data/spec/unit/has_and_belongs_to_many_collection_spec.rb +0 -96
  231. data/spec/unit/has_many_collection_spec.rb +0 -35
  232. data/spec/unit/rdf_vocab_spec.rb +0 -30
  233. data/spec/unit/rdf_xml_writer_spec.rb +0 -63
  234. data/spec/unit/relationship_graph_spec.rb +0 -115
  235. data/spec/unit/reload_on_save_spec.rb +0 -24
  236. data/spec/unit/rels_ext_datastream_spec.rb +0 -170
  237. data/spec/unit/rspec_matchers/match_fedora_datastream_matcher_spec.rb +0 -44
  238. data/spec/unit/rubydora_connection_spec.rb +0 -12
  239. data/spec/unit/semantic_node_spec.rb +0 -112
  240. data/spec/unit/service_definitions_spec.rb +0 -63
  241. data/spec/unit/solr_digital_object_spec.rb +0 -97
  242. data/spec/unit/unsaved_digital_object_spec.rb +0 -48
@@ -0,0 +1,50 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class ContainsAssociation < SingularAssociation #:nodoc:
4
+ # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
+ def reader(force_reload = false)
6
+ super || build
7
+ end
8
+
9
+ def find_target
10
+ reflection.build_association(target_uri)
11
+ end
12
+
13
+ def target_uri
14
+ "#{owner.uri}/#{reflection.name}"
15
+ end
16
+
17
+ private
18
+
19
+ def raise_on_type_mismatch(record)
20
+ return if record.is_a? LoadableFromJson::SolrBackedMetadataFile
21
+ super
22
+ end
23
+
24
+ def replace(record)
25
+ if record
26
+ raise_on_type_mismatch(record)
27
+ @updated = true
28
+ end
29
+
30
+ self.target = record
31
+ end
32
+
33
+ def new_record(method, attributes)
34
+ record = super
35
+ configure_datastream(record)
36
+ record
37
+ end
38
+
39
+ def configure_datastream(record)
40
+ # If you called has_metadata with a block, pass the block into the File class
41
+ if reflection.options[:block].class == Proc
42
+ reflection.options[:block].call(record)
43
+ end
44
+ if record.new_record? && reflection.options[:autocreate]
45
+ record.datastream_will_change!
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -15,36 +15,51 @@ module ActiveFedora
15
15
  end
16
16
  end
17
17
 
18
- ### TODO save relationship
19
- @owner.add_relationship(@reflection.options[:property], record)
18
+ owner[reflection.foreign_key] ||= []
19
+ owner[reflection.foreign_key] += [record.id]
20
20
 
21
21
  if @owner.new_record? and @reflection.options[:inverse_of]
22
22
  ActiveFedora::Base.logger.warn("has_and_belongs_to_many #{@reflection.inspect} is cowardly refusing to insert the inverse relationship into #{record}, because #{@owner} is not persisted yet.") if ActiveFedora::Base.logger
23
23
  elsif @reflection.options[:inverse_of]
24
- record.add_relationship(@reflection.options[:inverse_of], @owner)
24
+ inverse = @reflection.inverse_of
25
+ record[inverse.foreign_key] ||= []
26
+ record[inverse.foreign_key] += [owner.id]
25
27
  record.save
26
28
  end
27
29
 
28
30
  return true
29
31
  end
30
32
 
33
+ def concat_records(*records)
34
+ result = true
31
35
 
32
- def find_target
33
- page_size = @reflection.options[:solr_page_size]
34
- page_size ||= 200
35
- pids = @owner.ids_for_outbound(@reflection.options[:property])
36
- return [] if pids.empty?
37
- solr_result = []
38
- 0.step(pids.size,page_size) do |startIdx|
39
- query = ActiveFedora::SolrService.construct_query_for_pids(pids.slice(startIdx,page_size))
40
- solr_result += ActiveFedora::SolrService.query(query, rows: page_size)
36
+ records.flatten.each do |record|
37
+ raise_on_type_mismatch(record)
38
+ add_to_target(record) do |r|
39
+ result &&= insert_record(record)
41
40
  end
42
- return ActiveFedora::SolrService.reify_solr_results(solr_result)
41
+ end
42
+
43
+ result && records
43
44
  end
44
45
 
45
- # In a HABTM, just look in the rels-ext, no need to run a count query from solr.
46
+
47
+ def find_target
48
+ page_size = @reflection.options[:solr_page_size]
49
+ page_size ||= 200
50
+ ids = owner[reflection.foreign_key]
51
+ return [] if ids.blank?
52
+ solr_result = []
53
+ 0.step(ids.size,page_size) do |startIdx|
54
+ query = ActiveFedora::SolrService.construct_query_for_ids(ids.slice(startIdx,page_size))
55
+ solr_result += ActiveFedora::SolrService.query(query, rows: page_size)
56
+ end
57
+ return ActiveFedora::SolrService.reify_solr_results(solr_result)
58
+ end
59
+
60
+ # In a HABTM, just look in the RDF, no need to run a count query from solr.
46
61
  def count(options = {})
47
- @owner.ids_for_outbound(@reflection.options[:property]).size
62
+ owner[reflection.foreign_key].size
48
63
  end
49
64
 
50
65
  def first
@@ -59,19 +74,24 @@ module ActiveFedora
59
74
 
60
75
  def delete_records(records, method)
61
76
  records.each do |r|
62
- @owner.remove_relationship(@reflection.options[:property], r)
63
-
77
+ owner[reflection.foreign_key] -= [r.id]
78
+
64
79
  if (@reflection.options[:inverse_of])
65
- r.remove_relationship(@reflection.options[:inverse_of], @owner)
66
- # It looks like inverse_of points at a predicate, not at a relationship name,
67
- # which is what we should have done. Now we need a way to look up the
68
- # reflection by predicate
69
- name = r.class.reflection_name_for_predicate(@reflection.options[:inverse_of])
70
- r.association(name).reset
80
+ inverse = @reflection.inverse_of
81
+ r[inverse.foreign_key] -= [owner.id]
82
+ r.association(inverse.name).reset
71
83
  r.save
72
84
  end
73
85
  end
74
- @owner.save! unless @owner.new_record?
86
+ unless @owner.new_record? || @owner.destroyed?
87
+ @owner.save!
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def stale_state
94
+ owner[reflection.foreign_key]
75
95
  end
76
96
 
77
97
  end
@@ -14,7 +14,7 @@ module ActiveFedora
14
14
  # If the collection is empty the target is set to an empty array and
15
15
  # the loaded flag is set to true as well.
16
16
  def count_records
17
- count = if loaded?
17
+ count = if loaded?
18
18
  @target.size
19
19
  else
20
20
  @reflection.klass.count(:conditions => @counter_query)
@@ -28,18 +28,52 @@ module ActiveFedora
28
28
  return count
29
29
  end
30
30
 
31
- def insert_record(record, force = false, validate = true)
32
- set_belongs_to_association_for(record)
33
- record.save
31
+ def set_owner_attributes(record)
32
+ if klass == ActiveFedora::Base
33
+ inverse = find_polymorphic_inverse(record)
34
+ if inverse.belongs_to?
35
+ record[inverse.foreign_key] = owner.id
36
+ else # HABTM
37
+ record[inverse.foreign_key] ||= []
38
+ #TODO use primary_key instead of `owner.id`
39
+ record[inverse.foreign_key] += [owner.id]
40
+ end
41
+ elsif owner.persisted?
42
+ inverse = reflection.inverse_of
43
+ if inverse && inverse.collection?
44
+ record[inverse.foreign_key] ||= []
45
+ #TODO use primary_key instead of `owner.id`
46
+ record[inverse.foreign_key] += [owner.id]
47
+ elsif inverse && inverse.klass == ActiveFedora::Base
48
+ record[inverse.foreign_key] = owner.id
49
+ else
50
+ #TODO use primary_key instead of `owner.id`
51
+ record[reflection.foreign_key] = owner.id
52
+ end
53
+ end
54
+ end
55
+
56
+ def insert_record(record, validate = true, raise = false)
57
+ set_owner_attributes(record)
58
+ set_inverse_instance(record)
59
+
60
+ if raise
61
+ record.save!(validate: validate)
62
+ else
63
+ record.save(validate: validate)
64
+ end
34
65
  end
35
66
 
36
67
  protected
37
68
 
69
+ def find_polymorphic_inverse(record)
70
+ record.reflections.values.find{ |r| !r.has_many? && r.options[:property] == reflection.options[:property]}
71
+ end
72
+
38
73
  # Deletes the records according to the <tt>:dependent</tt> option.
39
74
  def delete_records(records, method)
40
75
  if method == :destroy
41
76
  records.each { |r| r.destroy }
42
- #update_counter(-records.length) unless inverse_updates_counter_cache?
43
77
  else
44
78
  # Find all the records that point to this and nullify them
45
79
  # keys = records.map { |r| r[reflection.association_primary_key] }
@@ -50,22 +84,22 @@ module ActiveFedora
50
84
  #update_counter(-scope.delete_all)
51
85
  else
52
86
 
53
-
54
87
  if reflection.inverse_of # Can't get an inverse when class_name: 'ActiveFedora::Base' is supplied
55
- inverse = reflection.inverse_of.name
88
+ inverse = reflection.inverse_of
56
89
  records.each do |record|
57
90
  if record.persisted?
58
- assoc = record.association(inverse)
59
- if assoc.reflection.collection?
91
+ if inverse.collection?
60
92
  # Remove from a has_and_belongs_to_many
61
- record.association(inverse).delete(@owner)
93
+ record.association(inverse.name).delete(@owner)
94
+ elsif inverse.klass == ActiveFedora::Base
95
+ record[inverse.foreign_key] = nil
62
96
  else
63
97
  # Remove from a belongs_to
64
- record.association(inverse).id_writer(nil)
98
+ record[reflection.foreign_key] = nil
65
99
  end
66
100
  # Check to see if the object still exists (may be already deleted).
67
101
  # In Rails, they do this with an update_all to avoid callbacks and validations, we may need the same.
68
- record.save! if record.class.exists?(record.pid)
102
+ record.save! if record.class.exists?(record.id)
69
103
  end
70
104
  end
71
105
  end
@@ -74,8 +108,6 @@ module ActiveFedora
74
108
  end
75
109
  end
76
110
  end
77
-
78
-
79
111
  end
80
112
  end
81
113
  end
@@ -0,0 +1,86 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class RDF < SingularAssociation #:nodoc:
4
+
5
+ def replace(values)
6
+ raise "can't modify frozen #{owner.class}" if owner.frozen?
7
+ destroy
8
+ values.each do |value|
9
+ uri = ActiveFedora::Base.id_to_uri(value)
10
+ owner.resource.insert [owner.rdf_subject, reflection.predicate, ::RDF::URI.new(uri)]
11
+ end
12
+ owner.send(:attribute_will_change!, reflection.name)
13
+ end
14
+
15
+ def reader
16
+ filtered_results.map { |val| ActiveFedora::Base.uri_to_id(val) }
17
+ end
18
+
19
+ def destroy
20
+ filtered_results.each do |candidate|
21
+ owner.resource.delete([owner.rdf_subject, reflection.predicate, candidate])
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # @return [Array<RDF::URI>] the rdf results filtered to objects that match the specified class_name consraint
28
+ def filtered_results
29
+ if filtering_required?
30
+ filter_by_class(rdf_uris)
31
+ else
32
+ rdf_uris
33
+ end
34
+ end
35
+
36
+ # TODO Detect when this is the only relationship for this predicate, then skip the filtering.
37
+ def filtering_required?
38
+ reflection.klass != ActiveFedora::Base
39
+ end
40
+
41
+ # @return [Array<RDF::URI>]
42
+ def rdf_uris
43
+ rdf_query.map(&:object)
44
+ end
45
+
46
+ # @return [Array<RDF::Statement>]
47
+ def rdf_query
48
+ owner.resource.query(subject: owner.rdf_subject, predicate: reflection.predicate).enum_statement
49
+ end
50
+
51
+
52
+ # TODO this is a huge waste of time that can be completely avoided if the attributes aren't sharing predicates.
53
+ # @return [Array<RDF::URI>]
54
+ def filter_by_class(candidate_uris)
55
+ return [] if candidate_uris.empty?
56
+ ids = candidate_uris.map {|uri| ActiveFedora::Base.uri_to_id(uri) }
57
+ results = ActiveFedora::SolrService.query(ActiveFedora::SolrService.construct_query_for_ids(ids), rows: 10000)
58
+
59
+ docs = results.select do |result|
60
+ ActiveFedora::SolrService.classes_from_solr_document(result).any? { |klass|
61
+ class_ancestors(klass).include? reflection.klass
62
+ }
63
+ end
64
+
65
+ docs.map {|doc| ::RDF::URI.new(ActiveFedora::Base.id_to_uri(doc['id']))}
66
+ end
67
+
68
+ ##
69
+ # Returns a list of all the ancestor classes up to ActiveFedora::Base including the class itself
70
+ # @param [Class] klass
71
+ # @return [Array<Class>]
72
+ # @example
73
+ # class Car < ActiveFedora::Base; end
74
+ # class SuperCar < Car; end
75
+ # class_ancestors(SuperCar)
76
+ # # => [SuperCar, Car, ActiveFedora::Base]
77
+ def class_ancestors(klass)
78
+ klass.ancestors.select {|k| k.instance_of?(Class) } - [Object, BasicObject]
79
+ end
80
+
81
+
82
+
83
+ end
84
+ end
85
+ end
86
+
@@ -5,6 +5,7 @@ module ActiveFedora
5
5
  # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
6
6
  def reader(force_reload = false)
7
7
  if force_reload
8
+ raise NotImplementedError, "Need to define the uncached method" #TODO
8
9
  klass.uncached { reload }
9
10
  elsif !loaded? || stale_target?
10
11
  reload
@@ -17,7 +18,6 @@ module ActiveFedora
17
18
  replace(record)
18
19
  end
19
20
 
20
-
21
21
  def create(attributes = {})
22
22
  new_record(:create, attributes)
23
23
  end
@@ -33,7 +33,9 @@ module ActiveFedora
33
33
  private
34
34
 
35
35
  def find_target
36
- scoped.first.tap { |record| set_inverse_instance(record) }
36
+ # TODO this forces a solr query, but I think it's likely we can just lookup from Fedora.
37
+ rec = scope.take
38
+ rec.tap { |record| set_inverse_instance(record) }
37
39
  end
38
40
 
39
41
  # Implemented by subclasses
@@ -45,12 +47,6 @@ module ActiveFedora
45
47
  replace(record)
46
48
  end
47
49
 
48
- def check_record(record)
49
- record = record.target if Association === record
50
- raise_on_type_mismatch(record) if record
51
- record
52
- end
53
-
54
50
  def new_record(method, attributes)
55
51
  attributes = {} #scoped.scope_for_create.merge(attributes || {})
56
52
  record = @reflection.send("#{method}_association", attributes)
@@ -0,0 +1,15 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class SingularRDF < RDF #:nodoc:
4
+
5
+ def replace(value)
6
+ super(Array(value))
7
+ end
8
+
9
+ def reader
10
+ super.first
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,195 @@
1
+ require 'deprecation'
2
+
3
+ module ActiveFedora
4
+ module AttachedFiles
5
+ extend ActiveSupport::Concern
6
+ extend Deprecation
7
+ self.deprecation_horizon = "active-fedora 10.0"
8
+
9
+ def ds_specs
10
+ self.class.child_resource_reflections
11
+ end
12
+ deprecation_deprecate :ds_specs
13
+
14
+ def serialize_attached_files
15
+ attached_files.each_value {|file| file.serialize! }
16
+ end
17
+
18
+ #
19
+ # Attached file management
20
+ #
21
+
22
+ # Returns all known attached files for the object. If the object has been
23
+ # saved to fedora, the persisted files will be included.
24
+ # Attached files that have been modified in memory are given preference over
25
+ # the copy in Fedora.
26
+ def attached_files
27
+ @attached_files ||= FilesHash.new(self)
28
+ end
29
+
30
+ def datastreams
31
+ attached_files
32
+ end
33
+ deprecation_deprecate :datastreams
34
+
35
+ def clear_attached_files
36
+ @attached_files = nil
37
+ end
38
+
39
+ def clear_datastreams
40
+ clear_attached_files
41
+ end
42
+ deprecation_deprecate :clear_datastreams
43
+
44
+ def datastream_assertions
45
+ resource.query(subject: resource, predicate: Ldp.contains).objects.map(&:to_s)
46
+ end
47
+
48
+ def load_attached_files
49
+ datastream_assertions.each do |ds_uri|
50
+ dsid = ds_uri.to_s.sub(uri + '/', '')
51
+ next if association(dsid.to_sym)
52
+ create_singleton_association(dsid)
53
+ end
54
+ end
55
+
56
+ # Adds datastream to the object.
57
+ # @return [String] dsid of the added datastream
58
+ def attach_file(file, file_path, opts={})
59
+ create_singleton_association(file_path)
60
+ attached_files[file_path] = file
61
+ file_path
62
+ end
63
+
64
+ def add_datastream(datastream, opts={})
65
+ attach_file(datastream, opts)
66
+ end
67
+ deprecation_deprecate :add_datastream
68
+
69
+ # @return [Array] all attached files that return true for `metadata?` and are not Rels-ext
70
+ def metadata_streams
71
+ attached_files.select { |k, ds| ds.metadata? }.values
72
+ end
73
+
74
+ #
75
+ # File Management
76
+ #
77
+
78
+ # Attach the given file to object
79
+ #
80
+ # @param [File] file the file to add
81
+ # @param [Hash] opts options: :dsid, :prefix, :mime_type
82
+ # @option opts [String] :dsid The datastream id
83
+ # @option opts [String] :prefix The datastream prefix (for auto-generated dsid)
84
+ # @option opts [String] :mime_type The Mime-Type of the file
85
+ # @option opts [String] :original_name The original name of the file (used for Content-Disposition)
86
+ def add_file_datastream(file, opts={})
87
+ find_or_create_child_resource(opts).tap do |node|
88
+ node.content = file
89
+ node.mime_type = if opts[:mimeType]
90
+ Deprecation.warn AttachedFiles, "The :mimeType option to add_file_datastream is deprecated and will be removed in active-fedora 10.0. Use :mime_type instead", caller
91
+ opts[:mimeType]
92
+ else
93
+ opts[:mime_type]
94
+ end
95
+ node.original_name = opts[:original_name]
96
+ end
97
+ end
98
+
99
+ def undeclared_files
100
+ @undeclared_files ||= []
101
+ end
102
+
103
+
104
+ private
105
+ def create_singleton_association(file_path)
106
+ self.undeclared_files << file_path.to_sym
107
+ association = Associations::ContainsAssociation.new(self, Reflection::AssociationReflection.new(:contains, file_path, {class_name: 'ActiveFedora::File'}, self.class))
108
+ @association_cache[file_path.to_sym] = association
109
+
110
+ self.singleton_class.send :define_method, accessor_name(file_path) do
111
+ @association_cache[file_path.to_sym].reader
112
+ end
113
+ association
114
+ end
115
+
116
+ def find_or_create_child_resource(opts)
117
+ association = association(opts[:dsid].to_sym) if opts[:dsid]
118
+ association ||= begin
119
+ file_path = FilePathBuilder.build(self, opts[:dsid], opts[:prefix])
120
+ create_singleton_association(file_path)
121
+ end
122
+ association.reader
123
+ end
124
+
125
+ ## Given a file_path return a standard name
126
+ def accessor_name(file_path)
127
+ file_path.gsub('-', '_')
128
+ end
129
+
130
+
131
+ module ClassMethods
132
+ extend Deprecation
133
+ self.deprecation_horizon = 'active-fedora version 10.0'
134
+
135
+ def ds_specs
136
+ child_resource_reflections
137
+ end
138
+ deprecation_deprecate :ds_specs
139
+
140
+ # This method is used to specify the details of a datastream.
141
+ # You can pass the name as the first argument and a hash of options as the second argument
142
+ # or you can pass the :name as a value in the args hash. Either way, name is required.
143
+ # Note that this method doesn't actually execute the block, but stores it, to be executed
144
+ # by any the implementation of the datastream(specified as :type)
145
+ #
146
+ # @param [Hash] args
147
+ # @option args [Class] :type The class that will represent this datastream, should extend ``ActiveFedora::File''
148
+ # @option args [String] :name the handle to refer to this datastream as
149
+ # @option args [String] :url
150
+ # @option args [Boolean] :autocreate Always create this datastream on new objects
151
+ # @yield block executed by some kinds of datastreams
152
+ def has_metadata(*args, &block)
153
+ if args.first.is_a? String
154
+ name = args.first
155
+ args = args[1] || {}
156
+ args[:name] = name
157
+ else
158
+ args = args.first || {}
159
+ end
160
+ name = args.delete(:name)
161
+ raise ArgumentError, "You must provide a :type property for the datastream '#{name}'" unless args[:type]
162
+ args[:class_name] = args.delete(:type).to_s
163
+ contains(name, args, &block)
164
+ end
165
+ deprecation_deprecate :has_metadata
166
+
167
+
168
+ # @overload has_file_datastream(name, args)
169
+ # Declares a file datastream exists for objects of this type
170
+ # @param [String] name
171
+ # @param [Hash] args
172
+ # @option args :type (ActiveFedora::File) The class the datastream should have
173
+ # @option args [Boolean] :autocreate Always create this datastream on new objects
174
+ # @overload has_file_datastream(args)
175
+ # Declares a file datastream exists for objects of this type
176
+ # @param [Hash] args
177
+ # @option args :name ("content") The dsid of the datastream
178
+ # @option args :type (ActiveFedora::File) The class the datastream should have
179
+ # @option args [Boolean] :autocreate Always create this datastream on new objects
180
+ def has_file_datastream(*args)
181
+ if args.first.is_a? String
182
+ name = args.first
183
+ args = args[1] || {}
184
+ args[:name] = name
185
+ else
186
+ args = args.first || {}
187
+ end
188
+ name = args.delete(:name)
189
+ args[:class_name] = args.delete(:type).to_s
190
+ contains(name, args)
191
+ end
192
+ deprecation_deprecate :has_file_datastream
193
+ end
194
+ end
195
+ end