active-fedora 3.1.6 → 3.2.0.pre1

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 (44) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile.lock +22 -22
  3. data/History.txt +9 -3
  4. data/active-fedora.gemspec +3 -3
  5. data/config/predicate_mappings.yml +1 -0
  6. data/config/service_mappings.yml +9 -0
  7. data/lib/active_fedora.rb +7 -1
  8. data/lib/active_fedora/base.rb +84 -30
  9. data/lib/active_fedora/datastream.rb +4 -1
  10. data/lib/active_fedora/datastream_collections.rb +304 -293
  11. data/lib/active_fedora/metadata_datastream.rb +2 -24
  12. data/lib/active_fedora/metadata_datastream_helper.rb +32 -5
  13. data/lib/active_fedora/named_relationships.rb +95 -0
  14. data/lib/active_fedora/nested_attributes.rb +1 -1
  15. data/lib/active_fedora/predicates.rb +76 -0
  16. data/lib/active_fedora/reflection.rb +9 -1
  17. data/lib/active_fedora/relationship.rb +1 -0
  18. data/lib/active_fedora/relationship_graph.rb +152 -0
  19. data/lib/active_fedora/relationships_helper.rb +32 -41
  20. data/lib/active_fedora/rels_ext_datastream.rb +3 -10
  21. data/lib/active_fedora/semantic_node.rb +47 -203
  22. data/lib/active_fedora/service_definitions.rb +89 -0
  23. data/lib/active_fedora/unsaved_digital_object.rb +40 -0
  24. data/lib/active_fedora/version.rb +1 -1
  25. data/spec/integration/base_spec.rb +106 -309
  26. data/spec/integration/datastream_collections_spec.rb +135 -0
  27. data/spec/integration/rels_ext_datastream_spec.rb +14 -35
  28. data/spec/integration/semantic_node_spec.rb +6 -10
  29. data/spec/unit/base_datastream_management_spec.rb +0 -3
  30. data/spec/unit/base_extra_spec.rb +5 -9
  31. data/spec/unit/base_spec.rb +103 -57
  32. data/spec/unit/{base_named_datastream_spec.rb → datastream_collections_spec.rb} +107 -150
  33. data/spec/unit/metadata_datastream_spec.rb +0 -1
  34. data/spec/unit/nokogiri_datastream_spec.rb +0 -1
  35. data/spec/unit/predicates_spec.rb +64 -0
  36. data/spec/unit/qualified_dublin_core_datastream_spec.rb +0 -7
  37. data/spec/unit/relationship_graph_spec.rb +95 -0
  38. data/spec/unit/relationship_spec.rb +4 -4
  39. data/spec/unit/relationships_helper_spec.rb +43 -104
  40. data/spec/unit/rels_ext_datastream_spec.rb +6 -6
  41. data/spec/unit/semantic_node_spec.rb +27 -116
  42. data/spec/unit/service_definitions_spec.rb +52 -0
  43. data/spec/unit/solr_config_options_spec.rb +1 -1
  44. metadata +35 -17
@@ -8,9 +8,10 @@ module ActiveFedora
8
8
  # </fields>
9
9
  class MetadataDatastream < Datastream
10
10
 
11
- # .to_solr (among other things) is provided by ActiveFedora::MetadataDatastreamHelper
12
11
  include ActiveFedora::MetadataDatastreamHelper
13
12
 
13
+ # .to_solr and .to_xml (among other things) are provided by ActiveFedora::MetadataDatastreamHelper
14
+ self.xml_model = ActiveFedora::MetadataDatastream
14
15
 
15
16
  def update_attributes(params={},opts={})
16
17
  result = params.dup
@@ -143,29 +144,6 @@ module ActiveFedora
143
144
  tmpl.send(:dirty=, false)
144
145
  tmpl
145
146
  end
146
-
147
- def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
148
- if xml.instance_of?(Nokogiri::XML::Builder)
149
- xml_insertion_point = xml.doc.root
150
- elsif xml.instance_of?(Nokogiri::XML::Document)
151
- xml_insertion_point = xml.root
152
- else
153
- xml_insertion_point = xml
154
- end
155
-
156
- builder = Nokogiri::XML::Builder.with(xml_insertion_point) do |xml|
157
- fields.each_pair do |field,field_info|
158
- element_attrs = field_info[:element_attrs].nil? ? {} : field_info[:element_attrs]
159
- values = field_info[:values]
160
- values = [values] unless values.respond_to? :each
161
- values.each do |val|
162
- builder_arg = "xml.#{field}(val, element_attrs)"
163
- eval(builder_arg)
164
- end
165
- end
166
- end
167
- return builder.to_xml
168
- end
169
147
 
