active-fedora 8.7.0 → 9.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +8 -15
  3. data/Gemfile +5 -5
  4. data/History.txt +0 -80
  5. data/README.md +1 -0
  6. data/Rakefile +0 -3
  7. data/active-fedora.gemspec +8 -7
  8. data/config/fedora.yml +5 -4
  9. data/config/predicate_mappings.yml +5 -5
  10. data/gemfiles/rails4.1.gemfile +10 -0
  11. data/gemfiles/rails4.2.beta.gemfile +10 -0
  12. data/lib/active_fedora.rb +151 -117
  13. data/lib/active_fedora/associations.rb +47 -15
  14. data/lib/active_fedora/associations/association.rb +29 -8
  15. data/lib/active_fedora/associations/association_scope.rb +5 -5
  16. data/lib/active_fedora/associations/belongs_to_association.rb +20 -63
  17. data/lib/active_fedora/associations/builder/association.rb +61 -25
  18. data/lib/active_fedora/associations/builder/belongs_to.rb +7 -94
  19. data/lib/active_fedora/associations/builder/collection_association.rb +11 -43
  20. data/lib/active_fedora/associations/builder/contains.rb +28 -0
  21. data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +13 -3
  22. data/lib/active_fedora/associations/builder/has_many.rb +16 -10
  23. data/lib/active_fedora/associations/builder/property.rb +14 -0
  24. data/lib/active_fedora/associations/builder/singular_association.rb +14 -18
  25. data/lib/active_fedora/associations/builder/singular_property.rb +12 -0
  26. data/lib/active_fedora/associations/collection_association.rb +57 -80
  27. data/lib/active_fedora/associations/contains_association.rb +50 -0
  28. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +44 -24
  29. data/lib/active_fedora/associations/has_many_association.rb +46 -14
  30. data/lib/active_fedora/associations/rdf.rb +86 -0
  31. data/lib/active_fedora/associations/singular_association.rb +4 -8
  32. data/lib/active_fedora/associations/singular_rdf.rb +15 -0
  33. data/lib/active_fedora/attached_files.rb +195 -0
  34. data/lib/active_fedora/attribute_methods.rb +122 -0
  35. data/lib/active_fedora/attribute_methods/dirty.rb +16 -0
  36. data/lib/active_fedora/attribute_methods/read.rb +61 -0
  37. data/lib/active_fedora/attribute_methods/write.rb +47 -0
  38. data/lib/active_fedora/attributes.rb +93 -44
  39. data/lib/active_fedora/attributes/primary_key.rb +12 -0
  40. data/lib/active_fedora/autosave_association.rb +2 -2
  41. data/lib/active_fedora/base.rb +16 -15
  42. data/lib/active_fedora/callbacks.rb +7 -7
  43. data/lib/active_fedora/change_set.rb +34 -0
  44. data/lib/active_fedora/cleaner.rb +62 -0
  45. data/lib/active_fedora/config.rb +4 -22
  46. data/lib/active_fedora/core.rb +173 -99
  47. data/lib/active_fedora/datastream.rb +4 -117
  48. data/lib/active_fedora/datastreams.rb +2 -263
  49. data/lib/active_fedora/datastreams/nokogiri_datastreams.rb +28 -51
  50. data/lib/active_fedora/{datastream_attribute.rb → delegated_attribute.rb} +57 -26
  51. data/lib/active_fedora/errors.rb +61 -0
  52. data/lib/active_fedora/fedora.rb +19 -0
  53. data/lib/active_fedora/fedora_attributes.rb +58 -26
  54. data/lib/active_fedora/file.rb +318 -0
  55. data/lib/active_fedora/file_configurator.rb +32 -32
  56. data/lib/active_fedora/file_path_builder.rb +24 -0
  57. data/lib/active_fedora/files_hash.rb +82 -0
  58. data/lib/active_fedora/fixity_service.rb +40 -0
  59. data/lib/active_fedora/indexing.rb +55 -82
  60. data/lib/active_fedora/indexing_service.rb +70 -0
  61. data/lib/active_fedora/ldp_resource.rb +26 -0
  62. data/lib/active_fedora/loadable_from_json.rb +112 -0
  63. data/lib/active_fedora/model.rb +5 -19
  64. data/lib/active_fedora/nested_attributes.rb +5 -6
  65. data/lib/active_fedora/nom_datastream.rb +15 -25
  66. data/lib/active_fedora/om_datastream.rb +26 -197
  67. data/lib/active_fedora/persistence.rb +95 -71
  68. data/lib/active_fedora/predicates.rb +4 -4
  69. data/lib/active_fedora/qualified_dublin_core_datastream.rb +17 -18
  70. data/lib/active_fedora/querying.rb +3 -4
  71. data/lib/active_fedora/railtie.rb +3 -6
  72. data/lib/active_fedora/rdf.rb +3 -1
  73. data/lib/active_fedora/rdf/datastream_indexing.rb +11 -0
  74. data/lib/active_fedora/rdf/fcrepo.rb +10 -324
  75. data/lib/active_fedora/rdf/indexing.rb +20 -16
  76. data/lib/active_fedora/rdf/ldp.rb +6 -0
  77. data/lib/active_fedora/rdf/ntriples_rdf_datastream.rb +1 -1
  78. data/lib/active_fedora/rdf/persistence.rb +5 -6
  79. data/lib/active_fedora/rdf/rdf_datastream.rb +44 -37
  80. data/lib/active_fedora/rdf/rdfxml_datastream.rb +13 -0
  81. data/lib/active_fedora/rdf/rels_ext.rb +26 -0
  82. data/lib/active_fedora/reflection.rb +256 -199
  83. data/lib/active_fedora/relation.rb +18 -6
  84. data/lib/active_fedora/relation/finder_methods.rb +69 -38
  85. data/lib/active_fedora/relation/query_methods.rb +7 -3
  86. data/lib/active_fedora/rspec_matchers/belong_to_associated_active_fedora_object_matcher.rb +7 -7
  87. data/lib/active_fedora/rspec_matchers/have_many_associated_active_fedora_objects_matcher.rb +8 -8
  88. data/lib/active_fedora/rspec_matchers/have_predicate_matcher.rb +9 -11
  89. data/lib/active_fedora/simple_datastream.rb +18 -13
  90. data/lib/active_fedora/solr_instance_loader.rb +18 -38
  91. data/lib/active_fedora/solr_service.rb +37 -20
  92. data/lib/active_fedora/sparql_insert.rb +45 -0
  93. data/lib/active_fedora/test_support.rb +1 -22
  94. data/lib/active_fedora/version.rb +1 -1
  95. data/lib/active_fedora/versionable.rb +90 -0
  96. data/lib/active_fedora/with_metadata.rb +37 -0
  97. data/lib/active_fedora/with_metadata/metadata_node.rb +70 -0
  98. data/lib/generators/active_fedora/config/config_generator.rb +0 -1
  99. data/lib/generators/active_fedora/config/solr/solr_generator.rb +1 -1
  100. data/lib/generators/active_fedora/model/model_generator.rb +5 -5
  101. data/lib/generators/active_fedora/model/templates/datastream_spec.rb.erb +1 -1
  102. data/lib/generators/active_fedora/model/templates/model_spec.rb.erb +2 -2
  103. data/lib/tasks/active_fedora_dev.rake +21 -27
  104. data/spec/config_helper.rb +1 -1
  105. data/spec/fixtures/mixed_rdf_descMetadata.nt +6 -6
  106. data/spec/fixtures/rails_root/config/predicate_mappings.yml +3 -19
  107. data/spec/fixtures/solr_rdf_descMetadata.nt +6 -6
  108. data/spec/integration/associations_spec.rb +133 -153
  109. data/spec/integration/attached_files_spec.rb +164 -0
  110. data/spec/integration/attributes_spec.rb +73 -12
  111. data/spec/integration/autosave_association_spec.rb +3 -3
  112. data/spec/integration/base_spec.rb +57 -351
  113. data/spec/integration/belongs_to_association_spec.rb +86 -76
  114. data/spec/integration/bug_spec.rb +3 -3
  115. data/spec/integration/collection_association_spec.rb +4 -4
  116. data/spec/integration/complex_rdf_datastream_spec.rb +54 -56
  117. data/spec/integration/delete_all_spec.rb +18 -15
  118. data/spec/integration/eradicate_spec.rb +54 -0
  119. data/spec/integration/fedora_solr_sync_spec.rb +7 -5
  120. data/spec/integration/field_to_solr_name_spec.rb +5 -5
  121. data/spec/integration/file_fixity_spec.rb +40 -0
  122. data/spec/integration/file_spec.rb +122 -0
  123. data/spec/integration/full_featured_model_spec.rb +53 -63
  124. data/spec/integration/has_and_belongs_to_many_associations_spec.rb +141 -114
  125. data/spec/integration/has_many_associations_spec.rb +142 -64
  126. data/spec/integration/json_serialization_spec.rb +50 -8
  127. data/spec/integration/model_spec.rb +12 -29
  128. data/spec/integration/nested_attribute_spec.rb +28 -20
  129. data/spec/integration/ntriples_datastream_spec.rb +60 -57
  130. data/spec/integration/om_datastream_spec.rb +51 -140
  131. data/spec/integration/rdf_nested_attributes_spec.rb +16 -14
  132. data/spec/integration/relation_delegation_spec.rb +7 -9
  133. data/spec/integration/relation_spec.rb +9 -7
  134. data/spec/integration/scoped_query_spec.rb +26 -26
  135. data/spec/integration/solr_instance_loader_spec.rb +69 -53
  136. data/spec/integration/solr_service_spec.rb +12 -73
  137. data/spec/integration/versionable_spec.rb +477 -0
  138. data/spec/integration/with_metadata_spec.rb +52 -0
  139. data/spec/samples/hydra-mods_article_datastream.rb +10 -6
  140. data/spec/samples/models/mods_article.rb +6 -2
  141. data/spec/samples/oral_history_sample.xml +1 -1
  142. data/spec/samples/oral_history_xml.xml +1 -1
  143. data/spec/samples/special_thing.rb +3 -3
  144. data/spec/spec_helper.rb +22 -12
  145. data/spec/support/an_active_model.rb +3 -7
  146. data/spec/unit/active_fedora_spec.rb +20 -17
  147. data/spec/unit/attached_files_spec.rb +203 -0
  148. data/spec/unit/attributes_spec.rb +286 -207
  149. data/spec/unit/base_active_model_spec.rb +8 -8
  150. data/spec/unit/base_datastream_management_spec.rb +11 -24
  151. data/spec/unit/base_extra_spec.rb +17 -67
  152. data/spec/unit/base_spec.rb +163 -428
  153. data/spec/unit/builder/has_and_belongs_to_many_spec.rb +2 -2
  154. data/spec/unit/callback_spec.rb +38 -23
  155. data/spec/unit/change_set_spec.rb +46 -0
  156. data/spec/unit/code_configurator_spec.rb +5 -5
  157. data/spec/unit/config_spec.rb +9 -14
  158. data/spec/unit/core_spec.rb +59 -8
  159. data/spec/unit/file_configurator_spec.rb +55 -53
  160. data/spec/unit/file_path_builder_spec.rb +18 -0
  161. data/spec/unit/file_spec.rb +221 -0
  162. data/spec/unit/files_hash_spec.rb +53 -0
  163. data/spec/unit/fixity_service_spec.rb +34 -0
  164. data/spec/unit/has_and_belongs_to_many_association_spec.rb +134 -0
  165. data/spec/unit/has_many_association_spec.rb +51 -0
  166. data/spec/unit/indexing_service_spec.rb +23 -0
  167. data/spec/unit/indexing_spec.rb +26 -0
  168. data/spec/unit/inheritance_spec.rb +9 -10
  169. data/spec/unit/model_spec.rb +15 -33
  170. data/spec/unit/nom_datastream_spec.rb +13 -10
  171. data/spec/unit/ntriples_datastream_spec.rb +81 -96
  172. data/spec/unit/om_datastream_spec.rb +137 -227
  173. data/spec/unit/persistence_spec.rb +28 -34
  174. data/spec/unit/predicates_spec.rb +29 -29
  175. data/spec/unit/property_spec.rb +1 -3
  176. data/spec/unit/qualified_dublin_core_datastream_spec.rb +27 -32
  177. data/spec/unit/query_spec.rb +116 -149
  178. data/spec/unit/rdf_datastream_spec.rb +25 -43
  179. data/spec/unit/rdf_resource_datastream_spec.rb +24 -123
  180. data/spec/unit/{rdfxml_rdf_datastream_spec.rb → rdfxml_datastream_spec.rb} +21 -25
  181. data/spec/unit/readonly_spec.rb +23 -0
  182. data/spec/unit/rspec_matchers/belong_to_associated_active_fedora_object_matcher_spec.rb +6 -6
  183. data/spec/unit/rspec_matchers/have_many_associated_active_fedora_objects_matcher_spec.rb +6 -6
  184. data/spec/unit/rspec_matchers/have_predicate_matcher_spec.rb +6 -6
  185. data/spec/unit/serializers_spec.rb +1 -1
  186. data/spec/unit/simple_datastream_spec.rb +12 -23
  187. data/spec/unit/solr_config_options_spec.rb +14 -17
  188. data/spec/unit/solr_service_spec.rb +38 -77
  189. data/spec/unit/sparql_insert_spec.rb +32 -0
  190. data/spec/unit/validations_spec.rb +8 -11
  191. metadata +96 -121
  192. data/lib/active_fedora/auditable.rb +0 -9
  193. data/lib/active_fedora/content_model.rb +0 -70
  194. data/lib/active_fedora/datastream_collections.rb +0 -302
  195. data/lib/active_fedora/datastream_hash.rb +0 -35
  196. data/lib/active_fedora/digital_object.rb +0 -55
  197. data/lib/active_fedora/fixture_exporter.rb +0 -33
  198. data/lib/active_fedora/fixture_loader.rb +0 -48
  199. data/lib/active_fedora/rdf/identifiable.rb +0 -66
  200. data/lib/active_fedora/rdf/project_hydra.rb +0 -12
  201. data/lib/active_fedora/rdf/rdfxml_rdf_datastream.rb +0 -13
  202. data/lib/active_fedora/rdf_xml_writer.rb +0 -49
  203. data/lib/active_fedora/relationship_graph.rb +0 -101
  204. data/lib/active_fedora/reload_on_save.rb +0 -16
  205. data/lib/active_fedora/rels_ext_datastream.rb +0 -100
  206. data/lib/active_fedora/rspec_matchers/match_fedora_datastream_matcher.rb +0 -41
  207. data/lib/active_fedora/rubydora_connection.rb +0 -35
  208. data/lib/active_fedora/semantic_node.rb +0 -164
  209. data/lib/active_fedora/service_definitions.rb +0 -88
  210. data/lib/active_fedora/sharding.rb +0 -58
  211. data/lib/active_fedora/solr_digital_object.rb +0 -68
  212. data/lib/active_fedora/unsaved_digital_object.rb +0 -58
  213. data/lib/generators/active_fedora/config/fedora/fedora_generator.rb +0 -12
  214. data/lib/generators/active_fedora/config/fedora/templates/fedora.yml +0 -38
  215. data/lib/generators/active_fedora/config/fedora/templates/fedora_conf/conf/development/fedora.fcfg +0 -953
  216. data/lib/generators/active_fedora/config/fedora/templates/fedora_conf/conf/test/fedora.fcfg +0 -953
  217. data/lib/tasks/active_fedora.rake +0 -83
  218. data/spec/fixtures/sharded_fedora.yml +0 -11
  219. data/spec/integration/auditable_spec.rb +0 -29
  220. data/spec/integration/datastream_collections_spec.rb +0 -127
  221. data/spec/integration/datastream_spec.rb +0 -90
  222. data/spec/integration/datastreams_spec.rb +0 -173
  223. data/spec/integration/load_from_solr_spec.rb +0 -66
  224. data/spec/integration/rels_ext_datastream_spec.rb +0 -82
  225. data/spec/support/mock_fedora.rb +0 -44
  226. data/spec/unit/content_model_spec.rb +0 -86
  227. data/spec/unit/datastream_collections_spec.rb +0 -420
  228. data/spec/unit/datastream_spec.rb +0 -83
  229. data/spec/unit/datastreams_spec.rb +0 -243
  230. data/spec/unit/has_and_belongs_to_many_collection_spec.rb +0 -96
  231. data/spec/unit/has_many_collection_spec.rb +0 -35
  232. data/spec/unit/rdf_vocab_spec.rb +0 -30
  233. data/spec/unit/rdf_xml_writer_spec.rb +0 -63
  234. data/spec/unit/relationship_graph_spec.rb +0 -115
  235. data/spec/unit/reload_on_save_spec.rb +0 -24
  236. data/spec/unit/rels_ext_datastream_spec.rb +0 -170
  237. data/spec/unit/rspec_matchers/match_fedora_datastream_matcher_spec.rb +0 -44
  238. data/spec/unit/rubydora_connection_spec.rb +0 -12
  239. data/spec/unit/semantic_node_spec.rb +0 -112
  240. data/spec/unit/service_definitions_spec.rb +0 -63
  241. data/spec/unit/solr_digital_object_spec.rb +0 -97
  242. data/spec/unit/unsaved_digital_object_spec.rb +0 -48
