Slide 1

Slide 1 text

1BUUFSOTUPEFBMXJUI CJH"DUJWF3FDPSE NPEFMT CZ-VJTNJ$BWBMMÉ

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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 https://github.com/discourse/discourse/blob/master/app/models/topic.rb

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

#JH.PEFMT

Slide 8

Slide 8 text

'BU.PEFMT

Slide 9

Slide 9 text

(JHBOUJD .PEFMT

Slide 10

Slide 10 text

8IZBSFUIFZCBE

Slide 11

Slide 11 text

431

Slide 12

Slide 12 text

-PX$PIFTJPO

Slide 13

Slide 13 text

5JHIU$PVQMJOH

Slide 14

Slide 14 text

#FDBVTF {{your_favourite_OO_guru}} TBZTTP

Slide 15

Slide 15 text

8IZBSFUIFZCBE JOQSBDUJDBMUFSNT  QMFBTF

Slide 16

Slide 16 text

!!class!Article!

Slide 17

Slide 17 text

!!!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

Slide 18

Slide 18 text

!!!!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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

! !!!!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!<

Slide 21

Slide 21 text

!!class!Article!!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!<!'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!<

Slide 22

Slide 22 text

before_validation after_validation before_save before_create after_create after_save after_commit

Slide 23

Slide 23 text

8IBUDBOXFEP BCPVUJU

Slide 24

Slide 24 text

.PEFMT

Slide 25

Slide 25 text

.PEFM

Slide 26

Slide 26 text

.PEFM .PEFM .PEFM

Slide 27

Slide 27 text

has_one

Slide 28

Slide 28 text

4DIFEVMJOH

Slide 29

Slide 29 text

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"

Slide 30

Slide 30 text

class)Scheduling)<)ActiveRecord::Base ))#"... ))include)Workflow

Slide 31

Slide 31 text

class)SchedulingsController)<)ApplicationController class)WorkflowsController)<)ApplicationController

Slide 32

Slide 32 text

4DIFEVMJOH

Slide 33

Slide 33 text

4DIFEVMJOH 8PSLqPX

Slide 34

Slide 34 text

class)Workflow)<)ActiveRecord::Base ))belongs_to):scheduling

Slide 35

Slide 35 text

class)Scheduling)<)ActiveRecord::Base ))#"... ))has_one):workflow

Slide 36

Slide 36 text

.BEFFYQMJDJUBO JNQMJDJUFOUJUZPGUIF EPNBJO

Slide 37

Slide 37 text

.PEFMTBSFTNBMMFS

Slide 38

Slide 38 text

/POQFSTJTUFE NPEFMT

Slide 39

Slide 39 text

ActiveModel

Slide 40

Slide 40 text

'PSNPCKFDUT

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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!

Slide 43

Slide 43 text

class)SignupsController)<)ApplicationController ))def)create ))))@signup)=)Signup.new(params[:signup]) ))))if)@signup.save ))))))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/

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

class)Signup)<)SimpleModel ))attr_accessor):name, ))HHHHHHHHHHHHHH:company_name, ))HHHHHHHHHHHHHH:email ))validates):email,)presence:)true ))#"…"more"validations"… ))def)persist! ))))@company)=)Company.create!(name:)company_name) ))))@user)=)@company.users.create!(name:)name,)email:)email) ))end end

Slide 46

Slide 46 text

$PODFSOT

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

ActiveSupport::Concern

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

class)Article)<)ActiveRecord::Base ))include)Taggable

Slide 51

Slide 51 text

