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

レールの伸ばし方

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 レールの伸ばし方

Rails Developers Meetup 2017での発表内容です。

大きいRailsアプリケーションの可読性を保つためのコツについてまとめました。

Avatar for Shinichi Maeshima

Shinichi Maeshima

December 09, 2017
Tweet

More Decks by Shinichi Maeshima

Other Decks in Technology

Transcript

  1. ద੾ͳந৅Խͷྫ CFGPSF class Registration def save ApplicationRecord.transaction do case @auth_hash[:provider]

    when 'twitter' then register_by_twitter! when 'facebook' then register_by_facebook! when 'github' then register_by_github! end Friendship.create!(user: invitation.sender, friend: user) Friendship.create!(user: user, friend: invitation.sender) invitation.joined_at = Time.zone.now invitation.receiver = user invitation.save! end true rescue ActiveRecord::RecordInvalid false end
  2. ద੾Ͱͳ͍ந৅Խͷྫ class IssuesController < ApplicationController before_filter :find_issue, :only => [:show,

    :edit, :update] before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] before_filter :find_project, :only => [:new, :create, :update_form] before_filter :authorize, :except => [:index] before_filter :find_optional_project, :only => [:index] before_filter :check_for_default_issue_status, :only => [:new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create, :update_form]
  3. ѱ͍ྫ 'BU$POUSPMMFS IUUQTHJUIVCDPNTIBLBDPEFGBUDPEFSFGBDUPSJOHUFDIOJRVFT def create @micropost = Micropost.new(micropost_params.merge(user: current_user)) if

    current_user.minor? && (profane_words_used = profane_words_in(@micropost.content)) current_user.increment(:profanity_count, profane_words_used.size) current_user.save(validate: false) send_parent_notifcation_of_profanity(profane_words_used) flash.now[:error] = <<-MSG.html_safe <p>Profanity: '#{profane_words_used.join(", ")}' not allowed! You've tried to use profanity #{view_context.pluralize(current_user.profanity_count, "time")}! </p><p class="parent-notification">Your parents have been notified!</p> MSG render 'static_pages/home' else if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else render 'static_pages/home' end end end end
  4. मਖ਼ޙ def create @micropost = Micropost.new(micropost_params.merge(user: current_user)) if @micropost.save_with_profanity_callbacks flash[:success]

    = "Micropost created!" redirect_to root_url else adjust_micropost_profanity_message render 'static_pages/home' end end
  5. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.thanks(@user).deliver_later redirect_to @user, notice: '登録完了しました' else render :new end end ͜ͷ͘Β͍͸ڐ༰
  6. 'BU.PEFMͷྫ class User < ApplicationRecord has_many :posts has_many :friendships has_many

    :friends, through: :friendships def create_post_with_notifications!(body) transaction do posts.create!(body: body) friends.each do |friend| friend.notifications.create!("#{name}さんが投稿しました") end end end # 大量の似たようなメソッド end
  7. 1030ʹ੾Γग़͢ class PostWithNotifications def self.create!(creator:, body:) new(creator: creator, body: body).create!

    end def initialize(creator:, body:) @creator = creator @body = body end def create! ActiveRecord::Base.transaction do create_post! create_notifications! end end  private attr_reader :creator, :body def create_post! creator.posts.create!(body: body) end def create_notifications! creator.friends.each { |friend| create_notification!(friend) } end def create_notification!(friend) friend.notifications.create!( “#{creator.name}さんが投稿しました" ) end end
  8. େྔͷCFGPSF@pMUFS class IssuesController < ApplicationController before_filter :find_issue, :only => [:show,

    :edit, :update] before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] before_filter :find_project, :only => [:new, :create, :update_form] before_filter :authorize, :except => [:index] before_filter :find_optional_project, :only => [:index] before_filter :check_for_default_issue_status, :only => [:new, :create] before_filter :build_new_issue_from_params, :only => [:new, :create, :update_form]
  9. આ໌༻ͷ΋ͬͱ؆୯ͳྫ class PostsController < ApplicationController before_filter :set_posts, only: :index before_filter

    :set_post, only: :show before_filter :set_recommendations def index end def show end private def set_posts @posts = post.page(params[:page).per(10) end def set_post @post = post.find(params[:id]) end def set_recommendations @recommendation_categories = Category.recommendations @recommendations = @recommendation_categories.posts end end
  10. CFGPSF@pMUFSBDUJPO class PostsController < ApplicationController def index set_recommendations set_posts end

    def show set_recommendations set_post end private def set_posts @posts = post.page(params[:page).per(10) end def set_post @post = post.find(params[:id]) end def set_recommendations @recommendation_categories = Category.recommendations @recommendations = Recommendations.new(@recommendation_categories) end end
  11. ΫΤϦϝιουԽ class PostsController < ApplicationController def index @recommendation_categories = fetch_recommendation_categories

    @recommendations = fetch_recommendations(@recommendation_categories) @posts = fetch_posts end def show @recommendation_categories = fetch_recommendation_categories @recommendations = fetch_recommendations(@recommendation_categories) @post = fetch_post end private def fetch_posts post.page(params[:page).per(10) end def fetch_post post.find(params[:id]) end def fetch_recommendation_categories Category.recommendations end def fetch_recommendations(categories) Recommenadtions.new(categories) end end
  12. Πϯελϯεม਺͕ଟ͍ͱʜ class PostsController < ApplicationController def index @recommendation_categories = fetch_recommendation_categories

    @recommendations = fetch_recommendations(@recommendation_categories) @posts = fetch_posts @ad = fetch_ad @widget = fetch_widget @sidebar = fetch_sidebar @title = fetch_title @cart = current_cart @search = build_search_form end def show # indexと同じようにたくさんのインスタンス変数への代入がある end private # たくさんのプライベートメソッド end
  13. 7JFX.PEFM 7JFX0CKFDU class PostsController < ApplicationController def index @view_model =

    PostIndexViewModel.new end end class PostIndexViewModel def posts @posts ||= Post.page(params[:page).per(10) end def recommendation_categories @recommendation_categories ||= Category.recommendations end def recommendations @recommendations ||= recommendation_categories.posts end end
  14. 7BMJEBUJPO DBMMCBDL ͷ ৔߹෼͚ class Post < ApplicationRecord validates :body,

    presence: true, length: { maximum: 1000 } validates :published_at, presence: true validate :publish_at_should_after_current, unless: :by_admin attr_reader :by_admin def update_by_admin(attrs) @by_admin = true update(attrs) end def publish_at_should_after_current return unless publish_at return if publish_at > Time.zone.now errors.add(:publish_at, 'は現在時刻より後にしてください') end end
  15. 'PS̼0CKFDU class PostForm include ActiveModel::Model attr_accessor :post, :body, :publish_at validates

    :body, presence: true, length: { maximum: 1000 } validates :publish_at, presence: true validate :publish_at_should_after_current def save post.attributes = { body: body, publish_at: publish_at } post.save if valid? end private def publish_at_should_after_current return unless publish_at return if publish_at > Time.zone.now errors.add(:publish_at, 'は現在時刻より後にしてください') end end
  16. 'PS̼0CKFDU class AdminPostForm include ActiveModel::Model attr_accessor :post, :body, :publish_at validates

    :body, presence: true, length: { maximum: 1000 } validates :publish_at, presence: true def save post.attributes = { body: body, publish_at: publish_at } post.save if valid? end end
  17. ͻͱͭͷΞΫγϣϯͰ ͨ͘͞Μ΍Δ͜ͱ͕͋Δ class PostsController < ApplicationController def create Hoge.prepare_create1 #

    なんらかの前処理1 @post = current_user.post.build(post_params) Hoge.prepare_create2 # なんらかの前処理2 unless @post.save Fuga.prepare_new # new を表示するためのいろいろな前処理 render :new and return end Foo.after # なんらかの後処理 Bar.after # なんらかの後処理2 end end
  18. 4FSWJDF0CKFDU class CreatePostService attr_reader :result def self.call(user:, params:) new(user: user,

    params: params).call end def initialize(user:, params:) @user = user @params = params @result = true end def call before_callback unless post.save fail! prepare_new return end after_callback self end def post @post ||= user.post.build(params) end private attr_reader :user, :paramas def before_callback Hoge.prepare_create1 Hoge.prepare_create2 end def after_callback Foo.after Bar.after end def prepare_new Fuga.prepare_new end def fail! @result = false end def post_params params.require(:post).permit(:body) end end
  19. 4FSWJDF0CKFDU class PostsController < ApplicationController def create service = CreatePostService.call(user:

    current_user, params: params) unless service.result @post = service.post render :new and return end redirect_to posts_path, notify: '作成されました' end end
  20. :VCBϙΤϜ w 7JFX.PEFM 'PSN0CKFDU 4FSWJDF0CKFDUͷ࡞੒ิॿ༻HFN w ن໿ʹΑΓ࡞ΓํͰ໎Θͳ͍Α͏ʹ w 1030ͩͱຖճ΍Βͳ͖Ό͍͚ͳ͍໘౗ͳهड़͸؆୯ʹ͔͚ΔΑ͏ʹ w

    ඞཁͳͱ͜Ζ͚ͩಋೖͰ͖ΔΑ͏ʹ w ಋೖ͠΍ࣺͯ͘͢΍͍͢࡞Γʹ w ബ͘࡞Δ w 3BJMTΤϯδχΞ͕ҧ࿨ײͳ͘࢖͑ΔΠϯλϑΣʔε
  21. 1030Ͱ7JFX.PEFM class PostViewModel attr_reader :post def initialize(post:, author:, other: nil)

    @post = post @author = author @other = other end def title post.title end def body post.body end private attr_reader :author, :other end
  22. ຖճಉ͡Α͏ͳॲཧΛ ॻ͔ͳ͚Ε͹ͳΒͳ͍ class PostViewModel attr_reader :post def initialize(post:, author:, other:

    nil) @post = post @author = author @other = other end def title post.title end def body post.body end private attr_reader :author, :other end
  23. :VCB7JFX.PEFM class PostViewModel < Yuba::ViewModel property :post property :author, public:

    true property :other, optional: true def title post.title end def body post.body end end
  24. !WJFX@NPEFM໰୊ class PostsController < ApplicationController def show @view_model = PostViewModel.new(post:

    post, author: author) end end <%= @view_model.post.title %> <%= @view_model.author.name %>
  25. ඞͣ!WJFX@NPEFMΛ ܦ༝͠ͳ͚Ε͹ͳΒͳ͍ class PostsController < ApplicationController def show @view_model =

    PostViewModel.new(post: post, author: author) end end <%= @view_model.post.title %> <%= @view_model.author.name %>
  26. 7JFX.PEFMΛల։ͯ͠ Πϯελϯεม਺ʹׂ౰ class PostsController < ApplicationController def show view_model =

    PostViewModel.new(post: post, author: author) render view_model: view_model end end <%= @post.title %> <%= @author.name %>
  27. :VCB4FSWJDF class PostController < ApplicationController def new @post = CreatePostService.call(user:

    current_user).post end def create service = CreatePostService.call(user: current_user, params: params) if service.success? redirect_to root_path else @post = service.post render :new end end end
  28. :VCB4FSWJDF class CreatePostService < Yuba::Service property :user, public: true property

    :params, optional: true def call if post.save notify_to_admin else fail! end end def post user.posts.build(post_params) end private def notify_to_admin AdminMailer.notify_create_post(post).deliver_later end def post_params params.require(:post).permit(:title, :body) end end