@@ -0,0 +1,122 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'active_support/core_ext/class/attribute'
3
+
4
+ module ActiveFedora
5
+ module AttributeMethods
6
+ extend ActiveSupport::Concern
7
+ include ActiveModel::AttributeMethods
8
+
9
+ AttrNames = Module.new {
10
+ def self.set_name_cache(name, value)
11
+ const_name = "ATTR_#{name}"
12
+ unless const_defined? const_name
13
+ const_set const_name, value.dup.freeze
14
+ end
15
+ end
16
+ }
17
+
18
+ class AttributeMethodCache
19
+ def initialize
20
+ @module = Module.new
21
+ @method_cache = ThreadSafe::Cache.new
22
+ end
23
+
24
+ def [](name)
25
+ @method_cache.compute_if_absent(name) do
26
+ safe_name = name.unpack('h*').first
27
+ temp_method = "__temp__#{safe_name}"
28
+ ActiveFedora::AttributeMethods::AttrNames.set_name_cache safe_name, name
29
+ @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
30
+ @module.instance_method temp_method
31
+ end
32
+ end
33
+
34
+ private
35
+ def method_body; raise NotImplementedError; end
36
+ end
37
+
38
+
39
+ included do
40
+ initialize_generated_modules
41
+ include Read
42
+ include Write
43
+ include Dirty
44
+ end
45
+
46
+ # Returns an array of names for the attributes available on this object.
47
+ #
48
+ # class Person < ActiveFedora::Base
49
+ # end
50
+ #
51
+ # person = Person.new
52
+ # person.attribute_names
53
+ # # => ["id", "created_at", "updated_at", "name", "age"]
54
+ def attribute_names
55
+ @attributes.keys
56
+ end
57
+
58
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
59
+ #
60
+ # class Person < ActiveFedora::Base
61
+ # end
62
+ #
63
+ # person = Person.create(name: 'Francesco', age: 22)
64
+ # person.attributes
65
+ # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
66
+ def attributes
67
+ attribute_names.each_with_object({}) { |name, attrs|
68
+ attrs[name] = read_attribute(name)
69
+ }
70
+ end
71
+
72
+
73
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
74
+ # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
75
+ # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
76
+ #
77
+ # Alias for the <tt>read_attribute</tt> method.
78
+ #
79
+ # class Person < ActiveRecord::Base
80
+ # belongs_to :organization
81
+ # end
82
+ #
83
+ # person = Person.new(name: 'Francesco', age: '22')
84
+ # person[:name] # => "Francesco"
85
+ # person[:age] # => 22
86
+ #
87
+ # person = Person.select('id').first
88
+ # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
89
+ # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
90
+ def [](attr_name)
91
+ read_attribute(attr_name) { |n| missing_attribute(n, caller) }
92
+ end
93
+
94
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
95
+ # (Alias for the protected <tt>write_attribute</tt> method).
96
+ #
97
+ # class Person < ActiveFedora::Base
98
+ # end
99
+ #
100
+ # person = Person.new
101
+ # person[:age] = '22'
102
+ # person[:age] # => 22
103
+ # person[:age] # => Fixnum
104
+ def []=(attr_name, value)
105
+ write_attribute(attr_name, value)
106
+ end
107
+
108
+ module ClassMethods
109
+ def initialize_generated_modules # :nodoc:
110
+ @generated_attribute_methods = Module.new { extend Mutex_m }
111
+ include @generated_attribute_methods
112
+ end
113
+ private
114
+ # @param name [Symbol] name of the attribute to generate
115
+ def generate_method(name)
116
+ generated_attribute_methods.synchronize do
117
+ define_attribute_methods name
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveFedora
2
+ module AttributeMethods
3
+ module Dirty
4
+ extend ActiveSupport::Concern
5
+
6
+ def set_value(*val)
7
+ attribute = val.first
8
+ unless [:has_model, :modified_date].include? attribute
9
+ attribute_will_change!(attribute)
10
+ end
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,61 @@
1
+ module ActiveFedora
2
+ module AttributeMethods
3
+ module Read
4
+ ReaderMethodCache = Class.new(AttributeMethodCache) {
5
+ private
6
+ # We want to generate the methods via module_eval rather than
7
+ # define_method, because define_method is slower on dispatch.
8
+ # Evaluating many similar methods may use more memory as the instruction
9
+ # sequences are duplicated and cached (in MRI). define_method may
10
+ # be slower on dispatch, but if you're careful about the closure
11
+ # created, then define_method will consume much less memory.
12
+ #
13
+ # But sometimes the database might return columns with
14
+ # characters that are not allowed in normal method names (like
15
+ # 'my_column(omg)'. So to work around this we first define with
16
+ # the __temp__ identifier, and then use alias method to rename
17
+ # it to what we want.
18
+ #
19
+ # We are also defining a constant to hold the frozen string of
20
+ # the attribute name. Using a constant means that we do not have
21
+ # to allocate an object on each call to the attribute method.
22
+ # Making it frozen means that it doesn't get duped when used to
23
+ # key the @attributes_cache in read_attribute.
24
+ def method_body(method_name, const_name)
25
+ <<-EOMETHOD
26
+ def #{method_name}
27
+ name = ::ActiveFedora::AttributeMethods::AttrNames::ATTR_#{const_name}
28
+ read_attribute(name) { |n| missing_attribute(n, caller) }
29
+ end
30
+ EOMETHOD
31
+ end
32
+ }.new
33
+
34
+ extend ActiveSupport::Concern
35
+
36
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after
37
+ # it has been typecast (for example, "2004-12-12" in a date column is cast
38
+ # to a date object, like Date.new(2004, 12, 12)).
39
+ def read_attribute(attr_name)
40
+ name = attr_name.to_s
41
+ @attributes.fetch(name, nil)
42
+ end
43
+
44
+ private
45
+ def attribute(attribute_name)
46
+ read_attribute(attribute_name)
47
+ end
48
+
49
+
50
+ module ClassMethods
51
+ def define_method_attribute(name)
52
+ method = ReaderMethodCache[name.to_s]
53
+ generated_attribute_methods.module_eval {
54
+ define_method name, method
55
+ }
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,47 @@
1
+ module ActiveFedora
2
+ module AttributeMethods
3
+ module Write
4
+ WriterMethodCache = Class.new(AttributeMethodCache) {
5
+ private
6
+
7
+ def method_body(method_name, const_name)
8
+ <<-EOMETHOD
9
+ def #{method_name}(value)
10
+ name = ::ActiveFedora::AttributeMethods::AttrNames::ATTR_#{const_name}
11
+ write_attribute(name, value)
12
+ end
13
+ EOMETHOD
14
+ end
15
+ }.new
16
+
17
+ extend ActiveSupport::Concern
18
+
19
+ included do
20
+ attribute_method_suffix "="
21
+ end
22
+
23
+ def write_attribute(attribute_name, value)
24
+ if self.class.properties.key?(attribute_name)
25
+ @attributes[attribute_name] = value
26
+ else
27
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attribute_name}'"
28
+ end
29
+ end
30
+
31
+ private
32
+ def attribute=(attribute_name, value)
33
+ write_attribute(attribute_name, value)
34
+ end
35
+
36
+ module ClassMethods
37
+ def define_method_attribute=(name)
38
+ method = WriterMethodCache[name.to_s]
39
+ generated_attribute_methods.module_eval {
40
+ define_method "#{name}=", method
41
+ }
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -1,17 +1,16 @@
1
1
  module ActiveFedora