module!Taggable !!extend!ActiveSupport::Concern !!included!do !!!!has_many!:taggings !!!!has_many!:tags,!through:!:taggings !!!!scope!:tagged_with,!+>(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

Slide 52

Slide 52 text

5IFZIFMQUPLFFQ DPIFTJPOIJHI

Slide 53

Slide 53 text

%3:OFTT

Slide 54

Slide 54 text

*OUSPEVDFMFTT JOEJSFDUJPOBOE DFSFNPOZUIBO PUIFSBCTUSBDUJPOT DPNQPTJUJPO EFDPSBUJPO

Slide 55

Slide 55 text

l0⒏DJBMTPMVUJPOzUP UIF#JH.PEFMTJTTVF JO3BJMT

Slide 56

Slide 56 text

app/controllers/concerns app/models/concerns

Slide 57

Slide 57 text

%PO`UpYUIFQSPCMFN KVTU IJEFJUCZNPWJOHDPEF BSPVOE 0CKFDUTFOEVQXJUIUIF TBNFNFUIPETBOE SFTQPOTJCJMJUJFT

Slide 58

Slide 58 text

/BNJOHDPOqJDUT 4IBSFETUBUF #PVOEBSJFTOPFYQMJDJU

Slide 59

Slide 59 text

$PNNPOQVSQPTF /PUCBTJDOBUVSFPGNPEFM $SPTTDVUUJOH %PNBJODPODFQU

Slide 60

Slide 60 text

#"app/models/article.rb class)Article)<)ActiveRecord::Base ))include)Accessors ))include)Validations ))include)Associations ))include)Scopes ))#"... end

Slide 61

Slide 61 text

#"app/models/article.rb class)Article)<)ActiveRecord::Base ))include)Taggable ))include)Searchable ))include)Movable ))include)Visible ))include)Trashable ))#"... end

Slide 62

Slide 62 text

3FTQPOTJCJMJUJFT

Slide 63

Slide 63 text

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ class)UserAuthenticator ))def)initialize(user) ))))@user)=)user ))end ))def)authenticate(unencrypted_password) ))))return)false)unless)@user ))))if)BCrypt::Password.new(@user.password_digest))==)unencrypted_password ))))))@user ))))else ))))))false ))))end ))end end

Slide 64

Slide 64 text

user)=)User.where(email:)params[:email]).first UserAuthenticator.new(user).authenticate(params[:password])

Slide 65

Slide 65 text

user)=)User.where(email:)params[:email]).first UserAuthenticator.new(user).authenticate(params[:password])

Slide 66

Slide 66 text

https://gist.github.com/brynary/4670393 user!=!User.where(email:!params[:email]).first UserAuthenticator.new(user).authenticate(params[:password]) class!ActiveUserPolicy !!LOGIN_PERIOD!=!14.days !! !!def!initialize(user) !!!!@user!=!user !!end !!def!active? [email protected]_confirmed?!&& [email protected]_login_at!

Slide 67

Slide 67 text

ActiveUserPolicy.new(user).active? ActiveUserPolicy::Query.new(current_company.users).find_each)do)|user| ))#"... end

Slide 68

Slide 68 text

4JOHMFSFTQPOTJCJMJUZ &YQMJDJUCPVOEBSJFT -JHIUFSUFTUT .PSFJOEJSFDUJPOBOEDFSFNPOZ

Slide 69

Slide 69 text

module)User::Active ))extend)ActiveSupport::Concern ))included)do ))))scope):active,)M>)do ))))))where(email_confirmed:)true). ))))))where('last_login)<)?',)14.days.ago) ))))end ))end ))def)active? ))))user.email_confirmed?)&& ))))user.last_login_at)<)14.days.ago ))end end https://gist.github.com/cavalle/4660239

Slide 70

Slide 70 text

https://gist.github.com/brynary/4670393 user!=!User.where(email:!params[:email]).first UserAuthenticator.new(user).authenticate(params[:password]) class!ActiveUserPolicy !!LOGIN_PERIOD!=!14.days !! !!def!initialize(user) !!!!@user!=!user !!end !!def!active? [email protected]_confirmed?!&& [email protected]_login_at!

Slide 71

Slide 71 text

user.active? current_company.users.active.each)do)|user| ))#"... end

Slide 72

Slide 72 text

ActiveUserPolicy.active?(user) ActiveUserPolicy::Query.new(current_company.users).find_each)do)|user| ))#"... end

Slide 73

Slide 73 text

3PMFT

Slide 74

Slide 74 text

class)Commenter ))def)initialize(person) ))))@person)=)person ))end ))def)recent_comments ))))@person.comments.where("created_at)>)?",)3.days.ago) ))end ))def)post_comment(text) ))))@person.comments.create(:body)=>)text) ))end ))def)update_comment(new_text) ))))raise)"Comment)not)owned)by)this)person")unless)comment.author)==)@person ))))comment.update_attribute(:body,)new_text) ))end end https://gist.github.com/4341122

Slide 75

Slide 75 text

class)Commenter ))def)initialize(person) ))))@person)=)person ))end ))def)recent_comments ))))@person.comments.where("created_at)>)?",)3.days.ago) ))end ))def)post_comment(text) ))))@person.comments.create(:body)=>)text) ))end ))def)update_comment(new_text) ))))raise)"Comment)not)owned)by)this)person")unless)comment.author)==)@person ))))comment.update_attribute(:body,)new_text) ))end end https://gist.github.com/4341122

Slide 76

Slide 76 text

(BUFXBZT

Slide 77

Slide 77 text

http://slides.jcoglan.com/di-eurucamp#13 class)Github::Client ))def)initialize(http_client) ))))@http)=)http_client ))end ))def)get_user(name) ))))data)=)@http.get("/users/#{name}").json_data ))))Github::User.new(data) ))end end

Slide 78

Slide 78 text

http_client)=)HTTPClient.new('https://api.github.com') github)=)Github::Client.new(http_client) github.get_user(params[:name])

Slide 79

Slide 79 text

TwitterPoster.new(current_user,)@comment.body).post FacebookPoster.new(current_user,)@comment.body).post NotificationsMailer.new_comment(@comment).deliver

Slide 80

Slide 80 text

5IJOFSNPEFMT &YQMJDJUJOUFSGBDFCFUXFFO PVSEPNBJOBOEFYUFSOBM TZTUFNT

Slide 81

Slide 81 text

$POUSPMMFST

Slide 82

Slide 82 text

:FT DPOUSPMMFST

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

4LJOOJFTUQPTTJCMF DPOUSPMMFST

Slide 85

Slide 85 text

class)CommentsController)<)ApplicationController ))#... ))def)create ))))@comment)=)@post.comments.build(post_params) ))))if)@comment.save ))))))redirect_to)@comment ))))else ))))))render)action:)'new' ))))end ))end

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

class)CommentsController)<)ApplicationController ))#... )) ))def)create ))))@comment)=)@post.comments.create!(post_params) )))) ))))Notifications.new_comment(@comment).deliver )))) ))))TwitterPoster.new(current_user,)@comment.body).post ))))FacebookPoster.new(current_user,)@comment.body).post ))end

Slide 88

Slide 88 text

*U`TPLUPVTF DPOUSPMMFSTGPS PSDIFTUSBUJPO

Slide 89

Slide 89 text

"WPJEDBMMCBDLT

Slide 90

Slide 90 text

%PO`UQVUBOZEPNBJOMPHJDJO UIFDPOUSPMMFS&ODBQTVMBUFJU JOEPNBJOPCKFDUT NPEFMT  NBJMFST HBUFXBZT

Slide 91

Slide 91 text

$POUFYUT

Slide 92

Slide 92 text

class!PostComment !!def!initialize(user,!entry,!attributes) !!!!@user!=!user !!!!@entry!=!entry !!!!@attributes!=!attributes !!end ! !!def!post !!!!@[email protected] [email protected]_attributes(@attributes) [email protected]!=!@entry [email protected]! ! !!!!LanguageDetector.new(@comment).set_language !!!!SpamChecker.new(@comment).check_spam !!!!CommentMailer.new(@comment).send_mail ! [email protected]_on_twitter? [email protected]_on_facebook? ! !!!!@comment !!end ! !!private ! !!def!post_to_twitter !!!!PostToTwitter.new(@user,!@comment).post !!end ! !!def!post_to_facebook !!!!PostToFacebook.new(@user,!@comment).action(:comment) !!end end class!CommentsController!

Slide 93

Slide 93 text

class)CommentsController)<)ApplicationController ))#... )) ))def)create ))))context)=)PostComment.new(current_user,)@post,)post_params) ))))@comment)=)context.post ))end

Slide 94

Slide 94 text

-FTTDBMMCBDLT 4JOHMFSFTQPOTJCJMJUZ -JHIUFSUFTUT .PSFJOEJSFDUJPO .PSFDFSFNPOZ

Slide 95

Slide 95 text

1VUUJOHJUUPHFUIFS

Slide 96

Slide 96 text

.PSFEPNBJONPEFMT &YUSBDUJOHSFTQPOTJCJMJUJFT &YQMJDJUFYUFSOBMJOUFSBDUJPOT $PPSEJOBUJOHGSPNBCPWF

Slide 97

Slide 97 text

4UZMFT

Slide 98

Slide 98 text

3BJMT8BZ

Slide 99

Slide 99 text

$POUSPMMFSTPWFS$POUFYUT $PODFSOTPWFS0CKFDUT (BUFXBZT "3GPSCBTJDEPNBJOMPHJD

Slide 100

Slide 100 text

00XBZ

Slide 101

Slide 101 text

$POUFYUTPWFS$POUSPMMFST 0CKFDUTPWFS$PODFSOT (BUFXBZT "3GPSQFSTJTUFODFPOMZ

Slide 102

Slide 102 text

%$*XBZ

Slide 103

Slide 103 text

$POUFYUTGPSFBDI6TF$BTF 3PMFTGPSBOZCVTJOFTTMPHJD "3GPSEBUBBDDFTTPOMZ

Slide 104

Slide 104 text

module)Customer ))def)add_to_cart(book) ))))self.cart)<<)book ))end end http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby

Slide 105

Slide 105 text

http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby class)AddToCart ))def)initialize(user,)book) ))))@user,)@book)=)user,)book ))))@user.extend)Customer ))end ))def)call ))))@user.add_to_cart(@book) ))end end

Slide 106

Slide 106 text

5IF3BJMTXBZ 5IF00XBZ 5IF%$*XBZ

Slide 107

Slide 107 text

:PVSXBZ

Slide 108

Slide 108 text

%BOL6XFM -VJTNJ$BWBMMÉ UXJUUFSDPNDBWBMMF HJUIVCDPNDBWBMMF QJOCPBSEJOVDBWBMMFUCJHNPEFMT