rails conf 2014 concerns, decorators, presenters, service-objects, helpers, help me...

52
1

Upload: justin-gordon

Post on 11-May-2015

4.801 views

Category:

Education


2 download

DESCRIPTION

Slides Ruby Conf 2014 on using simple techniques to create slimer, clearer models, controllers, and views in Ruby on Rails. blog: http://www.railsonmaui.com Code samples: https://github.com/justin808/fat-code-refactoring-techniques

TRANSCRIPT

Page 1: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

1

Page 2: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Concerns, Decorators, Presenters, Service

Objects, Helpers, Help me Decide!

RailsConf 2014

Chicago

April 22, 2014

Justin Gordon

@railsonmaui

Rails Consultant

www.railsonmaui.com

2

Page 3: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

3

ControllerModel &

Page 4: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Avoid the

Ball of Mud

May seem fun…

Guaranteed:

the perpetrator is

not doing the

cleanup!

4

Page 5: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

6

Page 6: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

7

How do we organize the mess?

Page 7: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Organizational Conventions Matter

8

Department Store

Page 8: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Organizational Conventions Matter

9

Thrift Store

Page 9: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Like Fashion…

Coding Style ➜ Personal Preference

10

Page 10: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

And Our Style is…

11

Page 11: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

DHH QuoteJG: "This is starting to boil down to utilize the

framework capabilities and move beyond only when

necessary.”

DHH: "Which is really just an extension of KISS

(Keep It Simple, Stupid). When you use the

framework code for what it’s

intended, you’re not cutting against

the grain. You don’t need to write as much code.

It’s clearer to everyone because it’s the same

approach everyone else is taking."12

Page 12: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Microposts Example

13

Micropost Model

User Model

Micropost Controller

User Controller

1

N

Page 13: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Refactoring Examples in Pull Requests

• https://github.com/justin808/fat-code-refactoring-

techniques/pulls

• Based on Michael Hartl’s “Rails Tutorial” MicroBlog

example application

14

Page 14: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Objectives

Patterns &

Techniques

15

DRY

Methods

< 5 Lines

Classes

< 100 lines

One Instance

Variable in

View

Easy to

Test

ConcernsDraper

Decorators

Validation

Classes

Presenters

Split-up

Controllers

ClarityEasy to

Change

Guidelines

Move Logic

to Models

Easy to

Find

Page 15: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

• Huge model file with even larger spec file.

• Break up the model/spec using Rails concerns. Try to

break it up by domain, but any logical split will help.

16

Scenario

Page 16: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario

• You’ve got duplicated code in two models, different

database tables.

• Tease out a concern that applies to both models. Since

your models extend ActiveRecord::Base, using regular

inheritance is problematic. Instead, use a concern.

17

Page 17: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Rails Concerns

18

Big Model

class macros (has_many, validates, etc.)

instance methods

class methods

Page 18: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Rails Concerns

19

Big Model

some-domain class macros

some-domain instance methods

some-domain class methods

other class macros

other instance methods

other class methods

Domain Concern

some-domain

class macrossome-domain

instance methodssome-domain

class methods

Page 19: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Concerns: How

• Discover set of related code for a problem domain

• Create a module with extends ActiveSupport::Concern

• Move code into the Concern

• Break out tests into corresponding test file for the Concern

20

Page 20: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

DHH on Domain vs. Technical Refactoring

"I’ve not yet found a case where the scope of the current

file/class couldn’t be brought under control by using a

domain-driven extraction approach."

"In a sea of 60 methods, there will always be domain-

based groupings, rather than technical groupings.

Never seen that not be the case."

21

Page 21: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Concerns: Example

• Break out Emailable Concern out of User model

• Captures domain logic of lower case emails on user

model

• Benefits: Smaller model, smaller spec

22

Page 22: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Objectives

Patterns &

Techniques

23

DRY

Methods

< 5 Lines

Classes

< 100 lines

