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
@@ -2,33 +2,37 @@ module ActiveFedora
2
2
  module RDF
3
3
  module Indexing
4
4
  extend ActiveSupport::Concern
5
+ included do
6
+ include Solrizer::Common
7
+ end
5
8
 
6
- def apply_prefix(name)
7
- prefix + name.to_s
9
+ def apply_prefix(name, file_path)
10
+ name.to_s
8
11
  end
9
12
 
10
- def to_solr(solr_doc = Hash.new) # :nodoc:
11
- fields.each do |field_key, field_info|
12
- values = resource.get_values(field_key)
13
- Array(values).each do |val|
14
- if val.kind_of? ::RDF::URI
15
- val = val.to_s
16
- elsif val.kind_of? ActiveTriples::Resource
17
- val = val.solrize
13
+ def to_solr(solr_doc={}, opts={}) # :nodoc:
14
+ super.tap do |solr_doc|
15
+ fields.each do |field_key, field_info|
16
+ values = resource.get_values(field_key)
17
+ Array(values).each do |val|
18
+ if val.kind_of? ::RDF::URI
19
+ val = val.to_s
20
+ elsif val.kind_of? ActiveTriples::Resource
21
+ val = val.solrize
22
+ end
23
+ self.class.create_and_insert_terms(apply_prefix(field_key, opts[:name]), val, field_info[:behaviors], solr_doc)
18
24
  end
19
- self.class.create_and_insert_terms(apply_prefix(field_key), val, field_info[:behaviors], solr_doc)
20
25
  end
21
26
  end
22
- solr_doc
23
27
  end
24
28
 
25
- # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
26
- def primary_solr_name(field)
29
+ # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
30
+ def primary_solr_name(field, file_path)
27
31
  config = self.class.config_for_term_or_uri(field)
28
32
  return nil unless config # punt on index names for deep nodes!
29
33
  if behaviors = config.behaviors
30
34
  behaviors.each do |behavior|
31
- result = ActiveFedora::SolrService.solr_name(apply_prefix(field), behavior, type: config.type)
35
+ result = ActiveFedora::SolrService.solr_name(apply_prefix(field, file_path), behavior, type: config.type)
32
36
  return result if Solrizer::DefaultDescriptors.send(behavior).evaluate_suffix(:text).stored?
33
37
  end
34
38
  raise RuntimeError "no stored fields were found"
@@ -41,7 +45,7 @@ module ActiveFedora
41
45
  config_for_term_or_uri(field).type
42
46
  end
43
47
  end
44
-
48
+
45
49
  private
46
50
  # returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
47
51
  def fields
@@ -0,0 +1,6 @@
1
+ require 'rdf'
2
+ class ActiveFedora::RDF::Ldp < RDF::StrictVocabulary("http://www.w3.org/ns/ldp#")
3
+ # Property definitions
4
+ property :contains
5
+ property :member
6
+ end
@@ -3,7 +3,7 @@ require 'rdf/ntriples'
3
3
  module ActiveFedora
4
4
  class NtriplesRDFDatastream < RDFDatastream
5
5
  def self.default_attributes
6
- super.merge(:controlGroup => 'M', :mimeType => 'application/n-triples')
6
+ super.merge(:mimeType => 'text/plain')
7
7
  end
8
8
 
9
9
  def serialization_format
@@ -5,7 +5,7 @@ module ActiveFedora
5
5
  # descendant so that it may be used to back an ActiveFedora::RDFDatastream.
6
6
  #
7
7
  # @see ActiveFedora::RDFDatastream.resource_class
8
- # @see ActiveFedora::Rdf::ObjectResource
8
+ # @see ActiveFedora::RDF::ObjectResource
9
9
  #
10
10
  module Persistence
11
11
  extend ActiveSupport::Concern
@@ -16,18 +16,17 @@ module ActiveFedora
16
16
  configure :base_uri => BASE_URI unless base_uri
17
17
  attr_accessor :datastream
18
18
  end
19
-
19
+
20
20
  # Overrides ActiveTriples::Resource
21
21
  def persist!
22
- return false unless datastream and datastream.respond_to? :digital_object
23
- @persisted ||= datastream.digital_object.save
22
+ return false unless datastream and datastream.respond_to? :save
23
+ @persisted ||= datastream.save
24
24
  end