2
2
  module Attributes
3
3
  extend ActiveSupport::Concern
4
- extend ActiveSupport::Autoload
5
4
  include ActiveModel::Dirty
6
-
7
- autoload :Serializers
8
5
 
9
6
  included do
10
7
  include Serializers
8
+ include PrimaryKey
9
+
11
10
  after_save :clear_changed_attributes
12
11
  def clear_changed_attributes
13
12
  @previously_changed = changes
14
- clear_attribute_changes(changes.keys)
13
+ @changed_attributes.clear
15
14
  end
16
15
  end
17
16
 
@@ -21,23 +20,46 @@ module ActiveFedora
21
20
  end
22
21
  end
23
22
 
23
+ def attribute_names
24
+ self.class.attribute_names
25
+ end
26
+
24
27
  def attributes
25
- self.class.defined_attributes.keys.each_with_object({"id" => id}) {|key, hash| hash[key] = self[key]}
28
+ attribute_names.each_with_object({"id" => id}) {|key, hash| hash[key] = self[key] }
26
29
  end
27
30
 
28
- # Calling inspect may trigger a bunch of loads, but it's mainly for debugging, so no worries.
31
+ # Calling inspect may trigger a bunch of datastream loads, but it's mainly for debugging, so no worries.
29
32
  def inspect
