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.
Files changed (215) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -5
  4. data/CONTRIBUTING.md +2 -0
  5. data/Gemfile +0 -2
  6. data/History.txt +2 -32
  7. data/README.md +143 -0
  8. data/Rakefile +5 -7
  9. data/active-fedora.gemspec +9 -9
  10. data/gemfiles/rails3.gemfile +11 -0
  11. data/gemfiles/rails4.gemfile +10 -0
  12. data/lib/active_fedora.rb +31 -4
  13. data/lib/active_fedora/association_relation.rb +18 -0
  14. data/lib/active_fedora/associations.rb +38 -171
  15. data/lib/active_fedora/associations/association.rb +163 -0
  16. data/lib/active_fedora/associations/association_scope.rb +39 -0
  17. data/lib/active_fedora/associations/belongs_to_association.rb +47 -25
  18. data/lib/active_fedora/associations/builder/association.rb +55 -0
  19. data/lib/active_fedora/associations/builder/belongs_to.rb +100 -0
  20. data/lib/active_fedora/associations/builder/collection_association.rb +56 -0
  21. data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +30 -0
  22. data/lib/active_fedora/associations/builder/has_many.rb +63 -0
  23. data/lib/active_fedora/associations/builder/singular_association.rb +32 -0
  24. data/lib/active_fedora/associations/{association_collection.rb → collection_association.rb} +203 -53
  25. data/lib/active_fedora/associations/collection_proxy.rb +862 -0
  26. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +35 -25
  27. data/lib/active_fedora/associations/has_many_association.rb +36 -11
  28. data/lib/active_fedora/associations/singular_association.rb +62 -0
  29. data/lib/active_fedora/attributes.rb +43 -139
  30. data/lib/active_fedora/autosave_association.rb +317 -0
  31. data/lib/active_fedora/base.rb +10 -327
  32. data/lib/active_fedora/callbacks.rb +1 -3
  33. data/lib/active_fedora/content_model.rb +16 -0
  34. data/lib/active_fedora/core.rb +151 -0
  35. data/lib/active_fedora/datastream_attribute.rb +76 -0
  36. data/lib/active_fedora/datastream_hash.rb +8 -13
  37. data/lib/active_fedora/datastreams.rb +39 -26
  38. data/lib/active_fedora/digital_object.rb +2 -2
  39. data/lib/active_fedora/fedora_attributes.rb +45 -0
  40. data/lib/active_fedora/fixture_loader.rb +1 -1
  41. data/lib/active_fedora/indexing.rb +6 -1
  42. data/lib/active_fedora/model.rb +0 -17
  43. data/lib/active_fedora/nested_attributes.rb +2 -2
  44. data/lib/active_fedora/null_relation.rb +7 -0
  45. data/lib/active_fedora/om_datastream.rb +3 -4
  46. data/lib/active_fedora/persistence.rb +41 -29
  47. data/lib/active_fedora/querying.rb +2 -163
  48. data/lib/active_fedora/rdf.rb +1 -0
  49. data/lib/active_fedora/rdf/indexing.rb +67 -0
  50. data/lib/active_fedora/rdf_datastream.rb +2 -50
  51. data/lib/active_fedora/rdf_node.rb +12 -7
  52. data/lib/active_fedora/rdf_node/term_proxy.rb +30 -21
  53. data/lib/active_fedora/rdfxml_rdf_datastream.rb +1 -1
  54. data/lib/active_fedora/reflection.rb +163 -20
  55. data/lib/active_fedora/relation.rb +33 -130
  56. data/lib/active_fedora/relation/calculations.rb +19 -0
  57. data/lib/active_fedora/relation/delegation.rb +22 -0
  58. data/lib/active_fedora/relation/finder_methods.rb +247 -0
  59. data/lib/active_fedora/relation/merger.rb +22 -0
  60. data/lib/active_fedora/relation/query_methods.rb +58 -0
  61. data/lib/active_fedora/relation/spawn_methods.rb +46 -0
  62. data/lib/active_fedora/relationship_graph.rb +0 -2
  63. data/lib/active_fedora/rels_ext_datastream.rb +1 -4
  64. data/lib/active_fedora/rubydora_connection.rb +1 -1
  65. data/lib/active_fedora/scoping.rb +20 -0
  66. data/lib/active_fedora/scoping/default.rb +38 -0
  67. data/lib/active_fedora/scoping/named.rb +32 -0
  68. data/lib/active_fedora/semantic_node.rb +54 -106
  69. data/lib/active_fedora/serialization.rb +19 -0
  70. data/lib/active_fedora/sharding.rb +58 -0
  71. data/lib/active_fedora/solr_digital_object.rb +15 -5
  72. data/lib/active_fedora/solr_instance_loader.rb +1 -1
  73. data/lib/active_fedora/solr_service.rb +1 -1
  74. data/lib/active_fedora/unsaved_digital_object.rb +6 -4
  75. data/lib/active_fedora/version.rb +1 -1
  76. data/lib/tasks/active_fedora.rake +3 -0
  77. data/lib/tasks/active_fedora_dev.rake +6 -5
  78. data/spec/config_helper.rb +14 -14
  79. data/spec/integration/associations_spec.rb +168 -455
  80. data/spec/integration/attributes_spec.rb +12 -11
  81. data/spec/integration/auditable_spec.rb +11 -11
  82. data/spec/integration/autosave_association_spec.rb +25 -0
  83. data/spec/integration/base_spec.rb +163 -163
  84. data/spec/integration/belongs_to_association_spec.rb +166 -0
  85. data/spec/integration/bug_spec.rb +7 -7
  86. data/spec/integration/collection_association_spec.rb +58 -0
  87. data/spec/integration/complex_rdf_datastream_spec.rb +88 -88
  88. data/spec/integration/datastream_collections_spec.rb +69 -69
  89. data/spec/integration/datastream_spec.rb +43 -43
  90. data/spec/integration/datastreams_spec.rb +63 -63
  91. data/spec/integration/delete_all_spec.rb +46 -39
  92. data/spec/integration/fedora_solr_sync_spec.rb +5 -5
  93. data/spec/integration/field_to_solr_name_spec.rb +34 -0
  94. data/spec/integration/full_featured_model_spec.rb +100 -101
  95. data/spec/integration/has_and_belongs_to_many_associations_spec.rb +341 -0
  96. data/spec/integration/has_many_associations_spec.rb +172 -24
  97. data/spec/integration/json_serialization_spec.rb +31 -0
  98. data/spec/integration/load_from_solr_spec.rb +48 -0
  99. data/spec/integration/model_spec.rb +35 -40
  100. data/spec/integration/nested_attribute_spec.rb +42 -43
  101. data/spec/integration/ntriples_datastream_spec.rb +131 -113
  102. data/spec/integration/om_datastream_spec.rb +67 -67
  103. data/spec/integration/persistence_spec.rb +7 -7
  104. data/spec/integration/rdf_nested_attributes_spec.rb +56 -56
  105. data/spec/integration/relation_delegation_spec.rb +26 -25
  106. data/spec/integration/relation_spec.rb +42 -0
  107. data/spec/integration/rels_ext_datastream_spec.rb +20 -20
  108. data/spec/integration/scoped_query_spec.rb +61 -51
  109. data/spec/integration/solr_instance_loader_spec.rb +5 -5
  110. data/spec/integration/solr_service_spec.rb +46 -46
  111. data/spec/samples/hydra-mods_article_datastream.rb +334 -334
  112. data/spec/samples/hydra-rights_metadata_datastream.rb +57 -57
  113. data/spec/samples/marpa-dc_datastream.rb +17 -17
  114. data/spec/samples/models/audio_record.rb +16 -16
  115. data/spec/samples/models/image.rb +2 -2
  116. data/spec/samples/models/mods_article.rb +5 -5
  117. data/spec/samples/models/oral_history.rb +18 -18
  118. data/spec/samples/models/seminar.rb +24 -24
  119. data/spec/samples/models/seminar_audio_file.rb +17 -17
  120. data/spec/samples/oral_history_sample_model.rb +21 -21
  121. data/spec/samples/special_thing.rb +14 -14
  122. data/spec/spec_helper.rb +11 -7
  123. data/spec/support/an_active_model.rb +2 -8
  124. data/spec/support/freeze_mocks.rb +12 -0
  125. data/spec/support/mock_fedora.rb +17 -16
  126. data/spec/unit/active_fedora_spec.rb +58 -60
  127. data/spec/unit/attributes_spec.rb +314 -0
  128. data/spec/unit/base_active_model_spec.rb +28 -27
  129. data/spec/unit/base_cma_spec.rb +5 -5
  130. data/spec/unit/base_datastream_management_spec.rb +27 -27
  131. data/spec/unit/base_extra_spec.rb +76 -48
  132. data/spec/unit/base_spec.rb +277 -348
  133. data/spec/unit/callback_spec.rb +18 -19
  134. data/spec/unit/code_configurator_spec.rb +17 -17
  135. data/spec/unit/config_spec.rb +8 -16
  136. data/spec/unit/content_model_spec.rb +79 -60
  137. data/spec/unit/datastream_collections_spec.rb +229 -229
  138. data/spec/unit/datastream_spec.rb +51 -63
  139. data/spec/unit/datastreams_spec.rb +87 -87
  140. data/spec/unit/file_configurator_spec.rb +217 -217
  141. data/spec/unit/has_and_belongs_to_many_collection_spec.rb +44 -25
  142. data/spec/unit/has_many_collection_spec.rb +26 -8
  143. data/spec/unit/inheritance_spec.rb +13 -12
  144. data/spec/unit/model_spec.rb +39 -45
  145. data/spec/unit/nom_datastream_spec.rb +15 -15
  146. data/spec/unit/ntriples_datastream_spec.rb +123 -118
  147. data/spec/unit/om_datastream_spec.rb +227 -233
  148. data/spec/unit/persistence_spec.rb +34 -15
  149. data/spec/unit/predicates_spec.rb +73 -73
  150. data/spec/unit/property_spec.rb +17 -9
  151. data/spec/unit/qualified_dublin_core_datastream_spec.rb +33 -33
  152. data/spec/unit/query_spec.rb +222 -198
  153. data/spec/unit/rdf_datastream_spec.rb +21 -28
  154. data/spec/unit/rdf_list_nested_attributes_spec.rb +34 -34
  155. data/spec/unit/rdf_list_spec.rb +65 -64
  156. data/spec/unit/rdf_node_spec.rb +7 -7
  157. data/spec/unit/rdf_xml_writer_spec.rb +10 -10
  158. data/spec/unit/rdfxml_rdf_datastream_spec.rb +27 -27
  159. data/spec/unit/relationship_graph_spec.rb +51 -51
  160. data/spec/unit/rels_ext_datastream_spec.rb +68 -74
  161. data/spec/unit/rspec_matchers/belong_to_associated_active_fedora_object_matcher_spec.rb +15 -15
  162. data/spec/unit/rspec_matchers/have_many_associated_active_fedora_objects_matcher_spec.rb +15 -15
  163. data/spec/unit/rspec_matchers/have_predicate_matcher_spec.rb +15 -15
  164. data/spec/unit/rspec_matchers/match_fedora_datastream_matcher_spec.rb +12 -12
  165. data/spec/unit/rubydora_connection_spec.rb +5 -5
  166. data/spec/unit/semantic_node_spec.rb +48 -107
  167. data/spec/unit/serializers_spec.rb +4 -4
  168. data/spec/unit/service_definitions_spec.rb +26 -26
  169. data/spec/unit/simple_datastream_spec.rb +17 -17
  170. data/spec/unit/solr_config_options_spec.rb +29 -28
  171. data/spec/unit/solr_digital_object_spec.rb +17 -25
  172. data/spec/unit/solr_service_spec.rb +95 -82
  173. data/spec/unit/unsaved_digital_object_spec.rb +24 -23
  174. data/spec/unit/validations_spec.rb +21 -21
  175. metadata +110 -159
  176. data/.rspec +0 -1
  177. data/.rubocop.yml +0 -1
  178. data/.rubocop_todo.yml +0 -938
  179. data/CONSOLE_GETTING_STARTED.textile +0 -1
  180. data/NOKOGIRI_DATASTREAMS.textile +0 -1
  181. data/README.textile +0 -116
  182. data/lib/active_fedora/associations/association_proxy.rb +0 -178
  183. data/lib/active_fedora/delegating.rb +0 -72
  184. data/lib/active_fedora/nokogiri_datastream.rb +0 -11
  185. data/spec/integration/delegating_spec.rb +0 -59
  186. data/spec/rails3_test_app/.gitignore +0 -4
  187. data/spec/rails3_test_app/.rspec +0 -1
  188. data/spec/rails3_test_app/Gemfile +0 -40
  189. data/spec/rails3_test_app/Rakefile +0 -7
  190. data/spec/rails3_test_app/app/controllers/application_controller.rb +0 -3
  191. data/spec/rails3_test_app/app/helpers/application_helper.rb +0 -2
  192. data/spec/rails3_test_app/app/views/layouts/application.html.erb +0 -14
  193. data/spec/rails3_test_app/config.ru +0 -4
  194. data/spec/rails3_test_app/config/application.rb +0 -42
  195. data/spec/rails3_test_app/config/boot.rb +0 -6
  196. data/spec/rails3_test_app/config/database.yml +0 -22
  197. data/spec/rails3_test_app/config/environment.rb +0 -5
  198. data/spec/rails3_test_app/config/environments/development.rb +0 -25
  199. data/spec/rails3_test_app/config/environments/production.rb +0 -49
  200. data/spec/rails3_test_app/config/environments/test.rb +0 -35
  201. data/spec/rails3_test_app/config/initializers/backtrace_silencers.rb +0 -7
  202. data/spec/rails3_test_app/config/initializers/inflections.rb +0 -10
  203. data/spec/rails3_test_app/config/initializers/mime_types.rb +0 -5
  204. data/spec/rails3_test_app/config/initializers/secret_token.rb +0 -7
  205. data/spec/rails3_test_app/config/initializers/session_store.rb +0 -8
  206. data/spec/rails3_test_app/config/locales/en.yml +0 -5
  207. data/spec/rails3_test_app/config/routes.rb +0 -58
  208. data/spec/rails3_test_app/db/seeds.rb +0 -7
  209. data/spec/rails3_test_app/run_tests +0 -3
  210. data/spec/rails3_test_app/script/rails +0 -6
  211. data/spec/rails3_test_app/spec/spec_helper.rb +0 -27
  212. data/spec/rails3_test_app/spec/unit/rails_3_init.rb +0 -15
  213. data/spec/unit/association_proxy_spec.rb +0 -12
  214. data/spec/unit/base_delegate_spec.rb +0 -197
  215. data/spec/unit/base_delegate_to_spec.rb +0 -73
