module TransForms module MainModel extend ActiveSupport::Concern module ClassMethods # This method will extend the BaseForm functionality with the # TransForms::MainModel::Active module. def set_main_model(model, options = {}) include TransForms::MainModel::Active # Stores the main_model record in a class_attribute class_attribute :main_model self.main_model = model # Defines an instance accessor for the main_model attr_accessor model # Implements proxy module that overwrites model_name method # to instead return an ActiveModel::Mame class for the # main_model class include TransForms::MainModel::Proxy if options[:proxy] end end module Active extend ActiveSupport::Concern included do # Since the after_save_on_error is not part of this module, we make sure # that the methods exist before calling it. It should not be necessary since # the Callbacks module is included by default in the FormBase, but added # anyway. If this module is included manually (like in one of the specs) then # it prevents any error to occur. after_save_on_error :assert_record_on_error if respond_to?(:after_save_on_error) end # Called from FormError to collect error messages from all possible # models involved in the form transation. def main_instance send main_model end # Combines the errors from the FormModel as well as the main model instances def errors @errors ||= FormErrors.new(self, super) end # In it's default implementation, this method will look at the class name # and try to match it to an attribute defined in the form model. If a match # is found, then it will assign the model to that attribute. # # This method is encouraged to overwrite when there is custom conditions # for how to handle the assignments. # # For example: # # class UserUpdater < ApplicationTransForm # attr_accessor :admin # set_main_model :user # # def model=(model) # if model.role == :admin # self.admin = model # else # self.user = model # end # end # # # ... # end # def model=(model) element = to_element(model) if respond_to?("#{element}=") send("#{element}=", model) else raise TransForms::NotImplementedError end end protected # This method is assigned to the after_save_on_error callback # # If an error was raised in a create! statement then the variable might not # have been assigned and would prevent error messages to be displayed. This # makes sure that the instance that raised the error will be assigned if it # would have been the main model. def assert_record_on_error(e) if e.respond_to?(:record) && e.record.present? element = to_element(e.record) if main_model == element.to_sym && send(element).nil? send("#{element}=", e.record) end end end # A method to retrieve the underscored version of a record's class name. # The procedure is slightly different in Rails 3 and Rails 4, that's what # this method takes into consideration. def to_element(record) if record.class.model_name.is_a?(ActiveModel::Name) record.class.model_name.element else record.class.model_name.underscore end end end end end TransForms::FormBase.send(:include, TransForms::MainModel)