30
- values = ["pid: #{pretty_pid.inspect}"]
31
- values << self.class.defined_attributes.keys.map {|r| "#{r}: #{send(r).inspect}"}
33
+ values = ["id: #{id.inspect}"]
34
+ values << self.class.attribute_names.map { |attr| "#{attr}: #{self[attr].inspect}" }
32
35
  "#<#{self.class} #{values.flatten.join(', ')}>"
33
36
  end
34
37
 
35
38
  def [](key)
36
- array_reader(key)
39
+ if assoc = self.association(key.to_sym)
40
+ # This is for id attributes stored in the rdf graph.
41
+ assoc.reader
42
+ elsif self.class.properties.key?(key.to_s)
43
+ # The attribute is stored in the RDF graph for this object
44
+ resource[key]
45
+ else
46
+ # The attribute is a delegate to a datastream
47
+ array_reader(key)
48
+ end
37
49
  end
38
50
 
39
51
  def []=(key, value)
40
- array_setter(key, value)
52
+ raise ReadOnlyRecord if readonly?
53
+ if assoc = self.association(key.to_sym)
54
+ # This is for id attributes stored in the rdf graph.
55
+ assoc.replace(value)
56
+ elsif self.class.properties.key?(key.to_s)
57
+ # The attribute is stored in the RDF graph for this object
58
+ self.send(key.to_s+"=", value)
59
+ else
60
+ # The attribute is a delegate to a datastream
61
+ array_setter(key, value)
62
+ end
41
63
  end