@@ -1,11 +1,34 @@
1
1
  module ActiveFedora
2
2
  # = Active Fedora Has And Belongs To Many Association
3
3
  module Associations
4
- class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
4
+ class HasAndBelongsToManyAssociation < CollectionAssociation #:nodoc:
5
5
  def initialize(owner, reflection)
6
6
  super
7
7
  end
8
8
 
9
+ def insert_record(record, force = true, validate = true)
10
+ if record.new_record?
11
+ if force
12
+ record.save!
13
+ else
14
+ return false unless record.save(:validate => validate)
15
+ end
16
+ end
17
+
18
+ ### TODO save relationship
19
+ @owner.add_relationship(@reflection.options[:property], record)
20
+
21
+ if @owner.new_record? and @reflection.options[:inverse_of]
22
+ logger.warn("has_and_belongs_to_many #{@reflection.inspect} is cowardly refusing to insert the inverse relationship into #{record}, because #{@owner} is not persisted yet.")
23
+ elsif @reflection.options[:inverse_of]
24
+ record.add_relationship(@reflection.options[:inverse_of], @owner)
25
+ record.save
26
+ end
27
+
28
+ return true
29
+ end
30
+
31
+
9
32
  def find_target
10
33
  page_size = @reflection.options[:solr_page_size]
