active-fedora 3.2.0.pre3 → 3.2.0.pre4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|