Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplifying Controllers - Confoo 2015

Simplifying Controllers - Confoo 2015

In this talk I explain the process through which my team went when rewriting the Checkout process in Shopify. The results strays from the typical way we build Rails application, but the codebase is much simpler and easier to test.

Guillaume Malette

February 20, 2015
Tweet

More Decks by Guillaume Malette

Other Decks in Programming

Transcript

  1. class PostsController < ApplicationController def new @post = Post.new respond_with

    @post end def create @post = Post.create(params[:post].permit(:title, :body)) respond_with @post end def show @post = Post.find(params[:id]) respond_with @post end def index @posts = Post.all respond_with @posts end end
  2. class PostsController < ApplicationController def new @post = Post.new respond_with

    @post end def create @post = Post.create(params[:post].permit(:title, :body)) redirect_to @post end def show @post = Post.find(params[:id]) respond_with @post end def index @posts = Post.all respond_with @posts end end
  3. class PostsController < ApplicationController def new @post = Post.new respond_with

    @post end def create @post = Post.create(params[:post].permit(:title, :body)) respond_with @post end def show @post = Post.find(params[:id]) respond_with @post end def index @posts = Post.all respond_with @posts end end
  4. abstractions is preserving information that is relevant in a given

    context, and forgetting information that is irrelevant in that context John V Guttag “
  5. def github github_login = env["omniauth.auth"].extra.raw_info.login github_token = env["omniauth.auth"].credentials.token github_user =

    User.where(:github_login => github_login).first if github_user.nil? && github_org_id = Errbit::Config.github_org_id client = Octokit::Client.new(access_token: github_token) org_ids = client.organizations.map { |org| org.id.to_s } if org_ids.include?(github_org_id) github_user = User.create( name: env["omniauth.auth"].extra.raw_info.name, email: env["omniauth.auth"].extra.raw_info.email ) end end if current_user if github_user && github_user != current_user flash[:error] = "User already registered with GitHub login '#{github_login}'!" else update_user_with_github_attributes(current_user, github_login, github_token) flash[:success] = "Successfully linked GitHub account!" end redirect_to user_path(current_user) elsif github_user update_user_with_github_attributes(github_user, github_login, github_token) flash[:success] = I18n.t "devise.omniauth_callbacks.success", :kind => "GitHub" sign_in_and_redirect github_user, :event => :authentication else flash[:error] = "There are no authorized users with GitHub login '#{github_login}'." redirect_to new_user_session_path end end
  6. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  7. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  8. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  9. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  10. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money end end
  11. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money if enough_money? hand_money receive_change end end
  12. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money if enough_money? hand_money receive_change go_back_home eat_chocolate end end
  13. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money if enough_money? hand_money receive_change go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  14. module Spree class CheckoutController < Spree::StoreController before_action :load_order_with_lock before_action :ensure_valid_state_lock_version,

    only: [:update] before_action :set_state_if_present before_action :ensure_order_not_completed before_action :ensure_checkout_allowed before_action :ensure_sufficient_stock_lines before_action :ensure_valid_state before_action :associate_user before_action :check_authorization before_action :apply_coupon_code
  15. module Spree class CheckoutController < Spree::StoreController before_action :load_order_with_lock before_action :ensure_valid_state_lock_version,

    only: [:update] before_action :set_state_if_present before_action :ensure_order_not_completed before_action :ensure_checkout_allowed before_action :ensure_sufficient_stock_lines before_action :ensure_valid_state before_action :associate_user before_action :check_authorization before_action :apply_coupon_code
  16. module Spree class CheckoutController < Spree::StoreController before_action :load_order_with_lock before_action :ensure_valid_state_lock_version,

    only: [:update] before_action :set_state_if_present before_action :ensure_order_not_completed before_action :ensure_checkout_allowed before_action :ensure_sufficient_stock_lines before_action :ensure_valid_state before_action :associate_user before_action :check_authorization before_action :apply_coupon_code
  17. class Spree::BaseController < ApplicationController include Spree::Core::ControllerHelpers::Auth include Spree::Core::ControllerHelpers::RespondWith include Spree::Core::ControllerHelpers::Common

    include Spree::Core::ControllerHelpers::Search include Spree::Core::ControllerHelpers::Store include Spree::Core::ControllerHelpers::StrongParameters
  18. class Spree::BaseController < ApplicationController include Spree::Core::ControllerHelpers::Auth include Spree::Core::ControllerHelpers::RespondWith include Spree::Core::ControllerHelpers::Common

    include Spree::Core::ControllerHelpers::Search include Spree::Core::ControllerHelpers::Store include Spree::Core::ControllerHelpers::StrongParameters
  19. class UsersController < ApplicationController skip_before_filter :authorize_mini_profiler, only: [:avatar] skip_before_filter :check_xhr,

    only: [:show, :password_reset, :update, :account_created, :activate_account, :perform_account_activation, :authorize_email, :user_preferences_redirect, :avatar, :my_redirect] before_filter :ensure_logged_in, only: [:username, :update, :change_email, :user_preferences_redirect, :upload_user_image, :pick_avatar, :destroy_user_image, :destroy, :check_emails] before_filter :respond_to_suspicious_request, only: [:create] skip_before_filter :verify_authenticity_token, only: [:create] skip_before_filter :redirect_to_login_if_required, only: [:check_username, :create, :get_honeypot_value, :account_created, :activate_account, :perform_account_activation, :send_activation_email, :authorize_email, :password_reset]
  20. ok!

  21. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  22. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  23. @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues = [] saved_issues

    = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end
  24. @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues = [] saved_issues

    = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end
  25. @issues = issues @copy = params[:copy].present? @attributes = parse_params_for_bulk_issue_attributes(params) @unsaved_issues

    = [] @saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! do |issue| @issues.detect {|other| issue.is_descendant_of?(other)} end end
  26. @issues = issues @copy = params[:copy].present? @attributes = attributes @unsaved_issues

    = [] @saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! do |issue| @issues.detect {|other| issue.is_descendant_of?(other)} end end
  27. class BulkIssuesUpdater attr_reader :issues, :copy, :attributes, :unsaved_issues, :saved_issues def initialize(issues,

    attributes, params) @issues = issues @copy = params[:copy].present? @attributes = attributes @unsaved_issues = [] @saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! do |issue| @issues.detect {|other| issue.is_descendant_of?(other)} end end end end
  28. @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues = [] saved_issues

    = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end
  29. @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments

    => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { stuff }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end
  30. orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks

    => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes
  31. def update_issue(orig_issue) orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments =>

    params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes issue end
  32. @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments

    => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { stuff }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end
  33. def update_issues @issues.each do |orig_issue| issue = update_issue(orig_issue) call_hook(:controller_issues_bulk_edit_before_save, {

    stuff }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end end
  34. def bulk_update @issues.sort! @copy = params[:copy].present? attributes = parse_params_for_bulk_issue_attributes(params) unsaved_issues

    = [] saved_issues = [] if @copy && params[:copy_subtasks].present? @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}} end @issues.each do |orig_issue| orig_issue.reload if @copy issue = orig_issue.copy({}, :attachments => params[:copy_attachments].present?, :subtasks => params[:copy_subtasks].present?, :link => link_copy?(params[:link_copy]) ) else issue = orig_issue end journal = issue.init_journal(User.current, params[:notes]) issue.safe_attributes = attributes call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) if issue.save saved_issues << issue else unsaved_issues << orig_issue end end if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  35. def bulk_update @issues.sort! updater = BulkIssuesUpdater.new( @issues, parse_params_for_bulk_issue_attributes(params), params )

    updater.update_issues if unsaved_issues.empty? flash[:notice] = l(:notice_successful_update) unless saved_issues.empty? if params[:follow] if @issues.size == 1 && saved_issues.size == 1 redirect_to issue_path(saved_issues.first) elsif saved_issues.map(&:project).uniq.size == 1 redirect_to project_issues_path(saved_issues.map(&:project).first) end else redirect_back_or_default _project_issues_path(@project) end else @saved_issues = @issues @unsaved_issues = unsaved_issues @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a bulk_edit render :action => 'bulk_edit' end end
  36. class PetFamilyForm attr_accessor :dog, :cat, :mouse validates :cat, :is_on_dog validates

    :mouse, :is_on_cat def save dog.save cat.save mouse.save end end
  37. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money if enough_money? hand_money receive_change go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  38. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate go_in_line take_out_wallet count_money if enough_money? hand_money receive_change go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  39. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  40. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  41. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  42. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  43. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  44. def emilie_eats_chocolate return eat_nothing unless parents_agree? go_to_grocery if grocery_is_closed? redirect_to

    convenience_store else grab_chocolate if payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  45. def emilie_eats_chocolate go_to_grocery if grocery_is_closed? redirect_to convenience_store else grab_chocolate if

    payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end
  46. def emilie_eats_chocolate go_to_grocery if grocery_is_closed? redirect_to convenience_store else grab_chocolate if

    payment_form.enough_money? payment_form.pay go_back_home eat_chocolate else go_back_home eat_fruits_instead end end end