11
34
  page_size ||= 200
@@ -19,35 +42,22 @@ module ActiveFedora
19
42
  return ActiveFedora::SolrService.reify_solr_results(solr_result)
20
43
  end
21
44
 
45
+ # In a HABTM, just look in the rels-ext, no need to run a count query from solr.
46
+ def count(options = {})
47
+ @owner.ids_for_outbound(@reflection.options[:property]).size
48
+ end
49
+
50
+ def first
51
+ load_target.first
52
+ end
53
+
22
54
  protected
23
55
 
24
56
  def count_records
25
57
  load_target.size
26
58
  end
27
59
 
28
- def insert_record(record, force = true, validate = true)
29
- if record.new_record?
30
- if force
31
- record.save!
32
- else
33
- return false unless record.save(:validate => validate)
34
- end
35
- end
36
-
37
- ### TODO save relationship
38
- @owner.add_relationship(@reflection.options[:property], record)
39
-
40
- if @owner.new_record? and @reflection.options[:inverse_of]
41
- logger.warn("has_and_belongs_to_many #{@reflection.inspect} is cowardly refusing to insert the inverse relationship into #{record}, because #{@owner} is not persisted yet.")
42
- elsif @reflection.options[:inverse_of]
43
- record.add_relationship(@reflection.options[:inverse_of], @owner)
44
- record.save
45
- end
46
-
47
- return true
48
- end
49
-
50
- def delete_records(records)
60
+ def delete_records(records, method)
51
61
  records.each do |r|