25
25
 
26
26
  # Overrides ActiveTriples::Resource
27
27
  def persisted?
28
- @persisted ||= (not datastream.new?)
28
+ @persisted ||= (not datastream.new_record?)
29
29
  end
30
-
31
30
  end
32
31
  end
33
32
  end
@@ -1,8 +1,7 @@
1
1
  module ActiveFedora
2
- class RDFDatastream < ActiveFedora::Datastream
3
- include Solrizer::Common
2
+ class RDFDatastream < File
4
3
  include ActiveTriples::NestedAttributes
5
- include RDF::Indexing
4
+ include RDF::DatastreamIndexing
6
5
  include ActiveTriples::Properties
7
6
  include ActiveTriples::Reflection
8
7
 
@@ -14,12 +13,22 @@ module ActiveFedora
14
13
  return @subject_block = block
15
14
  end
16
15
 
17
- @subject_block ||= lambda { |ds| ds.pid }
16
+ @subject_block ||= lambda { |ds| parent_uri(ds) }
17
+ end
18
+
19
+ # Trim the last segment off the URI to get the parents uri
20
+ def parent_uri(ds)
21
+ m = /^(.*)\/[^\/]*$/.match(ds.uri)
22
+ if m
23
+ m[1]
24
+ else
25
+ ::RDF::URI.new(nil)
26
+ end
18
27
  end
19
28
 
20
29
  ##
21
30
  # @param [Class] an object to set as the resource class, Must be a descendant of
22
- # ActiveTriples::Resource and include ActiveFedora::Rdf::Persistence.
31
+ # ActiveTriples::Resource and include ActiveFedora::RDF::Persistence.
23
32
  #
24
33
  # @return [Class] the object resource class
25
34
  def resource_class(klass=nil)
@@ -27,26 +36,26 @@ module ActiveFedora
27
36
  raise ArgumentError, "#{self} already has a resource_class #{@resource_class}, cannot redefine it to #{klass}" if @resource_class and klass != @resource_class
28
37
  raise ArgumentError, "#{klass} must be a subclass of ActiveTriples::Resource" unless klass < ActiveTriples::Resource
29
38
  end
30
-
39
+
31
40
  @resource_class ||= begin
32
41
  klass = Class.new(klass || ActiveTriples::Resource)
33
42
  klass.send(:include, RDF::Persistence)
34
43
  klass
35
44
  end
36
- end
45
+ end
37
46
  end
38
47
 
39
48
  before_save do
40
49
  if content.blank?
41
50
  ActiveFedora::Base.logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}" if ActiveFedora::Base.logger
42
- if ActiveSupport.version >= Gem::Version.new('5.0')
43
- throw(:abort)
44
- else
45
- false
46
- end
51
+ false
47
52
  end
48
53
  end
49
54
 
55
+ def parent_uri
56
+ self.class.parent_uri(self)
57
+ end
58
+
50
59
  def metadata?
51
60
  true
52
61
  end
@@ -58,15 +67,25 @@ module ActiveFedora
58
67
  def content=(new_content)
59
68
  resource.clear!
60
69
  resource << deserialize(new_content)
61
- new_content
70
+ content
71
+ end
72
+
73
+ def uri=(uri)
74
+ super
75
+ resource.set_subject!(parent_uri) if empty_or_blank_subject?
62
76
  end
63
77
 
64
78
  def content_changed?
65
79
  return false unless instance_variable_defined? :@resource
80
+ return true if empty_or_blank_subject? # can't be serialized because a subject hasn't been assigned yet.
66
81
  @content = serialize
67
82
  super
68
83
  end
69
84
 
85
+ def empty_or_blank_subject?
86
+ resource.rdf_subject.node? || resource.rdf_subject.value.blank?
87
+ end
88
+
70
89
  def freeze
71
90
  @resource.freeze
72
91
  end
@@ -81,13 +100,13 @@ module ActiveFedora
81
100
  @resource ||= begin
82
101
  klass = self.class.resource_class
83
102
  klass.properties.merge(self.class.properties).each do |prop, config|
