$30 off During Our Annual Pro Sale. View Details »

Keep your ActiveRecord models manageable the Rails way

Keep your ActiveRecord models manageable the Rails way

Rails is awesome! It makes very easy and fun to start new projects. However, as your application grows, you will eventually need to come off the Rails. Otherwise your codebase will become completely unmanageable. Everyone knows that.

You'll need presenters and a service layer, including role and use-case objects. DCI will be great too or, alternatively, you can go Hexagonal. After all, the web is just a delivery mechanism, the database is a mere persistence strategy and, of course, “Rails is a detail”.

But… Wait a minute! Is all that really true? Does the Golden Path no longer work as your application becomes larger? How is it, then, that Rails claims to be “optimised for sustainable productivity”?

Criticising Rails is so last year! This time we'll revisit the patterns and conventions that Rails encourages and push them to the limit to see what happens. We'll seek examples of large Rails applications keeping their models manageable without derailing. We'll also discuss the trade-offs of running on and off the Rails. And, maybe, we'll finally learn how to stop worrying and love the Rails way!

Luismi Cavallé

June 07, 2013
Tweet

More Decks by Luismi Cavallé

Other Decks in Programming

Transcript

  1. ,FFQ:PVS
    "DUJWF3FDPSE
    .PEFMT.BOBHFBCMF
    5IF3BJMT8BZ
    CZ-VJTNJ$BWBMMÉ

    View Slide

  2. require_dependency,'topic_view'
    require_dependency,'rate_limiter'
    require_dependency,'text_sentinel'
    require_dependency,'text_cleaner'
    class,Topic,<,ActiveRecord::Base
    ,,include,ActionView::Helpers
    ,,include,RateLimiter::OnCreateRecord
    ,,def,self.max_sort_order
    ,,,,2**31,7,1
    https://github.com/discourse/discourse/blob/master/app/models/topic.rb

    View Slide

  3. require_dependency,'slug'
    require_dependency,'avatar_lookup'
    require_dependency,'topic_view'
    require_dependency,'rate_limiter'
    require_dependency,'text_sentinel'
    require_dependency,'text_cleaner'
    class,Topic,<,ActiveRecord::Base
    ,,include,ActionView::Helpers
    ,,include,RateLimiter::OnCreateRecord
    ,,def,self.max_sort_order
    ,,,,2**31,7,1
    ,,end
    ,,def,self.featured_users_count
    ,,,,4
    ,,end
    ,,versioned,if:,:new_version_required?
    ,,acts_as_paranoid
    ,,after_recover,:update_flagged_posts_count
    ,,after_destroy,:update_flagged_posts_count
    ,,rate_limit,:default_rate_limiter
    ,,rate_limit,:limit_topics_per_day
    https://github.com/discourse/discourse/blob/master/app/models/topic.rb

    View Slide

  4. https://github.com/discourse/discourse/blob/master/app/models/topic.rb
    -0$
    BOEUIBU`TOPUUPPNVDI

    View Slide

  5. #JH.PEFMT

    View Slide

  6. 'BU.PEFMT

    View Slide

  7. (JHBOUJD
    .PEFMT

    View Slide

  8. 8IZBSFUIFZCBE

    View Slide

  9. 431

    View Slide

  10. -PX$PIFTJPO

    View Slide

  11. 5JHIU$PVQMJOH

    View Slide

  12. #FDBVTF
    {{your_favourite_OO_guru}}
    TBZTTP

    View Slide

  13. 8IZBSFUIFZCBE
    JOQSBDUJDBMUFSNT
    QMFBTF

    View Slide

  14. !!class!Article!!!!!attr_accessor!:moderation_option
    !
    !!!!attr_accessible!:text,!:reply_to_i
    !
    !!!!belongs_to!:topic
    !!!!belongs_to!:user,!!!!!:class_name!
    !!!!belongs_to!:reply_to,!:class_name!

    View Slide

  15. !!!belongs_to!:user,!!!!!:class_name!=>!Fore
    !!!belongs_to!:reply_to,!:class_name!=>!"Pos
    !!!has_many!:replies,!:class_name!!=>!"Post"
    !!!!!!!!!!!!!!!!!!!!!!:foreign_key!=>!"reply
    !!!!!!!!!!!!!!!!!!!!!!:dependent!!!=>!:nulli
    !!!
    !!!has_many!:taggings
    !!!has_many!:tags,!:through!=>!:taggings
    !!!validates!:text,!:presence!=>!true
    !!!delegate!:forum,!:to!=>!:topic
    !!!after_create!:set_topic_last_post_at
    !!!after_create!:skip_pending_review

    View Slide

  16. !!!!end
    !!!!def!pending_review
    !!!!!!where!:state!=>!'pending_review'
    !!!!end
    !!!!def!spam
    !!!!!!where!:state!=>!'spam'
    !!!!end
    !!!!
    !!!!def!tagged_with(tag)
    !!!!!!joins(:taggings!=>!:tags).where(:tags!=>!{!:name!=>!tag!})
    !!!!end
    !!!!def!visible
    !!!!!!joins(:topic).where(:forem_topics!=>!{!:hidden!=>!false!})
    !!!!end
    !!end
    !!
    !!def!tag_names=(tag_list)
    !!!!assign_tag_list!tag_list
    !!end
    !!private

    View Slide

  17. !!!!!end
    !!!end
    !!!
    !!!def!tag_names=(tag_list)
    !!!!!assign_tag_list!tag_list
    !!!end
    !!!private
    !!!def!subscribe_replier
    !!!!!if!topic!&&!user

    View Slide

  18. !
    !!!!def!blacklist_user
    !!!!!!user.update_attribute(:forem_state,!"spam")!if!user
    !!!!end
    !!!!
    !!!!def!assign_tag_list(tag_list)
    !!!!!!tag_names!=!tag_list.gsub(/\s+/,!"").split(",")
    !!!!!!existing!=!self.tags.map!{|t|!t.name!}
    !!!!!!(existing!@!tag_names).each!do!|name|
    !!!!!!!!self.tags.delete!Tag.find_by_name(name)
    !!!!!!end
    !!!!!!tag_names.each!do!|name|
    !!!!!!!!self.tags!<!!!!!!end!
    !!!!end
    !!end

    View Slide

  19. !!class!Article!!!!!attr_accessor!:moderation_option
    !
    !!!!attr_accessible!:text,!:reply_to_id,!:tag_names
    !
    !!!!belongs_to!:topic
    !!!!belongs_to!:user,!!!!!:class_name!=>!Forem.user_class.to_s
    !!!!belongs_to!:reply_to,!:class_name!=>!"Post"
    !
    !!!!has_many!:replies,!:class_name!!=>!"Post",
    !!!!!!!!!!!!!!!!!!!!!!!:foreign_key!=>!"reply_to_id",
    !!!!!!!!!!!!!!!!!!!!!!!:dependent!!!=>!:nullify
    !!!!
    !!!!has_many!:taggings
    !!!!has_many!:tags,!:through!=>!:taggings
    !
    !!!!validates!:text,!:presence!=>!true
    !
    !!!!delegate!:forum,!:to!=>!:topic
    !
    !!!!after_create!:set_topic_last_post_at
    !!!!after_create!:skip_pending_review
    !!!!after_save!:email_topic_subscribers
    !
    !!!!class!<!!!!!!def!by_created_at
    !!!!!!!!order!:created_at
    !!!!!!end
    !
    !!!!!!def!pending_review
    !!!!!!!!where!:state!=>!'pending_review'
    !!!!!!end
    !
    !!!!!!def!spam
    !!!!!!!!where!:state!=>!'spam'
    !!!!!!end
    !!!!!!
    !!!!!!def!tagged_with(tag)
    !!!!!!!!joins(:taggings!=>!:tags).where(:tags!=>!{!:name!=>!tag!})
    !!!!!!end
    !
    !!!!!!def!visible
    !!!!!!!!joins(:topic).where(:forem_topics!=>!{!:hidden!=>!false!})
    !!!!!!end
    !!!!end
    !!!!
    !!!!def!tag_names=(tag_list)
    !!!!!!assign_tag_list!tag_list
    !!!!end
    !
    !!!!private
    !
    !!!!def!subscribe_replier
    !!!!!!if!topic!&&!user
    !!!!!!!!topic.subscribe_user(user.id)
    !!!!!!end
    !!!!end
    !
    !!!!def!set_topic_last_post_at
    !!!!!!topic.update_attribute(:last_post_at,!created_at)
    !!!!end
    !
    !!!!def!blacklist_user
    !!!!!!user.update_attribute(:forem_state,!"spam")!if!user
    !!!!end
    !!!!
    !!!!def!assign_tag_list(tag_list)
    !!!!!!tag_names!=!tag_list.gsub(/\s+/,!"").split(",")
    !!!!!!existing!=!self.tags.map!{|t|!t.name!}
    !!!!!!(existing!@!tag_names).each!do!|name|
    !!!!!!!!self.tags.delete!Tag.find_by_name(name)
    !!!!!!end
    !!!!!!tag_names.each!do!|name|
    !!!!!!!!self.tags!<!!!!!!end!
    !!!!end
    !!end

    View Slide

  20. before_validation
    after_validation
    before_save
    before_create
    after_create
    after_save
    after_commit

    View Slide

  21. 8IBUDBOXFEP
    BCPVUJU

    View Slide

  22. View Slide

  23. 0QUJPO

    View Slide

  24. 3BJMT "DUJWF3FDPSE
    BOEUIFJSDPOWFOUJPOT
    BSFSVCCJTI

    View Slide

  25. #FDBVTF
    {{your_favourite_OO_guru}}
    TBZTTP

    View Slide

  26. View Slide

  27. :PVOFFE
    B4FSWJDF-BZFS

    View Slide

  28. :PVOFFE
    %$*

    View Slide

  29. :PVOFFE
    )FYBHPOBM

    View Slide

  30. 0QUJPO

    View Slide

  31. I8
    <38
    Rails

    View Slide

  32. $PODFSOT

    View Slide

  33. 8IBU
    8IZ
    )PX

    View Slide

  34. .JYJOT

    View Slide

  35. module!DogFort
    !!def!call_dog
    !!!!puts!"this!is!dog!"
    !!end
    end
    class!Dog
    !!include!DogFort
    end
    http://schneems.com/post/21380060358/concerned-about-code-reuse

    View Slide

  36. ActiveSupport::Concern

    View Slide

  37. module!M
    !!extend!ActiveSupport::Concern
    !!!!
    !!included!do
    !!!!scope!:disabled,!where(:disabled!=>!true)
    !!end
    !!
    !!module!ClassMethods
    !!!!#"class"methods
    !!end
    !!#"instance"methods
    end
    http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

    View Slide

  38. class!Article!!!include!Taggable

    View Slide

  39. module!Taggable
    !!extend!ActiveSupport::Concern
    !!included!do
    !!!!has_many!:taggings
    !!!!has_many!:tags,!through:!:taggings
    !!!!scope!:tagged_with,!B>(tag_name)!do
    !!!!!!joins(:tags).where(tags:!{!name:!tag_name!})!
    !!!!end
    !!end
    !!def!tag_list
    !!!!tags.pluck(:name).join(',!')
    !!end
    !!def!tag_list=(tag_list)
    !!!!assign_tag_list!tag_list
    !!end
    !!private
    !!def!assign_tag_list(tag_list)
    !!!!tag_names!=!tag_list.split(',').map(&:strip).uniq
    !!!!self.tags!=!tag_names.map!do!|tag_name|!
    !!!!!!Tag.where(name:!tag_name).first_or_initialize
    !!!!end
    !!end
    end

    View Slide

  40. 8IBU
    8IZ
    )PX

    View Slide

  41. 5IFZIFMQUPLFFQ
    DPIFTJPOIJHI
    XIFOVTFEQSPQFSMZ

    View Slide

  42. module!Taggable
    !!extend!ActiveSupport::Concern
    !!included!do
    !!!!has_many!:taggings
    !!!!has_many!:tags,!through:!:taggings
    !!!!scope!:tagged_with,!B>(tag_name)!do
    !!!!!!joins(:tags).where(tags:!{!name:!tag_name!})!
    !!!!end
    !!end
    !!def!tag_list
    !!!!tags.pluck(:name).join(',!')
    !!end
    !!def!tag_list=(tag_list)
    !!!!assign_tag_list!tag_list
    !!end
    !!private
    !!def!assign_tag_list(tag_list)
    !!!!tag_names!=!tag_list.split(',').map(&:strip).uniq
    !!!!self.tags!=!tag_names.map!do!|tag_name|!
    !!!!!!Tag.where(name:!tag_name).first_or_initialize
    !!!!end
    !!end
    end

    View Slide

  43. %3:OFTT

    View Slide

  44. *OUSPEVDFMFTT
    JOEJSFDUJPOBOE
    DFSFNPOZUIBO
    PUIFSBCTUSBDUJPOT
    DPNQPTJUJPO EFDPSBUJPO

    View Slide

  45. l0⒏DJBMTPMVUJPOzUP
    UIF#JH.PEFMTJTTVF
    JO3BJMT

    View Slide

  46. https://github.com/rails/rails/commit/f6bbc3f582bfc16da3acc152c702b04102fcab81

    View Slide

  47. app/controllers/concerns
    app/models/concerns

    View Slide

  48. 8IBU
    8IZ
    )PX

    View Slide

  49. 8IBU
    8IZOPU
    )PX

    View Slide

  50. http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  51. http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  52. https://twitter.com/hakunin/statuses/113364211727482880

    View Slide

  53. Ruby
    Science
    The reference for writing fantastic
    Rails applications.
    https://learn.thoughtbot.com/products/13-ruby-science

    View Slide

  54. .JYJO
    *OIFSJUBODF JT B DPNNPO NFUIPE PG SFVTF JO PCKFDUPSJFOUFE TPGUXBSF 3VCZ
    TVQQPSUT TJOHMF JOIFSJUBODF VTJOH TVCDMBTTFT BOE NVMUJQMF JOIFSJUBODF VTJOH
    NJYJOT .JYJOT DBO CF VTFE UP QBDLBHF DPNNPO IFMQFST PS QSPWJEF B DPNNPO
    QVCMJD JOUFSGBDF
    )PXFWFS NJYJOT IBWF TPNF ESBXCBDLT
    t 5IFZ VTF UIF TBNF OBNFTQBDF BT DMBTTFT UIFZSF NJYFE JOUP XIJDI DBO
    DBVTF OBNJOH DPOnJDUT
    t "MUIPVHI UIFZ IBWF BDDFTT UP JOTUBODF WBSJBCMFT GSPN DMBTTFT UIFZSF
    NJYFE JOUP NJYJOT DBOU FBTJMZ BDDFQU JOJUJBMJ[FS BSHVNFOUT TP UIFZ DBOU
    IBWF UIFJS PXO TUBUF
    t 5IFZ JOnBUF UIF OVNCFS PG NFUIPET BWBJMBCMF JO B DMBTT
    t 5IFZSF OPU FBTZ UP BEE BOE SFNPWF BU SVOUJNF
    https://learn.thoughtbot.com/products/13-ruby-science
    https://learn.thoughtbot.com/products/13-ruby-science

    View Slide

  55. *OIFSJUBODF JT B DPNNPO NFUIPE PG SFVTF JO PCKFDUPSJFOUFE TPGUXBSF 3VCZ
    TVQQPSUT TJOHMF JOIFSJUBODF VTJOH TVCDMBTTFT BOE NVMUJQMF JOIFSJUBODF VTJOH
    NJYJOT .JYJOT DBO CF VTFE UP QBDLBHF DPNNPO IFMQFST PS QSPWJEF B DPNNPO
    QVCMJD JOUFSGBDF
    )PXFWFS NJYJOT IBWF TPNF ESBXCBDLT
    t 5IFZ VTF UIF TBNF OBNFTQBDF BT DMBTTFT UIFZSF NJYFE JOUP XIJDI DBO
    DBVTF OBNJOH DPOnJDUT
    t "MUIPVHI UIFZ IBWF BDDFTT UP JOTUBODF WBSJBCMFT GSPN DMBTTFT UIFZSF
    NJYFE JOUP NJYJOT DBOU FBTJMZ BDDFQU JOJUJBMJ[FS BSHVNFOUT TP UIFZ DBOU
    IBWF UIFJS PXO TUBUF
    t 5IFZ JOnBUF UIF OVNCFS PG NFUIPET BWBJMBCMF JO B DMBTT
    t 5IFZSF OPU FBTZ UP BEE BOE SFNPWF BU SVOUJNF
    t 5IFZSF EJʺDVMU UP UFTU JO JTPMBUJPO TJODF UIFZ DBOU CF JOTUBOUJBUFE
    4ZNQUPNT
    https://learn.thoughtbot.com/products/13-ruby-science
    https://learn.thoughtbot.com/products/13-ruby-science

    View Slide

  56. TVQQPSUT TJOHMF JOIFSJUBODF VTJOH TVCDMBTTFT BOE NVMUJQMF JOIFSJUBODF VTJOH
    NJYJOT .JYJOT DBO CF VTFE UP QBDLBHF DPNNPO IFMQFST PS QSPWJEF B DPNNPO
    QVCMJD JOUFSGBDF
    )PXFWFS NJYJOT IBWF TPNF ESBXCBDLT
    t 5IFZ VTF UIF TBNF OBNFTQBDF BT DMBTTFT UIFZSF NJYFE JOUP XIJDI DBO
    DBVTF OBNJOH DPOnJDUT
    t "MUIPVHI UIFZ IBWF BDDFTT UP JOTUBODF WBSJBCMFT GSPN DMBTTFT UIFZSF
    NJYFE JOUP NJYJOT DBOU FBTJMZ BDDFQU JOJUJBMJ[FS BSHVNFOUT TP UIFZ DBOU
    IBWF UIFJS PXO TUBUF
    t 5IFZ JOnBUF UIF OVNCFS PG NFUIPET BWBJMBCMF JO B DMBTT
    t 5IFZSF OPU FBTZ UP BEE BOE SFNPWF BU SVOUJNF
    t 5IFZSF EJʺDVMU UP UFTU JO JTPMBUJPO TJODF UIFZ DBOU CF JOTUBOUJBUFE
    4ZNQUPNT
    t .FUIPET JO NJYJOT UIBU BDDFQU UIF TBNF QBSBNFUFST PWFS BOE PWFS
    https://learn.thoughtbot.com/products/13-ruby-science
    https://learn.thoughtbot.com/products/13-ruby-science

    View Slide

  57. http://blog.8thlight.com/eric-meyer/2012/11/16/composition-over-mixins.html

    View Slide

  58. http://blog.8thlight.com/eric-meyer/2012/11/16/composition-over-mixins.html

    View Slide

  59. http://blog.8thlight.com/eric-meyer/2012/11/16/composition-over-mixins.html

    View Slide

  60. http://blog.8thlight.com/eric-meyer/2012/11/16/composition-over-mixins.html

    View Slide

  61. %PO`UpYQSPCMFN KVTUNPWFJU
    .BOZNFUIPETSFTQPOTJCJMJUJFT
    /BNJOHDPOqJDUT
    4IBSFETUBUF
    /PFYQMJDJUCPVOEBSJFT

    View Slide

  62. http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns

    View Slide

  63. http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns

    View Slide

  64. +BWB 3VCZ
    /VNCFSPGNFUIPETJO4USJOHDMBTT

    View Slide

  65. article.method(:tag_names).source_location
    #"=>"["./app/models/concerns/taggable.rb","23]

    View Slide

  66. &OPVHISPQFUP
    IBOHPVSTFMWFT

    View Slide

  67. 6TFWT"CVTF

    View Slide

  68. 8IBU
    8IZ
    )PX

    View Slide

  69. $PNNPOQVSQPTF
    /PUCBTJDOBUVSFPGNPEFM
    $SPTTDVUUJOH
    %PNBJODPODFQU

    View Slide

  70. #"app/models/article.rb
    class!Article!!!include!Accessors
    !!include!Validations
    !!include!Associations
    !!include!Scopes
    !!#"...
    end

    View Slide

  71. #"app/models/article.rb
    class!Article!!!include!Taggable
    !!include!Searchable
    !!include!Movable
    !!include!Visible
    !!include!Trashable
    !!#"...
    end

    View Slide

  72. .PEFMT

    View Slide

  73. .PEFM

    View Slide

  74. .PEFM .PEFM
    .PEFM

    View Slide

  75. has_one

    View Slide

  76. 4DIFEVMJOH

    View Slide

  77. create_table!"schedulings"!do!|t|
    !!#"...
    !!t.integer!!"workflow_offset"
    !!t.string!!!"workflow_asset_url"
    !!t.text!!!!!"workflow_details"
    !!t.boolean!!"workflow_sent"
    !!t.string!!!"workflow_web_url"
    !!t.string!!!"workflow_template_url"

    View Slide

  78. class!Scheduling!!!#"...
    !!include!Workflow

    View Slide

  79. class!SchedulingsController!

    View Slide

  80. class!SchedulingsController!class!WorkflowsController!

    View Slide

  81. 4DIFEVMJOH

    View Slide

  82. 4DIFEVMJOH
    8PSLqPX

    View Slide

  83. class!Workflow!!!#"...
    !!belongs_to!:scheduling

    View Slide

  84. class!Scheduling!!!#"...
    !!has_one!:workflow

    View Slide

  85. .BEFFYQMJDJUBO
    JNQMJDJUFOUJUZPGUIF
    EPNBJO

    View Slide

  86. 3FGBDUPSUPXBSETB
    EFFQFSJOTJHIU

    View Slide

  87. .PEFMTBSFTNBMMFS

    View Slide

  88. /POQFSTJTUFE
    .PEFMT

    View Slide

  89. ActiveModel

    View Slide

  90. 'PSNPCKFDUT

    View Slide

  91. http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  92. 1SPKFDUT
    $PNQBOZ
    6TFST
    "1*5PLFOT

    View Slide

  93. 1SPKFDUT
    $PNQBOZ
    6TFST
    4JHOVQ
    "1*5PLFOT

    View Slide

  94. class!Signup
    !!extend!ActiveModel::N
    !!include!ActiveModel::
    !!include!ActiveModel::
    !!attr_accessor!:name
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  95. History
    History
    class!Signup
    !!extend!ActiveModel::Naming
    !!include!ActiveModel::Conversion
    !!include!ActiveModel::Validations
    !!attr_accessor!:name
    !!attr_accessor!:company_name
    !!attr_accessor!:email
    !!validates!:email,!presence:!true
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  96. class!Signup
    !!extend!ActiveModel::Naming
    !!include!ActiveModel::Conversion
    !!include!ActiveModel::Validations
    !!attr_accessor!:name
    !!attr_accessor!:company_name
    !!attr_accessor!:email
    !!validates!:email,!presence:!true
    !!#"…"more"validations"…
    !!#"Virtual"models"are"never"themselves"
    !!def!persisted?
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  97. !!include!ActiveModel::Validations
    !!attr_accessor!:name
    !!attr_accessor!:company_name
    !!attr_accessor!:email
    !!validates!:email,!presence:!true
    !!#"…"more"validations"…
    !!#"Virtual"models"are"never"themselves"p
    !!def!persisted?
    !!!!false
    !!end
    !!def!save
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  98. !!attr_accessor!:name
    !!attr_accessor!:company_name
    !!attr_accessor!:email
    !!validates!:email,!presence:!true
    !!#"…"more"validations"…
    !!#"Virtual"models"are"never"themselves"persisted
    !!def!persisted?
    !!!!false
    !!end
    !!def!save
    !!!!if!valid?
    !!!!!!persist!
    !!!!!!true
    !!!!else
    !!!!!!false
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  99. !!def!persisted?
    !!!!false
    !!end
    !!def!save
    !!!!if!valid?
    !!!!!!persist!
    !!!!!!true
    !!!!else
    !!!!!!false
    !!!!end
    !!end
    private
    !!def!persist!
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  100. !!!!!!true
    !!!!else
    !!!!!!false
    !!!!end
    !!end
    private
    !!def!persist!
    !!!!@company!=!Company.create!(name:!company_name)
    !!!!@[email protected]!(name:!name,!email:!email)
    !!end
    end
    class!SignupsController!!!def!create
    !!!!@signup!=!Signup.new(params[:signup])
    [email protected]
    !!!!!!redirect_to!dashboard_path
    !!!!else
    !!!!!!render!"new"
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  101. class!Signup
    !!extend!ActiveModel::Naming
    !!include!ActiveModel::Conversion
    !!include!ActiveModel::Validations
    !!attr_accessor!:name
    !!attr_accessor!:company_name
    !!attr_accessor!:email
    !!validates!:email,!presence:!true
    !!#"…"more"validations"…
    !!#"Virtual"models"are"never"themselves"persisted
    !!def!persisted?
    !!!!false
    !!end
    !!def!save
    !!!!if!valid?
    !!!!!!persist!
    !!!!!!true
    !!!!else
    !!!!!!false
    !!!!end
    !!end
    private
    !!def!persist!
    !!!!@company!=!Company.create!(name:!company_name)
    !!!!@[email protected]!(name:!name,!email:!email)
    !!end
    end
    class!SignupsController!!!def!create
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  102. class!SignupsController!!!def!create
    !!!!@signup!=!Signup.new(params[:signup])
    [email protected]
    !!!!!!redirect_to!dashboard_path
    !!!!else
    !!!!!!render!"new"
    !!!!end
    !!end
    end
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

    View Slide

  103. class!SimpleModel
    !!extend!ActiveModel::Naming
    !!include!ActiveModel::Conversion
    !!include!ActiveModel::Validations
    !!#"Virtual"models"are"never"themselves"persisted
    !!def!persisted?
    !!!!false
    !!end
    !!def!save
    !!!!if!valid?
    !!!!!!persist!
    !!!!!!true
    !!!!else
    !!!!!!false
    !!!!end
    !!end
    end

    View Slide

  104. class!Signup!!!attr_accessor!:name,
    !!88888888888888:company_name,
    !!88888888888888:email
    !!validates!:email,!presence:!true
    !!#"…"more"validations"…
    !!def!persist!
    !!!!@company!=!Company.create!(name:!company_name)
    !!!!@[email protected]!(name:!name,!email:!email)
    !!end
    end

    View Slide

  105. $POUSPMMFST

    View Slide

  106. :FT DPOUSPMMFST

    View Slide

  107. http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model

    View Slide

  108. 4LJOOJFTUQPTTJCMF
    DPOUSPMMFST

    View Slide

  109. class!CommentsController!!!#...
    !!def!create
    !!!!@[email protected](post_params)
    [email protected]
    !!!!!!redirect_to!@comment
    !!!!else
    !!!!!!render!action:!'new'
    !!!!end
    !!end

    View Slide

  110. https://gist.github.com/2838490#gistcomment-356060

    View Slide

  111. class!CommentsController!!!#...
    !!
    !!def!create
    !!!!@[email protected]!(post_params)
    !!!!
    !!!!Notifications.new_comment(@comment).deliver
    !!!!
    !!!!TwitterPoster.new(current_user,[email protected]).post
    !!!!FacebookPoster.new(current_user,[email protected]).post
    !!end

    View Slide

  112. http://david.heinemeierhansson.com/2012/emails-are-views.html

    View Slide

  113. https://twitter.com/dhh/status/283832623023927296

    View Slide

  114. *U`TPLUPVTF
    DPOUSPMMFSTGPS
    PSDIFTUSBUJPO

    View Slide

  115. $POUSPMMFSTVTFEBT
    TFSWJDFPCKFDUT
    DPOUFYUT VTFDBTFT
    TFSWJDFMBZFS

    View Slide

  116. "WPJEDBMMCBDLT

    View Slide

  117. 1SPQFSMZFODBQTVMBUF
    FYUFSOBMJOUFSGBDFT
    BTNBJMFSTBSF

    .PWFUIFMPHJDPVUPGUIF
    DPOUSPMMFSXIFOVTFEJO
    NVMUJQMFQMBDFT

    View Slide

  118. *OTVNNBSZ

    View Slide

  119. &YUSBDUUPDPODFSOT
    %JTDPWFSJNQMJDJUNPEFMT
    $PPSEJOBUFGSPNDPOUSPMMFST
    &ODBQTVMBUFFYUFSOBMJOUFSGBDFT

    View Slide

  120. 5IBOLT
    -VJTNJ$BWBMMÉ
    UXJUUFSDPNDBWBMMF
    HJUIVCDPNDBWBMMF
    QJOCPBSEJOVDBWBMMFUCJHNPEFMT
    5FSJNB
    LBTJI
    ந"#

    View Slide