52
62
  @owner.remove_relationship(@reflection.options[:property], r)
53
63
 
@@ -57,7 +67,7 @@ module ActiveFedora
57
67
  # which is what we should have done. Now we need a way to look up the
58
68
  # reflection by predicate
59
69
  name = r.class.reflection_name_for_predicate(@reflection.options[:inverse_of])
60
- r.send(name).reset
70
+ r.association(name).reset
61
71
  r.save
62
72
  end
63
73
  end
@@ -1,6 +1,6 @@
1
1
  module ActiveFedora
2
2
  module Associations
3
- class HasManyAssociation < AssociationCollection #:nodoc:
3
+ class HasManyAssociation < CollectionAssociation #:nodoc:
4
4
  def initialize(owner, reflection)
5
5
  super
6
6
  end
@@ -18,8 +18,6 @@ module ActiveFedora
18
18
  @target.size
19
19
  else
20
20
  @reflection.klass.count(:conditions => @counter_query)
21
- # load_target
22
- # @target.size
23
21
  end
24
22
 
25
23
  # If there's nothing in the database and @target has no new records
@@ -31,21 +29,48 @@ module ActiveFedora
31
29
  end
32
30
 
33
31
  def insert_record(record, force = false, validate = true)
34
- if @owner.new_record?
35
- logger.warn("has_many #{@reflection.inspect} is cowardly refusing to insert a relationship into #{record}, because #{@owner} is not persisted yet.")
36
- return true
37
- end
38
32
  set_belongs_to_association_for(record)
