# Model that we want to keep slim:

class User < ActiveRecord::Base

  validates_presence_of :email, :screen_name

end


# Additional requirements for sign up form:
# - Create an invoice
# - Test an invite token
# - Send a welcome email



# How a controller would look like if it supports the view
# without help from a model.

class RegistrationsController < ApplicationController

  def new
    @user = User.new
  end
  
  def create
    @user = User.new(params[:user])
    if @user.valid?
      if Invite.known?(params[:user][:token])
        User.transaction do
          @user.save!
          @user.invoices.create!(...)
        end
        Mailer.welcome(@user).deliver
        redirect_to root_path
      else
        @user.errors.add(:token, 'is not valid')
        render 'new'
      end
    else
      render 'new'
    end
  end

end




# How a controller would look like if we had a supporting FormModel:

class RegistrationsController < ApplicationController

  def new
    @user = SignUp.new
  end
  
  def create
    @user = SignUp.new
    if @user.save
      redirect_to root_path
      else
      render 'new'
    end
  end
  
  def edit
    @user = User.find(params[:id])
    @user = SignUp.new(@user)
  end

end




# Classic read-only "presenters" that are not useful in forms:

module SignUp

  def welcome_message
    "Hi, #{name}"
  end
  
  validates_presence_of :token # does not work
  
end

# How to apply them:

@user = User.new
@user.extend(SignUp) # equivalent to @user.singleton_class.include(SignUp)





# Form model, New Bamboo style

class SignUp
  include ActiveModel::Base
  
  attr_accessor :token
  
  delegates :email, :email=, :password, :password=, :to => :user
  
  validate :validate_token
  
  def initialize(user)
    @user = user
  end
  
  def valid?
    super
    @user.valid?
    errors.merge(@user.errors)
  end
  
  def save
    if valid?
      transaction do
        user = User.create!(:email => email, :password => password)
        user.invoices.create!
        Mailer.welcome(user).deliver
      end
      true
    else
      false
    end
  end
  
  private
  
  def validate_token
    unless Invite.token_known?(token)
      errors.add(:token, 'is not known')
    end
  end

end



# Form model with a FormModelTrait that gets rid of a lot of
# duplication by making some assumptions about the relationship
# between form model and inner model.
class SignUp

  # delegate attributes, merge validation errors
  does 'form_model', User
  
  attr_accessor :token
  
  validate :validate_token
  
  def do_save
    user = User.create!(:email => email, :password => password)
    user.invoices.create!
    Mailer.welcome(user).deliver
  end
  
  private
  
  def validate_token
    unless Invite.token_known?(token)
      errors.add(:token, 'is not known')
    end
  end

end


# Form model that gets rid of a lot of duplication by simply inheriting
# from the "inner model".

class SignUp < User

  disable_sti! # unclear how to do this
  
  attr_accessor :token
  
  validate :validate_token
  
  after_save :do_stuff
  
  attr_accessor :period
  
  def do_stuff
    invoices.create!
    Mailer.welcome(self).deliver
  end
  
  private
  
  def validate_token
    unless Invite.token_known?(token)
      errors.add(:token, 'is not known')
    end
  end

end