One Instance

Variable in

View

Easy to

Test

ConcernsDraper

Decorators

Validation

Classes

Presenters

Split-up

Controllers

ClarityEasy to

Change

Guidelines

Move Logic

to Models

Easy to

Find

Page 23: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario

• Model file creating detailed validation messages with

HTML tags and URL links.

• Move the message creation code into a Draper Decorator

for the model. These decorators work great for model

based presentation code.

24

Page 24: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators

25

Mode and

Model-

Concerns

Presentation

Code (views,

helpers)

Page 25: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators

26

Mode and

Model-

Concerns

Presentation

Code (views,

helpers)

Draper

Decorators

Page 26: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators: What?

• Popular gem that facilitates model decorators

• Very simple, easy to use

27

Page 27: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators: Why?• Removing presentation code from your model or model-

concerns

• Consolidating some helper, view, controller methods by

models

• Presentation code relating to one model, but multiple

controllers/views

• Consolidation of flash messages related to a given model

28

Page 28: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators: Why• Decorators are the ideal place to:

• format complex data for user display

• define commonly-used representations of an object, like a

name method that combines first_name and last_name

attributes

• mark up attributes with a little semantic HTML, like

turning a url field into a hyperlink

29

Page 29: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Draper Decorators: Alternatives

• View Helpers

• PORO, getting a handle to the controller or view

30

Page 30: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Example

Several views have code that format the

micropost.created_at:

Posted <%= time_ago_in_words(micropost.created_at) %> ago.

31

Page 31: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario• You have duplicated rendering code in several files.

• Remedy:

1. If rendering code, use a partial.

2. If ruby code, use either a view helper or create a static

method on a utility class. View helpers have access

other helpers. Utility classes require extra work to call

view context methods.

32

Page 32: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Objectives

Patterns &

Techniques

33

DRY

Methods

< 5 Lines

Classes

< 100 lines

One Instance

Variable in

View

Easy to

Test

ConcernsDraper

Decorators

Validation

Classes

Presenters

Split-up

Controllers

ClarityEasy to

Change

Guidelines

Move Logic

to Models

Easy to

Find

Page 33: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario

• You are setting too many instance variables in the

controller action. You also have local variables being

assigned in the view.

• Presenter pattern: Create a PORO that wraps up the

values and logic going from the controller to the view.

34

Page 34: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario

• Fragment caching in your view, but some extra queries

still run

• Use the Presenter pattern, with memoization in the

instance methods.

• @foobar ||= calculate_foobar

35

Page 35: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Presenters

36

Presenter Object

Wrapping Data

Needed by View

Smaller Controller

Action Creating Only

the Presenter Instance

Big Controller

Action Setting

Many Instance

Variables

View with ONE

Instance Variable

View with MANY

Instance Variables

beforeafter

Page 36: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario• Problem: A controller file is huge with many actions and many

more private methods.

• Solution:

1. Split up the controller into multiple files by having your routing

file map to different controllers.

2. Put any common functionality in a controller concern, similar to

how you would do it for a model. An alternative is having an

inheritance hierarchy of controllers. Mix-ins are more flexible.

37

Page 37: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario• Problem:

• Your Presenter class needs to access the view context, but it’s PORO.

• Solution:

1. Use this include in your PORO: “include Draper::ViewHelpers”.

2. Pass the controller instance into the constructor of the Presenter

(include required helpers in controller), or set the view context in the

view file.

3. Pass the view context into the methods that need it on the Presenter.

38

Page 38: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Objectives

Patterns &

Techniques

39

DRY

Methods

< 5 Lines

Classes

< 100 lines

One Instance

Variable in

View

Easy to

Test

ConcernsDraper

Decorators

Validation

Classes

Presenters

Split-up

Controllers

ClarityEasy to

Change

Guidelines

Move Logic

to Models

Easy to

Find

Page 39: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

40

If a minor posts profane words:

