Beyond MVC
-
Upload
kyle-rames -
Category
Technology
-
view
105 -
download
0
Transcript of Beyond MVC
1 class UserAuthenticator 2 def initialize(user) 3 @user = user 4 end 5 6 def authenticate(unencrypted_password) 7 return false unless @user 8 9 if BCrypt::Password.new(@user.password_digest) == 10 unencrypted_password 11 @user 12 else 13 false 14 end 15 end 16 end
1 class UserAuthenticator 2 def initialize(user) 3 @user = user 4 end 5 6 def authenticate(unencrypted_password) 7 return false unless @user 8 9 if BCrypt::Password.new(@user.password_digest) == 10 unencrypted_password 11 @user 12 else 13 false 14 end 15 end 16 end
1 class UserAuthenticator 2 def initialize(user) 3 @user = user 4 end 5 6 def authenticate(unencrypted_password) 7 return false unless @user 8 9 if BCrypt::Password.new(@user.password_digest) == 10 unencrypted_password 11 @user 12 else 13 false 14 end 15 end 16 end
1 class SessionsController < ApplicationController 2 def create 3 user = User.where(email: params[:email]).first 4 5 if UserAuthenticator.new(user).
authenticate(params[:password]) 6 self.current_user = user 7 redirect_to dashboard_path 8 else 9 flash[:alert] = "Login failed." 10 render "new" 11 end 12 end 13 end
1 class SessionsController < ApplicationController 2 def create 3 user = User.where(email: params[:email]).first 4 5 if UserAuthenticator.new(user).
authenticate(params[:password]) 6 self.current_user = user 7 redirect_to dashboard_path 8 else 9 flash[:alert] = "Login failed." 10 render "new" 11 end 12 end 13 end
Martin Fowler Says
The conceptual problem I run into in a lot of codebases is that rather than
representing a process, the "service objects" represent "a thing that does the
process".
1 class UserAddition 2 def begin 3 # ... 4 end 5 6 def collect_extra_info 7 # ... 8 end 9 10 def approve 11 # ... 12 end 13 14 def complete 15 # ... 16 end 17 end
1 class SignupForm 2 include ActiveModel::Model 3 4 validates_presence_of :email 5 6 delegate :name, :name= to: :company, prefix: true 7 delegate :name, :name=, :email, :email= to: :user 8 9 def company 10 @company ||= Company.new 11 end 12 13 def user 14 @user ||= company.build_user 15 end 16 17 def persisted? 18 false # Forms are never themselves persisted 19 end 20 21 def save 22 valid? ? persist!; true : false 23 end 24 25 private 26 27 def persist! 28 company.save && user.save 29 end 30 end
1 class SignupForm 2 include ActiveModel::Model 3 4 validates_presence_of :email 5 6 delegate :name, :name= to: :company, prefix: true 7 delegate :name, :name=, :email, :email= to: :user 8 9 def company 10 @company ||= Company.new 11 end 12 13 def user 14 @user ||= company.build_user 15 end 16 17 def persisted? 18 false # Forms are never themselves persisted 19 end 20 21 def save 22 valid? ? persist!; true : false 23 end 24 25 private 26 27 def persist! 28 company.save && user.save 29 end 30 end
1 class SignupForm 2 include ActiveModel::Model 3 4 validates_presence_of :email 5 6 delegate :name, :name= to: :company, prefix: true 7 delegate :name, :name=, :email, :email= to: :user 8 9 def company 10 @company ||= Company.new 11 end 12 13 def user 14 @user ||= company.build_user 15 end 16 17 def persisted? 18 false # Forms are never themselves persisted 19 end 20 21 def save 22 valid? ? persist!; true : false 23 end 24 25 private 26 27 def persist! 28 company.save && user.save 29 end 30 end
1 class SignupForm 2 include ActiveModel::Model 3 4 validates_presence_of :email 5 6 delegate :name, :name= to: :company, prefix: true 7 delegate :name, :name=, :email, :email= to: :user 8 9 def company 10 @company ||= Company.new 11 end 12 13 def user 14 @user ||= company.build_user 15 end 16 17 def persisted? 18 false # Forms are never themselves persisted 19 end 20 21 def save 22 valid? ? persist!; true : false 23 end 24 25 private 26 27 def persist! 28 company.save && user.save 29 end 30 end
1 class SignupsController < ApplicationController 2 def create 3 @signup = SignupForm.new(params[:signup_form]) 4 5 if @signup.save 6 redirect_to dashboard_path 7 else 8 render "new" 9 end 10 end 11 end
1 class SignupsController < ApplicationController 2 def create 3 @signup = SignupForm.new(params[:signup_form]) 4 5 if @signup.save 6 redirect_to dashboard_path 7 else 8 render "new" 9 end 10 end 11 end
1 class User < ActiveRecord::Base 2 3 validates_length_of :new_password, 4 minimum: 6, 5 if: :changing_password 6 7 ... 8 end
1 class User < ActiveRecord::Base 2 3 validates_length_of :new_password, 4 minimum: 6, 5 if: :changing_password 6 7 ... 8 end
1 class PasswordForm 2 include ActiveModel::Model 3 4 attr_accessor :original_password, :new_password 5 6 validate :verify_original_password 7 validates_presence_of :original_password, :new_password 8 validates_confirmation_of :new_password 9 validates_length_of :new_password, minimum: 6 10 11 def submit(params) 12 self.original_password = params[:original_password] 13 self.new_password = params[:new_password] 14 self.new_password_confirmation = params[ 15 :new_password_confirmation] 16 if valid? 17 @user.password = new_password; @user.save; true 18 else 19 false 20 end 21 end 22 23 def initialize(user); @user = user; end 24 def persisted?; false; end 25 def verify_original_password; ...; end 26 end
1 class PasswordForm 2 include ActiveModel::Model 3 4 attr_accessor :original_password, :new_password 5 6 validate :verify_original_password 7 validates_presence_of :original_password, :new_password 8 validates_confirmation_of :new_password 9 validates_length_of :new_password, minimum: 6 10 11 def submit(params) 12 self.original_password = params[:original_password] 13 self.new_password = params[:new_password] 14 self.new_password_confirmation = params[ 15 :new_password_confirmation] 16 if valid? 17 @user.password = new_password; @user.save; true 18 else 19 false 20 end 21 end 22 23 def initialize(user); @user = user; end 24 def persisted?; false; end 25 def verify_original_password; ...; end 26 end
1 class PasswordsController < ApplicationController 2 def new 3 @password_form = PasswordForm.new(current_user) 4 end 5 6 def create 7 @password_form = PasswordForm.new(current_user) 8 if @password_form.submit(params[:password_form]) 9 redirect_to current_user, notice: “Success!" 10 else 11 render "new" 12 end 13 end 14 end
1 <div id="profile"> 2 <dl> 3 <dt>Username:</dt> 4 <dd><%= @user.username %></dd> 5 <dt>Member Since:</dt> 6 <dd><%= @user.member_since %></dd> 7 <dt>Website:</dt> 8 <dd> 9 <% if @user.url.present? %> 10 <%= link_to @user.url, @user.url %> 11 <% else %> 12 <span class="none">None given</span> 13 <% end %> 14 </dd> 15 <dt>Twitter:</dt> 16 <dd> 17 <% if @user.twitter_name.present? %> 18 <%= link_to @user.twitter_name, "http://twitter.com/#{@user. 19 twitter_name}" %> 20 <% else %> 21 <span class="none">None given</span> 22 <% end %> 23 </dd> 24 </dl> 25 </div>
1 <div id="profile"> 2 <dl> 3 <dt>Username:</dt> 4 <dd><%= @user.username %></dd> 5 <dt>Member Since:</dt> 6 <dd><%= @user.member_since %></dd> 7 <dt>Website:</dt> 8 <dd> 9 <% if @user.url.present? %> 10 <%= link_to @user.url, @user.url %> 11 <% else %> 12 <span class="none">None given</span> 13 <% end %> 14 </dd> 15 <dt>Twitter:</dt> 16 <dd> 17 <% if @user.twitter_name.present? %> 18 <%= link_to @user.twitter_name, "http://twitter.com/#{@user. 19 twitter_name}" %> 20 <% else %> 21 <span class="none">None given</span> 22 <% end %> 23 </dd> 24 </dl> 25 </div>
1 class UserPresenter 2 attr_reader :user 3 delegate :username, :url, :twitter_name, 4 :full_name,:created_at to: :user 5 6 def initialize(user, template) 7 @user, @template = user, template 8 end 9 10 def member_since 11 user.created_at.strftime("%B %e, %Y") 12 end 13 14 def website 15 handle_none user.url { h.link_to(user.url, user.url) } 16 end 17 18 def twitter 19 handle_none user.twitter_name { h.link_to user.twitter_name, "http: 20 //twitter.com/#{user.twitter_name}" } 21 end 22 23 private 24 def h 25 @template 26 end 27 28 def handle_none(value) 29 ... 30 end 31 end
1 class UserPresenter 2 attr_reader :user 3 delegate :username, :url, :twitter_name, 4 :full_name,:created_at to: :user 5 6 def initialize(user, template) 7 @user, @template = user, template 8 end 9 10 def member_since 11 user.created_at.strftime("%B %e, %Y") 12 end 13 14 def website 15 handle_none user.url { h.link_to(user.url, user.url) } 16 end 17 18 def twitter 19 handle_none user.twitter_name { h.link_to user.twitter_name, "http: 20 //twitter.com/#{user.twitter_name}" } 21 end 22 23 private 24 def h 25 @template 26 end 27 28 def handle_none(value) 29 ... 30 end 31 end
1 class UserPresenter 2 attr_reader :user 3 delegate :username, :url, :twitter_name, 4 :full_name,:created_at to: :user 5 6 def initialize(user, template) 7 @user, @template = user, template 8 end 9 10 def member_since 11 user.created_at.strftime("%B %e, %Y") 12 end 13 14 def website 15 handle_none user.url { h.link_to(user.url, user.url) } 16 end 17 18 def twitter 19 handle_none user.twitter_name { h.link_to user.twitter_name, "http: 20 //twitter.com/#{user.twitter_name}" } 21 end 22 23 private 24 def h 25 @template 26 end 27 28 def handle_none(value) 29 ... 30 end 31 end
1 class UserPresenter 2 attr_reader :user 3 delegate :username, :url, :twitter_name, 4 :full_name,:created_at to: :user 5 6 def initialize(user, template) 7 @user, @template = user, template 8 end 9 10 def member_since 11 user.created_at.strftime("%B %e, %Y") 12 end 13 14 def website 15 handle_none user.url { h.link_to(user.url, user.url) } 16 end 17 18 def twitter 19 handle_none user.twitter_name { h.link_to user.twitter_name, "http: 20 //twitter.com/#{user.twitter_name}" } 21 end 22 23 private 24 def h 25 @template 26 end 27 28 def handle_none(value) 29 ... 30 end 31 end
1 <div id="profile"> 2 <dl> 3 <dt>Username:</dt> 4 <dd><%= @user_presenter.username %></dd> 5 <dt>Member Since:</dt> 6 <dd><%= @user_presenter.member_since %></dd> 7 <dt>Website:</dt> 8 <dd><%= @user_presenter.website %></dd> 9 <dt>Twitter:</dt> 10 <dd><%= @user_presenter.twitter %></dd> 11 </dl> 12 </div>
1 <div id="profile"> 2 <dl> 3 <dt>Username:</dt> 4 <dd><%= @user_presenter.username %></dd> 5 <dt>Member Since:</dt> 6 <dd><%= @user_presenter.member_since %></dd> 7 <dt>Website:</dt> 8 <dd><%= @user_presenter.website %></dd> 9 <dt>Twitter:</dt> 10 <dd><%= @user_presenter.twitter %></dd> 11 </dl> 12 </div>
1 def show 2 user = User.find(params[:id]) 3 @user_presenter = UserPresenter.new(user, view_context) 4 end
1 def show 2 user = User.find(params[:id]) 3 @user_presenter = UserPresenter.new(user, view_context) 4 end
1 class UserPresenter 2 attr_reader :user 3 delegate :username, :member_since, :url, 4 :twitter_name, :full_name,:created_at to: :user 5 6 def initialize(user, template) 7 @user, @template = user, template 8 end 9 10 def member_since 11 user.created_at.strftime("%B %e, %Y") 12 end 13 14 def website 15 handle_none user.url { h.link_to(user.url, user.url) } 16 end 17 18 def twitter 19 handle_none user.twitter_name { h.link_to user.twitter_name, "http: 20 //twitter.com/#{user.twitter_name}" } 21 end 22 23 private 24 def h 25 @template 26 end 27 28 def handle_none(value) 29 ... 30 end 31 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
1 class UserPresenter < Draper::Decorator 2 delegate_all 3 4 def member_since 5 object.created_at.strftime("%B %e, %Y") 6 end 7 8 def website 9 handle_none object.url do 10 h.link_to(object.url, object.url) 11 end 12 end 13 14 def twitter 15 handle_none object.twitter_name do 16 h.link_to object.twitter_name, 17 "http://twitter.com/#{object.twitter_name}" 18 end 19 end 20 21 private 22 def handle_none(value) 23 ... 24 end 25 end
References• http://blog.codeclimate.com/blog/2012/10/17/7-
ways-to-decompose-fat-activerecord-models/
• https://gist.github.com/blaix/5764401
• https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
• https://medium.com/@ryakh/rails-form-objects-84b6849c886e
Railscasts
• http://railscasts.com/episodes/416-form-objects
• http://railscasts.com/episodes/398-service-objects
• http://railscasts.com/episodes/287-presenters-from-scratch
• http://railscasts.com/episodes/286-draper