42
64
 
43
65
  # @return [Boolean] true if there is an reader method and it returns a
@@ -50,6 +72,9 @@ module ActiveFedora
50
72
  self.send("#{field}_will_change!")
51
73
  end
52
74
 
75
+ def local_attributes
76
+ self.class.local_attributes
77
+ end
53
78
 
54
79
 
55
80
  protected
@@ -58,52 +83,63 @@ module ActiveFedora
58
83
  # the callback methods seem to trigger this, which means just initing an object (after_init)
59
84
  # causes a load of all the datastreams.
60
85
  def attribute_method?(attr_name) #:nodoc:
61
- respond_to_without_attributes?(:attributes) && self.class.defined_attributes.include?(attr_name)
86
+ respond_to_without_attributes?(:attributes) && self.class.delegated_attributes.include?(attr_name)
62
87
  end
63
88
 
64
89
  private
65
-
66
90
  def array_reader(field, *args)
67
- if md = /^(.+)_id$/.match(field)
68
- # a belongs_to association reader
69
- association = association(md[1].to_sym)
70
- return association.id_reader if association
71
- end
72
- raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
91
+ raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.delegated_attributes.key?(field)
73
92
 
74
- value = self.class.defined_attributes[field].reader(self, *args)
75
- self.class.multiple?(field) ? value : value.first
93
+ val = self.class.delegated_attributes[field].reader(self, *args)
94
+ self.class.multiple?(field) ? val : val.first
76
95
  end