84
- klass.property(config.term,
85
- predicate: config.predicate,
86
- class_name: config.class_name,
103
+ klass.property(config.term,
104
+ predicate: config.predicate,
105
+ class_name: config.class_name,
87
106
  multivalue: config.multivalue)
88
107
  end
89
108
  klass.accepts_nested_attributes_for(*nested_attributes_options.keys) unless nested_attributes_options.blank?
90
- uri_stub = digital_object ? self.class.rdf_subject.call(self) : nil
109
+ uri_stub = self.class.rdf_subject.call(self)
91
110
 
92
111
  r = klass.new(uri_stub)
93
112
  r.datastream = self
@@ -98,10 +117,14 @@ module ActiveFedora
98
117
 
99
118
  alias_method :graph, :resource
100
119
 
120
+ def refresh_attributes
121
+ @resource = nil
122
+ end
123
+
101
124
  ##
102
125
  # This method allows for delegation.
103
126
  # This patches the fact that there's no consistent API for allowing delegation - we're matching the
104
- # OMDatastream implementation as our "consistency" point.
127
+ # OmDatastream implementation as our "consistency" point.
105
128
  # @TODO: We may need to enable deep RDF delegation at one point.
106
129
  def term_values(*values)
107
130
  self.send(values.first)
@@ -116,18 +139,13 @@ module ActiveFedora
116
139
  end
117
140
 
118
141
  def serialize
119
- resource.set_subject!(pid) if (digital_object or pid) and rdf_subject.node?
142
+ resource.set_subject!(parent_uri) if parent_uri and rdf_subject.node?
120
143
  resource.dump serialization_format
121
144
  end
122
145
 
123
146
  def deserialize(data=nil)
124
- return ::RDF::Graph.new if new? && data.nil?
125
-
126
- if data.nil?
127
- data = datastream_content
128
- elsif behaves_like_io?(data)
129
- data = io_content(data)
130
- end
147
+ return ::RDF::Graph.new if new_record? && data.nil?
148
+ data ||= remote_content
131
149
 
132
150
  # Because datastream_content can return nil, we should check that here.
133
151
  return ::RDF::Graph.new if data.nil?
@@ -140,16 +158,5 @@ module ActiveFedora
140
158
  raise "you must override the `serialization_format' method in a subclass"
141
159
  end
142
160
 
143
- private
144
-
145
- def io_content(data)
146
- begin
147
- data.rewind
148
- data.read
149
- ensure
150
- data.rewind
151
- end
152
- end
153
-
154
161
  end
155
162
  end
@@ -0,0 +1,13 @@
1
+ require 'rdf/rdfxml'
2
+
3
+ module ActiveFedora
4
+ class RDFXMLDatastream < RDFDatastream
5
+ def serialization_format
6
+ :rdfxml
7
+ end
8
+
9
+ def mime_type
10
+ 'text/xml'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'rdf'
2
+ class ActiveFedora::RDF::RelsExt < RDF::StrictVocabulary("http://fedora.info/definitions/v4/rels-ext#")
3
+ property :hasAnnotation
4
+ property :hasCollectionMember
5
+ property :hasConstituent
6
+ property :hasDependent
7
+ property :hasDerivation
8
+ property :hasDescription
9
+ property :hasEquivalent
10
+ property :hasExternalContent
11
+ property :hasMember
12
+ property :hasMetadata
13
+ property :hasPart
14
+ property :hasSubset
15
+ property :isAnnotationOf
16
+ property :isConstituentOf
17
+ property :isDependentOf
18
+ property :isDerivationOf
19
+ property :isDescriptionOf
20
+ property :isExternalContentOf
21
+ property :isMemberOf
22
+ property :isMemberOfCollection
23
+ property :isMetadataFor
24
+ property :isPartOf
25
+ property :isSubsetOf
26
+ end
@@ -8,23 +8,24 @@ module ActiveFedora
8
8
  end
9
9
 
10
10
  module ClassMethods
11
- def reflection_name_for_predicate(predicate)
12
- reflections.each do |k, v|
13
- return k if v.options[:property] == predicate
14
- end
15
- end
16
-
17
11
  def create_reflection(macro, name, options, active_fedora)
18
- case macro
19
- when :has_many, :belongs_to, :has_and_belongs_to_many
20
- klass = AssociationReflection
21
- reflection = klass.new(macro, name, options, active_fedora)
12
+ klass = case macro
13
+ when :has_many, :belongs_to, :has_and_belongs_to_many, :contains
14
+ AssociationReflection
15
+ when :rdf, :singular_rdf
16
+ RDFPropertyReflection
22
17
  end
18
+ reflection = klass.new(macro, name, options, active_fedora)
19
+ add_reflection name, reflection
23
20
 
24
- self.reflections = self.reflections.merge(name => reflection)
25
21
  reflection
26
22
  end
27
23
 
24
+ def add_reflection(name, reflection)
25
+ # FIXME this is where the problem with association_spec is caused (key is string now)
26
+ self.reflections = self.reflections.merge(name => reflection)
27
+ end
28
+
28
29
  # Returns a hash containing all AssociationReflection objects for the current class.
29
30
  # Example:
30
31
  #
@@ -35,6 +36,14 @@ module ActiveFedora
35
36
  read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
36
37
  end
37
38
 
39
+ def outgoing_reflections
40
+ reflections.select { |_, reflection| reflection.kind_of? RDFPropertyReflection }
41
+ end
42
+
43
+ def child_resource_reflections
44
+ reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains }
45
+ end
46
+
38
47
  # Returns the AssociationReflection object for the +association+ (use the symbol).
