active-fedora 3.1.6 → 3.2.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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