170
148
  # This method generates the various accessor and mutator methods on self for the datastream metadata attributes.
171
149
  # each field will have the 3 magic methods:
@@ -7,6 +7,8 @@ module ActiveFedora::MetadataDatastreamHelper
7
7
 
8
8
  module ClassMethods
9
9
 
10
+ attr_accessor :xml_model
11
+
10
12
  #get the Class's field list
11
13
  def fields
12
14
  @@classFields
@@ -20,15 +22,18 @@ module ActiveFedora::MetadataDatastreamHelper
20
22
  end
21
23
 
22
24
  def ensure_xml_loaded
23
- return if xml_loaded
25
+ return if xml_loaded
24
26
  self.xml_loaded = true
25
- self.class.from_xml content, self
27
+ if new?
28
+ ## Load up the template
29
+ self.class.from_xml nil, self
30
+ else
31
+ self.class.from_xml content, self
32
+ end
26
33
  end
27
34
 
28
35
  def serialize! # :nodoc:
29
- if dirty?
30
- self.content = self.to_xml
31
- end
36
+ self.content = self.to_xml ##TODO only do this when the xml will have changed to avoid a load of the datastream content.
32
37
  end
33
38
 
34
39
  def to_solr(solr_doc = Hash.new) # :nodoc:
@@ -70,5 +75,27 @@ module ActiveFedora::MetadataDatastreamHelper
70
75
  end
71
76
  end
72
77
 
78
+ def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
79
+ if xml.instance_of?(Nokogiri::XML::Builder)
80
+ xml_insertion_point = xml.doc.root
81
+ elsif xml.instance_of?(Nokogiri::XML::Document)
82
+ xml_insertion_point = xml.root
83
+ else
84
+ xml_insertion_point = xml
85
+ end
86
+
87
+ builder = Nokogiri::XML::Builder.with(xml_insertion_point) do |xml|
88
+ fields.each_pair do |field,field_info|
89
+ element_attrs = field_info[:element_attrs].nil? ? {} : field_info[:element_attrs]
90
+ values = field_info[:values]
91
+ values = [values] unless values.respond_to? :each
92
+ values.each do |val|
93
+ builder_arg = "xml.#{field}(val, element_attrs)"
94
+ eval(builder_arg)
95
+ end
96
+ end
97
+ end
98
+ return builder.to_xml
99
+ end
73
100
 
74
101
  end