39
48
  #
40
49
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
@@ -43,242 +52,290 @@ module ActiveFedora
43
52
  def reflect_on_association(association)
44
53
  val = reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
45
54
  unless val
46
- # When a has_many is paired with a has_and_belongs_to_many the assocation will have a plural name
55
+ # When a has_many is paired with a has_and_belongs_to_many the assocation will have a plural name
47
56
  association = association.to_s.pluralize.to_sym
48
57
  val = reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
49
58
  end
50
- val
59
+ val
51
60
  end
52
61
 
53
62
  def reflect_on_all_autosave_associations
54
63
  reflections.values.select { |reflection| reflection.options[:autosave] }
55
64
  end
65
+ end
56
66
 
57
67
 
58
- class MacroReflection
59
- # Returns the name of the macro.
60
- #
61
- # <tt>has_many :clients</tt> returns <tt>:clients</tt>
62
- attr_reader :name
63
-
64
- # Returns the macro type.
65
- #
66
- # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
67
- attr_reader :macro
68
-
69
- # Returns the hash of options used for the macro.
70
- #
71
- # <tt>has_many :clients</tt> returns +{}+
72
- attr_reader :options
73
-
74
- attr_reader :active_fedora
75
-
76
- # Returns the target association's class.
77
- #
78
- # class Author < ActiveRecord::Base
79
- # has_many :books
80
- # end
81
- #
82
- # Author.reflect_on_association(:books).klass
83
- # # => Book
84
- #
85
- # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
86
- # a new association object. Use +build_association+ or +create_association+
87
- # instead. This allows plugins to hook into association object creation.
88
- def klass
89
- @klass ||= class_name.constantize
90
- end
68
+ class MacroReflection
69
+ # Returns the name of the macro.
70
+ #
71
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
72
+ attr_reader :name
91
73
 
92
- def initialize(macro, name, options, active_fedora)
93
- @macro, @name, @options, @active_fedora = macro, name, options, active_fedora
94
- @automatic_inverse_of = nil
95
- end
74
+ # Returns the macro type.
75
+ #
76
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
77
+ attr_reader :macro
96
78
 
97
- # Returns a new, unsaved instance of the associated class. +options+ will
98
- # be passed to the class's constructor.
99
- def build_association(*options)
100
- klass.new(*options)
101
- end
79
+ # Returns the hash of options used for the macro.
80
+ #
81
+ # <tt>has_many :clients</tt> returns +{}+
82
+ attr_reader :options
102
83
 
84
+ attr_reader :active_fedora
103
85
 
104
- # Returns the class name for the macro.
105
- #
106
- # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
107
- def class_name
108
- @class_name ||= options[:class_name] || derive_class_name
109
- end
86
+ # Returns the target association's class.
87
+ #
88
+ # class Author < ActiveRecord::Base
89
+ # has_many :books
90
+ # end
91
+ #
92
+ # Author.reflect_on_association(:books).klass
93
+ # # => Book
94
+ #
95
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
96
+ # a new association object. Use +build_association+ or +create_association+
97
+ # instead. This allows plugins to hook into association object creation.
98
+ def klass
99
+ @klass ||= class_name.constantize
100
+ end
110
101
 
111
- # Returns whether or not this association reflection is for a collection
112
- # association. Returns +true+ if the +macro+ is either +has_many+ or
113
- # +has_and_belongs_to_many+, +false+ otherwise.
114
- def collection?
115
- @collection
116
- end
102
+ def initialize(macro, name, options, active_fedora)
103
+ @macro, @name, @options, @active_fedora = macro, name, options, active_fedora
104
+ @automatic_inverse_of = nil
105
+ end
117
106
 
118
- # Returns +true+ if +self+ is a +belongs_to+ reflection.
119
- def belongs_to?
120
- macro == :belongs_to
121
- end
107
+ # Returns a new, unsaved instance of the associated class. +options+ will
108
+ # be passed to the class's constructor.
109
+ def build_association(*options)
110
+ klass.new(*options)
111
+ end
122
112
 
123
- private
124
- def derive_class_name
125
- class_name = name.to_s.camelize
126
- class_name = class_name.singularize if collection?
127
- class_name
128
- end
129
113
 
130
- def derive_foreign_key
131
- if belongs_to?
132
- "#{name}_id"
133
- elsif options[:as]
134
- "#{options[:as]}_id"
135
- else
136
- active_fedora.name.foreign_key
137
- end
138
- end
114
+ # Returns the class name for the macro.
115
+ #
116
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
117
+ def class_name
118
+ @class_name ||= options[:class_name] || derive_class_name
139
119
  end
140
120
 
141
- # Holds all the meta-data about an association as it was specified in the
142
- # Active Record class.
143
- class AssociationReflection < MacroReflection #:nodoc:
121
+ # Returns whether or not this association reflection is for a collection
122
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
123
+ # +has_and_belongs_to_many+, +false+ otherwise.
124
+ def collection?
125
+ @collection
126
+ end
144
127
 
145
- def initialize(macro, name, options, active_fedora)
146
- super
147
- @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
148
- end
128
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
129
+ def belongs_to?
130
+ macro == :belongs_to
131
+ end
149
132
 
133
+ def has_many?
134
+ macro == :has_many
135
+ end
150
136
 
151
- # Returns a new, unsaved instance of the associated class. +options+ will
152
- # be passed to the class's constructor.
153
- def build_association(*options)
154
- klass.new(*options)
155
- end
137
+ def has_and_belongs_to_many?
138
+ macro == :has_and_belongs_to_many
139
+ end
156
140
 
157
- # Creates a new instance of the associated class, and immediately saves it
158
- # with ActiveFedora::Base#save. +options+ will be passed to the class's
159
- # creation method. Returns the newly created object.
160
- def create_association(*options)
161
- klass.create(*options)
141
+ private
142
+ def derive_class_name
143
+ class_name = name.to_s.camelize
144
+ class_name = class_name.singularize if collection?
145
+ class_name
162
146
  end
163
147
 
164
- def foreign_key
165
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
148
+ def derive_foreign_key
149
+ if belongs_to?
150
+ "#{name}_id"
151
+ elsif has_and_belongs_to_many?
152
+ "#{name.to_s.singularize}_ids"
153
+ elsif options[:as]
154
+ "#{options[:as]}_id"
155
+ else
156
+ # This works well if this is a has_many that is the inverse of a belongs_to, but it isn't correct for a has_many that is the invers of a has_and_belongs_to_many
157
+ active_fedora.name.foreign_key
158
+ end
166
159
  end
160
+ end
167
161
 
168
- # A chain of reflections from this one back to the owner. For more see the explanation in
169
- # ThroughReflection.
170
- def chain
171
- [self]
172
- end
162
+ # Holds all the meta-data about an association as it was specified in the
163
+ # Active Record class.
164
+ class AssociationReflection < MacroReflection #:nodoc:
173
165
 
174
- alias :source_macro :macro
166
+ def initialize(macro, name, options, active_fedora)
167
+ super
168
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
169
+ end
175
170
 
176
- def inverse_of
177
- return unless inverse_name
178
171
 
179
- @inverse_of ||= klass.reflect_on_association inverse_name
180
- end
172
+ # Returns a new, unsaved instance of the associated class. +options+ will
173
+ # be passed to the class's constructor.
174
+ def build_association(*options)
175
+ klass.new(*options)
176
+ end
181
177
 