39
- #force ? record.save! : record.save(:validate => validate)
40
33
  record.save
41
34
  end
42
35
 
43
36
  protected
44
37
 
45
38
  # Deletes the records according to the <tt>:dependent</tt> option.
46
- def delete_records(records)
47
- records.each do |r|
48
- r.remove_relationship(find_predicate, @owner)
39
+ def delete_records(records, method)
40
+ if method == :destroy
41
+ records.each { |r| r.destroy }
42
+ #update_counter(-records.length) unless inverse_updates_counter_cache?
43
+ else
44
+ # Find all the records that point to this and nullify them
45
+ # keys = records.map { |r| r[reflection.association_primary_key] }
46
+ # scope = scoped.where(reflection.association_primary_key => keys)
47
+
48
+ if method == :delete_all
49
+ raise "Not Implemented"
50
+ #update_counter(-scope.delete_all)
51
+ else
52
+
53
+
54
+ if reflection.inverse_of # Can't get an inverse when class_name: 'ActiveFedora::Base' is supplied
55
+ inverse = reflection.inverse_of.name
56
+ records.each do |record|
57
+ if record.persisted?
58
+ record.reload
59
+ assoc = record.association(inverse)
60
+ if assoc.reflection.collection?
61
+ # Remove from a has_and_belongs_to_many
62
+ record.association(inverse).delete(@owner)
63
+ else
64
+ # Remove from a belongs_to
65
+ record.association(inverse).id_writer(nil)
66
+ end
67
+ record.save!
68
+ end
69
+ end
70
+ end
71
+
72
+ #update_counter(-scope.update_all(reflection.foreign_key => nil))
73
+ end
49
74
  end
