ActiveRecord scopeアンチパターン / ActiveRecord scope Antipatterns

A1c45e2d61b0d9a7af26c52244be6d93?s=47 ngmt
September 06, 2018

ActiveRecord scopeアンチパターン / ActiveRecord scope Antipatterns

https://omotesandorb.connpass.com/event/98377/
のLTで使用した資料です

A1c45e2d61b0d9a7af26c52244be6d93?s=128

ngmt

September 06, 2018
Tweet

Transcript

  1. ActiveRecord scope Ξϯνύλʔϯ

  2. ࣗݾ঺հ • Rails ϝΠϯͷΤϯδχΞ • ग़ࣗ͸ශ๡ࢠ୔ࢁ͠·ΜͪΎ • @ngmt83 Ҏ্ʂ

  3. ֓ཁ • ࣗݾ঺հ • ͸͡Ίʹ • Ξϯνύλʔϯ • ϕλʔͳํ਑ •

    RailΛ৳͹͢ϊ΢ϋ΢, ࢀߟࢿྉ
  4. ͸͡Ίʹ model͸Ͳ͏଍ૡ͍ͯ΋ϑΝοτʹͳΔ Մಡੑ͕མͪͯ͘Δ ͦΕͰ΋Ͳ͏ʹ͔͍ͨ͠ ࠓճ͸scopeʹ஫໨͠·͢

  5. ͸͡Ίʹ ͱ͋ΔUser model class User < ApplicationRecord has_many :articles has_many

    :comments scope :created_after, ->(time) { where('created_at >= ?', time) } scope :filter_by_abc, ->(a, b, c) { if a where(bbb: b).where(ccc: c) else where.not(bbb: b).where.not(ccc: c) end } scope :xxx, -> { … } scope :yyy {…} scope :zzz {…} scope : scope : … end ΤσΟλΛ։͍ͨTUWJFXʹ ໨తͷϝιου͕ͳ͍ େྔͷTDPQFͰݟ௨͕͠ѱ͍
  6. Ξϯνύλʔϯ • default scopeύλʔϯ • SQLϕλॻ͖(or Arel)ύλʔϯ • ྨࣅscope૿৩ύλʔϯ •

    ൚༻ੑߴ͗͢ύλʔϯ
  7. આ໌ʹ࢖༻͢ΔERD

  8. default scopeύλʔϯ class User < ApplicationRecord default_scope { where.not(archived_at: nil)

    } > User.all User Load (4.2ms) SELECT "users".* FROM "users" WHERE "users"."archived_at" IS NOT NULL LIMIT ? [["LIMIT", 11]] .rb ί ϯ ι ʛ ϧ
  9. default scopeύλʔϯ • ʮdefault scopeΛ֎͢͜ͱ͸͋Γ͑ͳ͍ʯ →͋Γ͑ͳ͍ͳΜͯ͋Γ͑ͳ͍ • Α͋͘Δྫ Ϣʔβʹ͸ݟ͑ͳ͍͍͚ͯ͘Ͳɺ؅ཧऀతʹ͸ σʔλूܭͱ͔Ͱɺݟ͑ͨํ͕͍͍

  10. SQLϕλॻ͖(or Arel)ύλʔϯ class Article < ApplicationRecord scope :anti_created_after, ->(time) {

    where('created_at >= ?', time) if time.present? } >Article.anti_created_after(0) Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE (created_at >= ‘0') LIMIT ? [["LIMIT", 11]] => #<ActiveRecord::Relation […]> ※datetimeͷΧϥϜΛจࣈྻͰߜΓࠐΊͯ͠·͏ɺ0ͩͱ΄΅શ෦͕֘౰͢Δ
  11. SQLϕλॻ͖(or Arel)ύλʔϯ • railsͷmodelͱRDBͷtableߏ଄͕ີ݁߹ʹͳͬͯ͠·͏ • ଞͷscopeͱ૊Έ߹Θͤͯͷ࢖༻͕͠ʹ͍͘ • Մಡੑ͕௿͍ • Arelʹ͍ͭͯ

    • ॻ͍ͨਓ͸࢖͍͜ͳͤΔ͔΋͠Εͳ͍͕ɺͦͷΑ͏ͳਓ ͸ଟ͘ͳ͍ɻSQLΛ࢖͍͜ͳͤΔਓͷ΄͏͕·ͩଟ͍
  12. SQLϕλॻ͖(or Arel)ύλʔϯ class Article < ApplicationRecord scope :created_after, ->(time) {

    where(created_at: time..Time.current) } >Article.created_after(Time.current) >Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE "articles"."created_at" BETWEEN ? AND ? LIMIT ? [["created_at", "2018-09-06 11:07:01.342399"], ["created_at", "2018-09-06 11:07:01.342889"], ["LIMIT", 11]]
  13. ྨࣅscope૿৩ύλʔϯ class Article < ApplicationRecord scope :created_after, ->(time) { where(created_at:

    time..Time.current) } scope :anti_created_in_ten_years { where(created_at: (Time.current - 10.years)…Time.current) } scope :anti_created_in_a_year { where(created_at: (Time.current - 1.year)…Time.current) } scope :anti_created_in_a_month { where(created_at: (Time.current - 1.month)…Time.current) } scope :anti_created_in_a_week { where(created_at: (Time.current - 1.week)…Time.current) }
  14. ྨࣅscope૿৩ύλʔϯ • ৑௕ →࢖༻ස౓͕͍͘Βߴ͘ͱ΋େ఍͸Ҿ਺ͰͲ͏ ʹ͔͢΂͖

  15. ൚༻ੑߴ͗͢ύλʔϯ class Article < ApplicationRecord scope :anti_filter_by, ->(user, from, till,

    statuses) { written_by(user) .created_between(from, till) .filter_by_statuses(statuses) } scope :written_by, ->(user) { where(user: user) } scope :created_between, ->(from, till) { where(created_at: from..till) } scope :filter_by_statuses, ->(statuses) { where(status: statuses) } w Ҿ਺ͭΛࢦఆͯ͠ߜΓࠐΉέʔε ͸ଟ͍͔ʁ w Ҿ਺ͷਖ਼͠͞ΛνΣοΫ͢Δඞཁ ͸ͳ͍͔ʁ w νΣοΫ͢Δ৔߹JGͷࠞೖ΍৑௕ʹ ͳΒͳ͍͔ʁ w pMUFS@CZͱ͍͏໊લͰҙਤ͕఻Θ Δ͔ʁ
  16. ൚༻ੑߴ͗͢ύλʔϯ • scope1͕ͭ௕͘ͳΓɺmodelશମͷݟ௨͕͠ ѱ͍ • Ҿ਺͕ଟ͘ɺscope಺ʹifจ͕ࠞೖ͕ͪ͠ • ໋໊͕໎૸͠ɺҙਤ͕Θ͔Γʹ͍͘

  17. finderྫ(ݸਓతͳझຯ) class ArticlesFinder def initialize(writer: nil, from: nil, till: nil,

    statuses: nil) @writer = writer @from = from @till = till @statuses = statuses end def find return @query if @query @query = Article @query = @query.eager_load(:user).written_by(@writer) if @writer if @from && @till @query = @query.created_between(@from, @till) else @query = @query.created_after(@from) if @from @query = @query.created_before(@till) if @till end @query = @query.where(status: @statuses) if @statuses @query end end
  18. ϕλʔͳํ਑ • scope͸γϯϓϧΛҙࣝ(ݟΕ͹Θ͔Δ) • scopeͷ੹຿͸ߜΓࠐΉ͜ͱ • ฒ΂׵͑Δ͜ͱɺeager loading͢Δ͜ͱ͸ݫີʹ͸ ҧ͏ •

    ෳࡶͳݕࡧɺฒ΂ସ͑ɺeager loading͸ Finder(Repository)ύλʔϯΈ͍ͨͳ΋ͷΛ࢖͓͏
  19. RailΛ৳͹͢ϊ΢ϋ΢,ࢀߟࢿྉ • ActiveRecordσʔλॲཧΞϯνύλʔϯ @toshimaru_e ͞Μ https://speakerdeck.com/toshimaru/active-record-anti-patterns • DHHྲྀͷϧʔςΟϯάͰಘΒΕΔϝϦοτͱɾɾɾ kitchihike ͞ΜͷςοΫϒϩά

    https://tech.kitchhike.com/entry/2017/03/07/190739 • Ϩʔϧͷ৳͹͠ํ @netwillnet ͞Μ https://speakerdeck.com/willnet/rerufalseshen-basifang?slide=91
  20. Thanks! ;͒ΖʔΈʔ @ngmt83