Created
August 30, 2016 18:59
-
-
Save harlow/ccb529cab3b27534f5bc7e9e44d9746c to your computer and use it in GitHub Desktop.
Form objects example from https://forum.upcase.com/t/form-objects/2267
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment