active-fedora 7.0.0.rc2 → 7.0.0.rc3

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 (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