1. The post shall not be valid.

2. A counter will track how many times the

minor tried to use profanity.

3. The minor's parents shall be notified.

4. A special flash alert will alert the minor

to profanity usage.

Business Case

Page 40: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

–David Heinemeier Hansson

“I've yet to see a compelling "make action a

service object" example in the wild. Maybe they

exist somewhere, though. Then again, maybe

unicorns are real too.”

41

https://gist.github.com/dhh/10022098

Service Objects?

Page 41: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Service Objects Example

42

Big Micropost

Create Action

on Controller

MicropostCreationService

ControllerResponse

Flash, Flash-now, status

code

Tiny Micropost

Create Action

on Controller

https://github.com/justin808/fat-code-refactoring-techniques/pull/6

before

after

Page 42: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

A Bit Humbling…DHH: "Sorry to keep shooting the patterns down, but this is

exactly what I mean when I say that most code does not

need patterns, it just needs to be rewritten better."

JG: "I think it's a pattern either way. The pattern you

presented is to use validators rather than a separate

object."

DHH: Right, which Rails already has built in, and the code

is easier to follow with less work.43

Page 43: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Single Purpose Controller

• Controller with only one action

• https://github.com/justin808/fat-code-refactoring-

techniques/pull/7

44

Big Micropost

Create Action on

Controller

Micropost Controller

Just for Create

Rest of the

Micropost Controller

Page 44: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

DHH on Controllers“It’s [controller] intended to process the incoming request,

fetch the model, and direct the user to a view or another

action. If you’re yanking logic of that nature out of the

controller, you’re making an anemic controller. Shoving

this into a service object is imo the lazy

approach that doesn’t deliver any benefits in

terms of simpler code. It imo is the sweep-it-under-the-

rug approach.45

Page 45: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

DHH on the work of a Controller

"I’ve yet to see compelling controller code that couldn’t be

slimmed down by simply writing it better, spinning off

another controller, or moving domain logic to the model.

Here’s another example of a code ping pong I did off a

convoluted action in RedMine:

https://gist.github.com/dhh/10023987”

46

Page 46: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Plain Rails

47

Big Micropost

Create Action

on Controller

Micropost Model

User ModelSmall

Micropost

Create Action

on Controllerbefore

after

Page 47: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Scenario

• Excessive model logic in complicated controller method.

• Either:

• Move model logic out of controller and into the models,

utilizing Rails features such as validation.

• Create a non-AR based model to handle an interaction

between two models (aka “Service Object”)

48

Page 48: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

POR (Plain Old Rails)• Use Rails Models, Validation, and Controller for their

proper jobs

• KISS (Keep It Simple Stupid)

• Don’t Invent Patterns That Don’t Need to be Invented

• Know the why of the Rails way

• Know the Rails way before deviating

49

Page 49: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Refactoring Steps

• Move validation code and checks out of controller to

model

• Move creation of flash message to decorator

• Move validation code to validation class

50

Page 50: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

References

• Rails Guides: http://guides.rubyonrails.org/

• Patterns to Refactor Fat ActiveRecord Models:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-

decompose-fat-activerecord-models/

• DHH’s Example of 2 Controllers with Concerns:

https://gist.github.com/dhh/10022098

51

Page 51: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Thanks!Special thanks to those that helped review my code samples to this talk:

@dhh, @jeg2, @gylaz, @jodosha, @dreamr, @thatrubylove,

@therealadam, @robzolkos, Thoughtbot’s Learn program forum and

Ruby Rogues Parley Forum

52

Rails on Maui HQ, aka Sugar Ranch Maui

Page 52: Rails Conf 2014 Concerns, Decorators, Presenters, Service-objects, Helpers, Help Me Decide-april-22-2014

Thanks!• More details at my blog:

http://www.railsonmaui.com

• Feel free to contact me

regarding your projects

[email protected]

• http://airpair.me/railsonmaui

53