active-fedora 7.0.0.rc2 → 7.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +13 -3
  3. data/CONTRIBUTORS.md +1 -0
  4. data/Gemfile +2 -3
  5. data/active-fedora.gemspec +4 -3
  6. data/gemfiles/rails3.gemfile +1 -7
  7. data/gemfiles/rails4.1.gemfile +5 -0
  8. data/gemfiles/rails4.gemfile +1 -6
  9. data/lib/active_fedora.rb +6 -6
  10. data/lib/active_fedora/associations/association_scope.rb +1 -1
  11. data/lib/active_fedora/base.rb +2 -0
  12. data/lib/active_fedora/datastream.rb +29 -2
  13. data/lib/active_fedora/datastream_collections.rb +2 -2
  14. data/lib/active_fedora/datastream_hash.rb +1 -1
  15. data/lib/active_fedora/datastreams.rb +4 -23
  16. data/lib/active_fedora/file_configurator.rb +8 -7
  17. data/lib/active_fedora/om_datastream.rb +1 -1
  18. data/lib/active_fedora/rdf.rb +9 -0
  19. data/lib/active_fedora/rdf/configurable.rb +59 -0
  20. data/lib/active_fedora/rdf/identifiable.rb +59 -0
  21. data/lib/active_fedora/rdf/indexing.rb +24 -23
  22. data/lib/active_fedora/rdf/list.rb +154 -0
  23. data/lib/active_fedora/{ntriples_rdf_datastream.rb → rdf/ntriples_rdf_datastream.rb} +0 -0
  24. data/lib/active_fedora/rdf/object_resource.rb +20 -0
  25. data/lib/active_fedora/rdf/properties.rb +108 -0
  26. data/lib/active_fedora/rdf/rdf_datastream.rb +113 -0
  27. data/lib/active_fedora/{rdfxml_rdf_datastream.rb → rdf/rdfxml_rdf_datastream.rb} +0 -0
  28. data/lib/active_fedora/rdf/repositories.rb +36 -0
  29. data/lib/active_fedora/rdf/resource.rb +324 -0
  30. data/lib/active_fedora/rdf/term.rb +188 -0
  31. data/lib/active_fedora/relation.rb +1 -0
  32. data/lib/active_fedora/relation/finder_methods.rb +16 -17
  33. data/lib/active_fedora/relation/merger.rb +1 -1
  34. data/lib/active_fedora/relation/query_methods.rb +13 -5
  35. data/lib/active_fedora/reload_on_save.rb +16 -0
  36. data/lib/active_fedora/rubydora_connection.rb +0 -1
  37. data/lib/active_fedora/sharding.rb +1 -1
  38. data/lib/active_fedora/version.rb +1 -1
  39. data/script/console +10 -11
  40. data/spec/config_helper.rb +1 -1
  41. data/spec/fixtures/solr_rdf_descMetadata.nt +1 -1
  42. data/spec/integration/auditable_spec.rb +1 -1
  43. data/spec/integration/base_spec.rb +21 -3
  44. data/spec/integration/complex_rdf_datastream_spec.rb +32 -55
  45. data/spec/integration/field_to_solr_name_spec.rb +6 -8
  46. data/spec/integration/has_many_associations_spec.rb +10 -3
  47. data/spec/integration/load_from_solr_spec.rb +15 -17
  48. data/spec/integration/ntriples_datastream_spec.rb +19 -23
  49. data/spec/integration/om_datastream_spec.rb +1 -1
  50. data/spec/integration/rdf_nested_attributes_spec.rb +51 -70
  51. data/spec/integration/relation_spec.rb +24 -11
  52. data/spec/integration/scoped_query_spec.rb +5 -1
  53. data/spec/samples/hydra-mods_article_datastream.rb +4 -0
  54. data/spec/samples/hydra-rights_metadata_datastream.rb +5 -0
  55. data/spec/samples/marpa-dc_datastream.rb +6 -1
  56. data/spec/samples/special_thing.rb +5 -5
  57. data/spec/spec_helper.rb +0 -3
  58. data/spec/support/an_active_model.rb +5 -12
  59. data/spec/unit/active_fedora_spec.rb +2 -2
  60. data/spec/unit/attributes_spec.rb +4 -8
  61. data/spec/unit/base_datastream_management_spec.rb +5 -29
  62. data/spec/unit/base_spec.rb +4 -0
  63. data/spec/unit/code_configurator_spec.rb +2 -2
  64. data/spec/unit/config_spec.rb +2 -2
  65. data/spec/unit/core_spec.rb +2 -4
  66. data/spec/unit/datastream_spec.rb +15 -0
  67. data/spec/unit/datastreams_spec.rb +1 -12
  68. data/spec/unit/file_configurator_spec.rb +1 -1
  69. data/spec/unit/ntriples_datastream_spec.rb +52 -57
  70. data/spec/unit/om_datastream_spec.rb +3 -3
  71. data/spec/unit/query_spec.rb +3 -4
  72. data/spec/unit/rdf_configurable_spec.rb +37 -0
  73. data/spec/unit/rdf_datastream_spec.rb +5 -7
  74. data/spec/unit/rdf_list_nested_attributes_spec.rb +22 -36
  75. data/spec/unit/rdf_list_spec.rb +26 -38
  76. data/spec/unit/rdf_properties_spec.rb +70 -0
  77. data/spec/unit/rdf_repositories_spec.rb +28 -0
  78. data/spec/unit/rdf_resource_datastream_spec.rb +287 -0
  79. data/spec/unit/rdf_resource_spec.rb +341 -0
  80. data/spec/unit/rdfxml_rdf_datastream_spec.rb +10 -26
  81. data/spec/unit/reload_on_save_spec.rb +24 -0
  82. data/spec/unit/solr_service_spec.rb +3 -3
  83. metadata +45 -16
  84. data/lib/active_fedora/rdf_datastream.rb +0 -113
  85. data/lib/active_fedora/rdf_list.rb +0 -162
  86. data/lib/active_fedora/rdf_node.rb +0 -332
  87. data/lib/active_fedora/rdf_node/term_proxy.rb +0 -141
  88. data/lib/active_fedora/rdf_object.rb +0 -24
  89. data/lib/active_fedora/yaml_adaptor.rb +0 -12
  90. data/spec/unit/rdf_node_spec.rb +0 -36