77
96
 
78
97
  def array_setter(field, args)
79
- if md = /^(.+)_id$/.match(field)
80
- # a belongs_to association writer
81
- association = association(md[1].to_sym)
82
- return association.id_writer(args) if association
83
- end
84
- raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
98
+ raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.delegated_attributes.key?(field)
85
99
  if self.class.multiple?(field)
86
- unless args.nil? || ( args.respond_to?(:each) && !args.kind_of?(ActiveTriples::Resource) )
87
- raise ArgumentError, "Cannot set the multi-valued attribute `#{field}' to a single value."
88
- end
89
- elsif args.respond_to?(:each) # unique
90
- unless args.kind_of?(ActiveTriples::Resource)
91
- raise ArgumentError, "Cannot set the single-valued attribute `#{field}' to multiple values."
100
+ if args.present? && !args.respond_to?(:each)
101
+ raise ArgumentError, "You attempted to set the attribute `#{field}' on `#{self.class}' to a scalar value. However, this attribute is declared as being multivalued."
92
102
  end
103
+ elsif args.respond_to?(:each) # singular
104
+ raise ArgumentError, "You attempted to set the attribute `#{field}' on `#{self.class}' to an enumerable value. However, this attribute is declared as being singular."
93
105
  end
94
- self.class.defined_attributes[field].writer(self, args)
106
+ self.class.delegated_attributes[field].writer(self, args)
95
107
  end