182
-
183
- # Returns whether or not the association should be validated as part of
184
- # the parent's validation.
185
- #
186
- # Unless you explicitly disable validation with
187
- # <tt>:validate => false</tt>, validation will take place when:
188
- #
189
- # * you explicitly enable validation; <tt>:validate => true</tt>
190
- # * you use autosave; <tt>:autosave => true</tt>
191
- # * the association is a +has_many+ association
192
- def validate?
193
- !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
194
- end
178
+ # Creates a new instance of the associated class, and immediately saves it
179
+ # with ActiveFedora::Base#save. +options+ will be passed to the class's
180
+ # creation method. Returns the newly created object.
181
+ def create_association(*options)
182
+ klass.create(*options)
183
+ end
195
184
 
196
- def association_class
197
- case macro
198
- when :belongs_to
199
- Associations::BelongsToAssociation
200
- when :has_and_belongs_to_many
201
- Associations::HasAndBelongsToManyAssociation
202
- when :has_many
203
- Associations::HasManyAssociation
185
+ def foreign_key
186
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key
187
+ end
188
+
189
+ # Returns the RDF predicate as defined by the :predicate option
190
+ def predicate
191
+ options[:predicate]
192
+ end
193
+
194
+ def solr_key
195
+ ActiveFedora::SolrService.solr_name(predicate.to_s, :symbol)
196
+ end
197
+
198
+ def check_validity!
199
+ check_validity_of_inverse!
200
+ end
201
+
202
+ def check_validity_of_inverse!
203
+ unless options[:polymorphic]
204
+ if has_inverse? && inverse_of.nil?
205
+ raise InverseOfAssociationNotFoundError.new(self)
204
206
  end
205
207
  end
208
+ end
206
209
 
207
- VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_and_belongs_to_many, :belongs_to]
208
- INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
209
-
210
+ # A chain of reflections from this one back to the owner. For more see the explanation in
211
+ # ThroughReflection.
212
+ def chain
213
+ [self]
214
+ end
210
215
 
211
- private
216
+ alias :source_macro :macro
212
217
 
213
- def inverse_name
214
- options.fetch(:inverse_of) do
215
- if @automatic_inverse_of == false
216
- nil
217
- else
218
- @automatic_inverse_of ||= automatic_inverse_of
219
- end
220
- end
218
+ def has_inverse?
219
+ inverse_name
220
+ end
221
+
222
+ def inverse_of
223
+ return unless inverse_name
224
+
225
+ @inverse_of ||= klass.reflect_on_association inverse_name
226
+ end
227
+
228
+
229
+ # Returns whether or not the association should be validated as part of
230
+ # the parent's validation.
231
+ #
232
+ # Unless you explicitly disable validation with
233
+ # <tt>:validate => false</tt>, validation will take place when:
234
+ #
235
+ # * you explicitly enable validation; <tt>:validate => true</tt>
236
+ # * you use autosave; <tt>:autosave => true</tt>
237
+ # * the association is a +has_many+ association
238
+ def validate?
239
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
240
+ end
241
+
242
+ def association_class
243
+ case macro
244
+ when :contains
245
+ Associations::ContainsAssociation
246
+ when :belongs_to
247
+ Associations::BelongsToAssociation
248
+ when :has_and_belongs_to_many
249
+ Associations::HasAndBelongsToManyAssociation
250
+ when :has_many
251
+ Associations::HasManyAssociation
252
+ when :singular_rdf
253
+ Associations::SingularRDF
254
+ when :rdf
255
+ Associations::RDF
221
256
  end
257
+ end
222
258
 
223
- # Checks if the inverse reflection that is returned from the
224
- # +automatic_inverse_of+ method is a valid reflection. We must
225
- # make sure that the reflection's active_record name matches up
226
- # with the current reflection's klass name.
227
- #
228
- # Note: klass will always be valid because when there's a NameError
229
- # from calling +klass+, +reflection+ will already be set to false.
230
- def valid_inverse_reflection?(reflection)
231
- reflection &&
232
- klass.name == reflection.active_fedora.name &&
233
- can_find_inverse_of_automatically?(reflection)
259
+ VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_and_belongs_to_many, :belongs_to]
260
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
261
+
262
+
263
+ private
264
+
265
+ def inverse_name
266
+ options.fetch(:inverse_of) do
267
+ if @automatic_inverse_of == false
268
+ nil
269
+ else
270
+ @automatic_inverse_of ||= automatic_inverse_of
271
+ end
234
272
  end
