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.

MOROHASHI Kyosuke

May 31, 2013
Tweet

More Decks by MOROHASHI Kyosuke

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

  4. View Slide

  5. 31st
    on leaderboard

    View Slide

  6. Background

    View Slide

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

    View Slide

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

    View Slide

  9. ✓ One of solutions is the
    technique of ‘Concern’s, which
    divides model behavior into
    small units.
    ✓ implemented as
    ActiveSupport::Concern

    View Slide

  10. What is Rails’
    ActiveSupport::
    Concern ?

    View Slide

  11. Ruby’s Module
    extension by Rails

    View Slide

  12. ✓ automatically extends own
    ClassMethods
    module if exists.
    ✓ provides included {}
    callback for declarative
    methods.

    View Slide

  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

    View Slide

  14. What’s nice about
    ActiveSupport::Concern
    ?

    View Slide

  15. ✓ Provides a convention to write:
    ✓ Instance methods
    ✓ Class methods
    ✓ Configuration & customization
    by declarative methods.

    View Slide

  16. became first class citizen in
    Rails 4 <3 <3 <3

    View Slide

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

    View Slide

  18. What should
    we Concern
    about?

    View Slide

  19. Models’ common
    behaviors

    View Slide

  20. ✓ Users can tag the
    {question|answer}.
    ✓ Users can comment on the
    {question|answer}.
    ✓ Service providers must censor user-
    posted {question|answer}’s content.

    View Slide

  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

    View Slide

  22. Nested resource's
    parent.

    View Slide

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

    View Slide

  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

    View Slide

  25. use AS::Concern to
    extract common
    behaviors.

    View Slide

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

    View Slide

  27. Keep it a GLUE

    View Slide

  28. ✓ Add only a few methods to a
    base class.
    ✓ Keep ‘Loose Coupling’
    between Concern and
    models.

    View Slide

  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

    View Slide

  30. gist:moro/5652724
    module Censorable
    extend ActiveSupport::Concern
    (skip)
    def request_monitoring!
    MonitorRequest.submit(self)
    end
    end
    Good case

    View Slide

  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

    View Slide

  32. Tests become
    more obvious.

    View Slide

  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

    View Slide

  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

    View Slide

  35. How to glue
    class specific
    behavior?

    View Slide

  36. ✓ Plain old conditional branch
    ✓ Template methods

    View Slide

  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

    View Slide

  38. PROS: Less effect on base class.
    CONS: Too naive.
    Plain old conditional branch

    View Slide

  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

    View Slide

  40. PROS: Acquires polymorphism.
    CONS: pollutes base classes.
    Template method

    View Slide

  41. Keep Concern a thin, glue layer.
    But still something is wrong with
    interface with base class...

    View Slide

  42. in Ruby 2.0
    Concern meets
    Refinements

    View Slide

  43. “Refinements are intended
    to restrict the impact of
    monkey patching to a
    certain scope
    http://jp.rubyist.net/magazine/?Ruby200SpecialEn-refinement
    @shugomaeda says:

    View Slide

  44. Restrict the impact of
    glue code’s template
    method.

    View Slide

  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

    View Slide

  46. gist:moro/5652724
    using Censorable::MonitoringContent
    class MonitorRequest < ActiveRecord::Base
    class << self
    def submit(entity)
    content = entity.monitoring_content
    ...
    Refinements

    View Slide

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

    View Slide

  48. BTY: Concern as a service layer glue?
    ✓ Concern could:
    ✓ provide namespace to the
    extensions.
    ✓ glue base classes and the
    extensions.

    View Slide

  49. Ruby 2.0’s refinements are so
    great that we can extract
    concerns nicely with them.

    View Slide

  50. Wrapping up

    View Slide

  51. ✓ AS::Concern is a way to keep
    your application clean.
    ✓ I talked about a way to use it
    nicely :-)

    View Slide

  52. ActiveSupport::Concern
    is not ‘off the Rails’.

    View Slide

  53. It’s a supporter, like a bicycle:
    it helps your journey where there's no
    common rails.

    View Slide

  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/

    View Slide