96
108
 
97
109
  module ClassMethods
110
+ def attribute_names
111
+ @attribute_names ||= delegated_attributes.keys + association_attributes - system_attributes
112
+ end
113
+
114
+ # Attributes that are asserted about this RdfSource (not on a datastream)
115
+ def local_attributes
116
+ association_attributes + properties.keys - system_attributes
117
+ end
118
+
119
+ # Attributes that are required by ActiveFedora and Fedora
120
+ def system_attributes
121
+ ['has_model', 'create_date', 'modified_date']
122
+ end
123
+
124
+ # Attributes that represent associations to other repository objects
125
+ def association_attributes
126
+ outgoing_reflections.values.map { |reflection| reflection.foreign_key.to_s }
127
+ end
128
+
98
129
  def defined_attributes
99
- @defined_attributes ||= {}.with_indifferent_access
100
- return @defined_attributes unless superclass.respond_to?(:defined_attributes) and value = superclass.defined_attributes
101
- @defined_attributes = value.dup if @defined_attributes.empty?
102
- @defined_attributes
130
+ Deprecation.warn Attributes, "defined_attributes has been renamed to delegated_attributes. defined_attributes will be removed in ActiveFedora 9"
131
+ delegated_attributes
132
+ end
133
+
134
+ def delegated_attributes
135
+ @delegated_attributes ||= {}.with_indifferent_access
136
+ return @delegated_attributes unless superclass.respond_to?(:delegated_attributes) and value = superclass.delegated_attributes
137
+ @delegated_attributes = value.dup if @delegated_attributes.empty?
138
+ @delegated_attributes
103
139
  end
