Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

ॾڮګհ(@moro) Kyosuke MOROHASHI

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

31st on leaderboard

Slide 6

Slide 6 text

Background

Slide 7

Slide 7 text

Rails applications are applied to complex real-life problems.

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

What is Rails’ ActiveSupport:: Concern ?

Slide 11

Slide 11 text

Ruby’s Module extension by Rails

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

What’s nice about ActiveSupport::Concern ?

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

What should we Concern about?

Slide 19

Slide 19 text

Models’ common behaviors

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Nested resource's parent.

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

use AS::Concern to extract common behaviors.

Slide 26

Slide 26 text

How do we use AS::Concern nicely?

Slide 27

Slide 27 text

Keep it a GLUE

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Tests become more obvious.

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

How to glue class specific behavior?

Slide 36

Slide 36 text

✓ Plain old conditional branch ✓ Template methods

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

in Ruby 2.0 Concern meets Refinements

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Restrict the impact of glue code’s template method.

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Wrapping up

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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/