active-fedora 6.8.0 → 7.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +15 -5
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +0 -2
- data/History.txt +2 -32
- data/README.md +143 -0
- data/Rakefile +5 -7
- data/active-fedora.gemspec +9 -9
- data/gemfiles/rails3.gemfile +11 -0
- data/gemfiles/rails4.gemfile +10 -0
- data/lib/active_fedora.rb +31 -4
- data/lib/active_fedora/association_relation.rb +18 -0
- data/lib/active_fedora/associations.rb +38 -171
- data/lib/active_fedora/associations/association.rb +163 -0
- data/lib/active_fedora/associations/association_scope.rb +39 -0
- data/lib/active_fedora/associations/belongs_to_association.rb +47 -25
- data/lib/active_fedora/associations/builder/association.rb +55 -0
- data/lib/active_fedora/associations/builder/belongs_to.rb +100 -0
- data/lib/active_fedora/associations/builder/collection_association.rb +56 -0
- data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +30 -0
- data/lib/active_fedora/associations/builder/has_many.rb +63 -0
- data/lib/active_fedora/associations/builder/singular_association.rb +32 -0
- data/lib/active_fedora/associations/{association_collection.rb → collection_association.rb} +203 -53
- data/lib/active_fedora/associations/collection_proxy.rb +862 -0
- data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +35 -25
- data/lib/active_fedora/associations/has_many_association.rb +36 -11
- data/lib/active_fedora/associations/singular_association.rb +62 -0
- data/lib/active_fedora/attributes.rb +43 -139
- data/lib/active_fedora/autosave_association.rb +317 -0
- data/lib/active_fedora/base.rb +10 -327
- data/lib/active_fedora/callbacks.rb +1 -3
- data/lib/active_fedora/content_model.rb +16 -0
- data/lib/active_fedora/core.rb +151 -0
- data/lib/active_fedora/datastream_attribute.rb +76 -0
- data/lib/active_fedora/datastream_hash.rb +8 -13
- data/lib/active_fedora/datastreams.rb +39 -26
- data/lib/active_fedora/digital_object.rb +2 -2
- data/lib/active_fedora/fedora_attributes.rb +45 -0
- data/lib/active_fedora/fixture_loader.rb +1 -1
- data/lib/active_fedora/indexing.rb +6 -1
- data/lib/active_fedora/model.rb +0 -17
- data/lib/active_fedora/nested_attributes.rb +2 -2
- data/lib/active_fedora/null_relation.rb +7 -0
- data/lib/active_fedora/om_datastream.rb +3 -4
- data/lib/active_fedora/persistence.rb +41 -29
- data/lib/active_fedora/querying.rb +2 -163
- data/lib/active_fedora/rdf.rb +1 -0
- data/lib/active_fedora/rdf/indexing.rb +67 -0
- data/lib/active_fedora/rdf_datastream.rb +2 -50
- data/lib/active_fedora/rdf_node.rb +12 -7
- data/lib/active_fedora/rdf_node/term_proxy.rb +30 -21
- data/lib/active_fedora/rdfxml_rdf_datastream.rb +1 -1
- data/lib/active_fedora/reflection.rb +163 -20
- data/lib/active_fedora/relation.rb +33 -130
- data/lib/active_fedora/relation/calculations.rb +19 -0
- data/lib/active_fedora/relation/delegation.rb +22 -0
- data/lib/active_fedora/relation/finder_methods.rb +247 -0
- data/lib/active_fedora/relation/merger.rb +22 -0
- data/lib/active_fedora/relation/query_methods.rb +58 -0
- data/lib/active_fedora/relation/spawn_methods.rb +46 -0
- data/lib/active_fedora/relationship_graph.rb +0 -2
- data/lib/active_fedora/rels_ext_datastream.rb +1 -4
- data/lib/active_fedora/rubydora_connection.rb +1 -1
- data/lib/active_fedora/scoping.rb +20 -0
- data/lib/active_fedora/scoping/default.rb +38 -0
- data/lib/active_fedora/scoping/named.rb +32 -0
- data/lib/active_fedora/semantic_node.rb +54 -106
- data/lib/active_fedora/serialization.rb +19 -0
- data/lib/active_fedora/sharding.rb +58 -0
- data/lib/active_fedora/solr_digital_object.rb +15 -5
- data/lib/active_fedora/solr_instance_loader.rb +1 -1
- data/lib/active_fedora/solr_service.rb +1 -1
- data/lib/active_fedora/unsaved_digital_object.rb +6 -4
- data/lib/active_fedora/version.rb +1 -1
- data/lib/tasks/active_fedora.rake +3 -0
- data/lib/tasks/active_fedora_dev.rake +6 -5
- data/spec/config_helper.rb +14 -14
- data/spec/integration/associations_spec.rb +168 -455
- data/spec/integration/attributes_spec.rb +12 -11
- data/spec/integration/auditable_spec.rb +11 -11
- data/spec/integration/autosave_association_spec.rb +25 -0
- data/spec/integration/base_spec.rb +163 -163
- data/spec/integration/belongs_to_association_spec.rb +166 -0
- data/spec/integration/bug_spec.rb +7 -7
- data/spec/integration/collection_association_spec.rb +58 -0
- data/spec/integration/complex_rdf_datastream_spec.rb +88 -88
- data/spec/integration/datastream_collections_spec.rb +69 -69
- data/spec/integration/datastream_spec.rb +43 -43
- data/spec/integration/datastreams_spec.rb +63 -63
- data/spec/integration/delete_all_spec.rb +46 -39
- data/spec/integration/fedora_solr_sync_spec.rb +5 -5
- data/spec/integration/field_to_solr_name_spec.rb +34 -0
- data/spec/integration/full_featured_model_spec.rb +100 -101
- data/spec/integration/has_and_belongs_to_many_associations_spec.rb +341 -0
- data/spec/integration/has_many_associations_spec.rb +172 -24
- data/spec/integration/json_serialization_spec.rb +31 -0
- data/spec/integration/load_from_solr_spec.rb +48 -0
- data/spec/integration/model_spec.rb +35 -40
- data/spec/integration/nested_attribute_spec.rb +42 -43
- data/spec/integration/ntriples_datastream_spec.rb +131 -113
- data/spec/integration/om_datastream_spec.rb +67 -67
- data/spec/integration/persistence_spec.rb +7 -7
- data/spec/integration/rdf_nested_attributes_spec.rb +56 -56
- data/spec/integration/relation_delegation_spec.rb +26 -25
- data/spec/integration/relation_spec.rb +42 -0
- data/spec/integration/rels_ext_datastream_spec.rb +20 -20
- data/spec/integration/scoped_query_spec.rb +61 -51
- data/spec/integration/solr_instance_loader_spec.rb +5 -5
- data/spec/integration/solr_service_spec.rb +46 -46
- data/spec/samples/hydra-mods_article_datastream.rb +334 -334
- data/spec/samples/hydra-rights_metadata_datastream.rb +57 -57
- data/spec/samples/marpa-dc_datastream.rb +17 -17
- data/spec/samples/models/audio_record.rb +16 -16
- data/spec/samples/models/image.rb +2 -2
- data/spec/samples/models/mods_article.rb +5 -5
- data/spec/samples/models/oral_history.rb +18 -18
- data/spec/samples/models/seminar.rb +24 -24
- data/spec/samples/models/seminar_audio_file.rb +17 -17
- data/spec/samples/oral_history_sample_model.rb +21 -21
- data/spec/samples/special_thing.rb +14 -14
- data/spec/spec_helper.rb +11 -7
- data/spec/support/an_active_model.rb +2 -8
- data/spec/support/freeze_mocks.rb +12 -0
- data/spec/support/mock_fedora.rb +17 -16
- data/spec/unit/active_fedora_spec.rb +58 -60
- data/spec/unit/attributes_spec.rb +314 -0
- data/spec/unit/base_active_model_spec.rb +28 -27
- data/spec/unit/base_cma_spec.rb +5 -5
- data/spec/unit/base_datastream_management_spec.rb +27 -27
- data/spec/unit/base_extra_spec.rb +76 -48
- data/spec/unit/base_spec.rb +277 -348
- data/spec/unit/callback_spec.rb +18 -19
- data/spec/unit/code_configurator_spec.rb +17 -17
- data/spec/unit/config_spec.rb +8 -16
- data/spec/unit/content_model_spec.rb +79 -60
- data/spec/unit/datastream_collections_spec.rb +229 -229
- data/spec/unit/datastream_spec.rb +51 -63
- data/spec/unit/datastreams_spec.rb +87 -87
- data/spec/unit/file_configurator_spec.rb +217 -217
- data/spec/unit/has_and_belongs_to_many_collection_spec.rb +44 -25
- data/spec/unit/has_many_collection_spec.rb +26 -8
- data/spec/unit/inheritance_spec.rb +13 -12
- data/spec/unit/model_spec.rb +39 -45
- data/spec/unit/nom_datastream_spec.rb +15 -15
- data/spec/unit/ntriples_datastream_spec.rb +123 -118
- data/spec/unit/om_datastream_spec.rb +227 -233
- data/spec/unit/persistence_spec.rb +34 -15
- data/spec/unit/predicates_spec.rb +73 -73
- data/spec/unit/property_spec.rb +17 -9
- data/spec/unit/qualified_dublin_core_datastream_spec.rb +33 -33
- data/spec/unit/query_spec.rb +222 -198
- data/spec/unit/rdf_datastream_spec.rb +21 -28
- data/spec/unit/rdf_list_nested_attributes_spec.rb +34 -34
- data/spec/unit/rdf_list_spec.rb +65 -64
- data/spec/unit/rdf_node_spec.rb +7 -7
- data/spec/unit/rdf_xml_writer_spec.rb +10 -10
- data/spec/unit/rdfxml_rdf_datastream_spec.rb +27 -27
- data/spec/unit/relationship_graph_spec.rb +51 -51
- data/spec/unit/rels_ext_datastream_spec.rb +68 -74
- data/spec/unit/rspec_matchers/belong_to_associated_active_fedora_object_matcher_spec.rb +15 -15
- data/spec/unit/rspec_matchers/have_many_associated_active_fedora_objects_matcher_spec.rb +15 -15
- data/spec/unit/rspec_matchers/have_predicate_matcher_spec.rb +15 -15
- data/spec/unit/rspec_matchers/match_fedora_datastream_matcher_spec.rb +12 -12
- data/spec/unit/rubydora_connection_spec.rb +5 -5
- data/spec/unit/semantic_node_spec.rb +48 -107
- data/spec/unit/serializers_spec.rb +4 -4
- data/spec/unit/service_definitions_spec.rb +26 -26
- data/spec/unit/simple_datastream_spec.rb +17 -17
- data/spec/unit/solr_config_options_spec.rb +29 -28
- data/spec/unit/solr_digital_object_spec.rb +17 -25
- data/spec/unit/solr_service_spec.rb +95 -82
- data/spec/unit/unsaved_digital_object_spec.rb +24 -23
- data/spec/unit/validations_spec.rb +21 -21
- metadata +110 -159
- data/.rspec +0 -1
- data/.rubocop.yml +0 -1
- data/.rubocop_todo.yml +0 -938
- data/CONSOLE_GETTING_STARTED.textile +0 -1
- data/NOKOGIRI_DATASTREAMS.textile +0 -1
- data/README.textile +0 -116
- data/lib/active_fedora/associations/association_proxy.rb +0 -178
- data/lib/active_fedora/delegating.rb +0 -72
- data/lib/active_fedora/nokogiri_datastream.rb +0 -11
- data/spec/integration/delegating_spec.rb +0 -59
- data/spec/rails3_test_app/.gitignore +0 -4
- data/spec/rails3_test_app/.rspec +0 -1
- data/spec/rails3_test_app/Gemfile +0 -40
- data/spec/rails3_test_app/Rakefile +0 -7
- data/spec/rails3_test_app/app/controllers/application_controller.rb +0 -3
- data/spec/rails3_test_app/app/helpers/application_helper.rb +0 -2
- data/spec/rails3_test_app/app/views/layouts/application.html.erb +0 -14
- data/spec/rails3_test_app/config.ru +0 -4
- data/spec/rails3_test_app/config/application.rb +0 -42
- data/spec/rails3_test_app/config/boot.rb +0 -6
- data/spec/rails3_test_app/config/database.yml +0 -22
- data/spec/rails3_test_app/config/environment.rb +0 -5
- data/spec/rails3_test_app/config/environments/development.rb +0 -25
- data/spec/rails3_test_app/config/environments/production.rb +0 -49
- data/spec/rails3_test_app/config/environments/test.rb +0 -35
- data/spec/rails3_test_app/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/rails3_test_app/config/initializers/inflections.rb +0 -10
- data/spec/rails3_test_app/config/initializers/mime_types.rb +0 -5
- data/spec/rails3_test_app/config/initializers/secret_token.rb +0 -7
- data/spec/rails3_test_app/config/initializers/session_store.rb +0 -8
- data/spec/rails3_test_app/config/locales/en.yml +0 -5
- data/spec/rails3_test_app/config/routes.rb +0 -58
- data/spec/rails3_test_app/db/seeds.rb +0 -7
- data/spec/rails3_test_app/run_tests +0 -3
- data/spec/rails3_test_app/script/rails +0 -6
- data/spec/rails3_test_app/spec/spec_helper.rb +0 -27
- data/spec/rails3_test_app/spec/unit/rails_3_init.rb +0 -15
- data/spec/unit/association_proxy_spec.rb +0 -12
- data/spec/unit/base_delegate_spec.rb +0 -197
- data/spec/unit/base_delegate_to_spec.rb +0 -73
@@ -1,8 +1,6 @@
|
|
1
1
|
module ActiveFedora
|
2
2
|
class RDFDatastream < Datastream
|
3
3
|
|
4
|
-
include Solrizer::Common
|
5
|
-
|
6
4
|
before_save do
|
7
5
|
if content.blank?
|
8
6
|
logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}"
|
@@ -10,6 +8,7 @@ module ActiveFedora
|
|
10
8
|
end
|
11
9
|
end
|
12
10
|
include RdfNode
|
11
|
+
include Rdf::Indexing
|
13
12
|
|
14
13
|
class << self
|
15
14
|
##
|
@@ -30,12 +29,6 @@ module ActiveFedora
|
|
30
29
|
true
|
31
30
|
end
|
32
31
|
|
33
|
-
def prefix(name)
|
34
|
-
name = name.to_s unless name.is_a? String
|
35
|
-
pre = dsid.underscore
|
36
|
-
return "#{pre}__#{name}".to_sym
|
37
|
-
end
|
38
|
-
|
39
32
|
# Overriding so that one can call ds.content on an unsaved datastream and they will see the serialized format
|
40
33
|
def content
|
41
34
|
serialize
|
@@ -52,32 +45,14 @@ module ActiveFedora
|
|
52
45
|
@content = serialize
|
53
46
|
super
|
54
47
|
end
|
55
|
-
|
56
|
-
def to_solr(solr_doc = Hash.new) # :nodoc:
|
57
|
-
fields.each do |field_key, field_info|
|
58
|
-
values = get_values(rdf_subject, field_key)
|
59
|
-
if values
|
60
|
-
Array(values).each do |val|
|
61
|
-
val = val.to_s if val.kind_of? RDF::URI
|
62
|
-
self.class.create_and_insert_terms(prefix(field_key), val, field_info[:behaviors], solr_doc)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
solr_doc
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
48
|
# Populate a RDFDatastream object based on the "datastream" content
|
71
49
|
# Assumes that the datastream contains RDF content
|
72
50
|
# @param [String] data the "rdf" node
|
73
51
|
def deserialize(data = nil)
|
74
52
|
repository = RDF::Repository.new
|
75
53
|
return repository if new? and data.nil?
|
76
|
-
data ||= datastream_content
|
77
|
-
|
78
|
-
# Because datastream_content can return nil, we should check that here.
|
79
|
-
return repository if data.nil?
|
80
54
|
|
55
|
+
data ||= datastream_content
|
81
56
|
data.force_encoding('utf-8')
|
82
57
|
RDF::Reader.for(serialization_format).new(data) do |reader|
|
83
58
|
reader.each_statement do |statement|
|
@@ -129,29 +104,6 @@ module ActiveFedora
|
|
129
104
|
@graph = new_repository
|
130
105
|
end
|
131
106
|
|
132
|
-
# returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
|
133
|
-
def fields
|
134
|
-
field_map = {}.with_indifferent_access
|
135
|
-
|
136
|
-
rdf_subject = self.rdf_subject
|
137
|
-
query = RDF::Query.new do
|
138
|
-
pattern [rdf_subject, :predicate, :value]
|
139
|
-
end
|
140
|
-
|
141
|
-
query.execute(graph).each do |solution|
|
142
|
-
predicate = solution.predicate
|
143
|
-
value = solution.value
|
144
|
-
|
145
|
-
name, config = self.class.config_for_predicate(predicate)
|
146
|
-
next unless config
|
147
|
-
type = config.type
|
148
|
-
behaviors = config.behaviors
|
149
|
-
next unless type and behaviors
|
150
|
-
field_map[name] ||= {:values => [], :type => type, :behaviors => behaviors}
|
151
|
-
field_map[name][:values] << value.to_s
|
152
|
-
end
|
153
|
-
field_map
|
154
|
-
end
|
155
107
|
end
|
156
108
|
end
|
157
109
|
|
@@ -161,13 +161,9 @@ module ActiveFedora
|
|
161
161
|
end
|
162
162
|
|
163
163
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
self.class.config.each { |k, v| return v if v.predicate == term}
|
168
|
-
else
|
169
|
-
self.class.config[term.to_sym]
|
170
|
-
end
|
164
|
+
# In Rails 4 you can do "delegate :config_for_term_or_uri, :class", but in rails 3 it breaks.
|
165
|
+
def config_for_term_or_uri(val)
|
166
|
+
self.class.config_for_term_or_uri(val)
|
171
167
|
end
|
172
168
|
|
173
169
|
# @param [Symbol, RDF::URI] term predicate the predicate to insert into the graph
|
@@ -300,6 +296,15 @@ module ActiveFedora
|
|
300
296
|
@rdf_type
|
301
297
|
end
|
302
298
|
|
299
|
+
def config_for_term_or_uri(term)
|
300
|
+
case term
|
301
|
+
when RDF::URI
|
302
|
+
config.each { |k, v| return v if v.predicate == term}
|
303
|
+
else
|
304
|
+
config[term.to_sym]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
303
308
|
def config_for_predicate(predicate)
|
304
309
|
config.each do |term, value|
|
305
310
|
return term, value if value.predicate == predicate
|
@@ -75,33 +75,42 @@ module ActiveFedora
|
|
75
75
|
|
76
76
|
# Get the values off of the rdf nodes this proxy targets
|
77
77
|
def load_values
|
78
|
-
values =
|
78
|
+
values = parent.query(subject, options.predicate)
|
79
|
+
.map {|solution| coerce_to_primitive_type(solution.value)}
|
80
|
+
.select { |v| correct_rdf_type_for_term?(v)}
|
81
|
+
.map {|v| coerce_to_rdf_type(v) }
|
79
82
|
|
80
|
-
parent.query(subject, options.predicate).each do |solution|
|
81
|
-
v = solution.value
|
82
|
-
v = v.to_s if v.is_a? RDF::Literal
|
83
|
-
if options.type == :date
|
84
|
-
v = Date.parse(v)
|
85
|
-
end
|
86
|
-
# If the user provided options[:class_name], we should query to make sure this
|
87
|
-
# potential solution is of the right RDF.type
|
88
|
-
if options.class_name
|
89
|
-
klass = class_from_rdf_type(v)
|
90
|
-
values << v if klass == ActiveFedora.class_from_string(options.class_name, parent.class)
|
91
|
-
else
|
92
|
-
values << v
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
if options.class_name
|
97
|
-
values = values.map{ |found_subject| class_from_rdf_type(found_subject).new(parent.graph, found_subject)}
|
98
|
-
end
|
99
|
-
|
100
83
|
options.multivalue ? values : values.first
|
101
84
|
end
|
102
85
|
|
103
86
|
private
|
104
87
|
|
88
|
+
def coerce_to_rdf_type(v)
|
89
|
+
if options.class_name
|
90
|
+
class_from_rdf_type(v).new(parent.graph, v)
|
91
|
+
else
|
92
|
+
v
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# If the user provided options[:class_name], we should query to make sure this
|
97
|
+
# potential solution is of the right RDF.type
|
98
|
+
def correct_rdf_type_for_term?(v)
|
99
|
+
!options.class_name || (options.class_name && class_from_rdf_type(v) == ActiveFedora.class_from_string(options.class_name, parent.class))
|
100
|
+
end
|
101
|
+
|
102
|
+
def coerce_to_primitive_type(v)
|
103
|
+
v = v.to_s if v.is_a? RDF::Literal
|
104
|
+
case options.type
|
105
|
+
when :date
|
106
|
+
Date.parse(v)
|
107
|
+
when :integer
|
108
|
+
v.to_i
|
109
|
+
else
|
110
|
+
v
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
105
114
|
def target_class
|
106
115
|
parent.target_class(options)
|
107
116
|
end
|
@@ -41,10 +41,37 @@ module ActiveFedora
|
|
41
41
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
42
42
|
#
|
43
43
|
def reflect_on_association(association)
|
44
|
-
reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
|
44
|
+
val = reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
|
45
|
+
unless val
|
46
|
+
# When a has_many is paired with a has_and_belongs_to_many the assocation will have a plural name
|
47
|
+
association = association.to_s.pluralize.to_sym
|
48
|
+
val = reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
|
49
|
+
end
|
50
|
+
val
|
51
|
+
end
|
52
|
+
|
53
|
+
def reflect_on_all_autosave_associations
|
54
|
+
reflections.values.select { |reflection| reflection.options[:autosave] }
|
45
55
|
end
|
46
56
|
|
57
|
+
|
47
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
|
48
75
|
|
49
76
|
# Returns the target association's class.
|
50
77
|
#
|
@@ -64,6 +91,7 @@ module ActiveFedora
|
|
64
91
|
|
65
92
|
def initialize(macro, name, options, active_fedora)
|
66
93
|
@macro, @name, @options, @active_fedora = macro, name, options, active_fedora
|
94
|
+
@automatic_inverse_of = nil
|
67
95
|
end
|
68
96
|
|
69
97
|
# Returns a new, unsaved instance of the associated class. +options+ will
|
@@ -72,17 +100,6 @@ module ActiveFedora
|
|
72
100
|
klass.new(*options)
|
73
101
|
end
|
74
102
|
|
75
|
-
# Returns the name of the macro.
|
76
|
-
#
|
77
|
-
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
78
|
-
attr_reader :name
|
79
|
-
|
80
|
-
# Returns the hash of options used for the macro.
|
81
|
-
#
|
82
|
-
# <tt>has_many :clients</tt> returns +{}+
|
83
|
-
attr_reader :options
|
84
|
-
|
85
|
-
attr_reader :macro
|
86
103
|
|
87
104
|
# Returns the class name for the macro.
|
88
105
|
#
|
@@ -98,6 +115,11 @@ module ActiveFedora
|
|
98
115
|
@collection
|
99
116
|
end
|
100
117
|
|
118
|
+
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
119
|
+
def belongs_to?
|
120
|
+
macro == :belongs_to
|
121
|
+
end
|
122
|
+
|
101
123
|
private
|
102
124
|
def derive_class_name
|
103
125
|
class_name = name.to_s.camelize
|
@@ -105,33 +127,154 @@ module ActiveFedora
|
|
105
127
|
class_name
|
106
128
|
end
|
107
129
|
|
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
|
108
139
|
end
|
109
140
|
|
110
141
|
# Holds all the meta-data about an association as it was specified in the
|
111
142
|
# Active Record class.
|
112
143
|
class AssociationReflection < MacroReflection #:nodoc:
|
113
144
|
|
114
|
-
def initialize(macro, name, options,
|
145
|
+
def initialize(macro, name, options, active_fedora)
|
115
146
|
super
|
116
147
|
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
117
148
|
end
|
118
149
|
|
119
|
-
|
120
|
-
|
150
|
+
|
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)
|
121
155
|
end
|
122
|
-
|
156
|
+
|
123
157
|
# Creates a new instance of the associated class, and immediately saves it
|
124
|
-
# with
|
158
|
+
# with ActiveFedora::Base#save. +options+ will be passed to the class's
|
125
159
|
# creation method. Returns the newly created object.
|
126
160
|
def create_association(*options)
|
127
161
|
klass.create(*options)
|
128
162
|
end
|
129
163
|
|
164
|
+
def foreign_key
|
165
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
166
|
+
end
|
167
|
+
|
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
|
173
|
+
|
174
|
+
alias :source_macro :macro
|
175
|
+
|
176
|
+
def inverse_of
|
177
|
+
return unless inverse_name
|
178
|
+
|
179
|
+
@inverse_of ||= klass.reflect_on_association inverse_name
|
180
|
+
end
|
181
|
+
|
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
|
195
|
+
|
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
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
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
|
+
|
130
211
|
private
|
131
212
|
|
132
|
-
def
|
133
|
-
|
134
|
-
|
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
|
221
|
+
end
|
222
|
+
|
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)
|
234
|
+
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
|
253
|
+
end
|
254
|
+
|
255
|
+
false
|
256
|
+
end
|
257
|
+
|
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
|
274
|
+
|
275
|
+
# def derive_primary_key_name
|
276
|
+
# 'pid'
|
277
|
+
# end
|
135
278
|
|
136
279
|
end
|
137
280
|
end
|
@@ -1,18 +1,40 @@
|
|
1
1
|
module ActiveFedora
|
2
2
|
class Relation
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Delegation
|
5
|
+
|
5
6
|
|
6
7
|
attr_reader :loaded
|
8
|
+
attr_accessor :default_scoped
|
7
9
|
alias :loaded? :loaded
|
8
|
-
|
9
|
-
attr_accessor :limit_value, :where_values, :order_values
|
10
10
|
|
11
|
-
|
11
|
+
attr_accessor :values, :klass
|
12
|
+
|
13
|
+
def initialize(klass, values = {})
|
12
14
|
@klass = klass
|
13
15
|
@loaded = false
|
14
|
-
|
15
|
-
|
16
|
+
@values = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Tries to create a new record with the same scoped attributes
|
20
|
+
# defined in the relation. Returns the initialized object if validation fails.
|
21
|
+
#
|
22
|
+
# Expects arguments in the same format as +Base.create+.
|
23
|
+
#
|
24
|
+
# ==== Examples
|
25
|
+
# users = User.where(name: 'Oscar')
|
26
|
+
# users.create # #<User id: 3, name: "oscar", ...>
|
27
|
+
#
|
28
|
+
# users.create(name: 'fxn')
|
29
|
+
# users.create # #<User id: 4, name: "fxn", ...>
|
30
|
+
#
|
31
|
+
# users.create { |user| user.name = 'tenderlove' }
|
32
|
+
# # #<User id: 5, name: "tenderlove", ...>
|
33
|
+
#
|
34
|
+
# users.create(name: nil) # validation on name
|
35
|
+
# # #<User id: nil, name: nil, ...>
|
36
|
+
def create(*args, &block)
|
37
|
+
scoping { @klass.create(*args, &block) }
|
16
38
|
end
|
17
39
|
|
18
40
|
def reset
|
@@ -21,34 +43,6 @@ module ActiveFedora
|
|
21
43
|
self
|
22
44
|
end
|
23
45
|
|
24
|
-
|
25
|
-
# Returns the first records that was found.
|
26
|
-
#
|
27
|
-
# @example
|
28
|
-
# Person.where(name_t: 'Jones').first
|
29
|
-
# => #<Person @id="foo:123" @name='Jones' ... >
|
30
|
-
def first
|
31
|
-
if loaded?
|
32
|
-
@records.first
|
33
|
-
else
|
34
|
-
@first ||= limit(1).to_a[0]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns the last record sorted by id. ID was chosen because this mimics
|
39
|
-
# how ActiveRecord would achieve the same behavior.
|
40
|
-
#
|
41
|
-
# @example
|
42
|
-
# Person.where(name_t: 'Jones').last
|
43
|
-
# => #<Person @id="foo:123" @name='Jones' ... >
|
44
|
-
def last
|
45
|
-
if loaded?
|
46
|
-
@records.last
|
47
|
-
else
|
48
|
-
@last ||= order('id desc').limit(1).to_a[0]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
46
|
# Limits the number of returned records to the value specified
|
53
47
|
#
|
54
48
|
# @option [Integer] value the number of records to return
|
@@ -91,102 +85,17 @@ module ActiveFedora
|
|
91
85
|
relation
|
92
86
|
end
|
93
87
|
|
94
|
-
extend Deprecation
|
95
|
-
self.deprecation_horizon = 'active-fedora 7.0.0'
|
96
|
-
|
97
|
-
# Returns an Array of objects of the Class that +find+ is being
|
98
|
-
# called on
|
99
|
-
#
|
100
|
-
# @param[String,Symbol,Hash] args either a pid or :all or a hash of conditions
|
101
|
-
# @option args [Integer] :rows when :all is passed, the maximum number of rows to load from solr
|
102
|
-
# @option args [Boolean] :cast when true, examine the model and cast it to the first known cModel
|
103
|
-
def find(*args)
|
104
|
-
return to_a.find { |*block_args| yield(*block_args) } if block_given?
|
105
|
-
options = args.extract_options!
|
106
|
-
options = options.dup
|
107
|
-
|
108
|
-
cast = if @klass == ActiveFedora::Base && !options.has_key?(:cast)
|
109
|
-
Deprecation.warn(Relation, "find's cast option will default to true in ActiveFedora 7.0.0. To preserve existing behavior send parameter: `:cast=> false`", caller)
|
110
|
-
false
|
111
|
-
else
|
112
|
-
options.delete(:cast)
|
113
|
-
end
|
114
|
-
if options[:sort]
|
115
|
-
# Deprecate sort sometime?
|
116
|
-
sort = options.delete(:sort)
|
117
|
-
options[:order] ||= sort if sort.present?
|
118
|
-
end
|
119
|
-
|
120
|
-
if options.present?
|
121
|
-
options = args.first unless args.empty?
|
122
|
-
options = {conditions: options}
|
123
|
-
apply_finder_options(options).all
|
124
|
-
else
|
125
|
-
case args.first
|
126
|
-
when :first, :last, :all
|
127
|
-
Deprecation.warn Relation, "ActiveFedora::Base.find(#{args.first.inspect}) is deprecated. Use ActiveFedora::Base.#{args.first} instead. This option will be removed in ActiveFedora 7", caller
|
128
|
-
send(args.first)
|
129
|
-
else
|
130
|
-
find_with_ids(args, cast)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def find_with_ids(ids, cast)
|
136
|
-
expects_array = ids.first.kind_of?(Array)
|
137
|
-
return ids.first if expects_array && ids.first.empty?
|
138
|
-
|
139
|
-
ids = ids.flatten.compact.uniq
|
140
|
-
|
141
|
-
case ids.size
|
142
|
-
when 0
|
143
|
-
raise ArgumentError, "Couldn't find #{@klass.name} without an ID"
|
144
|
-
when 1
|
145
|
-
result = @klass.find_one(ids.first, cast)
|
146
|
-
expects_array ? [ result ] : result
|
147
|
-
else
|
148
|
-
find_some(ids, cast)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def find_some(ids, cast)
|
153
|
-
ids.map{|id| @klass.find_one(id, cast)}
|
154
|
-
end
|
155
|
-
|
156
|
-
# A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
|
157
|
-
# same arguments to this method as you can to <tt>find(:all)</tt>.
|
158
|
-
def all(*args)
|
159
|
-
args.any? ? apply_finder_options(args.first).to_a : to_a
|
160
|
-
end
|
161
|
-
|
162
|
-
|
163
|
-
|
164
88
|
def to_a
|
165
89
|
return @records if loaded?
|
166
90
|
args = @klass == ActiveFedora::Base ? {:cast=>true} : {}
|
167
|
-
args[:rows] =
|
168
|
-
args[:sort] =
|
91
|
+
args[:rows] = limit_value if limit_value
|
92
|
+
args[:sort] = order_values if order_values
|
169
93
|
|
170
|
-
|
171
|
-
@records = @klass.to_enum(:find_each, query, args).to_a
|
94
|
+
@records = to_enum(:find_each, where_values, args).to_a
|
172
95
|
|
173
96
|
@records
|
174
97
|
end
|
175
98
|
|
176
|
-
# Get a count of the number of objects from solr
|
177
|
-
# Takes :conditions as an argument
|
178
|
-
def count(*args)
|
179
|
-
return apply_finder_options(args.first).count if args.any?
|
180
|
-
opts = {}
|
181
|
-
opts[:rows] = @limit_value if @limit_value
|
182
|
-
opts[:sort] = @order_values if @order_values
|
183
|
-
|
184
|
-
query = @where_values.present? ? @where_values : {}
|
185
|
-
@klass.calculate :count, query, opts
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
|
190
99
|
|
191
100
|
def ==(other)
|
192
101
|
case other
|
@@ -231,13 +140,7 @@ module ActiveFedora
|
|
231
140
|
if conditions
|
232
141
|
where(conditions).destroy_all
|
233
142
|
else
|
234
|
-
to_a.each {|object|
|
235
|
-
begin
|
236
|
-
object.destroy
|
237
|
-
rescue ActiveFedora::ObjectNotFoundError
|
238
|
-
logger.error "When trying to destroy #{object.pid}, encountered an ObjectNotFoundError. Solr may be out of sync with Fedora"
|
239
|
-
end
|
240
|
-
}.tap { reset }.size
|
143
|
+
to_a.each {|object| object.destroy }.tap { reset }.size
|
241
144
|
end
|
242
145
|
end
|
243
146
|
|