Module: Hoodoo::ActiveRecord::Writer
- Defined in:
- lib/hoodoo/active/active_record/writer.rb
Overview
Support mixin for models subclassed from ActiveRecord::Base providing context-aware data writing, allowing service authors to auto-inherit persistence-related features from Hoodoo without changing their own code.
See individual module methods for examples, along with:
Dependency Hoodoo::ActiveRecord::ErrorMapping is also included automatically.
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
-
.included(model) ⇒ Object
Instantiates this module when it is included.
-
.instantiate(model) ⇒ Object
When instantiated in an ActiveRecord::Base subclass, all of the Hoodoo::ActiveRecord::Writer::ClassMethods methods are defined as class methods on the including class.
Instance Method Summary collapse
-
#persist_in(context) ⇒ Object
Instance equivalent of Hoodoo::ActiveRecord::Writer::ClassMethods.persist_in - see that for details.
Class Method Details
.included(model) ⇒ Object
Instantiates this module when it is included.
Example:
class SomeModel < ActiveRecord::Base
include Hoodoo::ActiveRecord::Writer
# ...
end
model
-
The ActiveRecord::Base descendant class that is including this module.
47 48 49 50 51 52 53 54 |
# File 'lib/hoodoo/active/active_record/writer.rb', line 47 def self.included( model ) unless model == Hoodoo::ActiveRecord::Base model.send( :include, Hoodoo::ActiveRecord::ErrorMapping ) instantiate( model ) end super( model ) end |
.instantiate(model) ⇒ Object
When instantiated in an ActiveRecord::Base subclass, all of the Hoodoo::ActiveRecord::Writer::ClassMethods methods are defined as class methods on the including class.
This module depends upon Hoodoo::ActiveRecord::ErrorMapping, so that will be auto-included first if it isn’t already.
model
-
The ActiveRecord::Base descendant that is including this module.
66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/hoodoo/active/active_record/writer.rb', line 66 def self.instantiate( model ) model.extend( ClassMethods ) # See instance method "persist_in" for how this gets used. # model.validate do if @nz_co_loyalty_hoodoo_writer_db_uniqueness_violation == true errors.add( :base, 'has already been taken' ) end end end |
Instance Method Details
#persist_in(context) ⇒ Object
Instance equivalent of Hoodoo::ActiveRecord::Writer::ClassMethods.persist_in - see that for details. The class method just calls here, having constructed an instance based on the attributes it was given. If you have already built an instance yourself, just call this instance method equivalent instead.
As an instance-based method, the return value and error handling semantics differ from the class-based counterpart. Instead of checking “persisted?”, check the return value of persist_in
. This means you can also use persist_in
to save a previousl persisted, but now updated record, should you so wish.
def create( context )
attributes = mapping_of( context.request.body )
model_instance = Unique.new( attributes )
# ...maybe make other changes to model_instance, then...
unless model_instance.persist_in( context ).equal?( :success )
# Error condition. If you're using the error handler mixin
# in Hoodoo::ActiveRecord::ErrorMapping, do this:
#
context.response.add_errors( model_instance.platform_errors )
return # Early exit
end
# ...any other processing...
context.response.set_resource( rendering_of( context, model_instance ) )
end
Parameters:
context
-
Hoodoo::Services::Context instance describing a call context. This is typically a value passed to one of the Hoodoo::Services::Implementation instance methods that a resource subclass implements.
Returns a Symbol of :success
or :failure
indicating the outcome of the same attempt. In the event of failure, the model will be invalid and not persisted; you can read errors immediately and should avoid unnecessarily re-running validations by calling valid?
or validate
on the instance.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/hoodoo/active/active_record/writer.rb', line 125 def persist_in( context ) # If this model has an ActiveRecord uniqueness validation, it is # still subject to race conditions and MUST be backed by a database # constraint. If this constraint fails, try to re-run model # validations just in case it was a race condition case; though of # course, it could be that there is *only* a database constraint and # no model validation. If there is *only* a model validation, the # model is ill-defined and at risk. # TODO: This flag is nasty but seems unavoidable. Whenever you query # the validity of a record, AR will always clear all errors and # then (re-)run validations. We cannot just add an error to # "base" and expect it to survive. Instead, it's necessary to # use this flag to signal to the custom validator added in the # 'self.instantiate' implementation earlier that it should add # an error. Trouble is, when do we clear the flag...? # # This solution works but is inelegant and fragile. # @nz_co_loyalty_hoodoo_writer_db_uniqueness_violation = false # First just see if we have any problems saving anyway. # errors_occurred = begin self.transaction( :requires_new => true ) do :any unless self.save end rescue ::ActiveRecord::RecordNotUnique => error :duplication end # If an exception caught a duplication violation then either there is # a race condition on an AR-level uniqueness validation, or no such # validation at all. Thus, re-run validations with "valid?" and if it # still seems OK we must be dealing with a database-only constraint. # Set the magic flag (ugh, see earlier) to signal that when # validations run, they should add a relevant error to "base". # if errors_occurred == :duplication if self.valid? @nz_co_loyalty_hoodoo_writer_db_uniqueness_violation = true self.validate end end return errors_occurred.nil? ? :success : :failure end |