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. ✓ One of solutions is the technique of ‘Concern’s, which

    divides model behavior into small units. ✓ implemented as ActiveSupport::Concern
  2. 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
  3. ✓ Provides a convention to write: ✓ Instance methods ✓

    Class methods ✓ Configuration & customization by declarative methods.
  4. ✓ Users can tag the {question|answer}. ✓ Users can comment

    on the {question|answer}. ✓ Service providers must censor user- posted {question|answer}’s content.
  5. 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
  6. # question and answers that the user has # /users/:user_id/questions

    # /users/:user_id/answers resources :users do resources :questions resources :answers end
  7. 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
  8. ✓ Add only a few methods to a base class.

    ✓ Keep ‘Loose Coupling’ between Concern and models.
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. Keep Concern a thin, glue layer. But still something is

    wrong with interface with base class...
  16. “Refinements are intended to restrict the impact of monkey patching

    to a certain scope http://jp.rubyist.net/magazine/?Ruby200SpecialEn-refinement @shugomaeda says:
  17. 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
  18. gist:moro/5652724 using Censorable::MonitoringContent class MonitorRequest < ActiveRecord::Base class << self

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

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

    ✓ provide namespace to the extensions. ✓ glue base classes and the extensions.
  21. ✓ AS::Concern is a way to keep your application clean.

    ✓ I talked about a way to use it nicely :-)
  22. 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/