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

Concerning 'Application'

Concerning 'Application'

Rails refactoring technique using ActiveSupport::Concern, talked on RubyKaigi 2013.

70e13d9877054026fda46d5a5b53a236?s=128

MOROHASHI Kyosuke

May 31, 2013
Tweet

Transcript

  1. Concerning ‘Applications’ Kyosuke MOROHASHI (@moro) RubyKaigi 2013, May 31, 2013

  2. ॾڮګհ(@moro) Kyosuke MOROHASHI

  3. None
  4. None
  5. 31st on leaderboard

  6. Background

  7. Rails applications are applied to complex real-life problems.

  8. Many rails users worry about the ‘Fat model’ problem.

  9. ✓ One of solutions is the technique of ‘Concern’s, which

    divides model behavior into small units. ✓ implemented as ActiveSupport::Concern
  10. What is Rails’ ActiveSupport:: Concern ?

  11. Ruby’s Module extension by Rails

  12. ✓ automatically extends own ClassMethods module if exists. ✓ provides

    included {} callback for declarative methods.
  13. gist:moro/5652724 module Censorable extend ActiveSupport::Concern included do has_many :monitor_requests, as:

    :entity, validates :censor_status, inclusion: Censorable end module ClassMethods def censor_passed where(status: Censorable::PASSED_STATUSES) end end def censor_passed? censor_status.passed? end
  14. What’s nice about ActiveSupport::Concern ?

  15. ✓ Provides a convention to write: ✓ Instance methods ✓

    Class methods ✓ Configuration & customization by declarative methods.
  16. became first class citizen in Rails 4 <3 <3 <3

  17. ActiveSupport:: Concern is a nice Rails’ module extension.

  18. What should we Concern about?

  19. Models’ common behaviors

  20. ✓ Users can tag the {question|answer}. ✓ Users can comment

    on the {question|answer}. ✓ Service providers must censor user- posted {question|answer}’s content.
  21. class Question < AR::Base include Taggable include Commentable include Censorable

    # and question specific definitions.. end class Answer < AR::Base include Taggable include Commentable include Censorable # and question answer definitions.. end
  22. Nested resource's parent.

  23. # question and answers that the user has # /users/:user_id/questions

    # /users/:user_id/answers resources :users do resources :questions resources :answers end
  24. module UsersResource extend ActiveSupport::Concern included do helper_method :owner hide_action :owner

    end private def owner @_owner ||= User.where(id: params[:user_id]).first end end
  25. use AS::Concern to extract common behaviors.

  26. How do we use AS::Concern nicely?

  27. Keep it a GLUE

  28. ✓ Add only a few methods to a base class.

    ✓ Keep ‘Loose Coupling’ between Concern and models.
  29. gist:moro/5652724 module Censorable extend ActiveSupport::Concern (skip) def feedback_monitor_judge(status) if PASSED_STATUSES.include?(status)

    .... else .... end end def request_monitoring! req = monitor_requests.create!(content: monitor res = post_monitor_request(req) unless res.code == '200' raise Censorable::MonitorRequestFailed.new(re end Bad case
  30. gist:moro/5652724 module Censorable extend ActiveSupport::Concern (skip) def request_monitoring! MonitorRequest.submit(self) end

    end Good case
  31. gist:moro/5652724 class MonitorRequest < ActiveRecord::Base class << self def submit(entity)

    req = create!(entity: entity, content: monito res = post_monitor_request(req) unless res.code == '200' raise Censorable::MonitorRequestFailed.new( end end end end Good case
  32. Tests become more obvious.

  33. gist:moro/5652724 describe Censorable do let(:question) { create(:question) } before do

    question.request_monitoring! end specify do MonitoringRequest. should have(1).record end end Bad case
  34. gist:moro/5652724 describe MonitoringRequest do let(:question) { create(:question) } before do

    MonitoringRequest.submit(question) end specify do MonitoringRequest. should have(1).record end end Good case
  35. How to glue class specific behavior?

  36. ✓ Plain old conditional branch ✓ Template methods

  37. gist:moro/5652724 class MonitorRequest < ActiveRecord::Base class << self def monitoring_content(obj)

    case obj when Question [obj.title, obj.body].join("\n\n") when Answer entity.body end end end end Plain old conditional branch
  38. PROS: Less effect on base class. CONS: Too naive. Plain

    old conditional branch
  39. gist:moro/5652724 class MonitorRequest < ActiveRecord::Base class << self def submit(entity)

    content = entity.monitoring_content ... class Question < AR::Base def monitoring_content [title, body].join("\n\n") end end class Answer < AR::Base def monitoring_content; body; end end Template method
  40. PROS: Acquires polymorphism. CONS: pollutes base classes. Template method

  41. Keep Concern a thin, glue layer. But still something is

    wrong with interface with base class...
  42. in Ruby 2.0 Concern meets Refinements

  43. “Refinements are intended to restrict the impact of monkey patching

    to a certain scope http://jp.rubyist.net/magazine/?Ruby200SpecialEn-refinement @shugomaeda says:
  44. Restrict the impact of glue code’s template method.

  45. gist:moro/5652724 module Censorable module MonitoringContent refine Question do def monitoring_content

    [title, body].join("\n\n") end end refine Answer do def monitoring_content; body ; end end end end Refinements
  46. gist:moro/5652724 using Censorable::MonitoringContent class MonitorRequest < ActiveRecord::Base class << self

    def submit(entity) content = entity.monitoring_content ... Refinements
  47. BTY: Concern as a service layer glue? ✓ Refinements let

    us define: ✓ context specific extensions. ✓ multi-classes ext. in one place.
  48. BTY: Concern as a service layer glue? ✓ Concern could:

    ✓ provide namespace to the extensions. ✓ glue base classes and the extensions.
  49. Ruby 2.0’s refinements are so great that we can extract

    concerns nicely with them.
  50. Wrapping up

  51. ✓ AS::Concern is a way to keep your application clean.

    ✓ I talked about a way to use it nicely :-)
  52. ActiveSupport::Concern is not ‘off the Rails’.

  53. It’s a supporter, like a bicycle: it helps your journey

    where there's no common rails.
  54. Acknowledgements ✓ All of Ruby community: especially Ruby team ,

    Rails team, RubyKaigi staffs and you. ✓ My clients, colleagues, friends and family. ✓ Nice material authors: ✓ http://good-wallpapers.com/misc/22337 ✓ http://www.fontspace.com/pete-klassen/ringbearer ✓ http://subtlepatterns.com/ ✓ http://www.flickr.com/photos/dailyinvention/23126614/ ✓ http://www.flickr.com/photos/bdamgaard/510134092/