active-fedora 3.2.0.pre3 → 3.2.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/History.txt +3 -0
- data/active-fedora.gemspec +1 -1
- data/lib/active_fedora.rb +4 -0
- data/lib/active_fedora/base.rb +35 -126
- data/lib/active_fedora/callbacks.rb +252 -0
- data/lib/active_fedora/model.rb +0 -3
- data/lib/active_fedora/persistence.rb +116 -0
- data/lib/active_fedora/rubydora_connection.rb +1 -1
- data/lib/active_fedora/solr_digital_object.rb +19 -0
- data/lib/active_fedora/unsaved_digital_object.rb +6 -3
- data/lib/active_fedora/validations.rb +82 -0
- data/lib/active_fedora/version.rb +1 -1
- data/spec/integration/base_loader_spec.rb +1 -2
- data/spec/integration/full_featured_model_spec.rb +1 -0
- data/spec/support/mock_fedora.rb +3 -3
- data/spec/unit/base_active_model_spec.rb +2 -2
- data/spec/unit/base_delegate_spec.rb +1 -1
- data/spec/unit/base_extra_spec.rb +2 -2
- data/spec/unit/base_spec.rb +77 -97
- data/spec/unit/callback_spec.rb +44 -0
- data/spec/unit/relationship_graph_spec.rb +2 -7
- data/spec/unit/relationships_spec.rb +4 -2
- data/spec/unit/service_definitions_spec.rb +6 -4
- data/spec/unit/solr_config_options_spec.rb +1 -1
- data/spec/unit/solr_service_spec.rb +3 -2
- data/spec/unit/validations_spec.rb +43 -0
- metadata +13 -4
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
3.2.0
|
2
2
|
|
3
|
+
Added ActiveModel validations.
|
4
|
+
Added callbacks on initialize, save, update, create, delete and find
|
5
|
+
Added Base.create
|
3
6
|
HYDRA-730 ActiveFedora isn't being initialized unless it is specified in the project Gemfile
|
4
7
|
Don't create pids until save
|
5
8
|
inspect and equality methods
|
data/active-fedora.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "active-fedora"
|
7
7
|
s.version = ActiveFedora::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["Matt Zumwalt", "McClain Looney"]
|
9
|
+
s.authors = ["Matt Zumwalt", "McClain Looney", "Justin Coyne"]
|
10
10
|
s.email = ["matt.zumwalt@yourmediashelf.com"]
|
11
11
|
s.homepage = %q{http://yourmediashelf.com/activefedora}
|
12
12
|
s.summary = %q{A convenience libary for manipulating MODS (Metadata Object Description Schema) documents.}
|
data/lib/active_fedora.rb
CHANGED
@@ -17,6 +17,7 @@ module ActiveFedora #:nodoc:
|
|
17
17
|
autoload :AttributeMethods
|
18
18
|
autoload :Base
|
19
19
|
autoload :ContentModel
|
20
|
+
autoload :Callbacks
|
20
21
|
autoload :Reflection
|
21
22
|
autoload :Relationships
|
22
23
|
autoload :FileManagement
|
@@ -25,11 +26,13 @@ module ActiveFedora #:nodoc:
|
|
25
26
|
autoload :Delegating
|
26
27
|
autoload :DigitalObject
|
27
28
|
autoload :UnsavedDigitalObject
|
29
|
+
autoload :SolrDigitalObject
|
28
30
|
autoload :Model
|
29
31
|
autoload :MetadataDatastream
|
30
32
|
autoload :MetadataDatastreamHelper
|
31
33
|
autoload :NokogiriDatastream
|
32
34
|
autoload :Property
|
35
|
+
autoload :Persistence
|
33
36
|
autoload :QualifiedDublinCoreDatastream
|
34
37
|
autoload :RelsExtDatastream
|
35
38
|
autoload :ServiceDefinitions
|
@@ -39,6 +42,7 @@ module ActiveFedora #:nodoc:
|
|
39
42
|
autoload :DatastreamCollections
|
40
43
|
autoload :NamedRelationships
|
41
44
|
autoload :Predicates
|
45
|
+
autoload :Validations
|
42
46
|
|
43
47
|
end
|
44
48
|
|
data/lib/active_fedora/base.rb
CHANGED
@@ -101,30 +101,40 @@ module ActiveFedora
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
# Constructor.
|
105
|
-
#
|
104
|
+
# Constructor. You may supply a custom +:pid+, or we call the Fedora Rest API for the
|
105
|
+
# next available Fedora pid, and mark as new object.
|
106
106
|
# Also, if +attrs+ does not contain +:pid+ but does contain +:namespace+ it will pass the
|
107
107
|
# +:namespace+ value to Fedora::Repository.nextid to generate the next pid available within
|
108
108
|
# the given namespace.
|
109
|
-
#
|
110
|
-
# If there is a pid, we're re-hydrating an existing object, and new object is false. Once the @inner_object is stored,
|
111
|
-
# we configure any defined datastreams.
|
112
109
|
def initialize(attrs = nil)
|
113
110
|
attrs = {} if attrs.nil?
|
114
111
|
attributes = attrs.dup
|
115
|
-
@inner_object = attributes.delete(:
|
116
|
-
|
117
|
-
if attributes[:pid]
|
118
|
-
@inner_object = DigitalObject.find(self.class, attributes[:pid])
|
119
|
-
else
|
120
|
-
@inner_object = UnsavedDigitalObject.new(self.class, attributes.delete(:namespace))
|
121
|
-
self.relationships_loaded = true
|
122
|
-
end
|
123
|
-
end
|
112
|
+
@inner_object = UnsavedDigitalObject.new(self.class, attributes.delete(:namespace), attributes.delete(:pid))
|
113
|
+
self.relationships_loaded = true
|
124
114
|
load_datastreams
|
125
115
|
|
126
|
-
[:
|
116
|
+
[:new_object,:create_date, :modified_date].each { |k| attributes.delete(k)}
|
127
117
|
self.attributes=attributes
|
118
|
+
run_callbacks :initialize
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# Initialize an empty model object and set the +inner_obj+
|
123
|
+
# example:
|
124
|
+
#
|
125
|
+
# class Post < ActiveFedora::Base
|
126
|
+
# has_metadata :name => "properties", :type => ActiveFedora::MetadataDatastream
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# post = Post.allocate
|
130
|
+
# post.init_with(DigitalObject.find(pid))
|
131
|
+
# post.properties.title # => 'hello world'
|
132
|
+
def init_with(inner_obj)
|
133
|
+
@inner_object = inner_obj
|
134
|
+
load_datastreams
|
135
|
+
run_callbacks :find
|
136
|
+
run_callbacks :initialize
|
137
|
+
self
|
128
138
|
end
|
129
139
|
|
130
140
|
def self.datastream_class_for_name(dsid)
|
@@ -139,6 +149,11 @@ module ActiveFedora
|
|
139
149
|
ds_specs[args[:name]]= {:type => args[:type], :label => args.fetch(:label,""), :control_group => args.fetch(:control_group,"X"), :disseminator => args.fetch(:disseminator,""), :url => args.fetch(:url,""),:block => block}
|
140
150
|
end
|
141
151
|
|
152
|
+
def self.create(args)
|
153
|
+
obj = self.new(args)
|
154
|
+
obj.save
|
155
|
+
obj
|
156
|
+
end
|
142
157
|
|
143
158
|
## Given a method name, return the best-guess dsid
|
144
159
|
def corresponding_datastream_name(method_name)
|
@@ -149,61 +164,7 @@ module ActiveFedora
|
|
149
164
|
nil
|
150
165
|
end
|
151
166
|
|
152
|
-
#Saves a Base object, and any dirty datastreams, then updates
|
153
|
-
#the Solr index for this object.
|
154
|
-
def save
|
155
|
-
|
156
|
-
# If it's a new object, set the conformsTo relationship for Fedora CMA
|
157
|
-
if new_object?
|
158
|
-
result = create
|
159
|
-
else
|
160
|
-
result = update
|
161
|
-
end
|
162
|
-
self.update_index if @metadata_is_dirty == true && ENABLE_SOLR_UPDATES
|
163
|
-
@metadata_is_dirty = false
|
164
|
-
return result
|
165
|
-
end
|
166
|
-
|
167
|
-
def save!
|
168
|
-
save
|
169
|
-
end
|
170
167
|
|
171
|
-
# Refreshes the object's info from Fedora
|
172
|
-
# Note: Currently just registers any new datastreams that have appeared in fedora
|
173
|
-
def refresh
|
174
|
-
# inner_object.load_attributes_from_fedora
|
175
|
-
end
|
176
|
-
|
177
|
-
#Deletes a Base object, also deletes the info indexed in Solr, and
|
178
|
-
#the underlying inner_object. If this object is held in any relationships (ie inbound relationships
|
179
|
-
#outside of this object it will remove it from those items rels-ext as well
|
180
|
-
def delete
|
181
|
-
inbound_relationships(:objects).each_pair do |predicate, objects|
|
182
|
-
objects.each do |obj|
|
183
|
-
if obj.respond_to?(:remove_relationship)
|
184
|
-
obj.remove_relationship(predicate,self)
|
185
|
-
obj.save
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
#Fedora::Repository.instance.delete(@inner_object)
|
191
|
-
pid = self.pid ## cache so it's still available after delete
|
192
|
-
begin
|
193
|
-
@inner_object.delete
|
194
|
-
rescue RestClient::ResourceNotFound =>e
|
195
|
-
raise ObjectNotFoundError, "Unable to find #{pid} in the repository"
|
196
|
-
end
|
197
|
-
if ENABLE_SOLR_UPDATES
|
198
|
-
ActiveFedora::SolrService.instance.conn.delete(pid)
|
199
|
-
# if defined?( Solrizer::Solrizer )
|
200
|
-
# solrizer = Solrizer::Solrizer.new
|
201
|
-
# solrizer.solrize_delete(pid)
|
202
|
-
# end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
168
|
#
|
208
169
|
# Datastream Management
|
209
170
|
#
|
@@ -445,12 +406,6 @@ module ActiveFedora
|
|
445
406
|
@inner_object.new? ? Time.now : @inner_object.profile["objLastModDate"]
|
446
407
|
end
|
447
408
|
|
448
|
-
#return the error list of the inner object (unless it's a new object)
|
449
|
-
def errors
|
450
|
-
#@inner_object.errors
|
451
|
-
[]
|
452
|
-
end
|
453
|
-
|
454
409
|
#return the label of the inner object (unless it's a new object)
|
455
410
|
def label
|
456
411
|
@inner_object.label
|
@@ -546,7 +501,7 @@ module ActiveFedora
|
|
546
501
|
unless klass.ancestors.include? ActiveFedora::Base
|
547
502
|
raise "Cannot adapt #{self.class.name} to #{klass.name}: Not a ActiveFedora::Base subclass"
|
548
503
|
end
|
549
|
-
klass.
|
504
|
+
klass.allocate.init_with(inner_object)
|
550
505
|
end
|
551
506
|
# ** EXPERIMENTAL **
|
552
507
|
#
|
@@ -574,7 +529,7 @@ module ActiveFedora
|
|
574
529
|
|
575
530
|
create_date = solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)].nil? ? solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date).to_s] : solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)]
|
576
531
|
modified_date = solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)].nil? ? solr_doc[ActiveFedora::SolrService.solr_name(:system_modified, :date).to_s] : solr_doc[ActiveFedora::SolrService.solr_name(:system_modified, :date)]
|
577
|
-
obj = self.new(
|
532
|
+
obj = self.allocate.init_with(SolrDigitalObject.new(:pid=>solr_doc[SOLR_DOCUMENT_ID],:create_date=>create_date,:modified_date=>modified_date))
|
578
533
|
#set by default to load any dependent relationship objects from solr as well
|
579
534
|
#need to call rels_ext once so it exists when iterating over datastreams
|
580
535
|
obj.rels_ext
|
@@ -586,24 +541,6 @@ module ActiveFedora
|
|
586
541
|
obj
|
587
542
|
end
|
588
543
|
|
589
|
-
# Updates Solr index with self.
|
590
|
-
def update_index
|
591
|
-
if defined?( Solrizer::Fedora::Solrizer )
|
592
|
-
#logger.info("Trying to solrize pid: #{pid}")
|
593
|
-
solrizer = Solrizer::Fedora::Solrizer.new
|
594
|
-
solrizer.solrize( self )
|
595
|
-
else
|
596
|
-
#logger.info("Trying to update solr for pid: #{pid}")
|
597
|
-
SolrService.instance.conn.update(self.to_solr)
|
598
|
-
end
|
599
|
-
end
|
600
|
-
|
601
|
-
|
602
|
-
def update_attributes(properties)
|
603
|
-
self.attributes=properties
|
604
|
-
save
|
605
|
-
end
|
606
|
-
|
607
544
|
# A convenience method for updating indexed attributes. The passed in hash
|
608
545
|
# must look like this :
|
609
546
|
# {{:name=>{"0"=>"a","1"=>"b"}}
|
@@ -680,11 +617,6 @@ module ActiveFedora
|
|
680
617
|
end
|
681
618
|
end
|
682
619
|
|
683
|
-
# This can be overriden to assert a different model
|
684
|
-
# It's normally called once in the lifecycle, by #create#
|
685
|
-
def assert_content_model
|
686
|
-
add_relationship(:has_model, ActiveFedora::ContentModel.pid_from_ruby_class(self.class))
|
687
|
-
end
|
688
620
|
|
689
621
|
private
|
690
622
|
def configure_defined_datastreams
|
@@ -727,39 +659,16 @@ module ActiveFedora
|
|
727
659
|
end
|
728
660
|
end
|
729
661
|
|
730
|
-
|
731
|
-
|
732
|
-
# Deals with preparing new object to be saved to Fedora, then pushes it and its datastreams into Fedora.
|
733
|
-
def create
|
734
|
-
@inner_object = @inner_object.save #replace the unsaved digital object with a saved digital object
|
735
|
-
assert_content_model
|
736
|
-
@metadata_is_dirty = true
|
737
|
-
update
|
738
|
-
end
|
739
|
-
|
740
|
-
# Pushes the object and all of its new or dirty datastreams into Fedora
|
741
|
-
def update
|
742
|
-
datastreams.each {|k, ds| ds.serialize! }
|
743
|
-
@metadata_is_dirty = datastreams.any? {|k,ds| ds.changed? && (ds.class.included_modules.include?(ActiveFedora::MetadataDatastreamHelper) || ds.instance_of?(ActiveFedora::RelsExtDatastream))}
|
744
|
-
|
745
|
-
result = @inner_object.save
|
746
|
-
|
747
|
-
### Rubydora re-inits the datastreams after a save, so ensure our copy stays in synch
|
748
|
-
@inner_object.datastreams.each do |dsid, ds|
|
749
|
-
datastreams[dsid] = ds
|
750
|
-
ds.model = self if ds.kind_of? RelsExtDatastream
|
751
|
-
end
|
752
|
-
refresh
|
753
|
-
return !!result
|
754
|
-
end
|
755
|
-
|
756
662
|
end
|
757
663
|
|
758
664
|
Base.class_eval do
|
665
|
+
include ActiveFedora::Persistence
|
759
666
|
include Model
|
760
667
|
include Solrizer::FieldNameMapper
|
761
668
|
include Loggable
|
762
669
|
include ActiveModel::Conversion
|
670
|
+
include Validations
|
671
|
+
include Callbacks
|
763
672
|
extend ActiveModel::Naming
|
764
673
|
include Delegating
|
765
674
|
include Associations
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
|
3
|
+
module ActiveFedora
|
4
|
+
# = Active Fedora Callbacks, adapted from ActiveRecord
|
5
|
+
#
|
6
|
+
# Callbacks are hooks into the life cycle of an Active Fedora object that allow you to trigger logic
|
7
|
+
# before or after an alteration of the object state. This can be used to make sure that associated and
|
8
|
+
# dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
|
9
|
+
# before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
|
10
|
+
# the <tt>Base#save</tt> call for a new record:
|
11
|
+
#
|
12
|
+
# * (-) <tt>save</tt>
|
13
|
+
# * (-) <tt>valid</tt>
|
14
|
+
# * (1) <tt>before_validation</tt>
|
15
|
+
# * (-) <tt>validate</tt>
|
16
|
+
# * (2) <tt>after_validation</tt>
|
17
|
+
# * (3) <tt>before_save</tt>
|
18
|
+
# * (4) <tt>before_create</tt>
|
19
|
+
# * (-) <tt>create</tt>
|
20
|
+
# * (5) <tt>after_create</tt>
|
21
|
+
# * (6) <tt>after_save</tt>
|
22
|
+
# * (7) <tt>after_commit</tt>
|
23
|
+
#
|
24
|
+
# Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
|
25
|
+
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
|
26
|
+
# are instantiated as well.
|
27
|
+
#
|
28
|
+
# That's a total of twelve callbacks, which gives you immense power to react and prepare for each state in the
|
29
|
+
# Active Fedora life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
|
30
|
+
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
|
31
|
+
#
|
32
|
+
# Examples:
|
33
|
+
# class CreditCard < ActiveFedora::Base
|
34
|
+
# # Strip everything but digits, so the user can specify "555 234 34" or
|
35
|
+
# # "5552-3434" or both will mean "55523434"
|
36
|
+
# before_validation(:on => :create) do
|
37
|
+
# self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# class Subscription < ActiveFedora::Base
|
42
|
+
# before_create :record_signup
|
43
|
+
#
|
44
|
+
# private
|
45
|
+
# def record_signup
|
46
|
+
# self.signed_up_on = Date.today
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
#
|
51
|
+
# == Inheritable callback queues
|
52
|
+
#
|
53
|
+
# Besides the overwritable callback methods, it's also possible to register callbacks through the
|
54
|
+
# use of the callback macros. Their main advantage is that the macros add behavior into a callback
|
55
|
+
# queue that is kept intact down through an inheritance hierarchy.
|
56
|
+
#
|
57
|
+
# class Topic < ActiveFedora::Base
|
58
|
+
# before_delete :destroy_author
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# class Reply < Topic
|
62
|
+
# before_delete :destroy_readers
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# Now, when <tt>Topic#delete</tt> is run only +destroy_author+ is called. When <tt>Reply#delete</tt> is
|
66
|
+
# run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
|
67
|
+
# where the +before_delete+ method is overridden:
|
68
|
+
#
|
69
|
+
# class Topic < ActiveFedora::Base
|
70
|
+
# def before_delete() destroy_author end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# class Reply < Topic
|
74
|
+
# def before_delete() destroy_readers end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# In that case, <tt>Reply#delete</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
|
78
|
+
# So, use the callback macros when you want to ensure that a certain callback is called for the entire
|
79
|
+
# hierarchy, and use the regular overwriteable methods when you want to leave it up to each descendant
|
80
|
+
# to decide whether they want to call +super+ and trigger the inherited callbacks.
|
81
|
+
#
|
82
|
+
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
|
83
|
+
# callbacks before specifying the associations. Otherwise, you might trigger the loading of a
|
84
|
+
# child before the parent has registered the callbacks and they won't be inherited.
|
85
|
+
#
|
86
|
+
# == Types of callbacks
|
87
|
+
#
|
88
|
+
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
89
|
+
# inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
|
90
|
+
# are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
|
91
|
+
# creating mix-ins), and inline eval methods are deprecated.
|
92
|
+
#
|
93
|
+
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
|
94
|
+
#
|
95
|
+
# class Topic < ActiveFedora::Base
|
96
|
+
# before_delete :delete_parents
|
97
|
+
#
|
98
|
+
# private
|
99
|
+
# def delete_parents
|
100
|
+
# self.class.delete_all "parent_id = #{id}"
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# The callback objects have methods named after the callback called with the record as the only parameter, such as:
|
105
|
+
#
|
106
|
+
# class BankAccount < ActiveFedora::Base
|
107
|
+
# before_save EncryptionWrapper.new
|
108
|
+
# after_save EncryptionWrapper.new
|
109
|
+
# after_initialize EncryptionWrapper.new
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# class EncryptionWrapper
|
113
|
+
# def before_save(record)
|
114
|
+
# record.credit_card_number = encrypt(record.credit_card_number)
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# def after_save(record)
|
118
|
+
# record.credit_card_number = decrypt(record.credit_card_number)
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# alias_method :after_find, :after_save
|
122
|
+
#
|
123
|
+
# private
|
124
|
+
# def encrypt(value)
|
125
|
+
# # Secrecy is committed
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# def decrypt(value)
|
129
|
+
# # Secrecy is unveiled
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
|
134
|
+
# a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
|
135
|
+
# initialization data such as the name of the attribute to work with:
|
136
|
+
#
|
137
|
+
# class BankAccount < ActiveFedora::Base
|
138
|
+
# before_save EncryptionWrapper.new("credit_card_number")
|
139
|
+
# after_save EncryptionWrapper.new("credit_card_number")
|
140
|
+
# after_initialize EncryptionWrapper.new("credit_card_number")
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# class EncryptionWrapper
|
144
|
+
# def initialize(attribute)
|
145
|
+
# @attribute = attribute
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# def before_save(record)
|
149
|
+
# record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# def after_save(record)
|
153
|
+
# record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# alias_method :after_find, :after_save
|
157
|
+
#
|
158
|
+
# private
|
159
|
+
# def encrypt(value)
|
160
|
+
# # Secrecy is committed
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# def decrypt(value)
|
164
|
+
# # Secrecy is unveiled
|
165
|
+
# end
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# The callback macros usually accept a symbol for the method they're supposed to run, but you can also
|
169
|
+
# pass a "method string", which will then be evaluated within the binding of the callback. Example:
|
170
|
+
#
|
171
|
+
# class Topic < ActiveFedora::Base
|
172
|
+
# before_delete 'self.class.delete_all "parent_id = #{id}"'
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
|
176
|
+
# is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
|
177
|
+
#
|
178
|
+
# class Topic < ActiveFedora::Base
|
179
|
+
# before_delete 'self.class.delete_all "parent_id = #{id}"',
|
180
|
+
# 'puts "Evaluated after parents are deleted"'
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# == <tt>before_validation*</tt> returning statements
|
184
|
+
#
|
185
|
+
# If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
|
186
|
+
# aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
|
187
|
+
# ActiveFedora::RecordInvalid exception. Nothing will be appended to the errors object.
|
188
|
+
#
|
189
|
+
# == Canceling callbacks
|
190
|
+
#
|
191
|
+
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
|
192
|
+
# cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
|
193
|
+
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
|
194
|
+
# methods on the model, which are called last.
|
195
|
+
#
|
196
|
+
# == Debugging callbacks
|
197
|
+
#
|
198
|
+
# The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
|
199
|
+
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
|
200
|
+
# defines what part of the chain the callback runs in.
|
201
|
+
#
|
202
|
+
# To find all callbacks in the before_save callback chain:
|
203
|
+
#
|
204
|
+
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
|
205
|
+
#
|
206
|
+
# Returns an array of callback objects that form the before_save chain.
|
207
|
+
#
|
208
|
+
# To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
|
209
|
+
#
|
210
|
+
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
|
211
|
+
#
|
212
|
+
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
|
213
|
+
#
|
214
|
+
module Callbacks
|
215
|
+
extend ActiveSupport::Concern
|
216
|
+
|
217
|
+
CALLBACKS = [
|
218
|
+
:after_initialize, :after_find, :before_validation, :after_validation,
|
219
|
+
:before_save, :around_save, :after_save, :before_create, :around_create,
|
220
|
+
:after_create, :before_update, :around_update, :after_update,
|
221
|
+
:before_delete, :around_delete, :after_delete
|
222
|
+
]
|
223
|
+
|
224
|
+
included do
|
225
|
+
extend ActiveModel::Callbacks
|
226
|
+
include ActiveModel::Validations::Callbacks
|
227
|
+
|
228
|
+
define_model_callbacks :initialize, :find, :only => :after
|
229
|
+
define_model_callbacks :save, :create, :update, :delete
|
230
|
+
end
|
231
|
+
|
232
|
+
def destroy #:nodoc:
|
233
|
+
run_callbacks(:destroy) { super }
|
234
|
+
end
|
235
|
+
|
236
|
+
def save #:nodoc:
|
237
|
+
run_callbacks(:save) { super }
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
|
243
|
+
def create #:nodoc:
|
244
|
+
run_callbacks(:create) { super }
|
245
|
+
end
|
246
|
+
|
247
|
+
def update(*) #:nodoc:
|
248
|
+
run_callbacks(:update) { super }
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|