104
140
 
105
- def defined_attributes= val
106
- @defined_attributes = val
141
+ def delegated_attributes= val
142
+ @delegated_attributes = val
107
143
  end
108
144
 
109
145
  def has_attributes(*fields)
@@ -128,15 +164,29 @@ module ActiveFedora
128
164
  # @param [Symbol] field the field to query
129
165
  # @return [Boolean]
130
166
  def multiple?(field)
131
- defined_attributes[field].multiple
167
+ raise UnknownAttributeError, "#{self} does not have an attribute `#{field}'" unless delegated_attributes.key?(field)
168
+ delegated_attributes[field].multiple
132
169
  end
133
170
 
134
- def find_or_create_defined_attribute(field, dsid, args)
135
- self.defined_attributes[field] ||= DatastreamAttribute.new(field, dsid, datastream_class_for_name(dsid), args)
171
+
172
+ def property name, properties
173
+ find_or_create_defined_attribute(name, nil, {multiple: true}.merge(properties))
174
+ super
136
175
  end
137
176
 
138
177
  private
139
178
 
179
+ def find_or_create_defined_attribute(field, dsid, args)
180
+ delegated_attributes[field] ||= DelegatedAttribute.new(field, dsid, datastream_class_for_name(dsid), args)
181
+ end
182
+
183
+ # @param [String] dsid the datastream id
184
+ # @return [Class] the class of the datastream
185
+ def datastream_class_for_name(dsid)
186
+ reflection = reflect_on_association(dsid)
187
+ reflection ? reflection.klass : ActiveFedora::File
188
+ end
189
+
140
190
  def create_attribute_reader(field, dsid, args)
141
191
  find_or_create_defined_attribute(field, dsid, args)
142
192
 
@@ -147,9 +197,8 @@ module ActiveFedora
147
197
 
148
198
  def create_attribute_setter(field, dsid, args)
149
199
  find_or_create_defined_attribute(field, dsid, args)
150
-
151
200
  define_method "#{field}=".to_sym do |v|
152
- self[field] = v
201
+ self[field]=v
153
202
  end
154
203
  end
155
204
  end