class Registration # Make this model play nice with `form_for`. Gives us validations, initialize, etc include ActiveModel::Model # Accessors for the fields we are exposing in the form attr_accessor :email, :password, :zip # This is an implementation detail of our authentication (Clearance). It's ultimately going to # sign this object in and that process expects a user object that has these fields available delegate :remember_token, :id, to: user # For this object to be valid, we want the child objects (user and profile) to be valid. This is # the validation method that does this. validate :validate_children # We tell registration it's a user. This will keep it pointing to Users controller and allow it to use # user translations def self.model_name User.model_name end # This is what our controller calls to save the user. def save if valid? # Create a transaction. If any of the database stuff in here fails, they will raise an exception # (because they are bang methods) and rollback the transaction. ActiveRecord::Base.transaction do user.save! profile.save! end end end private # Initialize the user object with the arguments that apply to user def user @user ||= User.new(email: email, password: password) end # initialize the profile object with the arguments that apply to profile def profile @profile ||= user.build_profile(zip: zip) end # the implementation of our validation method. We just delegate to our # two child objects which define their validations (presence on all fields in this case) def validate_children if user.invalid? promote_errors(user.errors) end if profile.invalid? promote_errors(profile.errors) end end # Errors on `user` or `profile` aren't helpful to us because `user.email` isn't on our form. `email` is however. # Take all errors from our child objects and promote them to the same field on the base object. This will make # them render properly on the form. def promote_errors(child_errors) child_errors.each do |attribute, message| errors.add(attribute, message) end end end