@@ -16,10 +16,14 @@ module ActiveFedora
16
16
 
17
17
  def to_solr(solr_doc = Hash.new) # :nodoc:
18
18
  fields.each do |field_key, field_info|
19
- values = get_values(rdf_subject, field_key)
20
- Array(values).each do |val|
21
- val = val.to_s if val.kind_of? RDF::URI
22
- Solrizer.insert_field(solr_doc, apply_prefix(field_key), val, *field_info[:behaviors])
19
+ values = resource.get_values(field_key)
20
+ Array(values).each do |val|
21
+ if val.kind_of? RDF::URI
22
+ val = val.to_s
23
+ elsif val.kind_of? Rdf::Resource
24
+ val = val.solrize
25
+ end
26
+ self.class.create_and_insert_terms(apply_prefix(field_key), val, field_info[:behaviors], solr_doc)
23
27
  end
24
28
  end
25
29
  solr_doc
@@ -28,12 +32,16 @@ module ActiveFedora
28
32
  # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
29
33
  def primary_solr_name(field)
30
34
  config = self.class.config_for_term_or_uri(field)
35
+ return nil unless config # punt on index names for deep nodes!
31
36
  if behaviors = config.behaviors
32
- ActiveFedora::SolrService.solr_name(apply_prefix(field), behaviors.first, type: config.type)
37
+ behaviors.each do |behavior|
38
+ result = ActiveFedora::SolrService.solr_name(apply_prefix(field), behavior, type: config.type)
39
+ return result if Solrizer::DefaultDescriptors.send(behavior).evaluate_suffix(:text).stored?
40
+ end
41
+ raise RuntimeError "no stored fields were found"
33
42
  end
