RailsCasts - Ruby on Rails Screencasts

RailsCasts Pro episodes are now free!

Learn more or hide this

Form Objects

#416 Form Objects pro

Jun 03, 2013 | 18 minutes | Refactoring, Forms
Models have a tendency to become a complex mess as an application grows. In this episode you will learn a couple of techniques to extract form-behavior out into its own class.
Click to Play Video ▶
Tweet
  • Download:
  • source code Project Files in Zip (69.4 KB)
  • mp4 Full Size H.264 Video (45 MB)
  • m4v Smaller H.264 Video (25.6 MB)
  • webm Full Size VP8 Video (29.6 MB)
  • ogv Full Size Theora Video (59.7 MB)
Browse_code Browse Source Code

Resources

app/forms/password_form.rb
class PasswordForm
 # Rails 4: include ActiveModel::Model
 extend ActiveModel::Naming
 include ActiveModel::Conversion
 include ActiveModel::Validations
 def persisted?
 false
 end
 attr_accessor :original_password, :new_password
 validate :verify_original_password
 validates_presence_of :original_password, :new_password
 validates_confirmation_of :new_password
 validates_length_of :new_password, minimum: 6
 def initialize(user)
 @user = user
 end
 
 def submit(params)
 self.original_password = params[:original_password]
 self.new_password = params[:new_password]
 self.new_password_confirmation = params[:new_password_confirmation]
 if valid?
 @user.password = new_password
 @user.save!
 true
 else
 false
 end
 end
 def verify_original_password
 unless @user.authenticate(original_password)
 errors.add :original_password, "is not correct"
 end
 end
end
app/forms/signup_form.rb
class SignupForm
 # Rails 4: include ActiveModel::Model
 extend ActiveModel::Naming
 include ActiveModel::Conversion
 include ActiveModel::Validations
 def persisted?
 false
 end
 
 def self.model_name
 ActiveModel::Name.new(self, nil, "User")
 end
 validates_presence_of :username
 validate :verify_unique_username
 validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
 validates_length_of :password, minimum: 6
 
 delegate :username, :email, :password, :password_confirmation, to: :user
 delegate :twitter_name, :github_name, :bio, to: :profile
 
 def user
 @user ||= User.new
 end
 
 def profile
 @profile ||= user.build_profile
 end
 
 def submit(params)
 user.attributes = params.slice(:username, :email, :password, :password_confirmation)
 profile.attributes = params.slice(:twitter_name, :github_name, :bio)
 self.subscribed = params[:subscribed]
 if valid?
 generate_token
 user.save!
 profile.save!
 true
 else
 false
 end
 end
 def subscribed
 user.subscribed_at
 end
 def subscribed=(checkbox)
 user.subscribed_at = Time.zone.now if checkbox == "1"
 end
 def generate_token
 begin
 user.token = SecureRandom.hex
 end while User.exists?(token: user.token)
 end
 def verify_unique_username
 if User.exists? username: username
 errors.add :username, "has already been taken"
 end
 end
end
users_controller.rb
def new
 @signup_form = SignupForm.new
end
def create
 @signup_form = SignupForm.new
 if @signup_form.submit(params[:user])
 session[:user_id] = @signup_form.user.id
 redirect_to @signup_form.user, notice: "Thank you for signing up!"
 else
 render "new"
 end
end
users/new.html.erb
<%= form_for @signup_form do |f| %>
passwords_controller.rb
def new
 @password_form = PasswordForm.new(current_user)
end
def create
 @password_form = PasswordForm.new(current_user)
 if @password_form.submit(params[:password_form])
 redirect_to current_user, notice: "Successfully changed password."
 else
 render "new"
 end
end
passwords/new.html.erb
<%= form_for @password_form, url: passwords_path, method: :post do |f| %>
loading

AltStyle によって変換されたページ (->オリジナル) /