50
75
  end
51
76
 
@@ -0,0 +1,62 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class SingularAssociation < Association #:nodoc:
4
+
5
+ # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
6
+ def reader(force_reload = false)
7
+ if force_reload
8
+ klass.uncached { reload }
9
+ elsif !loaded? || stale_target?
10
+ reload
11
+ end
12
+ target
13
+ end
14
+
15
+ # Implements the writer method, e.g. foo.items= for Foo.has_many :items
16
+ def writer(record)
17
+ replace(record)
18
+ end
19
+
20
+
21
+ def create(attributes = {})
22
+ new_record(:create, attributes)
23
+ end
24
+
25
+ def create!(attributes = {})
26
+ build(attributes).tap { |record| record.save! }
27
+ end
28
+
29
+ def build(attributes = {})
30
+ new_record(:build, attributes)
31
+ end
32
+
33
+ private
34
+
35
+ def find_target
36
+ scoped.first.tap { |record| set_inverse_instance(record) }
37
+ end
38
+
39
+ # Implemented by subclasses
40
+ def replace(record)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def set_new_record(record)
45
+ replace(record)
46
+ end
47
+
48
+ def check_record(record)
49
+ record = record.target if Association === record
50
+ raise_on_type_mismatch(record) if record
51
+ record
52
+ end
53
+
54
+ def new_record(method, attributes)
55
+ attributes = {} #scoped.scope_for_create.merge(attributes || {})
56
+ record = @reflection.send("#{method}_association", attributes)
57
+ set_new_record(record)
58
+ record
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,9 +2,7 @@ module ActiveFedora
2
2
  module Attributes
3
3
  extend ActiveSupport::Concern
4
4
  extend ActiveSupport::Autoload
5
- extend Deprecation
6
- self.deprecation_horizon = 'active-fedora 7.0.0'
7
-
5
+ include ActiveModel::Dirty
8
6
 
9
7
  autoload :Serializers
10
8
 
@@ -19,14 +17,19 @@ module ActiveFedora
19
17
 
20
18
  def attributes=(properties)
21
19
  properties.each do |k, v|
22
- respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
20
+ respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "#{self.class} does not have an attribute `#{k}'")
23
21
  end
24
22
  end
25
23
 
24
+ def attributes
25
+ self.class.defined_attributes.keys.each_with_object({"id" => id}) {|key, hash| hash[key] = self[key]}
26
+ end
27
+
26
28
  # Calling inspect may trigger a bunch of loads, but it's mainly for debugging, so no worries.
27
29
  def inspect
28
- values = self.class.defined_attributes.keys.map {|r| "#{r}:#{send(r).inspect}"}
29
- "#<#{self.class} pid:\"#{pretty_pid}\", #{values.join(', ')}>"
30
+ values = ["pid: #{pretty_pid.inspect}"]
31
+ values << self.class.defined_attributes.keys.map {|r| "#{r}: #{send(r).inspect}"}
32
+ "#<#{self.class} #{values.flatten.join(', ')}>"
30
33
  end
31
34
 
32
35
  def [](key)
@@ -37,37 +40,53 @@ module ActiveFedora
37
40
  array_setter(key, value)
38
41
  end
39
42
 
43
+ protected
44
+
45
+ # override activemodel so it doesn't trigger a load of all the attributes.
46
+ # the callback methods seem to trigger this, which means just initing an object (after_init)
47
+ # causes a load of all the datastreams.
48
+ def attribute_method?(attr_name) #:nodoc:
49
+ respond_to_without_attributes?(:attributes) && self.class.defined_attributes.include?(attr_name)
50
+ end
40
51
 
41
52
  private
42
53
  def array_reader(field, *args)
54
+ if md = /^(.+)_id$/.match(field)
55
+ # a belongs_to association reader
56
+ association = association(md[1].to_sym)
57
+ return association.id_reader if association
58
+ end
43
59
  raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
44
60
  if args.present?
45
- instance_exec(*args, &self.class.defined_attributes[field][:reader])
61
+ instance_exec(*args, &self.class.defined_attributes[field].reader)
46
62
  else