@@ -0,0 +1,95 @@
1
+ module ActiveFedora
2
+ module NamedRelationships
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ class_attribute :class_named_relationships_desc
6
+ self.class_named_relationships_desc = {}
7
+ end
8
+
9
+ # ** EXPERIMENTAL **
10
+ #
11
+ # Check to make sure a subject,name, and predicate triple does not already exist
12
+ # with the same subject but different name.
13
+ # This method is used to ensure conflicting has_relationship calls are not made because
14
+ # predicates cannot be reused across relationship names. Otherwise, the mapping of relationship name
15
+ # to predicate in RELS-EXT would be broken.
16
+ def named_predicate_exists_with_different_name?(subject,name,predicate)
17
+ if named_relationships_desc.has_key?(subject)
18
+ named_relationships_desc[subject].each_pair do |existing_name, args|
19
+ return true if !args[:predicate].nil? && args[:predicate] == predicate && existing_name != name
20
+ end
21
+ end
22
+ return false
23
+ end
24
+
25
+ # ** EXPERIMENTAL **
26
+ #
27
+ # Return hash that stores named relationship metadata defined by has_relationship calls
28
+ # ====Example
29
+ # For the following relationship
30
+ #
31
+ # has_relationship "audio_records", :has_part, :type=>AudioRecord
32
+ # Results in the following returned by named_relationships_desc
33
+ # {:self=>{"audio_records"=>{:type=>AudioRecord, :singular=>nil, :predicate=>:has_part, :inbound=>false}}}
34
+ def named_relationships_desc
35
+ @class_named_relationships_desc ||= Hash[:self => {}]
36
+ #class_named_relationships_desc
37
+ end
38
+
39
+ # ** EXPERIMENTAL **
40
+ #
41
+ # Internal method that ensures a relationship subject such as :self and :inbound
42
+ # exist within the named_relationships_desc hash tracking named relationships metadata.
43
+ def register_named_subject(subject)
44
+ unless named_relationships_desc.has_key?(subject)
45
+ named_relationships_desc[subject] = {}
46
+ end
47
+ end
48
+
49
+ # ** EXPERIMENTAL **
50
+ #
51
+ # Internal method that adds relationship name and predicate pair to either an outbound (:self)
52
+ # or inbound (:inbound) relationship types.
53
+ def register_named_relationship(subject, name, predicate, opts)
54
+ register_named_subject(subject)
55
+ opts.merge!({:predicate=>predicate})
56
+ named_relationships_desc[subject][name] = opts
57
+ end
58
+
59
+ # ** EXPERIMENTAL **
60
+ #
61
+ # Used in has_relationship call to create dynamic helper methods to
62
+ # append and remove objects to and from a named relationship
63
+ # ====Example
64
+ # For the following relationship
65
+ #
66
+ # has_relationship "audio_records", :has_part, :type=>AudioRecord
67
+ #
68
+ # Methods audio_records_append and audio_records_remove are created.
69
+ # Boths methods take an object that is kind_of? ActiveFedora::Base as a parameter
70
+ def create_named_relationship_methods(name)
71
+ append_method_name = "#{name.to_s.downcase}_append"
72
+ remove_method_name = "#{name.to_s.downcase}_remove"
73
+ self.send(:define_method,:"#{append_method_name}") {|object| add_named_relationship(name,object)}
74
+ self.send(:define_method,:"#{remove_method_name}") {|object| remove_named_relationship(name,object)}
75
+ end
76
+
77
+ # ** EXPERIMENTAL **
78
+ # Similar to +create_named_relationship_methods+ except we are merely creating an alias for outbound portion of bidirectional
79
+ #
80
+ # ====Example
81
+ # has_bidirectional_relationship "members", :has_collection_member, :is_member_of_collection
82
+ #
83
+ # Method members_outbound_append and members_outbound_remove added
84
+ # This method will create members_append which does same thing as members_outbound_append
85
+ # and will create members_remove which does same thing as members_outbound_remove
86
+ def create_bidirectional_named_relationship_methods(name,outbound_name)
87
+ append_method_name = "#{name.to_s.downcase}_append"
88
+ remove_method_name = "#{name.to_s.downcase}_remove"
89
+ self.send(:define_method,:"#{append_method_name}") {|object| add_named_relationship(outbound_name,object)}
90
+ self.send(:define_method,:"#{remove_method_name}") {|object| remove_named_relationship(outbound_name,object)}
91
+ end
92
+
93
+
94
+ end
95
+ end
@@ -8,7 +8,7 @@ module ActiveFedora
8
8
  module NestedAttributes #:nodoc:
9
9
  extend ActiveSupport::Concern
10
10
  included do
11
- class_inheritable_accessor :nested_attributes_options, :instance_writer => false
11
+ class_attribute :nested_attributes_options, :instance_writer => false
12
12
  self.nested_attributes_options = {}
13
13
  end
14
14
 