34
43
  end
35
44
 
36
-
37
45
  module ClassMethods
38
46
  def prefix(dsid, name)
39
47
  Deprecation.warn Indexing, "prefix is deprecated and will be removed in active-fedora 8.0.0.", caller
@@ -45,28 +53,21 @@ module ActiveFedora
45
53
  config_for_term_or_uri(field).type
46
54
  end
47
55
  end
48
-
56
+
49
57
  private
50
58
  # returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
51
59
  def fields
52
60
  field_map = {}.with_indifferent_access
53
61
 
54
- rdf_subject = self.rdf_subject
55
- query = RDF::Query.new do
56
- pattern [rdf_subject, :predicate, :value]
57
- end
58
-
59
- query.execute(graph).each do |solution|
60
- predicate = solution.predicate
61
- value = solution.value
62
-
63
- name, config = self.class.config_for_predicate(predicate)
64
- next unless config
65
- type = config.type
66
- behaviors = config.behaviors
67
- next unless type and behaviors
68
- field_map[name] ||= {:values => [], :type => type, :behaviors => behaviors}
69
- field_map[name][:values] << value.to_s
62
+ self.class.properties.each do |name, config|
63
+ type = config[:type]
64
+ behaviors = config[:behaviors]
65
+ next unless type and behaviors
66
+ next if config[:class_name] && config[:class_name] < ActiveFedora::Base
67
+ resource.query(:subject => rdf_subject, :predicate => config[:predicate]).each_statement do |statement|
68
+ field_map[name] ||= {:values => [], :type => type, :behaviors => behaviors}
69
+ field_map[name][:values] << statement.object.to_s
70
+ end
70
71
  end
71
72
  field_map
72
73
  end
@@ -0,0 +1,154 @@
1
+ module ActiveFedora::Rdf
2
+ ##
3
+ # An implementation of RDF::List intregrated with ActiveFedora::Rdf.
4
+ #
5
+ # A thoughtful reflection period is encouraged before using the
6
+ # rdf:List concept in your data. The community may pursue other
7
+ # options for ordered sets.
8
+ class List < RDF::List
9
+ include ActiveFedora::Rdf::NestedAttributes
10
+ extend Configurable
11
+ extend Properties
12
+
13
+ delegate :rdf_subject, :mark_for_destruction, :marked_for_destruction?, :set_value, :get_values, :parent, :dump, :attributes=, to: :resource
14
+ alias_method :to_ary, :to_a
15
+
16
+ class << self
17
+ def from_uri(uri, vals)
18
+ list = ListResource.from_uri(uri, vals)
19
+ self.new(list.rdf_subject, list)
20
+ end
21
+ end
22
+
23
+ def resource
24
+ graph
25
+ end
26
+
27
+ def initialize(*args)
28
+ super
29
+ parent = graph.parent if graph.respond_to? :parent
30
+ @graph = ListResource.new(subject) << graph unless graph.kind_of? Resource
31
+ graph << parent if parent
32
+ graph.list = self
33
+ graph.singleton_class.properties = self.class.properties
34
+ graph.singleton_class.properties.keys.each do |property|
35
+ graph.singleton_class.send(:register_property, property)
36
+ end
37
+ graph.insert RDF::Statement.new(subject, RDF.type, RDF.List)
38
+ graph.reload
39
+ end
40
+
41
+ def []=(idx, value)
42
+ raise IndexError "index #{idx} too small for array: minimum 0" if idx < 0
43
+
44
+ if idx >= length
45
+ (idx - length).times do
46
+ self << RDF::OWL.Nothing
47
+ end
48
+ return self << value
49
+ end
50
+ each_subject.with_index do |v, i|
51
+ next unless i == idx
52
+ resource.set_value(v, RDF.first, value)
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Override to return AF::Rdf::Resources as values, where
58
+ # appropriate.
59
+ def each(&block)
60
+ return super unless block_given?
61
+
62
+ super do |value|
63
+ block.call(node_from_value(value))
64
+ end
65
+ end
66
+
67
+ ##
68
+ # Do these like #each.
69
+ def first
70
+ node_from_value(super)
71
+ end
72
+
73
+ def shift
74
+ node_from_value(super)
75
+ end
76
+
77
+ ##
78
+ # Find an AF::Rdf::Resource from the value returned by RDF::List
79
+ def node_from_value(value)
80
+ if value.kind_of? RDF::Resource
81
+ type_uri = resource.query([value, RDF.type, nil]).to_a.first.try(:object)
82
+ klass = ActiveFedora::Rdf::Resource.type_registry[type_uri]
83
+ klass ||= Resource
84
+ return klass.from_uri(value,resource)
85
+ end
86
+ value
87
+ end
88
+
89
+ ##
90
+ # This class is the graph/Resource that backs the List and
91
+ # supplies integration with the rest of ActiveFedora::Rdf
92
+ class ListResource < Resource
93
+ attr_reader :list
94
+
95
+ def list=(list)
96
+ @list ||= list
97
+ end
98
+
99
+ def attributes=(values)
100
+ raise ArgumentError, "values must be a Hash, you provided #{values.class}" unless values.kind_of? Hash
101
+ values.with_indifferent_access.each do |key, value|
102
+ if self.singleton_class.properties.keys.map{ |k| "#{k}_attributes"}.include?(key)
103
+ klass = properties[key[0..-12]]['class_name']
104
+ klass = ActiveFedora.class_from_string(klass, final_parent.class) if klass.is_a? String
105
+ value.is_a?(Hash) ? attributes_hash_to_list(values[key], klass) : attributes_to_list(value, klass)
106
+ end
107
+ end
108
+ persist!
109
+ super
110
+ end
111
+
112
+ private
113
+ def attributes_to_list(value, klass)
114
+ value.each do |entry|
115
+ item = klass.new()
116
+ item.attributes = entry
117
+ list << item
118
+ end
119
+ end
120
+
121
+ def attributes_hash_to_list(value, klass)
122
+ value.each do |counter, attr|
123
+ item = klass.new()
124
+ item.attributes = attr if attr
125
+ list[counter.to_i] = item
126
+ end
127
+ end
128
+ end
129
+
130
+ ##
131
+ # Monkey patch to allow lists to have subject URIs.
132
+ # Overrides RDF::List to prevent URI subjects
133
+ # from being replaced with nodes.
134
+ #
135
+ # @NOTE Lists built this way will return false for #valid?
136
+ def <<(value)
137
+ value = case value
138
+ when nil then RDF.nil
139
+ when RDF::Value then value
140
+ when Array then RDF::List.new(nil, graph, value)
141
+ else value
142
+ end
143
+
144
+ if empty?
145
+ resource.set_value(RDF.first, value)
146
+ resource.insert([subject, RDF.rest, RDF.nil])
147
+ resource << value if value.kind_of? Resource
148
+ return self
149
+ end
150
+ super
151
+ resource << value if value.kind_of? Resource
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveFedora::Rdf
2
+ ##
3
+ # A class of RdfResources to act as the primary/root resource associated
4
+ # with a Datastream and ActiveFedora::Base object.
5
+ #
6
+ # @see ActiveFedora::RDFDatastream
7
+ class ObjectResource < Resource
8
+ configure :base_uri => 'info:fedora/'
9
+ attr_accessor :datastream
10
+
11
+ def persist!
12
+ return false unless datastream and datastream.respond_to? :digital_object
13
+ @persisted ||= datastream.digital_object.save
14
+ end
15
+
16
+ def persisted?
17
+ @persisted ||= (not datastream.new?)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,108 @@
1
+ module ActiveFedora::Rdf
2
+ ##
3
+ # Implements property configuration common to Rdf::Resource,
4
+ # RDFDatastream, and others. It does its work at the class level,
5
+ # and is meant to be extended.
6
+ #
7
+ # Define properties at the class level with:
8
+ #
9
+ # property :title, predicate: RDF::DC.title, class_name: ResourceClass
10
+ #
11
+ # or with the 'old' style:
12
+ #
13
+ # map_predicates do |map|
14
+ # map.title(in: RDF::DC)
15
+ # end
16
+ #
17
+ # You can pass a block to either to set index behavior.
18
+ module Properties
19
+ extend Deprecation
20
+ attr_accessor :properties
21
+
22
+ ##
23
+ # Registers properties for Resource-like classes
24
+ # @param [Symbol] name of the property (and its accessor methods)
25
+ # @param [Hash] opts for this property, must include a :predicate
26
+ # @yield [index] index sets solr behaviors for the property
27
+ def property(name, opts={}, &block)
28
+ config[name] = ActiveFedora::Rdf::NodeConfig.new(name, opts[:predicate], opts.except(:predicate)).tap do |config|
29
+ config.with_index(&block) if block_given?
30
+ end
31
+ behaviors = config[name].behaviors.flatten if config[name].behaviors and not config[name].behaviors.empty?
32
+
33
+ self.properties[name] = {
34
+ behaviors: behaviors,
35
+ type: config[name].type,
36
+ class_name: config[name].class_name,
37
+ predicate: config[name].predicate,
38
+ term: config[name].term,
39
+ multivalue: config[name].multivalue
40
+ }
41
+ register_property(name)
42
+ end
43
+
44
+ def properties
45
+ @properties ||= if superclass.respond_to? :properties
46
+ superclass.properties.dup
47
+ else
48
+ {}.with_indifferent_access
49
+ end
50
+ end
51
+
52
+ def config
53
+ @config ||= if superclass.respond_to? :config
54
+ superclass.properties.dup
55
+ else
56
+ {}.with_indifferent_access
57
+ end
58
+ end
59
+
60
+ def config_for_term_or_uri(term)
61
+ return config[term.to_sym] unless term.kind_of? RDF::Resource
62
+ config.each { |k, v| return v if v.predicate == term.to_uri }
63
+ end
64
+
65
+ def fields
66
+ properties.keys.map(&:to_sym)
67
+ end
68
+
69
+ private
70
+
71
+ ##
72
+ # Private method for creating accessors for a given property.
73
+ # If used on an ActiveFedora::Datastream it will create accessors which use the datastream's resource.
74
+ # @param [#to_s] name Name of the accessor to be created, get/set_value is called on the resource using this.
75
+ def register_property(name)
76
+ parent = Proc.new{self}
77
+ parent = Proc.new{resource} if self < ActiveFedora::Datastream
78
+ define_method "#{name}=" do |*args|
79
+ instance_eval(&parent).set_value(name.to_sym, *args)
80
+ end
81
+ define_method name do
82
+ instance_eval(&parent).get_values(name.to_sym)
83
+ end
84
+ end
85
+
86
+ public
87
+ # Mapper is for backwards compatibility with AF::RDFDatastream
88
+ class Mapper
89
+ attr_accessor :parent
90
+ def initialize(parent)
91
+ @parent = parent
92
+ end
93
+ def method_missing(name, *args, &block)
94
+ properties = args.first || {}
95
+ vocab = properties.delete(:in)
96
+ to = properties.delete(:to) || name
97
+ predicate = vocab.send(to)
98
+ parent.property(name, properties.merge(predicate: predicate), &block)
99
+ end
100
+ end
101
+ def map_predicates
102
+ Deprecation.warn Properties, "map_predicates is deprecated and will be removed in active-fedora 8.0.0. Use property :name, predicate: predicate instead.", caller
103
+ mapper = Mapper.new(self)
104
+ yield(mapper)
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,113 @@
1
+ module ActiveFedora
2
+ class RDFDatastream < ActiveFedora::Datastream
3
+ include Solrizer::Common
4
+ include ActiveFedora::Rdf::NestedAttributes
5
+ include Rdf::Indexing
6
+ extend Rdf::Properties
7
+
8
+ delegate :rdf_subject, :set_value, :get_values, :attributes=, :to => :resource
9
+
10
+ class << self
11
+ def rdf_subject &block
12
+ if block_given?
13
+ return @subject_block = block
14
+ end
15
+
16
+ @subject_block ||= lambda { |ds| ds.pid }
17
+ end
18
+
19
+ # Utility method which can be overridden to determine the object
20
+ # resource that is created.
21
+ def resource_class
22
+ Rdf::ObjectResource
23
+ end
24
+ end
25
+
26
+ before_save do
27
+ if content.blank?
28
+ logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}"
29
+ false
30
+ end
31
+ end
32
+
33
+ def metadata?
34
+ true
35
+ end
36
+
37
+ def content
38
+ serialize
39
+ end
40
+
41
+ def content=(content)
42
+ resource.clear!
43
+ resource << RDF::Reader.for(serialization_format).new(content)
44
+ content
45
+ end
46
+
47
+ def content_changed?
48
+ return false unless instance_variable_defined? :@resource
49
+ @content = serialize
50
+ super
51
+ end
52
+
53
+ def freeze
54
+ @resource.freeze
55
+ end
56
+
57
+ ##
58
+ # The resource is the RdfResource object that stores the graph for
59
+ # the datastream and is the central point for its relationship to
60
+ # other nodes.
61
+ #
62
+ # set_value, get_value, and property accessors are delegated to this object.
63
+ def resource
64
+ @resource ||= begin
65
+ r = self.class.resource_class.new(digital_object ? self.class.rdf_subject.call(self) : nil)
66
+ r.singleton_class.properties = self.class.properties
67
+ r.singleton_class.properties.keys.each do |property|
68
+ r.singleton_class.send(:register_property, property)
69
+ end
70
+ r.datastream = self
71
+ r.singleton_class.accepts_nested_attributes_for(*nested_attributes_options.keys) unless nested_attributes_options.blank?
72
+ r << RDF::Reader.for(serialization_format).new(datastream_content) if datastream_content
73
+ r
74
+ end
75
+ end
76
+
77
+ alias_method :graph, :resource
78
+
79
+ ##
80
+ # This method allows for delegation.
81
+ # This patches the fact that there's no consistent API for allowing delegation - we're matching the
82
+ # OMDatastream implementation as our "consistency" point.
83
+ # @TODO: We may need to enable deep RDF delegation at one point.
84
+ def term_values(*values)
85
+ self.send(values.first)
86
+ end
87
+
88
+ def update_indexed_attributes(hash)
89
+ hash.each do |fields, value|
90
+ fields.each do |field|
91
+ self.send("#{field}=", value)
92
+ end
93
+ end
94
+ end
95
+
96
+ def serialize
97
+ resource.set_subject!(pid) if (digital_object or pid) and rdf_subject.node?
98
+ resource.dump serialization_format
99
+ end
100
+
101
+ def deserialize(data=nil)
102
+ return RDF::Graph.new if new? && data.nil?
103
+ data ||= datastream_content
104
+ data.force_encoding('utf-8')
105
+ RDF::Graph.new << RDF::Reader.for(serialization_format).new(data)
106
+ end
107
+
108
+ def serialization_format
109
+ raise "you must override the `serialization_format' method in a subclass"
110
+ end
111
+
112
+ end
113
+ end