47
- instance_exec &self.class.defined_attributes[field][:reader]
63
+ instance_exec &self.class.defined_attributes[field].reader
48
64
  end
49
65
  end
50
66
 
51
67
  def array_setter(field, args)
68
+ if md = /^(.+)_id$/.match(field)
69
+ # a belongs_to association writer
70
+ association = association(md[1].to_sym)
71
+ return association.id_writer(args) if association
72
+ end
52
73
  raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
53
- instance_exec(args, &self.class.defined_attributes[field][:setter])
74
+ instance_exec(args, &self.class.defined_attributes[field].writer)
54
75
  end
55
76
 
56
77
  # @return [Boolean] true if there is an reader method and it returns a
57
78
  # value different from the new_value.
58
79
  def value_has_changed?(field, new_value)
59
- begin
60
- new_value != array_reader(field)
61
- rescue NoMethodError
62
- false
63
- end
80
+ new_value != array_reader(field)
64
81
  end
65
82
 
66
83
  def mark_as_changed(field)
67
84
  self.send("#{field}_will_change!")
68
85
  end
69
86
 
70
-
87
+ def datastream_for_attribute(dsid)
88
+ datastreams[dsid] || raise(ArgumentError, "Undefined datastream id: `#{dsid}' in has_attributes")
89
+ end
71
90
 
72
91
  module ClassMethods
73
92
  def defined_attributes
@@ -83,7 +102,8 @@ module ActiveFedora
83
102
 
84
103
  def has_attributes(*fields)
85
104
  options = fields.pop
86
- datastream = options.delete(:datastream)
105
+ datastream = options.delete(:datastream).to_s
106
+ raise ArgumentError, "You must provide a datastream to has_attributes" if datastream.blank?
87
107
  define_attribute_methods fields
88
108
  fields.each do |f|
89
109
  create_attribute_reader(f, datastream, options)
@@ -102,51 +122,17 @@ module ActiveFedora
102
122
  # @param [Symbol] field the field to query
103
123
  # @return [Boolean]
104
124
  def multiple?(field)
105
- defined_attributes[field][:multiple]
125
+ defined_attributes[field].multiple
106
126
  end
107
127
 
108
-
128
+ def find_or_create_defined_attribute(field, dsid, args)
129
+ self.defined_attributes[field] ||= DatastreamAttribute.new(field, dsid, datastream_class_for_name(dsid), args)
130
+ end
109
131
 
110
132
  private
111
- def create_attribute_reader(field, dsid, args)
112
- self.defined_attributes[field] ||= {}
113
- self.defined_attributes[field][:reader] = lambda do |*opts|
114
- ds = self.send(dsid)
115
- if ds.kind_of?(ActiveFedora::RDFDatastream)
116
- ds.send(field)
117
- else
118
- terminology = args[:at] || [field]
119
- if terminology.length == 1 && opts.present?
120
- ds.send(terminology.first, *opts)
121
- else
122
- ds.send(:term_values, *terminology)
123
- end
124
- end
125
- end
126
133
 
127
- if !args[:multiple].nil?
128
- self.defined_attributes[field][:multiple] = args[:multiple]
129
- elsif !args[:unique].nil?
130
- i = 0
131
- begin
132
- match = /in `(delegate.*)'/.match(caller[i])
133
- i+=1
134
- end while match.nil?
135
-
136
- prev_method = match.captures.first
137
- Deprecation.warn Attributes, "The :unique option for `#{prev_method}' is deprecated. Use :multiple instead. :unique will be removed in ActiveFedora 7", caller(i+1)
138
- self.defined_attributes[field][:multiple] = !args[:unique]
139
- else
140
- i = 0
141
- begin
142
- match = /in `(delegate.*)'/.match(caller[i])
143
- i+=1
144
- end while match.nil?
145
-
146
- prev_method = match.captures.first
147
- Deprecation.warn Attributes, "You have not explicitly set the :multiple option on `#{prev_method}'. The default value will switch from true to false in ActiveFedora 7, so if you want to future-proof this application set `multiple: true'", caller(i+ 1)
148
- self.defined_attributes[field][:multiple] = true # this should be false for ActiveFedora 7
149
- end
134
+ def create_attribute_reader(field, dsid, args)
135
+ find_or_create_defined_attribute(field, dsid, args)
150
136
 