@@ -0,0 +1,76 @@
1
+ module ActiveFedora
2
+
3
+ module Predicates
4
+ def self.find_graph_predicate(predicate)
5
+ #TODO, these could be cached
6
+ case predicate
7
+ when :has_model, "hasModel", :hasModel
8
+ xmlns="info:fedora/fedora-system:def/model#"
9
+ begin
10
+ rel_predicate = predicate_lookup(predicate,xmlns)
11
+ rescue UnregisteredPredicateError
12
+ xmlns = nil
13
+ rel_predicate = nil
14
+ end
15
+ else
16
+ xmlns="info:fedora/fedora-system:def/relations-external#"
17
+ begin
18
+ rel_predicate = predicate_lookup(predicate,xmlns)
19
+ rescue UnregisteredPredicateError
20
+ xmlns = nil
21
+ rel_predicate = nil
22
+ end
23
+ end
24
+
25
+ unless xmlns && rel_predicate
26
+ rel_predicate, xmlns = find_predicate(predicate)
27
+ end
28
+ vocabularies[xmlns][rel_predicate]
29
+ end
30
+
31
+ def self.vocabularies
32
+ return @vocabularies if @vocabularies
33
+ @vocabularies = {}
34
+ predicate_mappings.keys.each { |ns| @vocabularies[ns] = RDF::Vocabulary.new(ns)}
35
+ @vocabularies
36
+ end
37
+
38
+
39
+ # If predicate is a symbol, looks up the predicate in the predicate_mappings
40
+ # If predicate is not a Symbol, returns the predicate untouched
41
+ # @raise UnregisteredPredicateError if the predicate is a symbol but is not found in the predicate_mappings
42
+ def self.predicate_lookup(predicate,namespace="info:fedora/fedora-system:def/relations-external#")
43
+ if predicate.class == Symbol
44
+ if predicate_mappings[namespace].has_key?(predicate)
45
+ return predicate_mappings[namespace][predicate]
46
+ else
47
+ raise ActiveFedora::UnregisteredPredicateError
48
+ end
49
+ end
50
+ return predicate
51
+ end
52
+
53
+ def self.predicate_config
54
+ @@predicate_config ||= YAML::load(File.open(ActiveFedora.predicate_config)) if File.exist?(ActiveFedora.predicate_config)
55
+ end
56
+
57
+ def self.predicate_mappings
58
+ predicate_config[:predicate_mapping]
59
+ end
60
+
61
+ def self.default_predicate_namespace
62
+ predicate_config[:default_namespace]
63
+ end
64
+
65
+ def self.find_predicate(predicate)
66
+ predicate_mappings.each do |namespace,predicates|
67
+ if predicates.fetch(predicate,nil)
68
+ return predicates[predicate], namespace
69
+ end
70
+ end
71
+ raise ActiveFedora::UnregisteredPredicateError, "Unregistered predicate: #{predicate.inspect}"
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -2,6 +2,13 @@ module ActiveFedora
2
2
  module Reflection # :nodoc:
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ class_attribute :reflections
7
+ self.reflections = {}
8
+ end
9
+
10
+
11
+
5
12
  module ClassMethods
6
13
  def create_reflection(macro, name, options, active_fedora)
7
14
  case macro
@@ -9,7 +16,8 @@ module ActiveFedora
9
16
  klass = AssociationReflection
10
17
  reflection = klass.new(macro, name, options, active_fedora)
11
18
  end
12
- write_inheritable_hash :reflections, name => reflection
19
+
20
+ self.reflections = self.reflections.merge(name => reflection)
13
21
  reflection
14
22
  end
15
23
 
@@ -5,6 +5,7 @@ module ActiveFedora
5
5
 
6
6
  attr_accessor :subject, :predicate, :object, :is_literal, :data_type
7
7
  def initialize(attr={})
8
+ ActiveSupport::Deprecation.warn("ActiveFedora::Releationship is deprecated and will be removed in the next release")
8
9
  attr = {:is_literal => false}.merge(attr)
9
10
  @is_literal = attr[:is_literal] # must happen first
10
11
  self.subject = attr[:subject]
@@ -0,0 +1,152 @@
1
+ module ActiveFedora
2
+ class RelationshipGraph
3
+
4
+ attr_accessor :relationships, :dirty
5
+
6
+
7
+ def initialize
8
+ self.dirty = false
9
+ self.relationships = {}
10
+ end
11
+
12
+ def add(predicate, object, literal=false)
13
+ unless relationships.has_key? predicate
14
+ relationships[predicate] = []
15
+ end
16
+ object = RDF::Literal.new(object) if literal
17
+ unless relationships[predicate].include?(object)
18
+ @dirty = true
19
+ relationships[predicate] << object
20
+ end
21
+ end
22
+
23
+ def delete(predicate, object)
24
+ return unless relationships.has_key? predicate
25
+ if relationships[predicate].include?(object)
26
+ @dirty = true
27
+ relationships[predicate].delete(object)
28
+ end
29
+ if object.respond_to?(:internal_uri) && relationships[predicate].include?(object.internal_uri)
30
+ @dirty = true
31
+ relationships[predicate].delete(object.internal_uri)
32
+ elsif object.is_a? String
33
+ relationships[predicate].delete_if{|obj| obj.respond_to?(:internal_uri) && obj.internal_uri == object}
34
+ end
35
+
36
+ end
37
+
38
+ def [](predicate)
39
+ relationships[predicate]
40
+ end
41
+
42
+ def to_graph(subject_uri)
43
+ # need to destroy relationships and rewrite it.
44
+ subject = RDF::URI.new(subject_uri)
45
+ graph = RDF::Graph.new
46
+ relationships.each do |predicate, values|
47
+ values.each do |object|
48
+ graph.insert build_statement(subject, predicate, object)
49
+ end
50
+ end
51
+
52
+ graph
53
+ end
54
+
55
+ # Create an RDF statement
56
+ # @param uri a string represending the subject
57
+ # @param predicate a predicate symbol
58
+ # @param target an object to store
59
+ def build_statement(uri, predicate, target)
60
+ raise "Not allowed anymore" if uri == :self
61
+ target = target.internal_uri if target.respond_to? :internal_uri
62
+ subject = RDF::URI.new(uri) #TODO cache
63
+ if target.is_a? RDF::Literal or target.is_a? RDF::Resource
64
+ object = target
65
+ else
66
+ begin
67
+ target_uri = (target.is_a? URI) ? target : URI.parse(target)
68
+ if target_uri.scheme.nil?
69
+ raise ArgumentError, "Invalid target \"#{target}\". Must have namespace."
70
+ end
71
+ if target_uri.to_s =~ /\A[\w\-]+:[\w\-]+\Z/
72
+ raise ArgumentError, "Invalid target \"#{target}\". Target should be a complete URI, and not a pid."
73
+ end
74
+ rescue URI::InvalidURIError
75
+ raise ArgumentError, "Invalid target \"#{target}\". Target must be specified as a literal, or be a valid URI."
76
+ end
77
+ object = RDF::URI.new(target)
78
+ end
79
+ RDF::Statement.new(subject, ActiveFedora::Predicates.find_graph_predicate(predicate), object)
80
+
81
+ end
82
+
83
+ # def find_graph_predicate(predicate)
84
+ # #TODO, these could be cached
85
+ # case predicate
86
+ # when :has_model, "hasModel", :hasModel
87
+ # xmlns="info:fedora/fedora-system:def/model#"
88
+ # begin
89
+ # rel_predicate = ActiveFedora::Predicates.predicate_lookup(predicate,xmlns)
90
+ # rescue UnregisteredPredicateError
91
+ # xmlns = nil
92
+ # rel_predicate = nil
93
+ # end
94
+ # else
95
+ # xmlns="info:fedora/fedora-system:def/relations-external#"
96
+ # begin
97
+ # rel_predicate = ActiveFedora::Predicates.predicate_lookup(predicate,xmlns)
98
+ # rescue UnregisteredPredicateError
99
+ # xmlns = nil
100
+ # rel_predicate = nil
101
+ # end
102
+ # end
103
+ #
104
+ # unless xmlns && rel_predicate
105
+ # rel_predicate, xmlns = ActiveFedora::Predicates.find_predicate(predicate)
106
+ # end
107
+ # self.class.vocabularies[xmlns][rel_predicate]
108
+ # end
109
+ # def self.vocabularies
110
+ # return @vocabularies if @vocabularies
111
+ # @vocabularies = {}
112
+ # predicate_mappings.keys.each { |ns| @vocabularies[ns] = RDF::Vocabulary.new(ns)}
113
+ # @vocabularies
114
+ # end
115
+
116
+ # # If predicate is a symbol, looks up the predicate in the predicate_mappings
117
+ # # If predicate is not a Symbol, returns the predicate untouched
118
+ # # @raise UnregisteredPredicateError if the predicate is a symbol but is not found in the predicate_mappings
119
+ # def self.predicate_lookup(predicate,namespace="info:fedora/fedora-system:def/relations-external#")
120
+ # if predicate.class == Symbol
121
+ # if predicate_mappings[namespace].has_key?(predicate)
122
+ # return predicate_mappings[namespace][predicate]
123
+ # else
124
+ # raise ActiveFedora::UnregisteredPredicateError
125
+ # end
126
+ # end
127
+ # return predicate
128
+ # end
129
+
130
+ # def self.predicate_config
131
+ # @@predicate_config ||= YAML::load(File.open(ActiveFedora.predicate_config)) if File.exist?(ActiveFedora.predicate_config)
132
+ # end
133
+
134
+ # def self.predicate_mappings
135
+ # predicate_config[:predicate_mapping]
136
+ # end
137
+
138
+ # def self.default_predicate_namespace
139
+ # predicate_config[:default_namespace]
140
+ # end
141
+
142
+ # def self.find_predicate(predicate)
143
+ # predicate_mappings.each do |namespace,predicates|
144
+ # if predicates.fetch(predicate,nil)
145
+ # return predicates[predicate], namespace
146
+ # end
147
+ # end
148
+ # raise ActiveFedora::UnregisteredPredicateError, "Unregistered predicate: #{predicate.inspect}"
149
+ # end
150
+
151
+ end
152
+ end