В недавнем проекте я использовал form object шаблон, чтобы создать пользователя Devise и компанию за один шаг. Это включает в себя обход Devise RegistrationsController и создание собственного SignupsController.
# config/routes.rb
# Signups
get 'signup' => 'signups#new', as: :new_signup
post 'signup' => 'signups#create', as: :signups
# app/controllers/signups_controller.rb
class SignupsController < ApplicationController
def new
@signup = Signup.new
end
def create
@signup = Signup.new(params[:signup])
if @signup.save
sign_in @signup.user
redirect_to projects_path, notice: 'You signed up successfully.'
else
render action: :new
end
end
end
Модель регистрации, на которую ссылаются, определяется как объект формы.
# app/models/signup.rb
# The signup class is a form object class that helps with
# creating a user, account and project all in one step and form
class Signup
# Available in Rails 4
include ActiveModel::Model
attr_reader :user
attr_reader :account
attr_reader :membership
attr_accessor :name
attr_accessor :company_name
attr_accessor :email
attr_accessor :password
validates :name, :company_name, :email, :password, presence: true
def save
# Validate signup object
return false unless valid?
delegate_attributes_for_user
delegate_attributes_for_account
delegate_errors_for_user unless @user.valid?
delegate_errors_for_account unless @account.valid?
# Have any errors been added by validating user and account?
if !errors.any?
persist!
true
else
false
end
end
private
def delegate_attributes_for_user
@user = User.new do |user|
user.name = name
user.email = email
user.password = password
user.password_confirmation = password
end
end
def delegate_attributes_for_account
@account = Account.new do |account|
account.name = company_name
end
end
def delegate_errors_for_user
errors.add(:name, @user.errors[:name].first) if @user.errors[:name].present?
errors.add(:email, @user.errors[:email].first) if @user.errors[:email].present?
errors.add(:password, @user.errors[:password].first) if @user.errors[:password].present?
end
def delegate_errors_for_account
errors.add(:company_name, @account.errors[:name].first) if @account.errors[:name].present?
end
def persist!
@user.save!
@account.save!
create_admin_membership
end
def create_admin_membership
@membership = Membership.create! do |membership|
membership.user = @user
membership.account = @account
membership.admin = true
end
end
end
Отличное чтение по объектам формы (и источник для моей работы) — это эта запись в блоге CodeClimate о рефакторинге.
В целом, я предпочитаю этот подход использованию accepts_nested_attributes_for, хотя могут быть и более эффективные способы. Дайте мне знать, если вы найдете его!
===
Изменить: добавлены модели, на которые есть ссылки, и их ассоциации для лучшего понимания.
class User < ActiveRecord::Base
# Memberships and accounts
has_many :memberships
has_many :accounts, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :account
end
class Account < ActiveRecord::Base
# Memberships and members
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships
has_many :admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => true }
has_many :non_admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => false }
end
Эта структура в модели смоделирована вместе с saucy, драгоценным камнем от thinkbot. Источник не находится на Github AFAIK, но его можно извлечь из драгоценного камня. Я многому научился, переделывая его.
person
Thomas Klemm
schedule
25.03.2013