151
137
  define_method field do |*opts|
152
138
  val = array_reader(field, *opts)
@@ -154,94 +140,12 @@ module ActiveFedora
154
140
  end
155
141
  end
156
142
 
157
-
158
143
  def create_attribute_setter(field, dsid, args)
159
- self.defined_attributes[field] ||= {}
160
- self.defined_attributes[field][:setter] = lambda do |v|
161
- ds = self.send(dsid)
162
- mark_as_changed(field) if value_has_changed?(field, v)
163
- if ds.kind_of?(ActiveFedora::RDFDatastream)
164
- ds.send("#{field}=", v)
165
- else
166
- terminology = args[:at] || [field]
167
- ds.send(:update_indexed_attributes, {terminology => v})
168
- end
169
- end
144
+ find_or_create_defined_attribute(field, dsid, args)
170
145
  define_method "#{field}=".to_sym do |v|
171
146
  self[field]=v
172
147
  end
173
148
  end
174
-
175
- end
176
-
177
-
178
- public
179
-
180
- # A convenience method for updating indexed attributes. The passed in hash
181
- # must look like this :
182
- # {{:name=>{"0"=>"a","1"=>"b"}}
183
- #
184
- # This will result in any datastream field of name :name having the value [a,b]
185
- #
186
- # An index of -1 will insert a new value. any existing value at the relevant index
187
- # will be overwritten.
188
- #
189
- # As in update_attributes, this overwrites _all_ available fields by default.
190
- #
191
- # If you want to specify which datastream(s) to update,
192
- # use the :datastreams argument like so:
193
- # m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>"my_ds")
194
- # or
195
- # m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>["my_ds", "my_other_ds"])
196
- #
197
- def update_indexed_attributes(params={}, opts={})
198
- Deprecation.warn(Attributes, 'update_indexed_attributes is deprecated and will be removed in ActiveFedora 7.0.0. Consider using dsid.update_indexed_attributes() instead.', caller)
199
- if ds = opts[:datastreams]
200
- ds_array = []
201
- ds = [ds] unless ds.respond_to? :each
202
- ds.each do |dsname|
203
- ds_array << datastreams[dsname]
204
- end
205
- else
206
- ds_array = metadata_streams
207
- end
208
- result = {}
209
- ds_array.each do |d|
210
- result[d.dsid] = d.update_indexed_attributes(params,opts)
211
- end
212
- return result
213
- end
214
-
215
- # Updates the attributes for each datastream named in the params Hash
216
- # @param [Hash] params A Hash whose keys correspond to datastream ids and whose values are appropriate Hashes to submit to update_indexed_attributes on that datastream
217
- # @param [Hash] opts (currently ignored.)
218
- # @example Update the descMetadata and properties datastreams with new values
219
- # article = HydrangeaArticle.new
220
- # ds_values_hash = {
221
- # "descMetadata"=>{ [{:person=>0}, :role]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"} },
222
- # "properties"=>{ "notes"=>"foo" }
223
- # }
224
- # article.update_datastream_attributes( ds_values_hash )
225
- def update_datastream_attributes(params={}, opts={})
226
- Deprecation.warn(Attributes, 'update_datastream_attributes is deprecated and will be removed in ActiveFedora 7.0.0. Consider using delegate_to instead.', caller)
227
- result = params.dup
228
- params.each_pair do |dsid, ds_params|
229
- if datastreams.include?(dsid)
230
- result[dsid] = datastreams[dsid].update_indexed_attributes(ds_params)
231
- else
232
- result.delete(dsid)
233
- end
234
- end
235
- return result
236
- end
237
-
238
- def get_values_from_datastream(dsid,field_key,default=[])
239
- Deprecation.warn(Attributes, 'get_values_from_datastream is deprecated and will be removed in ActiveFedora 7.0.0. Consider using Datastream#get_values instead.', caller)
240
- if datastreams.include?(dsid)
241
- return datastreams[dsid].get_values(field_key,default)
242
- else
243
- return nil
244
- end
245
149
  end
246
150
  end
247
151
  end