235
-
236
-
237
- # returns either nil or the inverse association name that it finds.
238
- def automatic_inverse_of
239
- if can_find_inverse_of_automatically?(self)
240
- inverse_name = ActiveSupport::Inflector.underscore(active_fedora.name).to_sym
241
-
242
- begin
243
- reflection = klass.reflect_on_association(inverse_name)
244
- rescue NameError
245
- # Give up: we couldn't compute the klass type so we won't be able
246
- # to find any associations either.
247
- reflection = false
248
- end
249
-
250
- if valid_inverse_reflection?(reflection)
251
- return inverse_name
252
- end
273
+ end
274
+
275
+ # Checks if the inverse reflection that is returned from the
276
+ # +automatic_inverse_of+ method is a valid reflection. We must
277
+ # make sure that the reflection's active_record name matches up
278
+ # with the current reflection's klass name.
279
+ #
280
+ # Note: klass will always be valid because when there's a NameError
281
+ # from calling +klass+, +reflection+ will already be set to false.
282
+ def valid_inverse_reflection?(reflection)
283
+ reflection &&
284
+ klass.name == reflection.active_fedora.name &&
285
+ can_find_inverse_of_automatically?(reflection)
286
+ end
287
+
288
+
289
+ # returns either false or the inverse association name that it finds.
290
+ def automatic_inverse_of
291
+ if can_find_inverse_of_automatically?(self)
292
+ inverse_name = ActiveSupport::Inflector.underscore(active_fedora.name).to_sym
293
+
294
+ begin
295
+ reflection = klass.reflect_on_association(inverse_name)
296
+ rescue NameError
297
+ # Give up: we couldn't compute the klass type so we won't be able
298
+ # to find any associations either.
299
+ reflection = false
253
300
  end
254
301
 
255
- false
302
+ if valid_inverse_reflection?(reflection)
303
+ return inverse_name
304
+ end
256
305
  end
257
306
 
258
- # Checks to see if the reflection doesn't have any options that prevent
259
- # us from being able to guess the inverse automatically. First, the
260
- # <tt>inverse_of</tt> option cannot be set to false. Second, we must
261
- # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
262
- # Third, we must not have options such as <tt>:polymorphic</tt> or
263
- # <tt>:foreign_key</tt> which prevent us from correctly guessing the
264
- # inverse association.
265
- #
266
- # Anything with a scope can additionally ruin our attempt at finding an
267
- # inverse, so we exclude reflections with scopes.
268
- def can_find_inverse_of_automatically?(reflection)
269
- reflection.options[:inverse_of] != false &&
270
- VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
271
- !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] }
272
- #&& !reflection.scope
273
- end
307
+ false
308
+ end
274
309
 
275
- # def derive_primary_key_name
276
- # 'pid'
277
- # end
310
+ # Checks to see if the reflection doesn't have any options that prevent
311
+ # us from being able to guess the inverse automatically. First, the
312
+ # <tt>inverse_of</tt> option cannot be set to false. Second, we must
313
+ # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
314
+ # Third, we must not have options such as <tt>:polymorphic</tt> or
315
+ # <tt>:foreign_key</tt> which prevent us from correctly guessing the
316
+ # inverse association.
317
+ #
318
+ # Anything with a scope can additionally ruin our attempt at finding an
319
+ # inverse, so we exclude reflections with scopes.
320
+ def can_find_inverse_of_automatically?(reflection)
321
+ reflection.options[:inverse_of] != false &&
322
+ VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
323
+ !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] }
324
+ #&& !reflection.scope
325
+ end
326
+ end
278
327
 
328
+ class RDFPropertyReflection < AssociationReflection
329
+
330
+ def derive_foreign_key
331
+ name
332
+ end
333
+
334
+ def derive_class_name
335
+ class_name = name.to_s.sub(/_ids?$/, '').camelize
336
+ class_name = class_name.singularize if collection?
337
+ class_name
279
338
  end
280
339
  end
281
340
  end
282
341
  end
283
-
284
-