Fearless Refactoring

Rails Controllers
Andrzej Krzywda

2014 - 2015 Andrzej Krzywda
Contents

Rails controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

What is the problem with Rails controllers? . . . . . . . . . . . . . . . . . . . . . . . . . . 2
     Why the focus on controllers? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
     Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
     The gateway drug - service objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
     The Boy Scout rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
     Inspiration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

Why service objects? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
     Why not service objects? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

What is a Rails service object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
     What it¡¯s not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
     Duplicate, duplicate, duplicate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Refactoring and the human factor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
     Do we really need to change the existing code? . . . . . . . . . . . . . . . . . . . . . . . 11
     Refactoring takes a lot of time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
     I wouldn¡¯t refactor this part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
     I would refactor it differently . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
     Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

How to use this book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
     The structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Refactoring recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

Inline controller filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
CONTENTS

Explicitly render views with locals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

Extract render/redirect methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Extract a Single Action Controller class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Extract routing constraint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
     Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Extract an adapter object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

Extract a repository object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
     Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
CONTENTS

     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Extract a service object using the SimpleDelegator . . . . . . . . . . . . . . . . . . . . . . 61
     Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

Extract conditional validation into Service Object . . . . . . . . . . . . . . . . . . . . . . 73
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
     Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Extract a form object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
     Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
     Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
     Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
     Warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

Example: TripReservationsController#create

 85

Extract a service object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
     Move the whole action code to the service, using SimpleDelegator . . . . . . . . . . . . 88
     Explicit dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
     What¡¯s an external dependency? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Service - controller communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
     How do we deal with failures? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
     Extracting exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
     No more controller dependency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
     Move the service to its own file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
CONTENTS

     Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

Example: logging time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

The starting point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
     The ¡®aha¡¯ moment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Instantiating service objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
     Boring style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
     Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
     Dependor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

The repository pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
     ActiveRecord class as a repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
     Explicit repository object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
     No logic in repos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
     Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
     The danger of too small repositories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
     In-memory repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Wrap external API with an adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
     Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
     Another long example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
     Adapters and architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
     Multiple Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
     Injecting and configuring adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
     One more implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
     The result . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
     Changing underlying gem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
     Adapters configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
     Testing adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
     Dealing with exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
     Adapters ain¡¯t easy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
     Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

In-Memory Fake Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
     Why use them? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
     Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
     How to keep the fake adapter and the real one in sync? . . . . . . . . . . . . . . . . . . . 138
     When to use Fake Adapters? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
CONTENTS

4 ways to early return from a rails controller . . . . . . . . . . . . . . . . . . . . . . . . . 140
     1. redirect_to and return (classic) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
     2. extracted_method and return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
     2.b extracted_method or return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
     3. extracted_method{ return } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
     4. extracted_method; return if performed? . . . . . . . . . . . . . . . . . . . . . . . . . . 142
     throw :halt (sinatra bonus) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
     throw :halt (rails?) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
     why not before filter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

Service::Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

Validations: Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
     Where the fun begins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
     Where the fun ends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
     Where it¡¯s fun again . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
     When it¡¯s miserable again . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
     When it might come useful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

Validations: Objectify . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
     Not so far from our comfort zone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
     One step further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
     Almost there . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
     Rule as an object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
     Reusable rules, my way . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
     or the highway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
     Cooperation with rails forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

Good tests tell a story. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

Unit tests vs class tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
     Class tests slow me down . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
     Test units, not classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
     The Billing example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
     Service objects as a way of testing Rails apps (without factory_girl) . . . . . . . . . . . . 165
CONTENTS

Related topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

Service controller communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

Naming Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
     The special .call method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

Where to keep services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

Routing constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

Rails controller - the lifecycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
     Accessing instance variables in the view . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
     Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

Thank you . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

Bonus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184

Thanks to repositories¡­ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
     System description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
     The first solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
     The second attempt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
     The scary solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
     The source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
     Sample console session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189

Pretty, short urls for every route in your Rails app . . . . . . . . . . . . . . . . . . . . . . 191
     Top level routing for multiple resources . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
     Render or redirect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
     Top level routing for everything . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
     Is it any good? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Rails controllers

                                                          1
    What is the problem with Rails
    controllers?

    At the beginning they all start simple, as the one scaffolded with a generator:

1   def create

2   @issue = Issue.new(issue_params)

3

4   respond_to do |format|

5        if @issue.save

6        format.html { redirect_to @issue, notice: ¡®Success.¡¯ }

7        format.json { render action: 'show', status: :created, location: @issue }

8        else

9        format.html { render action: 'new' }

10       format.json { render json: @issue.errors, status: :unprocessable_entity }

11       end

12  end

13  end

    For some resources the controllers and actions stay simple. In some of them, though, they start to
    grow. It¡¯s not unusual to see actions like this one:

1 class IssuesController < ApplicationController
2 default_search_scope :issues

 3

 4 before_filter :find_issue, :only => [:show, :edit, :update]
 5 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
 6 before_filter :find_project, :only => [:new, :create, :update_form]
 7 before_filter :authorize, :except => [:index]
 8 before_filter :find_optional_project, :only => [:index]
 9 before_filter :check_for_default_issue_status, :only => [:new, :create]
10 before_filter :build_new_issue_from_params, :only => [:new, :create, :update_form]
11 accept_rss_auth :index, :show
12 accept_api_auth :index, :show, :create, :update, :destroy

13

14 rescue_from Query::StatementInvalid, :with => :query_statement_invalid

15

16  def bulk_update

17  @issues.sort!

18  @copy = params[:copy].present?

19  attributes = parse_params_for_bulk_issue_attributes(params)

20

21  unsaved_issues = []

                                                  2
    What is the problem with Rails controllers?                                                       3

22      saved_issues = []

23

24      if @copy && params[:copy_subtasks].present?

25       # Descendant issues will be copied with the parent task

26       # Don't copy them twice

27       @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}

28      end

29

30      @issues.each do |orig_issue|

31       orig_issue.reload

32       if @copy

33           issue = orig_issue.copy({},

34              :attachments => params[:copy_attachments].present?,

35              :subtasks => params[:copy_subtasks].present?

36           )

37       else

38           issue = orig_issue

39       end

40       journal = issue.init_journal(User.current, params[:notes])

41       issue.safe_attributes = attributes

42       call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })

43       if issue.save

44           saved_issues << issue

45       else

46           unsaved_issues << orig_issue

47       end

48      end

49

50      if unsaved_issues.empty?

51       flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?

52       if params[:follow]

53           if @issues.size == 1 && saved_issues.size == 1

54              redirect_to issue_path(saved_issues.first)

55           elsif saved_issues.map(&:project).uniq.size == 1

56              redirect_to project_issues_path(saved_issues.map(&:project).first)

57           end

58       else

59           redirect_back_or_default _project_issues_path(@project)

60       end

61      else

62       @saved_issues = @issues

63       @unsaved_issues = unsaved_issues

64       @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).all

65       bulk_edit

66       render :action => 'bulk_edit'

67      end

68  end

69 end

    Typical ¡°patterns¡± include:
What is the problem with Rails controllers?  4

 before_filter groups
 multiple, nested if-branches
 setting @ivars to access them in the view
 Controller inheritance
 Controller mixins/concers

On their own, none of the technique is inherently bad. However, when you group them together,
things start to be difficult. Some problems appear:

 Have you ever looked for the place where the @ivar is set, because it wasn¡¯t obvious?

 Did you need to change some of the before_filters and were feared that some other place of
  code will break?

 Have you ever looked at a Rails project and found it hard to track what are the system
  functions? What are the use cases?

 Have you tried to refactor a controller and gave up, as it seemed to be very risky and time-
  consuming?

This book is meant to solve the problems.

Why the focus on controllers?

Controllers are the entry points to your app. It¡¯s like multiple ¡®main¡¯ methods. Keep the place clean,
as this is your home.

When the controller contains a mix of concerns, then it¡¯s harder to make other places (views, models)
really clean.

There¡¯s a debate going on, whether your app is a Rails app or whether it should be separated and keep
Rails as an implementation detail. This book tries to stay neutral on such visionary topics. There¡¯s
however a lot of win (mostly testing), when you make a clear isolation between the controller and
your application.

Testing

It¡¯s rare to find a Rails app with a test suite run below 3 minutes. Even more, it¡¯s not uncommon
to have a build taking 30 minutes. You can¡¯t be agile this way. We should focus on getting the tests
run as quickly as possible. It¡¯s easy to say, but harder to do. This book introduces techniques, that
make it possible. I¡¯ve seen a project, for which a typical build time went from 40 minutes, down to 5
minutes. Still not perfect, but it was a huge productivity improvement. It all started with improving
our controllers.
What is the problem with Rails controllers?  5

The gateway drug - service objects

What I noticed is that once you start caring about the controllers, you start caring more about the
whole codebase. The most popular way of having better controllers is through introducing the layer
of service objects.

Service objects are like a gateway drug. In bigger teams, it¡¯s not always easy to agree on refactoring
needs. It¡¯s best to start with small steps. Service objects are the perfect small step. After that you¡¯ll
see further improvements.

The Boy Scout rule

When you work on a big project, you¡¯re pushed to deliver new features. The business rarely
understands the reasons for refactoring. The programmers often dream about the mythical ¡°let¡¯s
take one month to just refactor and clean up our codebase¡±. This doesn¡¯t happen often. Even if it
happens, it¡¯s really difficult to define the goal.
Refactoring is an ongoing activity. Refactoring is a team activity. Refactoring is best when everyone
understands the reasons and agrees on the direction of the code changes. After the team agrees on
the needs, you can apply The Boy Scout Rule:

       Always leave the campground cleaner than you found it.

Whenever you add a new feature or change an existing one, try to improve the existing code. Thanks
to that, with every day, you¡¯re making your project better.

Inspiration

This book is a distilled knowledge from many different resources - books, lectures, studying code
repositories.

     Martin Fowler ¡°Refactoring: Improving the Design of Existing Code¡±
     Michael Feathers ¡°Working Effectively with Legacy Code¡±
     Joshua Kierevsky ¡°Refactoring to Patterns¡±
     (Uncle Bob) Robert C. Martin ¡°Clean Code: A Handbook of Agile Software Craftsmanship¡±
     (video) Ruby Midwest 2011 - Keynote: Architecture the Lost Years by Robert Martin - http:

       //www.youtube.com/watch?v=WpkDN78P884
     Steve Freeman and Nat Pryce - ¡°Growing Object-Oriented Software, Guided by Tests¡±
     James O. Coplien, Gertrud Bjrnvig - ¡°Lean Architecture: for Agile Software Development¡±
Why service objects?

Services is not something that you usually introduce at the beginning of a Rails project (although
it¡¯s worth considering).
Rails gives you a nice speed of work at the beginning. Problems start appearing later:

   1. The build is slow
   2. Developer write less tests
   3. Changes cause new bugs
   4. It feels like the app is a monolith instead of a set of nicely integrated components

Services are not the silver bullet. They don¡¯t solve all the problems. They are good as the first step
into the process of improving the design of your application.
Thanks to services you achieve the following goals:

   1. isolate from the Rails HTTP-related parts
   2. faster build time
   3. easier testing
   4. easier reuse for API
   5. less coupling
   6. thinner controllers

From my experience, services are a nice trigger for the whole team. Once you have them, interesting
ideas come up, that can help in the design of the Rails app.

Why not service objects?

If your app is fairly small (mostly CRUD), you don¡¯t see the problem of frequent bugs, your tests
are fast enough and introducing new changes is very fast, then you probably don¡¯t gain much from
introducing the service layer. You¡¯ll be fine with just having the code in the controller actions.

                                                          6
What is a Rails service object?

In my observation, different programming communities have different meaning of service objects.
Before I describe ¡®the Rails meaning¡¯ I¡¯d like to quote some more generic definitions.
According to Martin Fowler¡¯s P of EEA Catalog:

       Defines an application¡¯s boundary with a layer of services that establishes a set of
       available operations and coordinates the application¡¯s response in each operation.

P of EAA Catalog: Service Layer
Bryan Helmkamp, the author of the famous: 7 Patterns to Refactor Fat ActiveRecord Models
described it as:

       Some actions in a system warrant a Service Object to encapsulate their operation. I
       reach for Service Objects when an action meets one or more of these criteria:

            The action is complex (e.g. closing the books at the end of an accounting period)
            The action reaches across multiple models (e.g. an e-commerce purchase using

             Order, CreditCard and Customer objects)
            The action interacts with an external service (e.g. posting to social networks)
            The action is not a core concern of the underlying model (e.g. sweeping up

             outdated data after a certain time period).
            There are multiple ways of performing the action (e.g. authenticating with an

             access token or password). This is the Gang of Four Strategy pattern.

According to Eric Evans and his Domain-Driven Design: Tackling Complexity in the Heart of
Software book:

       Service: A standalone operation within the context of your domain. A Service Object
       collects one or more services into an object. Typically you will have only one instance
       of each service object type within your execution context.

In the Rails world, the most popular definition seems to be: everything that happens in the controller
without all the HTTP-related stuff (params, render, redirect).
A service object encapsulates a single process of the business logic.

    http://martinfowler.com/eaaCatalog/serviceLayer.html
    http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
    http://www.amazon.com/gp/product/0321125215

                                                          7
What is a Rails service object?  8

What it¡¯s not

The concept of SOA (Service Oriented Architecture) is conceptually similar, in the way of having
a set of services. However, in practice, SOA includes the protocol layer (http, soap), which is less
relevant to the idea of service objects.

It¡¯s also not a book about microservices. The techniques from this book will make your modules
better isolated. Isolated modules are the step forward into microservices, but we don¡¯t go as far in
this book.
Refactoring

We keep talking about refactoring here, what is it?
Let¡¯s ask the experts.
Martin Fowler

       Refactoring is a controlled technique for improving the design of an existing code base.
       Its essence is applying a series of small behavior-preserving transformations, each of
       which ¡°too small to be worth doing¡±.

Joshua Kerievsky

       By continuously improving the design of code, we make it easier and easier to work
       with. This is in sharp contrast to what typically happens: little refactoring and a great
       deal of attention paid to expediently adding new features. If you get into the hygienic
       habit of refactoring continuously, you¡¯ll find that it is easier to extend and maintain
       code.

Michael Feathers

       One of the clearest preconditions for refactoring is the existence of tests. Martin Fowler
       is pretty explicit about that in his Refactoring book, and everything I¡¯ve experienced
       with teams backs it up. Want to do make things better? Sure, but if you don¡¯t have tests
       to support you, you¡¯re gambling. Can you do a little refactoring to get tests in place?
       Yes, I advise it, but when you start to do significant refactoring, you¡¯d better have tests
       to back you up. If you don¡¯t, it¡¯s only a matter of time before your teammates take away
       your keyboard.

Let me retrieve some very important keywords from those quotes:

     A controlled technique
     Improving the design
     Existing code base
     Series of transformations
     Continuously
     Habit

                                                          9
Refactoring  10

 Maintain
 Tests
 Teammates

I¡¯d like to focus on a few of those. Some of them are obvious. I like the part about habit and about
teammates. At the technical level, most programmers understand what refactoring is about. What¡¯s
missing is the systematic approach and the team support. If only 1 person in the team is ¡°brave¡±
enough to refactor code, then you may have a problem. Also, if refactoring only happens at certain
moments, then you lack the habit part, you don¡¯t do it continuously.

The reason you don¡¯t refactor as a habit is the perception of fear and cost. Fear - you¡¯re afraid that
the changes will break and you can¡¯t afford the breaks. Cost - probably for good reasons, you¡¯re
worried that the programmers won¡¯t deliver business value.

I admit, I met programmers who took so much time to ¡®refactor¡¯ without any delivery, that it crossed
the limits of profitability. There¡¯s no place for such things. We all exist in some kind of business
context and understanding what¡¯s important is part of our job. The real skill of refactoring is in
balancing the delivery with maintainability. If someone is refactoring for 1 week, then please, help
them. They probably need help in being able to split the refactoring into smaller steps.

This is where this book aims to help you. The transformations presented in this book are very small,
very focused. Once you practice the techniques, you should be able to do any of them in a matter of
minutes, not hours.

Duplicate, duplicate, duplicate

It will be surprising and unintuitive at the beginning, but many of the techniques in this book involve
code duplication. Usually, it¡¯s just a temporary duplication.

Duplication is against the core parts of Rails DNA. We follow the DRY (Don¡¯t Repeat Yourself) rule,
everywhere we can, right?

The main reason for duplication is safety. Often, we want to inline a method call, so that we can
safely make changes to the block of code without any fear of breaking other places. Another reason
is to be able to look at the code in one place to clearly see what is going on. At the end, we¡¯ll go
through the code and eliminate the duplication.

Let¡¯s now talk more about the human factor of refactoring.
Refactoring and the human factor

Not all team members are equally pro-refactoring.
I know how annoying it may be, when you¡¯re doing your best to improve the code, you learn how
to do it the best way, you try to introduce it and then it¡¯s not only not appreciated, but also often
rejected. Why is that?
There¡¯s plenty of reasons, but most of them come down to the fact that a project is a team work. In
a team, we work with other people. We all share the same goal - to let the project succeed. However,
we¡¯re humans - each of us has a different perception of the goal and the path that leads to this goal.
You may think that it¡¯s obvious that a better code will result in the success of the project. In most
cases that¡¯s true. However, people have different perceptions of which code is actually good.
You may notice different levels of the refactoring-scepticism.

Do we really need to change the existing code?

If you see such attitude, then one way of dealing with it is going back to the reasons for the
refactoring. Is it because you spend a tremendous time on bug fixing? Is it because adding new
features takes more and more time? Maybe it¡¯s because the app is slow and refactoring can help you
extract some parts and later optimize them?
There¡¯s never refactoring for the sake of refactoring.
Whatever is the reason, make sure everyone understands it. If there¡¯s no understanding, move the
discussion one step back. Why is it that your perception of the situation is different? Programmers
are smart and logical people. Once you all see facts, metrics and numbers, it¡¯s easier to agree on the
same solution.

Refactoring takes a lot of time

This is a fair argument. We all care about the time we spend on the project. Even if it¡¯s you doing
the refactoring, then everyone is aware of the fact, that this time has a cost.
The best way to deal with this argument is to keep improving refactoring skills. Both yours and
your teammates. Examples from the Fowlers¡¯ ¡°Refactoring¡± book are great. If you¡¯ve done a quick
refactoring, maybe it¡¯s worth showing to your team on a projector or record a screencast?
Become so good at refactoring that it almost happens at no-time. Split the bigger changes into
multiple smaller ones. If you keep delivering value, while cleaning up the codebase then you don¡¯t
need to justify the time spent.

                                                         11
Refactoring and the human factor  12

I wouldn¡¯t refactor this part

I made the mistake of refactoring the part of the code that wasn¡¯t really that important to change. It
depends on the time spent. It¡¯s always good to improve the code everywhere, but what is the price?
Does it take you 10 minutes, 1 hour? 10 hours? 10 days? Is it worth it?
Time is our currency, make sure we spend it in the best way.
Some parts of code might be very costly to test or to do QA. It all depends on the context of your
project. Some projects may require external auditing after every change. If that¡¯s your reality, then
you can¡¯t just happily changing the code every hour.

I would refactor it differently

This problem appears when we have different visions of the refactoring. Let¡¯s say you learnt
everything you could about DCI and you¡¯re sure that¡¯s the best direction to go with your project.
You envision the contexts, the roles, the objects. Slowly, you keep extracting more code into modules
that are later (hopefully) used as roles.
At the same time, your colleague kept studying the concept of microservices. His goal is to split the
app into multiple services talking to each other via HTTP/JSON.
Where you see contexts, he sees services.
This represents an architectural difference between both of you.
To be honest, this is a nice problem to have. It means, that people in your team are passionate. They
put time into learning new concepts and they are constantly trying to image the app in a different
way.
How to deal with it?
I¡¯ve chosen DCI and microservices as examples, but you could have a much different pair. What
matters here is that most of the good architectures are not in fact that much different, however
surprising it may sound.
If you want to go DCI and your colleague wants to go microservices, then you have more in common
than conflicts. Putting behaviour in the modules, as a step into using them as DCI roles is also a step
into the microservices direction (you split the logic in a way that can be used by the microservice,
later on). Your main difference is probably the last step - really, that¡¯s a nice problem to have :)

Summary

No matter what is the reason of the initial misunderstanding about the refactoring, make sure
everyone understands it the same. Most of the times, there¡¯s always something rational behind
the refactoring need.

    http://andrzejonsoftware.blogspot.com/2011/02/dci-and-rails.html
Tools

I¡¯ve used all of the editors available for Rails development.
I¡¯ve been with TextMate at the beginning, then switched to vim, loved it, tried to master it for years.
Then I paired with my friend and he used RubyMine, which I really liked. I used it for some time,
but then tried Sublime and went back to vim. My current editor of choice is RubyMine again.
I don¡¯t want to start an editor war here. You already have your choice, I have mine. In the spirit
of this book, let¡¯s treat editors as tools. Some of them are good for certain things, while others are
better for other tasks.
When I work on a fresh Rails app, then I almost always use vim. It¡¯s light, fast, fun to use it.
When I work on an existing, big, legacy Rails app, I go with RubyMine. It does have a slow start
(indexing takes a while), but the navigation options are excellent for me. I have different modes
of using RubyMine. When I¡¯m just getting familiar with the codebase (my job involves reviewing
many Rails apps a month), I navigate with mouse, however uncool it sounds. When I start making
changes, I enter the keyboard-only mode.
Where RubyMine shines for me is the refactoring capabilities. I¡¯m sure it¡¯s all possible with vim as
well, I just never got around to configure it the right way. RubyMine has it all set up.
Ruby, as a dynamic language doesn¡¯t help with automatic refactorings. RubyMine takes a semi-
automatic approach - whenever it¡¯s not sure, it lets you review the planned changes. It¡¯s also really
good with heuristics - when you make a method from a block of code, RM checks if there are other
such blocks and asks if they need to be changed.
Personally, I recommend using RubyMine if you want to do more refactorings on a daily basis.
It¡¯s good to at least see its capabilities and then go back to your favourite editor (vim, right?) and
configure it to do the same.

                                                         13
How to use this book

The structure

From now on, this book is organised into 3 main parts:

     Recipes
     Examples
     Patterns

Recipes

We start with Recipes. Recipes are very precise descriptions of several techniques. A recipe takes
your code from point A through several Steps to point B. It¡¯s a short and safe trip.
Recipes are as safe as it¡¯s possible to be safe with a dynamic language. A recipe clearly states a
Prerequisite - where you need to be with your code, before you apply this receipe. Afterwards,
it contains a clear step-by-step Algorithm. The algorithm is short, precise, easy to remember. It¡¯s
composed of several Steps. Each recipe contains an Example (sometimes more). The examples are
meant to be simple. They¡¯re simple so that the main point of the recipe is very clear.
Recipes are designed in a way to be easily referenced in the future.
I want you to come back to the recipes as often as you need, before you¡¯re fully confident with
applying them on your own. They are your cheatsheet here. The confidence is the keyword here.
There¡¯s no longer place for doubts during your coding activities. There¡¯s no time for that. Refactoring
needs to be in your muscle memory.
Every recipe contains a list of Warnings. Ruby programmers are very creative people. We come up
with such original ideas, that it¡¯s sometimes difficult to predict. I tried to collect as many edge cases
as possible to make them explicit here - what to watch for.
At the end, we have Benefits and Next Steps. They remind you what you achieved. The show you
why the world is now better. Even more - there¡¯s usually a list of what you can consider to do
afterwards. Depends on your time, you may want to jump to the next recipe and apply it to the
current codebase.
The recipes are ordered from the simplest to the more complex ones. The reasoning behind this is
that you know what other recipes rely on - what are their Prerequisites. This should help you in the
first reading.

                                                         14
How to use this book  15

Examples

Examples make the next part of the book. The idea here is to follow one piece of code from point
A to point Z. We start with a non-trivial, quite typical Rails action. We discuss the possibilities and
choose which recipe is going to be the next one to apply.

Examples show the recipes in a bigger context. You can see the reasoning behind every decision. It¡¯s
here where you¡¯re exposed to real-world controllers with all their beauty and ugliness.

Patterns

In the Patterns chapter we go in-depth with many of the concepts that appeared in the book. We
discuss the theoretical aspect of each of the patterns, but also show a lot of code.
Refactoring recipes

                                                         16
Inline controller filters

Using controller filters is a very popular approach in Rails apps. This technique is used for
implementing cross-cutting concerns, like authorization, auditing and data loading.
Often, the filters introduce coupling between the controller action and the result of the filters.
Sometimes the coupling doesn¡¯t hurt much. Sometimes, though, the filters prepare some global
state using the instance variables. That makes the coupling worse, as it¡¯s difficult to extract a service
object from a controller.
In case of a simple filter, it¡¯s easy to simplify the situation by inlining it.
It¡¯s very similar to the original ¡°Inline method¡± refactoring, described by Fowler.
Before we dig deeper, let¡¯s make sure what filters were about. Here we have some snippets from the
documentation:

       ¡± Filters are methods that are run before, after or ¡°around¡± a controller action.¡±

       ¡°Filters are inherited, so if you set a filter on ApplicationController, it will be run on
       every controller in your application.¡±

       ¡°¡±Before¡± filters may halt the request cycle. A common ¡°before¡± filter is one which
       requires that a user is logged in for an action to be run.¡±

       If a ¡°before¡± filter renders or redirects, the action will not run.

       If there are additional filters scheduled to run after that filter, they are also cancelled.

       ¡°after¡± filters cannot stop the action from running.

       ¡± Around¡± filters are responsible for running their associated actions by yielding, similar
       to how Rack middlewares work.

       Note that an ¡°around¡± filter also wraps rendering.

It¡¯s important to remember that filters use a different communication protocol. For a filter it¡¯s enough
to return false or call render or redirect to halt the chain. When you inline them in an action it no
longer works this way. You must append all the render/redirect expressions with a return statement.

                                                         17
    Inline controller filters                                                      18

    Example

    This example is taken from the Redmine project. There¡¯s a TimelogController which handles
    submitting time logs. It has quite a few before_filters. For the example we simplified them a
    bit:

1 class TimelogController < ApplicationController

2

3 before_filter :find_project_for_new_time_entry, :only => [:create]
4 before_filter :find_time_entry, :only => [:show, :edit, :update]
5 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]

6

7 before_filter :find_optional_project, :only => [:index, :report]
8 before_filter :find_optional_project_for_new_time_entry, :only => [:new]

9

10     def create

11     @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :sp\

12 ent_on => User.current.today)

13     @time_entry.safe_attributes = params[:time_entry]

14

15     call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry \

16 })

17

18     if @time_entry.save

19          respond_to do |format|

20          format.html {

21               ...

22     end

23

24     private

25

26     def find_project_for_new_time_entry

27          find_optional_project_for_new_time_entry

28          if @project.nil?

29          render_404

30          end

31     end

32

33     def find_optional_project_for_new_time_entry

34          if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_i\

35 d])).present?

36          @project = Project.find(project_id)

37          end

38          if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).p\

39 resent?

40          @issue = Issue.find(issue_id)

41          @project ||= @issue.project

42          end

43     rescue ActiveRecord::RecordNotFound

44          render_404
    Inline controller filters                                                      19

45     end

46     end

    The first step is to determine which filters apply to the action that we want to extract as a service
    object. The ¡®filters algebra¡¯ (except, only) is very simple, so we know it¡¯s only :find_project_for_-
    new_time_entry.

    The create action is coupled with the filters via the instance variables that need to be set, in this
    case: @project and @issue.

    The ¡®inline controller filter¡¯ technique is a simple change:

1 class TimelogController < ApplicationController

2

3 before_filter :find_time_entry, :only => [:show, :edit, :update]
4 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]

5

6 before_filter :find_optional_project, :only => [:index, :report]

7 before_filter :find_optional_project_for_new_time_entry, :only => [:new]

8

9      def create

10     find_project_for_new_time_entry

11     @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :sp\

12 ent_on => User.current.today)

13     @time_entry.safe_attributes = params[:time_entry]

14

15     call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry \

16 })

17

18     if @time_entry.save

19          respond_to do |format|

20          format.html {

21          ...

22     end

    All we did, we moved the call to find_project_for_new_time_entry from the filter to the controller
    action.

    When is this refactoring useful?

    It¡¯s useful when you want to bring together the code that belongs together so that you can move it
    as a whole somewhere else. It¡¯s one of those ¡®eliminate Rails magic¡¯ techniques, that help reasoning
    about the code in one place.

    Warnings

    When you have dependencies between filters (yes, I¡¯ve seen those), then you can¡¯t just take one filter
    from the middle and inline it. This may break the functionality. One example is a group of filters,
Inline controller filters                                              20

when one filter depends on the data set by the previous filter. If you have such situation, though,
it¡¯s even more recommended to inline them, but make it with caution!

It¡¯s best to start inlining filter with the last filter. When you put it at the beginning of the method,
it¡¯s more or less the same, as being the last filter. This way you can stack filters in the action, in a
safe way.

Resources

http://guides.rubyonrails.org/action_controller_overview.html#filters
 Explicitly render views with locals

  Introduction

   The default practice in Rails apps is not to care about calling views. They are called and rendered
   using conventions. Whenever an action is called, there¡¯s an implicit call to render, so you don¡¯t have
   to do that manually. Less code, more conventions.
   Such conventions are very useful at the early stage of the project. They speed up the prototyping
   phase. Once the project becomes more complex, it might sometimes be useful to be more explicit
   with our code.
   There are three things that are implicit.

         The call to render itself
         The path to the view file
         The way the data is passed to the view

   In theory, this refactoring is simple. Go to the view, replace all @foo with foo. The same in the
   controller. Also, in the controller, at the end of the action, call

1 render ¡°products/new¡±, :locals => {:foo => foo}

   In practice, you need to be careful when the view renders a partial. You need to explicitly pass the
   local variable further down. Also, views are not tied to one action. There are typical reusable views
   like ¡®new¡¯, ¡®edit¡¯, ¡®_form¡¯. In those cases all the actions need now to pass locals in appropriate places.
   It¡¯s also important to remember, that you¡¯re not just rendering a view. You¡¯re rendering the whole
   layout. The view is just rendered inside. What that means, is that the whole layout depends on the
   @ivars or locals. It¡¯s easy to forget to check what exactly the layout depends on.
   The nice thing about render :locals, is that it doesn¡¯t mean all or nothing. It means that, if your view
   relies on many @ivars, for the safety, you can make the transition, gradually. One @ivar into local,
   at a time. It also means, that you don¡¯t have to be scared that the layout depends on some @ivar set
   in an ApplicationController before_filter (a common pattern).
   After this transformation is done, you¡¯ve got a little verbose ¡°render¡± calls. They¡¯re now taking
   params explicitly. It¡¯s good to apply the ¡°Extract render/redirect methods¡± refactoring afterwards.

                                                             21
    Explicitly render views with locals                           22

    Algorithm

       1. Go to the view, replace all @ivar with var
       2. Do the same in all partials that are called from the view and always pass the params to partials

           explicitly with render ¡°products/form¡±, {product: product}
       3. At the end of the action add an explicit render call with a full path and the locals: render

            ¡°products/new¡±, :locals => {product: product}

       4. Find all controllers that were using the views/partials that you changed and apply the same.

    Example

    The example comes from the lobste.rs project (a HackerNews clone). The ¡®tree¡¯ action is responsible
    for retrieving all users in the system, grouping them by parent (a person who invited the user). The
    view then takes this data structure and displays as a tree-like representation, with nesting.

1 class UsersController < ApplicationController

2

3   def tree

4       @title = "Users"

5

6       users = User.order("id DESC").to_a

7

8       @user_count = users.length

9       @users_by_parent = users.group_by(&:invited_by_user_id)

10  end

11 end

1 <div class="box wide">
2 <p><strong>Users (<%= @user_count %>)</strong></p>

3

4 <ul class="root">

5

6       <% subtree = @users_by_parent[nil] %>

7       <% ancestors = [] %>

8

9       <% while subtree %>

10       <% if (user = subtree.pop) %>

11       <li>

12       <a href="/u/<%= user.username %>"

13            <% if !user.is_active? %>

14             class="inactive_user"

15            <% elsif user.is_new? %>

16             class="new_user"

17            <% end %>

18            ><%= user.username %></a>&nbsp;(<%= user.karma %>)
    Explicitly render views with locals                                                       23

19         <% if user.is_admin? %>

20            (administrator)

21         <% elsif user.is_moderator? %>

22            (moderator)

23         <% end %>

24         <% if (children = @users_by_parent[user.id]) %>

25            <% # drill down deeper in the tree %>

26            <% ancestors << subtree %>

27            <% subtree = children %>

28            <ul class="user_tree">

29         <% else %>

30            </li>

31         <% end %>

32       <% else %>

33         <% # climb back out %>

34         <% subtree = ancestors.pop %>

35         <% if subtree %>

36         </ul></li>

37         <% end %>

38       <% end %>

39  <% end %>

40  </ul>

41 </div>

    The action prepares 3 instance variables: @title, @user_count and @users_by_parent.

    It¡¯s worth noting that @title is only used in the layout, not in the direct view. The ¡°title concern¡±
    is orthogonal to what the action does. Let¡¯s leave it as it was. We don¡¯t gain much by moving it to
    locals, yet.

    Before we go further, let¡¯s double check that the view doesn¡¯t call any other partial. It doesn¡¯t in this
    case. If it called a partial, we¡¯d have to change the partial as well (and all places that call it).

    The @user_count and @users_by_parent are quite simple to change. Let¡¯s first change the controller:

1   def tree

2   @title = "Users"

3

4   users = User.order("id DESC").to_a

5

6   user_count = users.length

7   users_by_parent = users.group_by(&:invited_by_user_id)

8   render ¡®users/tree', locals: {user_count: user_count, users_by_parent: users_by_parent}

9   end

    We also change the view, by doing two find/replace operations:

    @user_count -> user_count

    @users_by_parant -> users_by_parent

    This is a relatively safe transformation. If the view and controllers are so simple, you can even change
    them without test coverage.
   Explicitly render views with locals                                                       24

   Benefits

   With more explicitness we gain a clear interface to the view layer. We know what needs to be passed.
   The view is no longer this place which magically accesses some global data, but it behaves more like
   a proper object.

   This technique makes it easier, if one day, this view will be turned into a JavaScript widget (a popular
   trend recently). In that case, we can move the html part to the client-side (handlebars, mustache).
   This widget would make an ajax call to get the same data that we¡¯re now explicitly passing. Note
   that this requires more explicitness with helpers usage. See chapter ¡°Turn helper calls into passing
   locals¡± for more details.

   After this refactoring, we¡¯re now able to safely do the ¡°Extract Service Object with SimpleDelegator¡±
   transformation.

   Another refactoring that is now easier to make is the ¡°Extract Single Action Controller class¡±.

   Warnings

   The locals will not be automatically available in partials, neither inside your action view nor inside
   the layout.

   Make sure that you know all the actions that call this particular view, you¡¯re changing. You need to
   change all the calls.

   Some views (or helpers) are accessing the instance variables in a different way. Here¡¯s a snippet from
   the Redmine project:

1 def error_messages_for(*objects)

2  html = ""

3  objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact

4  errors = objects.map {|o| o.errors.full_messages}.flatten

5  ...

6  end

   As you see, here instance_variable_get is used, which tries to access the @ivar. Those places need to
   be changed before you change ivars into local variables. It¡¯s a common trap to fall into, if you just
   search for ¡®@¡¯ in the file.

   Some developers like to test whether the assigns are set with:

1 assert assigns(:time_entry).errors[:issue_id].present?

   After this refactoring technique, you need to change this test. As long as you have no service object
   yet, it¡¯s better to test controller+view as a black box. This means, turn the test into one that test the
   html output (whether the error is displayed). See chapters ¡°Testing¡± and ¡°Refactor to test controller
   + view as a black box¡± for details, which tests make most sense, depending on the state of your code.
Explicitly render views with locals  25

Resources

http://thepugautomatic.com/2013/05/locals/
http://therealadam.com/2014/02/09/a-tale-of-two-rails-views/
http://www.naildrivin5.com/blog/2014/02/09/a-defense-of-ivars-in-rails-controllers.html
http://www.slideshare.net/elia.schito/rails-oo-views
https://github.com/hudge/proffer - An Action Controller module to hide instance variables from
views by default
Find Rails partials references in vim
It uses grep with pattern, under the hood.

    http://travisjeffery.com/b/2012/02/find-rails-partial-references-in-vim/
   Extract render/redirect methods

   Introduction

   Calls to render and redirect in the controller are usually very verbose. It¡¯s even more visible, when
   you apply the ¡°Explicitly render view with locals¡± refactoring.

   Algorithm

      1. Identify all render and redirect calls in your controllers¡¯ actions.
      2. Extract a private method for each render and redirect call you found with descriptive name

          that shows your intention.
      3. Find and remove any duplicated methods you might created during this refactoring in the

          same controller.

   Example

1  def tree

2  @title = "Users"

3

4  users = User.order("id DESC").to_a

5

6  user_count = users.length

7  users_by_parent = users.group_by(&:invited_by_user_id)

8  render 'tree', locals: {user_count: user_count, users_by_parent: users_by_parent}

9  end

   This can easily be turned into the following:

                                                  26
    Extract render/redirect methods     27

1   def tree

2   @title = "Users"

3   users = User.order("id DESC").to_a

4   render_tree(users)

5   end

6

7 private

8

9 def render_tree(users)

10  render 'tree', locals: {user_count: users.length, users_by_parent: users.group_by(&:invited_by_u\

11 ser_id)}

12  end

    Benefits

    Thanks to this transformation, the action is much more concise and easier to read. We leave the
    details to the method definition. At the level of the action, we don¡¯t need to know it.
    Often, the same render/redirect calls exist in other actions as well. The extracted methods can be
    reused in that case.
    RubyMine has support for finding such places and asking if you want to change them as well - when
    you apply the ¡°Extract method¡± RubyMine refactoring.

    Warnings

    This is a relatively safe refactoring. It relies on a simple ¡°Extract method¡± technique which doesn¡¯t
    have any side effect.
Extract a Single Action Controller
class

Introduction

A typical Rails controller doesn¡¯t follow the Single Responsibility Principle. Each action is usually a
separate responsibility. In the early phases of a Rails app, it may make sense to keep them together,
as they operate on one resource.
The controller actions share dependency to a common set of ¡®state¡¯. This state often includes filters
and helper methods, like params or models loading methods.
At some point, the coupling of multiple actions together, brings more troubles than benefits. It¡¯s hard
to change any code, without the fear of breaking some other parts.
The idea behind this refactoring technique aims at reducing the fear of breaking changes. It¡¯s a safe
technique, which you can follow step-by-step to end with a code that is isolated and easier to change.

Algorithm

   1. A new route declaration above the previous (first wins)
   2. Create an empty controller CreateProductController which inherits from the previous
   3. Copy the action content to the new controller
   4. Remove the action from the previous controller
   5. Copy the filters/methods that are used by the action to the new controller
   6. Make the new controller inherit from the ApplicationController
   7. Change routes to add ¡®except: [:foo_action]¡¯

Example

Let¡¯s take a typical scaffolded controller:

                                                         28
    Extract a Single Action Controller class                                                      29

1 class ProductsController < ApplicationController

2 before_action :set_product, only: [:show, :edit, :update, :destroy]

3

4   def index

5   @products = Product.all

6   end

7

8   def show

9   end

10

11  def new

12  @product = Product.new

13  end

14

15  def edit

16  end

17

18  def create

19  @product = Product.new(product_params)

20

21  respond_to do |format|

22       if @product.save

23       format.html { redirect_to @product, notice: 'Product was successfully created.' }

24       format.json { render :show, status: :created, location: @product }

25       else

26       format.html { render :new }

27       format.json { render json: @product.errors, status: :unprocessable_entity }

28       end

29  end

30  end

31

32  def update

33  respond_to do |format|

34       if @product.update(product_params)

35       format.html { redirect_to @product, notice: 'Product was successfully updated.' }

36       format.json { render :show, status: :ok, location: @product }

37       else

38       format.html { render :edit }

39       format.json { render json: @product.errors, status: :unprocessable_entity }

40       end

41  end

42  end

43

44  def destroy

45  @product.destroy

46  respond_to do |format|

47       format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }

48       format.json { head :no_content }

49  end

50  end

51
    Extract a Single Action Controller class                  30

52  private

53      def set_product

54      @product = Product.find(params[:id])

55      end

56

57      def product_params

58      params.require(:product).permit(:name, :description)

59      end

60 end

    Let¡¯s try to extract the create action into an isolated controller. We need to start with the routes.
    We¡¯re making it look like this:

1 post 'products' => 'create_product#create'
2 resources :products

    It¡¯s worth reminding, that in the routes declaration, the highest route take precedence. That¡¯s why
    we¡¯re putting it above the existing line. Basically we¡¯re overwriting the way we¡¯re dealing with the
    POST request to the /products URL. From the outside point of view, nothing changes. We¡¯re not
    changing the URL structure here. We¡¯re just changing the internal flow.

    The create_product#create is pointing to the create action of the CreateProductController
    controller.

    Let¡¯s create the controller in app/controllers/create_product_controller.rb.

    For now let¡¯s just make it inherit from the previous controller:

1 class CreateProductController < ProductsController
2 end

    You can now run your tests or use the app manually. Nothing has changed, it all still works, thanks
    to inheritance.

    The next step is to copy the create method and paste it to the new controller. At this time, if your
    code doesn¡¯t rely on any meta-programming magic, you don¡¯t need to care about other methods it
    may be using. They will all be available through inheritance.

    However, there¡¯s one thing that will break. Whenever you render views, Rails uses conventions to
    find the view file. By default, it tries to find the directory of the name of the controller. In our case,
    that would be app/views/create_product/new.erb.

    The best solution is to be explicit with the full path to the view - render ¡°products/new¡± instead
    of the render :new. Thanks to this solution you don¡¯t need to move the view files. The views are
    already separated by action.

    We need to change the render calls in all places in the action. Please note, that render calls are
    sometimes implicit. If you don¡¯t call render explicitly or you don¡¯t redirect, Rails will do the render
    call for you.
    Extract a Single Action Controller class                                                    31

    Another change may be required, if your views use partials. The calls to partials also need to be
    using the full path.

    In our case, this:

1 <h1>New product</h1>

2

3 <%= render 'form' %>

4

5 <%= link_to 'Back', products_path %>

    becomes this:

1 <h1>New product</h1>

2

3 <%= render ¡®products/form' %>

4

5 <%= link_to 'Back', products_path %>

    The controller now looks like this:

1 class CreateProductController < ProductsController

2

3   def create

4       @product = Product.new(product_params)

5

6       respond_to do |format|

7        if @product.save

8            format.html { redirect_to @product, notice: 'Product was successfully created.' }

9            format.json { render ¡°products/show¡±, status: :created, location: @product }

10       else

11           format.html { render ¡°products/new¡± }

12           format.json { render json: @product.errors, status: :unprocessable_entity }

13       end

14      end

15  end

16 end

    Run your tests, all should be good.

    You may wonder, how come the call to product_params works, if it¡¯s a private method in the base
    class. The thing is, Ruby¡¯s way of inheritance is slightly unusual. As long, as you¡¯re not prepending
    the call with an explicit receiver, like self.product_params the access to private methods work.
    There¡¯s a good guide here

        http://www.skorks.com/2010/04/ruby-access-control-are-private-and-protected-methods-only-a-guideline/
    Extract a Single Action Controller class                                                          32

    The next step is to remove the previous implementation in the original controller. Simply delete the
    whole create method in the ProductsController.

    The tests are running OK.

    We¡¯d like to get rid of the inheritance. Inheritance is still a way of coupling your code. We wanted
    to escape from there.

    Before we do it, we need to copy all the filters and methods that the create action depends on. In
    our case it¡¯s only product_params.

1 class CreateProductController < ProductsController

2

3   def create

4       @product = Product.new(product_params)

5

6       respond_to do |format|

7        if @product.save

8            format.html { redirect_to @product, notice: 'Product was successfully created.' }

9            format.json { render :show, status: :created, location: @product }

10       else

11           format.html { render :new }

12           format.json { render json: @product.errors, status: :unprocessable_entity }

13       end

14      end

15  end

16

17  private

18

19      def product_params

20       params.require(:product).permit(:name, :description)

21      end

22 end

    If there were any filters, we¡¯d just copy them, together with their method implementation body.
    Now we¡¯re ready to get rid of the inheritance:

1 class CreateProductController < ApplicationController
2 end

    All tests should still run fine.

    The next step is to make it explicit in the routes, that we no longer use the resources-generated
    create call. We don¡¯t really need to do it, but it¡¯s better to be explicit with such things. We¡¯re
    adding the except declaration:
Extract a Single Action Controller class  33

1 post 'products' => 'create_product#create'
2 resources :products, except: [:create]

   There¡¯s now the optional phase of cleaning the code duplications, that appeared when we copied
   the filters and methods.

   It all depends on the context now. In our case, we duplicated the product_params method. This is
   not DRY, is it?

   The duplicated products_params method doesn¡¯t bother me much. It¡¯s not that we change those
   params so often. We usually do that in the early phases of the application. If you¡¯re reading this
   book, you¡¯re probably a bit later in the progress.

   However, sometimes you may want to extract some things to one place and call them directly. We
   could create a class called ProductParams in the app/controllers/products_params.rb file. Then,
   instead of calling product_params, we¡¯d call: ProductParams.new(params).whitelist method. The
   same would apply to any other method, that you prefer not to be duplicated. Just remember, code
   duplication is not always bad in legacy systems. Sometimes it¡¯s a good trade-off - the code is more
   explicit and isolated.

Benefits

The benefits are most clear for projects with really huge controllers. Let¡¯s say your controller is >
1000 LOC. Then extracting the one action that you change most often will result in just 200 LOC to
grasp at one time.

If you copy all the dependent methods, then you can change the structure, as you want. It¡¯s all
isolated now. Changing one action doesn¡¯t bring the risk of breaking other actions.

This technique is a a good step in the direction of extracting a service object. It removes the coupling
to the controller methods, a step that you would need to make, anyway.

Warnings

You may rely on functional tests (the controller tests) in your application. In that case, they will stop
working if you move actions to another controller. The fix requires moving the functional tests (for
this action) to a new file, specific to the new controller. You may consider switching to integration
tests at this moment, not to rely, where things are in the controller layer. There are pros and cons of
both approaches, though.

If your controllers create a deep inheritance tree, you need to adjust this code accordingly. All the
controller ¡°parents¡± may contain the methods that the action uses. Be careful here, as it¡¯s easy to get
lost in such environment.
Extract a Single Action Controller class                                          34

Resources

Explaining focused controllers

    http://www.jonathanleighton.com/articles/2012/explaining-focused-controller/
 Extract routing constraint

  Introduction

   It might happen that over time one controller action that used to be doing one thing turns into
   something bigger, doing two things. It¡¯s usually a gradual process. You start with something simple.
   You add more code for one usecase. And little bit more for another. And what used to be one
   action, doing one thing or at least very similar things, is now responsibile for two rather unrelated
   business requirements. What used to simple, pragmatic and coherent is now unnecessarily coupled
   and cluttered.
   First thing we would like to do with such code is clean it up by splitting the action into two smaller
   ones. All that, while remaining the HTTP API that our frontend or mobile clients might relay on.

  Prerequisites

   Explicitly rendered template

   In first step of this technique we duplicate controller actions and expect them to work identically.
   Because of that, they cannot relay on conventions to render the template. It must be stated explicitely.

1 def show
2 @post = Post.last
3 render :show # <- explicit template to render
4 end

  Algorithm

       1. Go to the controller, duplicate existing action method under different name.
       2. Create a constraint that can recognize which action should be called. Put it in routes.rb
       3. Duplicate the relevant routing rule in routes.rb
       4. Protect first routing rule with the constraint.
       5. Change the second routing rule so it delegates to the new controller action. If necessary, protect

           it with similar constraint.
       6. Remove the irrelevant code from controller actions. Make them do only one thing.
       7. (Optionally) Move the constraint(s) to separate file(s).

                                                             35
   Extract routing constraint                          36

   Example

   The example that we are going to operate on is a webhook implementation for a karma app. User
   can give karma to coworkers when they do something valuable by typing ¡°+1 coworkerName¡± in
   chat. App can also show stats when people type in ¡°+1 !stats¡±. Whenever people write something
   starting with ¡°+1¡± in the chat, there is a http request (webhook) going from chat server to our app.

   The params we receive in our app look like this:

1{

2   team_id:  "T0001",

3   user_id:  "U2147483697",

4 user_name: "rupert",

5   text:     "+1 fidel",

6 trigger_word: "+1",

7}

   And our app can respond with such JSON:

1{         "text": "rupert(406) gave +1 for fidel(7)"

2

3}

   Initial implementation

   We start with one route
    Extract routing constraint                                                             37

1 Rails.application.routes.draw do
2 post "/slack" => "slack#create"
3 end

   and one really long action

1 class SlackController < ApplicationController
2 skip_before_action :verify_authenticity_token

3

4 CannotPlusOneYourself = Class.new(StandardError)
5 MissingRecipient = Class.new(StandardError)

6

7   def create

8   team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

9   team.slack_team_domain = params[:team_domain]

10  team.save!

11

12  sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

13  sender.slack_user_id = params[:user_id]

14  sender.save!

15

16  recipient_name.present? or raise MissingRecipient

17  if recipient_name == "!stats"

18  msg = team.team_members.sort_by{|tm| tm.points }.reverse.map{|tm| "#{tm.slack_user_name}: #{tm\

19 .points}"}.join(", ")

20

21  respond_to do |format|

22       format.json do

23        render json: {text: msg}

24       end

25  end

26  else

27  recipient = team.team_members.find_or_initialize_by(slack_user_name: recipient_name)

28  recipient.save!

29

30  raise CannotPlusOneYourself if sender == recipient

31  recipient.increment!(:points)

32

33  respond_to do |format|

34       format.json do

35        render json: {text: "#{sender.slack_user_name}(#{sender.points}) gave +1 for #{recipient.s\

36 lack_user_name}(#{recipient.points})"}

37       end

38  end

39  end

40 rescue CannotPlusOneYourself

41  respond_to do |format|

42  format.json do

43       render json: {text: "Nope... not gonna happen."}

44  end
    Extract routing constraint                                                                 38

45      end

46 rescue MissingRecipient

47      respond_to do |format|

48       format.json do

49           render json: {text: "?"}

50       end

51      end

52  end

53

54  private

55

56  def recipient_name

57      MessageParser.new(params[:text], params[:trigger_word]).recipient_name

58  end

59 end

    You can see that this action is responsible for multiple things:

         giving +1 to a colleague
         making sure you cheaters cannot give +1 themselves
         handling empty input. +1 without telling the recipient
         handling special command !stats which is about listing current points instead of giving them

          to anyone.

    Let¡¯s now follow our algorithm to refactor this big action.

    Duplicate actions

    I created 2 more copies of #create action and now we have #create, #stats, #empty. 3 identical
    actions.

1 class SlackController < ApplicationController
2 skip_before_action :verify_authenticity_token

3

4 CannotPlusOneYourself = Class.new(StandardError)
5 MissingRecipient = Class.new(StandardError)

6

7   def create

8       team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

9       team.slack_team_domain = params[:team_domain]

10      team.save!

11

12      sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

13      sender.slack_user_id = params[:user_id]

14      sender.save!

15
    Extract routing constraint                                                                 39

16  recipient_name.present? or raise MissingRecipient

17  if recipient_name == "!stats"

18       msg = team.team_members.sort_by{|tm| tm.points }.reverse.map{|tm| "#{tm.slack_user_name}: #{tm\

19 .points}"}.join(", ")

20

21       respond_to do |format|

22       format.json do

23            render json: {text: msg}

24       end

25       end

26  else

27       recipient = team.team_members.find_or_initialize_by(slack_user_name: recipient_name)

28       recipient.save!

29

30       raise CannotPlusOneYourself if sender == recipient

31       recipient.increment!(:points)

32

33       respond_to do |format|

34       format.json do

35            render json: {text: "#{sender.slack_user_name}(#{sender.points}) gave +1 for #{recipient.s\

36 lack_user_name}(#{recipient.points})"}

37       end

38       end

39  end

40 rescue CannotPlusOneYourself

41  respond_to do |format|

42       format.json do

43       render json: {text: "Nope... not gonna happen."}

44       end

45  end

46 rescue MissingRecipient

47  respond_to do |format|

48       format.json do

49       render json: {text: "?"}

50       end

51  end

52  end

53

54  # exactly as #create

55  def stats

56  team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

57  # ...

58

59  if recipient_name == "!stats"

60       # ...

61  else

62       # ...

63  end

64 rescue CannotPlusOneYourself

65  # ...

66 rescue MissingRecipient
    Extract routing constraint                                                  40

67      # ...

68  end

69

70  # exactly as #create

71  def empty

72      team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

73      # ...

74

75      if recipient_name == "!stats"

76       # ...

77      else

78       # ...

79      end

80 rescue CannotPlusOneYourself

81      # ...

82 rescue MissingRecipient

83      # ...

84  end

85

86  private

87

88  def recipient_name

89      MessageParser.new(params[:text], params[:trigger_word]).recipient_name

90  end

91 end

    If there are any filters applying to oryginal action, make sure they are also applied to
    duplicated action. Remember about the prerequisites. Actions need to explicitely render the
    template.

    Preparing constraints

    In config/routes.rb we are now going to create constraints that will be used to recognize what¡¯s
    going on and which action should be actually triggered.
    We want to handle 2 additional actions so we need 2 constraints. Here they are:

1 class StatsConstraint

2 def matches?(request)

3       MessageParser.new(

4        request.request_parameters['text'],

5        request.request_parameters['trigger_word']

6       ).recipient_name == "!stats"

7   end

8 end

9

10 class EmptyConstraint

11 def matches?(request)

12      MessageParser.new(
    Extract routing constraint                                                                             41

13       request.request_parameters['text'],

14       request.request_parameters['trigger_word']

15      ).recipient_name.empty?

16  end

17 end

18

19 Rails.application.routes.draw do

20 post "/slack" => "slack#create"

21 end

    In constraints it is not yet certain which controller action will be executed for incoming request
    (because that will be effect of constraints results). So you don¡¯t have access to params object which
    merges together submited data, with routing data. But you do have access to request_parameters
    and query_parameters as well as number of other request object methods

   Duplicating routing rules

   That¡¯s simple.

1 Rails.application.routes.draw do
2 post "/slack" => "slack#create"
3 post "/slack" => "slack#create"
4 post "/slack" => "slack#create"
5 end

   Remember, this doesn¡¯t mean our controller action will be executed multiple times. Router tries to
   match first rule from top to bottom and executes first rule that matches. It doesn¡¯t look any further.
   So this duplication is harmless.

   Protecting rules with constraints

   Now it is time to apply our constraints to the rules.

1 Rails.application.routes.draw do
2 post "/slack" => "slack#create", constraints: StatsConstraint.new
3 post "/slack" => "slack#create", constraints: EmptyConstraint.new
4 post "/slack" => "slack#create"
5 end

   Even though we have our constraints, they still hit the same action. But this is going to change in
   next step.

    Changing rules mapping to actions

    Again, very small changes to routes.rb.

        http://api.rubyonrails.org/v4.1.7/classes/ActionDispatch/Request.html#method-i-request_parameters
        http://api.rubyonrails.org/v4.1.7/classes/ActionDispatch/Request.html#method-i-query_parameters
       http://api.rubyonrails.org/v4.1.7/classes/ActionDispatch/Request.html
    Extract routing constraint                                                             42

1 Rails.application.routes.draw do
2 post "/slack" => "slack#stats", constraints: StatsConstraint.new
3 post "/slack" => "slack#empty", constraints: EmptyConstraint.new
4 post "/slack" => "slack#create"
5 end

   And we finally start using the actions that we duplicated in first step. As they are implemented the
   same way, they all should be working correctly.

    Removing obsolete, unreachable code from actions

    Now, knowing that when some necessary actions are protected with the constraint on routing level
    you no longer need to check these constraints in actions code. You can now safely strip the actions
    down to their essence.

    This is how the controller looks after that.

1 class SlackController < ApplicationController

2 skip_before_action :verify_authenticity_token

3

4 CannotPlusOneYourself = Class.new(StandardError)
5 MissingRecipient = Class.new(StandardError)

6

7   def create

8   team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

9   team.slack_team_domain = params[:team_domain]

10  team.save!

11

12  sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

13  sender.slack_user_id = params[:user_id]

14  sender.save!

15

16  recipient_name.present? or raise MissingRecipient

17  recipient = team.team_members.find_or_initialize_by(slack_user_name: recipient_name)

18  recipient.save!

19

20  raise CannotPlusOneYourself if sender == recipient

21  recipient.increment!(:points)

22

23  respond_to do |format|

24  format.json do

25       render json: {text: "#{sender.slack_user_name}(#{sender.points}) gave +1 for #{recipient.sla\

26 ck_user_name}(#{recipient.points})"}

27  end

28  end

29 rescue CannotPlusOneYourself

30  respond_to do |format|

31  format.json do

32       render json: {text: "Nope... not gonna happen."}
    Extract routing constraint                                                                 43

33       end

34      end

35  end

36

37  def stats

38      team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

39      team.slack_team_domain = params[:team_domain]

40      team.save!

41

42      sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

43      sender.slack_user_id = params[:user_id]

44      sender.save!

45

46      msg = team.team_members.sort_by{|tm| tm.points }.reverse.map{|tm| "#{tm.slack_user_name}: #{tm.p\

47 oints}"}.join(", ")

48

49      respond_to do |format|

50       format.json do

51           render json: {text: msg}

52       end

53      end

54  end

55

56  def empty

57      team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

58      team.slack_team_domain = params[:team_domain]

59      team.save!

60

61      sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

62      sender.slack_user_id = params[:user_id]

63      sender.save!

64

65      respond_to do |format|

66       format.json do

67           render json: {text: "?"}

68       end

69      end

70  end

71

72  private

73

74  def recipient_name

75      MessageParser.new(params[:text], params[:trigger_word]).recipient_name

76  end

77 end

    We can also see now in more clear picture that #stats and #empty were having some side-effects
    on db which are not really necessary for their proper behavior. We could leave them as they were or
    remove them depending on our business requirements. I decided to remove some of the code even
    further.
    Extract routing constraint                                                             44

1 class SlackController < ApplicationController
2 skip_before_action :verify_authenticity_token

3

4 CannotPlusOneYourself = Class.new(StandardError)
5 MissingRecipient = Class.new(StandardError)

6

7   def create

8   team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

9   team.slack_team_domain = params[:team_domain]

10  team.save!

11

12  sender = team.team_members.find_or_initialize_by(slack_user_name: params[:user_name])

13  sender.slack_user_id = params[:user_id]

14  sender.save!

15

16  recipient_name.present? or raise MissingRecipient

17  recipient = team.team_members.find_or_initialize_by(slack_user_name: recipient_name)

18  recipient.save!

19

20  raise CannotPlusOneYourself if sender == recipient

21  recipient.increment!(:points)

22

23  respond_to do |format|

24       format.json do

25       render json: {text: "#{sender.slack_user_name}(#{sender.points}) gave +1 for #{recipient.sla\

26 ck_user_name}(#{recipient.points})"}

27       end

28  end

29 rescue CannotPlusOneYourself

30  respond_to do |format|

31       format.json do

32       render json: {text: "Nope... not gonna happen."}

33       end

34  end

35  end

36

37  def stats

38  team = Team.find_or_initialize_by(slack_team_id: params[:team_id])

39  team.slack_team_domain = params[:team_domain]

40  team.save!

41

42  msg = team.team_members.sort_by{|tm| tm.points }.reverse.map{|tm| "#{tm.slack_user_name}: #{tm.p\

43 oints}"}.join(", ")

44

45  respond_to do |format|

46       format.json do

47       render json: {text: msg}

48       end

49  end

50  end

51
    Extract routing constraint                                                     45

52  def empty

53      respond_to do |format|

54       format.json do

55           render json: {text: "?"}

56       end

57      end

58  end

59

60  private

61

62  def recipient_name

63      MessageParser.new(params[:text], params[:trigger_word]).recipient_name

64  end

65 end

    You can go further with refactoring this code by extracting a Service Object.

    Benefits

    After applying this recipe, your action is splitted into two actions. It¡¯s more clear what each one is
    responsible for. By isolating the actions you also reduce the risk that ¡°fixing¡± a logic of one action
    can influence the behaviour of the other branch of code.

    There¡¯s always a mental overhead of dealing with a big if-heavy piece of code. Extracting a routing
    constraint is a way of flattening the if-heavy code.

    Your tests should now also be more explicit. It¡¯s easier to test smaller things - you decide which
    action your test focuses on.

    Warnings

    Rendering same views on duplicated actions

    In step 5: Change the routing rule so it delegates to the new controller action you must be careful
    about what is being rendered in new action and existing action. If the old, existing action was relying
    on conventions to render the view, you must now explicitely render the same view in new action.
    Before:
   Extract routing constraint                                                               46

1 def show

2 if params[:asc]

3      @post = Post.first

4  else

5      @post = Post.last

6  end

7 end

   After:

1 def show
2 @post = Post.last
3 end

4

5 def asc
6 @post = Post.first
7 render :show
8 end

   That¡¯s why one of the prerequisites to apply this technique is to have explicitly rendered template.

   Filters

   You need to apply the same filters (if there are any) for duplicated actions there were applied for
   oryginal action.

   Tests

   You may rely on functional tests (the controller tests) in your application. In that case, they will
   stop working for some of the usecases of the old action. The fix requires changing the functional
   tests for some usecases to use one of the newly defined actions. You may also consider switching to
   integration tests at this moment, not to rely, where things are in the controller layer. There are pros
   and cons of both approaches, though.

   Resources

        Inside book - Patterns: Routing constraints
        How to use Rails route constraints
        Using Routing Constraints to Root Your App
        Pretty, short urls for every route in your Rails app

      http://blog.8thlight.com/ben-voss/2013/01/12/how-to-use-rails-route-constraints.html
      http://viget.com/extend/using-routing-constraints-to-root-your-app
      http://blog.arkency.com/2014/01/short-urls-for-every-route-in-your-rails-app/
Extract routing constraint                                             47

 Advanced constraints
 ActionDispatch::Request documentation

http://guides.rubyonrails.org/routing.html#advanced-constraints
http://api.rubyonrails.org/v4.1.7/classes/ActionDispatch/Request.html
   Extract an adapter object

   Introduction

   The adapter pattern is explained in depth in the Adapter pattern chapter

   Algorithm

      1. Extract external library code to private methods of your controller
      2. Parametrize these methods - remove explicit request / params / session statements
      3. Pack return values from external lib calls into simple data structures.
      4. Create an adapter class inside the same file as the controller
      5. Move newly created controller methods to adapter (one by one), replace these method calls

          with calls to adapter object
      6. Pack exceptions raised by an external library to your exceptions
      7. Move your adapter to another file (ex. app/adapters/your_adapter.rb)

   Example

   Let¡¯s start with an action that queries Facebook for the information about friends. It is then wrapped
   with a JSON and returned to the client.

1 class FriendsController < ApplicationController

2  def index

3      friend_facebook_ids = Koala::Facebook::API.new(request.headers['X-Facebook-Token']).get_connecti\

4 ons('me', 'friends').map { |friend| friend['id'] }

5      render json: User.where(facebook_id: friend_facebook_ids)

6 rescue Koala::Facebook::AuthenticationError => exc

7      render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

8  end

9 end

   In this example the koala gem is used.
   First of all, extract the Koala::Facebook::API object creation to a private method:

   https://github.com/arsduo/koala

                                                         48
    Extract an adapter object                                                                  49

1 class FriendsController < ApplicationController

2   def index

3       friend_facebook_ids = facebook_api.get_connections('me', 'friends').map { |friend| friend['id'] }

4       render json: User.where(facebook_id: friend_facebook_ids)

5 rescue Koala::Facebook::AuthenticationError => exc

6       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

7   end

8

9   private

10  def facebook_api

11      Koala::Facebook::API.new(request.headers['X-Facebook-Token'])

12  end

13 end

    There is one more external library method call within this code, let¡¯s extract it too:

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: friend_facebook_ids)

4 rescue Koala::Facebook::AuthenticationError => exc

5       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

6   end

7

8   private

9 def facebook_api

10      Koala::Facebook::API.new(request.headers['X-Facebook-Token'])

11  end

12

13 def friend_facebook_ids

14      facebook_api.get_connections('me', 'friends').map { |friend| friend['id'] }

15  end

16 end

    As you can see, the friend_facebook_ids local variable can be removed in this step too. It is not
    needed anymore.

    Inside the facebook_api method the request object is explicitly referenced. Since an adapter should
    not depend on the controller¡¯s state, this method should be parametrized. The friend_facebook_ids
    is using the facebook_api method, so it should be parametrized too:
    Extract an adapter object                                                                           50

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: friend_facebook_ids(request.headers['X-Facebook-Token']))

4 rescue Koala::Facebook::AuthenticationError => exc

5       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

6   end

7

8   private

9 def facebook_api(token)

10      Koala::Facebook::API.new(token)

11  end

12

13 def friend_facebook_ids(token)

14      facebook_api(token).get_connections('me', 'friends').map { |friend| friend['id'] }

15  end

16 end

    In this example the focus is on getting Facebook IDs only. That means a step with packaging return
    values to data structures is unnecessary. The return value is simple enough (it is not an external
    library entity).

    Now, the FacebookAdapter class should be created:

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: friend_facebook_ids(request.headers['X-Facebook-Token']))

4 rescue Koala::Facebook::AuthenticationError => exc

5       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

6   end

7

8   private

9 def facebook_api(token)

10      Koala::Facebook::API.new(token)

11  end

12

13 def friend_facebook_ids(token)

14      facebook_api(token).get_connections('me', 'friends').map { |friend| friend['id'] }

15  end

16 end

17

18 class FacebookAdapter
19 end

    You can start moving your private methods to a newly created adapter. Let¡¯s start with the
    facebook_api:
    Extract an adapter object                                                                           51

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: friend_facebook_ids(request.headers['X-Facebook-Token']))

4 rescue Koala::Facebook::AuthenticationError => exc

5       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

6   end

7

8   private

9 def facebook_adapter

10      FacebookAdapter.new

11  end

12

13 def friend_facebook_ids(token)

14      facebook_adapter.facebook_api(token).get_connections('me', 'friends').map { |friend| friend['id'\

15 ] }

16  end

17 end

18

19 class FacebookAdapter

20 def facebook_api(token)

21      Koala::Facebook::API.new(token)

22  end

23 end

    For convenience, the facebook_adapter method is created at this point. Note that you need to
    call facebook_api on an adapter now so the friend_facebook_ids method needs to be changed
    temporarily too.

    Next step is to extract friends_facebook_ids too:

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: facebook_adapter.friend_facebook_ids(request.headers['X-Fac\

4 ebook-Token']))

5 rescue Koala::Facebook::AuthenticationError => exc

6       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

7   end

8

9   private

10 def facebook_adapter

11      FacebookAdapter.new

12  end

13 end

14

15 class FacebookAdapter

16 def facebook_api(token)

17      Koala::Facebook::API.new(token)

18  end

19

20 def friend_facebook_ids(token)
    Extract an adapter object                                                                  52

21      facebook_api(token).get_connections('me', 'friends').map { |friend| friend['id'] }

22  end

23 end

    In this point all interactions with an external library is done through an adapter object.

    The problem is that an internal implementation detail (the exception) of FacebookAdapter leaks to
    the controller. To fix it, the Koala::Facebook::AuthenticationError exception must be rescued
    inside FacebookAdapter and a custom exception should be raised:

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: facebook_adapter.friend_facebook_ids(request.headers['X-Fac\

4 ebook-Token']))

5 rescue FacebookAdapter::AuthenticationError => exc

6       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

7   end

8

9   private

10 def facebook_adapter

11      FacebookAdapter.new

12  end

13 end

14

15 class FacebookAdapter
16 AuthenticationError = Class.new(StandardError)

17

18 def facebook_api(token)

19      Koala::Facebook::API.new(token)

20  end

21

22 def friend_facebook_ids(token)

23      facebook_api(token).get_connections('me', 'friends').map { |friend| friend['id'] }

24 rescue Koala::Facebook::AuthenticationError => exc

25      raise AuthenticationError.new(exc.message)

26  end

27 end

    The adapter extraction is done. It can be refactored to have more convenient interface, like making
    Koala::Facebook::API an instance variable initialized in the constructor with a token passed during
    the adapter creation. It looks like this:
    Extract an adapter object                                                                  53

1 class FriendsController < ApplicationController

2   def index

3       render json: User.where(facebook_id: facebook_adapter.friend_facebook_ids)

4 rescue FacebookAdapter::AuthenticationError => exc

5       render json: { error: "Authentication Error: #{exc.message}" }, status: :unauthorized

6   end

7

8   private

9 def facebook_adapter

10      FacebookAdapter.new(request.headers['X-Facebook-Token'])

11  end

12 end

13

14 class FacebookAdapter

15 AuthenticationError = Class.new(StandardError)

16

17 def initialize(token)

18      @api = Koala::Facebook::API.new(token)

19  end

20

21 def friend_facebook_ids(token)

22      @api.get_connections('me', 'friends').map { |friend| friend['id'] }

23 rescue Koala::Facebook::AuthenticationError => exc

24      raise AuthenticationError.new(exc.message)

25  end

26

27  private

28  attr_reader :api

29 end

    You can move your code to another file to take advantage of the Rails autoloader. Your adapter object
    is complete.

    Benefits

    Creating an adapter object allows you to provide a layer of abstraction around your external libraries.
    Since you decide what interface your adapter is going to expose, it¡¯s easy to use another library doing
    the same job. In such case you need to only change adapter¡¯s code.

    If you have code which can¡¯t be changed by you and it has a dependency which you provide, you
    can use an adapter to easily exchange this dependency with something else. This is especially useful
    if you have code which uses some legacy gem and you want to get rid of it, providing a new gem
    with the same functionality (but different API).

    Adapters can be also useful for testing - you can easily exchange a real integration with an external
    service (like Facebook) with an object which returns prepared responses. This is called in-memory
    adapter and it¡¯s a very useful technique to make your tests running faster.
Extract an adapter object  54

Adapters are also good for your application¡¯s architecture - you can find reasoning about code much
simpler if you know that external world interaction is done by adapters.

  Warnings

   Some external libraries can maintain a state between method calls. In such case you should perform
   memoization of your adapter instance within controller:

1 def facebook_adapter
2 @facebook_adapter ||= FacebookAdapter.new(request.headers['X-Facebook-Token'])
3 end

Resources

Hexagonal Architecture
The concept of adapters may be used as a building block for the Ports and Adapters architecture
(previously called the hexagonal architecture)

   http://alistair.cockburn.us/Hexagonal+architecture
Extract a repository object

Introduction

This technique helps to hide the direct ActiveRecord calls with a wrapper object. This wrapper object
is called a repository.

Prerequisites

It makes the recipe much easier, if there¡¯s no loading data in the controller filters. Use the Inline
Controller Filters recipe before going further.

Algorithm

   1. Create a class called ProductsRepository inside the same file as the controller
   2. Find all calls to typical Product.find/all/new/save/create methods in the controller
   3. Create those methods in the repo object
   4. Add a private method, called repo in the controller (possibly in the ApplicationController)

       where you instantiate the repo.
   5. Move the repository class to app/repos/

Example

Let¡¯s start with a typical Product(name, description) scaffold.
The action index is a good start.

                                                         55
    Extract a repository object                                                                 56

1 class ProductsController < ApplicationController

2

3   def index

4       @products = repo.all

5   end

6

7   ...

8

9   private

10

11      def repo

12       @products_repo ||= ProductsRepo.new

13      end

14 end

15

16 class ProductsRepo

17

18  def all

19      Product.all

20  end

21

22 end

    Then, we can apply this patter to all ActiveRecord calls in all actions.

1 class ProductsController < ApplicationController

2

3   def index

4       @products = repo.all

5   end

6

7   def show

8       @product = repo.find(params[:id])

9   end

10

11  def new

12      @product = repo.new

13  end

14

15  def edit

16      @product = repo.find(params[:id])

17  end

18

19  def create

20      @product = Product.new(product_params)

21

22      respond_to do |format|

23       if repo.save(@product)

24           format.html { redirect_to @product, notice: 'Product was successfully created.' }

25           format.json { render :show, status: :created, location: @product }
    Extract a repository object                                                                   57

26       else

27           format.html { render :new }

28           format.json { render json: @product.errors, status: :unprocessable_entity }

29       end

30      end

31  end

32

33  def update

34      @product = repo.find(params[:id])

35      respond_to do |format|

36       if repo.update(@product, product_params)

37           format.html { redirect_to @product, notice: 'Product was successfully updated.' }

38           format.json { render :show, status: :ok, location: @product }

39       else

40           format.html { render :edit }

41           format.json { render json: @product.errors, status: :unprocessable_entity }

42       end

43      end

44  end

45

46  def destroy

47      @product = repo.find(params[:id])

48      repo.destroy(@product)

49      respond_to do |format|

50       format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }

51       format.json { head :no_content }

52      end

53  end

54

55  private

56

57

58  def product_params

59       params.require(:product).permit(:name, :description)

60      end

61

62      def repo

63       @products_repo ||= ProductsRepo.new

64      end

65 end

66

67 class ProductsRepo

68 def find(product_id)

69      Product.find(product_id)

70  end

71

72  def all

73      Product.all

74  end

75

76  def new
    Extract a repository object                                                           58

77      Product.new

78  end

79

80 def update(product, params)

81      product.update(params)

82  end

83

84 def destroy(product)

85      product.destroy

86  end

87

88  def save(product)

89      product.save

90  end

91 end

    You may notice that there¡¯s one call to Product.new left. It wasn¡¯t moved to the repo. This is because,
    we¡¯ll be turning the new/save pair into a single create in the near future. Also, Product.new doesn¡¯t
    really change anything in terms of storage. It¡¯s just creating an object in memory, so persistence-wise
    it¡¯s not interesting.

    Let¡¯s now change the update API so that it takes only simple structures. We need to make some
    changes, because of that. We start with a simple step:

1   def update

2       respond_to do |format|

3        @product = repo.update(params[:id], product_params)

4        if @product.valid?

5            format.html { redirect_to @product, notice: ¡®Updated.¡¯ }

6            format.json { render :show, status: :ok, location: @product }

7        else

8            format.html { render :edit }

9            format.json { render json: @product.errors, status: :unprocessable_entity }

10       end

11      end

12  end

13

14 class ProductsRepository

15

16 def update(product_id, params)

17      find(product_id).tap do |product|

18       product.update(params)

19      end

20  end

21 end

    Previously we checked the result of the .update method via the boolean value. Now, we¡¯re returning
    the product object and we check the result via .valid?. We need to do it, as we need to get the
    product object reference.

    Let¡¯s now convert the create action with the same pattern:
    Extract a repository object                                                                   59

1   def create

2       respond_to do |format|

3        @product = repo.create(product_params)

4        if @product.valid?

5            format.html { redirect_to @product, notice: ¡®Created.¡¯ }

6            format.json { render :show, status: :created, location: @product }

7        else

8            format.html { render :new }

9            format.json { render json: @product.errors, status: :unprocessable_entity }

10       end

11      end

12  end

13

14 class ProductsRepository

15

16 def create(product_params)

17      Product.create(product_params)

18  end

19 end

    As part of this change, we turned the code to use Product.create, instead of the new/save pair. It
    is working the same way.

    Let¡¯s change the destroy action now:

1 def destroy

2       repo.destroy(params[:id])

3       respond_to do |format|

4        format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }

5        format.json { head :no_content }

6       end

7   end

    The full repository implementation now:

1 class ProductsRepo

2 def find(product_id)

3       Product.find(product_id)

4   end

5

6   def all

7       Product.all

8   end

9

10  def new

11      Product.new

12  end

13

14 def update(product_id, params)
    Extract a repository object                                                                60

15      find(product_id).tap do |product|

16       product.update(params)

17      end

18  end

19

20 def destroy(product_id)

21      find(product_id).destroy

22  end

23

24 def create(product_params)

25      Product.create(product_params)

26  end

27 end

    The last step is to move the ProductsRepo class to its own file. I recommend putting it into the
    app/repos/products_repo.rb file.

    We¡¯ve now achieved a small part decoupling between the controller and the repo. When the
    controller calls the repo, it doesn¡¯t know about the ActiveRecord layer at all. This is now isolated.
    The whole communication in this direction happens using the id and the params structure.

    There¡¯s still the fact, that the repo does return ActiveRecord objects. This is in a way a leaky
    abstraction. However, the current state is already an improvement in separating the concerns.

    Benefits

    This recipe results in more discipline in your code. It¡¯s a sign to the developers, that the data storage
    should go through this object. It¡¯s more of a psychological/discipline effect than a technical one.

    In terms of technical gains - this recipe prepares you to have a clear persistence API. It gives you a
    new layer, so code is better organised.

    A repository is a contract to the database.

    Warnings

    No every ActiveRecord model makes a good repository boundary. For example, in the blog platform
    project, a Post can be a good repository, while a CommentsRepo may not be such a good idea.
    However, if there¡¯s many things you can do with a comment (apart from just creating it) - replying
    to it, liking it, starring it, editing it etc. then - yes, this is a sign that a Comment deserves a repo.

    Resources

    Implementing the repository pattern in Ruby
    Adam presents a slightly different approach to repositories with a very good explanation.

       http://hawkins.io/2013/10/implementing_the_repository_pattern/
Extract a service object using the

SimpleDelegator

New projects have a tendency to keep adding things into controllers. There are things which don¡¯t
quite fit any model and developers still haven¡¯t figured out the domain exactly. So these features
land in controllers. In later phases of the project we usually have better insight into the domain.
We would like to restructure domain logic and business objects. But the unclean state of controllers,
burdened with too many responsibilities is stopping us from doing it.

To start working on our models we need to first untangle them from the surrounding mess. This
technique helps you extract objects decoupled from HTTP aspect of your application. Let controllers
handle that part. And let service objects do the rest. This will move us one step closer to better
separation of responsibilities and will make other refactorings easier later.

Prerequisites

Public methods

As of Ruby 2.0, Delegator does not delegate protected methods any more. You might need to
temporarly change access levels of some your controller methods for this technique to work. Once
you finish all steps, you should be able to bring the acess level back to old value. Such change can
be done in two ways.

    by moving the method definition into public scope.
     Change

1  class A

2  def method_is_public

3  end

4

5  protected

6

7  def method_is_protected

8  end

9  end

   into

                            61
Extract a service object using the SimpleDelegator            62

1   class A

2   def method_is_public

3   end

4

5   def method_is_protected

6   end

7

8   protected

9

10  end

     by overwriting method access level after its definition
      Change

1   class A

2   def method_is_public

3   end

4

5   protected

6

7   def method_is_protected

8   end

9   end

    into

1   class A

2   def method_is_public

3   end

4

5   protected

6

7   def method_is_protected

8   end

9

10  public :method_is_protected

11  end

I would recommend using the second way. It is simpler to add and simpler to remove later. The
second way is possible because #public is not a language syntax feature but just a normal method
call executed on current class.

Inlined filters

Although not strictly necessary for this technique to work, it is however recommended to inline
filters. It might be that those filters contain logic that should be actually moved into the service
objects. It will be easier for you to spot it after doing so.

   http://ruby-doc.org/core-2.1.5/Module.html#method-i-public
    Extract a service object using the SimpleDelegator                                 63

    Algorithm

       1. Move the action definition into new class and inherit from SimpleDelegator.
       2. Step by step bring back controller responsibilities into the controller.
       3. Remove inheriting from SimpleDelegator.
       4. (Optional) Use exceptions for control flow in unhappy paths.

    Example

    This example will be a much simplified version of a controller responsible for receiving payment
    gateway callbacks. Such HTTP callback request is received by our app from gateway¡¯s backend and
    its result is presented to the user¡¯s browser. I¡¯ve seen many controllers out there responsible for doing
    something more or less similar. Because it is such an important action (from business point of view)
    it usually quickly starts to accumulate more and more responsibilities.

    Let¡¯s say our customer would like to see even more features added here, but before proceeding we
    decided to refactor first. I can see that Active Record models would deserve some touch here as well,
    let¡¯s only focus on controller right now.

1 class PaymentGatewayController < ApplicationController
2 ALLOWED_IPS = ["127.0.0.1"]
3 before_filter :whitelist_ip

4

5 def callback

6   order = Order.find(params[:order_id])

7   transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, :m\

8 erchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card, \

9 :nature, :require_capture, :amount, :currency))

10  if transaction.successful?

11       order.paid!

12       OrderMailer.order_paid(order.id).deliver

13       redirect_to successful_order_path(order.id)

14  else

15       redirect_to retry_order_path(order.id)

16  end

17 rescue ActiveRecord::RecordNotFound => e

18  redirect_to missing_order_path(params[:order_id])

19  rescue => e

20  Honeybadger.notify(e)

21  AdminOrderMailer.order_problem(order.id).deliver

22  redirect_to failed_order_path(order.id), alert: t("order.problems")

23  end

24

25  private

26

27  def whitelist_ip
    Extract a service object using the SimpleDelegator                             64

28      raise UnauthorizedIpAccess unless ALLOWED_IPS.include?(request.remote_ip)

29  end

30 end

    About filters

    In this example I decided not to move the verification done by the whitlist_ip before filter into the
    service object. This IP address check of issuer¡¯s request actually fits into controller responsibilities
    quite well.

    Move the action definition into new class and inherit from

    SimpleDelegator

    For start you can even keep the class inside the controller.

1 class PaymentGatewayController < ApplicationController
2 # New service inheriting from SimpleDelegator
3 class ServiceObject < SimpleDelegator

4       # copy-pasted method

5       def callback

6        order = Order.find(params[:order_id])

7        transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, \

8 :merchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card\

9 , :nature, :require_capture, :amount, :currency))

10       if transaction.successful?

11           order.paid!

12           OrderMailer.order_paid(order.id).deliver

13           redirect_to successful_order_path(order.id)

14       else

15           redirect_to retry_order_path(order.id)

16       end

17      rescue ActiveRecord::RecordNotFound => e

18       redirect_to missing_order_path(params[:order_id])

19      rescue => e

20       Honeybadger.notify(e)

21       AdminOrderMailer.order_problem(order.id).deliver

22       redirect_to failed_order_path(order.id), alert: t("order.problems")

23      end

24  end

25

26 ALLOWED_IPS = ["127.0.0.1"]
27 before_filter :whitelist_ip

28

29  def callback

30      # Create the instance and call the method

31      ServiceObject.new(self).callback

32  end
    Extract a service object using the SimpleDelegator                             65

33

34  private

35

36  def whitelist_ip

37      raise UnauthorizedIpAccess unless ALLOWED_IPS.include?(request.remote_ip)

38  end

39 end

    We created new class ServiceObject which inherits from SimpleDelegator. That means that
    every method which is not defined will delegate to an object. When creating an instance of
    SimpleDelegator the first argument is the object that methods will be delegated to.

1 def callback
2 ServiceObject.new(self).callback
3 end

    We provide self as this first method argument, which is the controller instance that is currently
    processing the request. That way all the methods which are not defined in ServiceObject class
    such as redirect_to, respond, failed_order_path, params, etc are called on controller instance.
    Which is good because our controller has these methods defined.

   Step by step bring back controller responsibilities into the
   controller

   First, we are going to extract the redirect_to that is part of last rescue clause.

1 rescue => e
2 Honeybadger.notify(e)
3 AdminOrderMailer.order_problem(order.id).deliver
4 redirect_to failed_order_path(order.id), alert: t("order.problems")
5 end

   To do that we could re-raise the exception and catch it in controller. But in our case it is not that easy
   because we need access to order.id to do proper redirect. There are few ways we can workaround
   such obstacle:

         use params[:order_id] instead of order.id in controller (simplest way)
         expose order or order.id from service object to controller
         expose order or order.id in new exception

   Here, we are going to use the first, simplest way. The third way will be shown as well later in this
   chapter.
    Extract a service object using the SimpleDelegator                           66

1 class ServiceObject < SimpleDelegator

2 def callback

3       order = Order.find(params[:order_id])

4       transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, :m\

5 erchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card, \

6 :nature, :require_capture, :amount, :currency))

7       if transaction.successful?

8        order.paid!

9        OrderMailer.order_paid(order.id).deliver

10       redirect_to successful_order_path(order.id)

11      else

12       redirect_to retry_order_path(order.id)

13      end

14 rescue ActiveRecord::RecordNotFound => e

15      redirect_to missing_order_path(params[:order_id])

16  rescue => e

17      Honeybadger.notify(e)

18      AdminOrderMailer.order_problem(order.id).deliver

19      raise # re-raise instead of redirect

20  end

21 end

22

23 def callback
24 ServiceObject.new(self).callback
25 rescue # we added this clause here
26 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")
27 end

    Next, we are going to do very similar thing with the redirect_to from ActiveRecord::RecordNotFound
    exception.

1 class ServiceObject < SimpleDelegator

2 def callback

3       order = Order.find(params[:order_id])

4       transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, :m\

5 erchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card, \

6 :nature, :require_capture, :amount, :currency))

7       if transaction.successful?

8        order.paid!

9        OrderMailer.order_paid(order.id).deliver

10       redirect_to successful_order_path(order.id)

11      else

12       redirect_to retry_order_path(order.id)

13      end

14 rescue ActiveRecord::RecordNotFound => e

15      raise # Simply re-raise

16  rescue => e

17      Honeybadger.notify(e)

18      AdminOrderMailer.order_problem(order.id).deliver

19      raise
    Extract a service object using the SimpleDelegator                           67

20  end

21 end

22

23 def callback

24 ServiceObject.new(self).callback

25 rescue ActiveRecord::RecordNotFound => e # One more rescue clause

26 redirect_to missing_order_path(params[:order_id])

27 rescue

28 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

29 end

    We are left with two redirect_to statements. To eliminte them we need to return the status of the
    operation to the controller. For now, we will just use Boolean for that. We will also need to again
    use params[:order_id] instead of order.id.

1 class ServiceObject < SimpleDelegator

2 def callback

3       order = Order.find(params[:order_id])

4       transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, :m\

5 erchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card, \

6 :nature, :require_capture, :amount, :currency))

7       if transaction.successful?

8        order.paid!

9        OrderMailer.order_paid(order.id).deliver

10       return true # returning status

11      else

12       return false # returning status

13      end

14 rescue ActiveRecord::RecordNotFound => e

15      raise

16  rescue => e

17      Honeybadger.notify(e)

18      AdminOrderMailer.order_problem(order.id).deliver

19      raise

20  end

21 end

22

23 def callback

24 if ServiceObject.new(self).callback

25      # redirect moved here

26      redirect_to successful_order_path(params[:order_id])

27  else

28      # and here

29      redirect_to retry_order_path(params[:order_id])

30  end

31 rescue ActiveRecord::RecordNotFound => e

32 redirect_to missing_order_path(params[:order_id])

33 rescue

34 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

35 end
    Extract a service object using the SimpleDelegator                           68

    Now we need to take care of params method. Starting with params[:order_id]. This change is really
    small.

1 class ServiceObject < SimpleDelegator

2 # We introduce new order_id method argument

3 def callback(order_id)

4       order = Order.find(order_id)

5       transaction = order.order_transactions.create(callback: params.slice(:status, :error_message, :m\

6 erchant_error_message, :shop_orderid, :transaction_id, :type, :payment_status, :masked_credit_card, \

7 :nature, :require_capture, :amount, :currency))

8       if transaction.successful?

9        order.paid!

10       OrderMailer.order_paid(order.id).deliver

11       return true

12      else

13       return false

14      end

15 rescue ActiveRecord::RecordNotFound => e

16      raise

17  rescue => e

18      Honeybadger.notify(e)

19      AdminOrderMailer.order_problem(order.id).deliver

20      raise

21  end

22 end

23

24 def callback

25 # Provide the argument for method call

26 if ServiceObject.new(self).callback(params[:order_id])

27      redirect_to successful_order_path(params[:order_id])

28  else

29      redirect_to retry_order_path(params[:order_id])

30  end

31 rescue ActiveRecord::RecordNotFound => e

32 redirect_to missing_order_path(params[:order_id])

33 rescue

34 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

35 end

    The rest of params is going to be be provided as second method argument.
    Extract a service object using the SimpleDelegator                           69

1 class ServiceObject < SimpleDelegator

2 # One more argument

3 def callback(order_id, gateway_transaction_attributes)

4       order = Order.find(order_id)

5       transaction = order.order_transactions.create(

6          # that we use here

7          callback: gateway_transaction_attributes

8       )

9       if transaction.successful?

10         order.paid!

11         OrderMailer.order_paid(order.id).deliver

12         return true

13      else

14         return false

15      end

16 rescue ActiveRecord::RecordNotFound => e

17      raise

18  rescue => e

19      Honeybadger.notify(e)

20      AdminOrderMailer.order_problem(order.id).deliver

21      raise

22  end

23 end

24

25 def callback

26 # Providing second argument

27 if ServiceObject.new(self).callback(

28         params[:order_id],

29         gateway_transaction_attributes

30      )

31      redirect_to successful_order_path(params[:order_id])

32  else

33      redirect_to retry_order_path(params[:order_id])

34  end

35 rescue ActiveRecord::RecordNotFound => e

36 redirect_to missing_order_path(params[:order_id])

37 rescue

38 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

39 end

40

41 private

42

43 # Extracted to small helper method

44 def gateway_transaction_attributes

45 params.slice(:status, :error_message, :merchant_error_message,

46      :shop_orderid, :transaction_id, :type, :payment_status,

47      :masked_credit_card, :nature, :require_capture, :amount, :currency

48  )

49 end
    Extract a service object using the SimpleDelegator                                           70

    Remove inheriting from SimpleDelegator

    When you no longer use any of the controller methods in the Service you can remove the inheritance
    from SimpleDelegator. You just no longer need it. It is a temporary hack that makes the transition
    to service object easier.

1 # Removed inheritance

2 class ServiceObject

3 def callback(order_id, gateway_transaction_attributes)

4       order = Order.find(order_id)

5       transaction = order.order_transactions.create(callback: gateway_transaction_attributes)

6       if transaction.successful?

7          order.paid!

8          OrderMailer.order_paid(order.id).deliver

9          return true

10      else

11         return false

12      end

13 rescue ActiveRecord::RecordNotFound => e

14      raise

15  rescue => e

16      Honeybadger.notify(e)

17      AdminOrderMailer.order_problem(order.id).deliver

18      raise

19  end

20 end

21

22 def callback

23 # ServiceObject constructor doesn't need

24 # controller instance as argument anymore

25 if ServiceObject.new.callback(

26         params[:order_id],

27         gateway_transaction_attributes

28      )

29      redirect_to successful_order_path(params[:order_id])

30  else

31      redirect_to retry_order_path(params[:order_id])

32  end

33 rescue ActiveRecord::RecordNotFound => e

34 redirect_to missing_order_path(params[:order_id])

35 rescue

36 redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

37 end

    This would be a good time to also give a meaningful name (such as PaymentGatewayCallbackSer-
    vice) to the service object and extract it to a separate file (such as app/services/payment_gate-
    way_callback_service.rb). Remember, you don¡¯t need to add app/services/ to Rails autoloading
    configuration for it to work (explanation).

       http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload/
    Extract a service object using the SimpleDelegator                                                 71

    (Optional) Use exceptions for control flow in unhappy paths

    You can see that code must deal with exceptions in a nice way (as this is critical path in the system).
    But for communicating the state of transaction it is using Boolean values. We can simplify it by
    always using exceptions for any unhappy path.

1 class PaymentGatewayCallbackService
2 # New custom exception

3 TransactionFailed = Class.new(StandardError)

4

5 def callback(order_id, gateway_transaction_attributes)

6       order = Order.find(order_id)

7       transaction = order.order_transactions.create(callback: gateway_transaction_attributes)

8       # raise the exception when things went wrong

9       transaction.successful? or raise TransactionFailed

10      order.paid!

11      OrderMailer.order_paid(order.id).deliver

12 rescue ActiveRecord::RecordNotFound, TransactionFailed => e

13      raise

14  rescue => e

15      Honeybadger.notify(e)

16      AdminOrderMailer.order_problem(order.id).deliver

17      raise

18  end

19 end

20

21 class PaymentGatewayController < ApplicationController
22 ALLOWED_IPS = ["127.0.0.1"]
23 before_filter :whitelist_ip

24

25  def callback

26      PaymentGatewayCallbackService.new.callback(params[:order_id], gateway_transaction_attributes)

27      redirect_to successful_order_path(params[:order_id])

28  # Rescue and redirect

29 rescue PaymentGatewayCallbackService::TransactionFailed => f

30      redirect_to retry_order_path(params[:order_id])

31 rescue ActiveRecord::RecordNotFound => e

32      redirect_to missing_order_path(params[:order_id])

33  rescue

34      redirect_to failed_order_path(params[:order_id]), alert: t("order.problems")

35  end

36

37  # ...

38 end

    ¡°What about performance?¡± you might ask. After all, whenever someone mentions exceptions on the
    Internet, people seem to start raising the performance argument for not using them. Let me answer
    that way:
Extract a service object using the SimpleDelegator                                     72

 Cost of using exceptions is negligable when the exception doesn¡¯t occur.

 When the exception occurs its performance cost is 3-4x times lower compared to one simple
  SQL statement.

Hard data for those statements. Feel free to reproduce on your Ruby implementation and Rails
version.

In other words, exceptions may hurt performance when used inside a ¡°hot loop¡± in your program and
in such case should be avoided. Service Objects usually don¡¯t have such performance implications.
If using exceptions helps you clean the code of services and controller, performance shouldn¡¯t stop
you. There are probably plenty of other opportunities to speed up your app compared to removing
exceptions. So please, let¡¯s not use such argument in situations like that.

Benefits

This is a great way to decouple flow and business logic from HTTP concerns. It makes the code
cleaner and easier to reason about. If you want to keep refactoring the code you can easily focus on
controller-service communication or service-model. You just introduced a nice boundary.
From now on you can also use Service Objects for setting proper state in your tests.

Resources

     In the book - Inline controller filters
     In the book - Service objects as a way of testing Rails apps
     Delegator does not delegate protected methods
     Module#public documentation
     SimpleDelegator documentation
     Don¡¯t forget about eager_load when extending autoload paths
     Cost of using exceptions for control flow compared to one SQL statement. Retweet here

https://gist.github.com/paneq/a643b9a3cc694ba3eb6e
https://bugs.ruby-lang.org/issues/9542
http://ruby-doc.org/core-2.1.5/Module.html#method-i-public
http://www.ruby-doc.org/stdlib-2.1.5/libdoc/delegate/rdoc/SimpleDelegator.html
http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload/
https://gist.github.com/paneq/a643b9a3cc694ba3eb6e
https://twitter.com/pankowecki/status/535818231194615810
Extract conditional validation into
Service Object

Introduction

From time to time it happens that you Active Record model is created or updated in multiple
service objects that serve different usecases. In such case those models might accumulate conditional
validations that are specific to the context of one particular service object usage. It might be
reasonable to move a validation from model to the only service object that cares about it. Leaving
the model cleaner, simpler, and unaware of additional rules that sometimes must be checked.
You can usually recognize such situation when model is using if or unless statement to restrict the
validation. But the condition does not depend directly on the internal state of the model, but rather
indirectly on the external state of the system. Using virtual attr_accessor to enable or disable such
validation is a common indicator.
This can be also aplied for validations governed by on: :create condition which at the end are just
a shortcut for expressing if: :new_record? condition.

Prerequisites

Service object created for the exact context (intent) in which the conditional validation is only used.

Algorithm

   1. Make an object from the validation.
   2. Assign the validation object to constant.
   3. Split save! into validation and saving separately.
   4. Use the validation in service object.

            Call the validation after calling valid?.
            Remove the validation from model.
            Remove the accessor from model.

                                                         73
   Extract conditional validation into Service Object                                      74

   Example

   Our system deals with products which are sometimes imported from external system and we need
   to have external_code of the product in such case. For some reasons imported_from_external is
   not kept as boolean on database and future updates (by user or admin) might be able to remove the
   external_code. It¡¯s just the first import that must have it.

1 class Product < ActiveRecord::Base
2 attr_accessor :imported_from_external

3

4 validates_presence_of :name
5 validates_format_of :internal_code, with: /\A[A-Z]{5,}\z/
6 validates_format_of :external_code, with: /\A[0-9]{7,}\z/, if: :imported_from_external
7 end

   We already have a service object for that usecase:

1 class ImportProductFromExternalSystem

2 def call(product_attributes)

3      product = Product.new(product_attributes)

4      product.imported_from_external = true

5      product.save!

6  end

7 end

   Make an object from the validation

   You can read more about that in the theoretical chapter called Validations: Objectify. But the basic
   idea is to use an instance of rails validator instead of dealing with the DSL.
   In case of #validates_format_of the validator behind is ActiveModel::Validations::FormatValidator.

1 class Product < ActiveRecord::Base

2  attr_accessor :imported_from_external

3

4  validates_presence_of :name

5  validates_format_of :internal_code, with: /\A[A-Z]{5,}\z/

6  validates_format_of :external_code, with: /\A[0-9]{7,}\z/, if: :imported_from_external

7 + validate ActiveModel::Validations::FormatValidator.new(attributes: [:external_code], with: /\A[0-\

8 9]{7,}\z/), if: :imported_from_external

9 end

   We changed validates_format_of into validate. The list of attributes must be explicitely passed
   as attributes setting and it is supposed to be an Array. We can keep the if: :imported_from_-
   external as it was before.
   Extract conditional validation into Service Object                                          75

   Assign the validation object to constant.

   We want to assign the instance of our FormatValidator to a constant so that we can refer it by
   name in our service object later. The name will be ImportedProductExternalCodeFormatValidator.
   Let¡¯s create app/validators/imported_product_external_code_format_validator.rb file (if you
   don¡¯t have the app/validators directory yet, making it to work might require restarting your rails
   app/server/spring server). And put our validation definition there:

1 ImportedProductExternalCodeFormatValidator = ActiveModel::Validations::FormatValidator.new(
2 attributes: [:external_code],
3 with: /\A[0-9]{7,}\z/
4)

   In our product.rb file we use it as an argument to validate.

1 class Product < ActiveRecord::Base

2  attr_accessor :imported_from_external

3

4  validates_presence_of :name

5  validates_format_of :internal_code, with: /\A[A-Z]{5,}\z/

6  validate ActiveModel::Validations::FormatValidator.new(attributes: [:external_code], with: /\A[0-\

7 9]{7,}\z/), if: :imported_from_external

8  validate ImportedProductExternalCodeFormatValidator, if: :imported_from_external

9  end

   Split save! into validation and saving separately

   We need this step as a preparation for the next one.

   If want to be fully compatibile we need to raise ActiveRecord::RecordInvalid when the code was
   previously calling #save!. If you are using #save you can just return false when the record is
   invalid.

1 class ImportProductFromExternalSystem

2 def call(product_attributes)

3      product = Product.new(product_attributes)

4      product.imported_from_external = true

5      product.valid? or raise ActiveRecord::RecordInvalid.new(product)

6      product.save!(validate: false)

7  end

8 end

   Use the validation in service object

   We usually try to split our recipes into atomic steps that keep the code working properly and high-
   level tests passing. That¡¯s why this step that is a bit bigger than usually is still one step. You need to
   execute it fully to have the app still working. But you can think about it as three smaller steps.
   Extract conditional validation into Service Object                                     76

   Call the validation after calling valid?

   Because product.valid? is cleaning errors we need to first call product.valid? and then our own
   validator that might potentially add one more error to the model.
   Also we raise an exception manualy when the errors collection is not empty.

1 class ImportProductFromExternalSystem

2 def call(product_attributes)

3       product = Product.new(product_attributes)

4       product.imported_from_external = true

5       product.valid?

6       ImportedProductExternalCodeFormatValidator.validate(p)

7       product.errors.empty? or raise ActiveRecord::RecordInvalid.new(product)

8       product.save!(validate: false)

9  end

10 end

   Remove the validation from model

   Now that the service object is responsible for using that conditional validation in the place where
   we need to care about it, we are free to remove that validation from the model.

1 class Product < ActiveRecord::Base
2 attr_accessor :imported_from_external

3

4 validates_presence_of :name
5 validates_format_of :internal_code, with: /\A[A-Z]{5,}\z/
6 validates_format_of :external_code, with: /\A[0-9]{7,}\z/, if: :imported_from_external
7 end

   Remove the accessor from model
   And we are free as well to remove the accessor that was used to communicate the conditional fact.

1 class Product < ActiveRecord::Base
2 attr_accessor :imported_from_external

3

4 validates_presence_of :name
5 validates_format_of :internal_code, with: /\A[A-Z]{5,}\z/
6 end

   Benefits

   When your model is used in many different situations it tends to accumulate knowledge about all
   the contexts (service objects) using it. Let the higher level object such as service objects deal with
   nuances of that one particular interaction and its business requirement. Keeping the model clean
   from knowing what¡¯s happening everywhere around it. When conditional validation is used only
   in one place, you can move it that one place and drop the conditional aspect of it.
Extract conditional validation into Service Object  77

Warnings

     Remember that calling #valid? on models clears its errors first so you need to run your
       additional validators after it.

     Make sure your validations are order-indepented. As extracted validation will be called as last
       one.

     Normally validations are performed in one transaction together with save. If your validations
       perform SQL queriers, you might need to manually wrap validation and saving into a
       transaction.

Resources

     In the book: Validations: Contexts
     In the book: Validations: Objectify
Extract a form object

Introduction

It often comes that there¡¯s complicated validation logic in your model just to accept proper
parameters submitted by your application user. This can end up pretty bad with conditional
validations and logic in view. Form objects are great example of how you can verify if submitted
data are relevant for your application. They¡¯re often compared to boarder guards. Data which pass
through this checkpoint are assumed as a correct and not examined again.

Prerequisites

Algorithm

   1. Create new class, e.g. under app/forms directory
   2. Include ActiveModel::Model to have a possibility to use validations and other Rails conven-

       tions related to view rendering and form submission (routing)
   3. Define required attributes on your form object
   4. Copy validations relevant in this particular context from your model
   5. Use the form object in controller and view
   6. Remove validations from your model which are covered by a form object

Example

Initial implementation

Let¡¯s start with signup form done in a typical Rails-way. It collects new user¡¯s name, e-mail and a
password. Separate signup path controller and view has been already created.

                                                         78
    Extract a form object                                                                        79

1 class SignupsController < ApplicationController

2   def new

3       @user = User.new

4   end

5

6   def create

7       @user = User.new(signup_params)

8

9       respond_to do |format|

10       if @user.save

11           format.html { redirect_to @user, notice: 'Signup successfull.' }

12       else

13           format.html { render new_signup_path }

14       end

15      end

16  end

17

18  private

19

20  def signup_params

21      params.require(:user).permit(:name, :email, :password)

22  end

23 end

24

25 class User < ActiveRecord::Base
26 attr_accessor :password

27

28 validates :name, presence: true
29 validates :email, presence: true
30 validates :password, presence: { on: :create}, length: { within: 8..255, allow_blank: true }
31 end

32

33 # app/views/signups/new.html.erb
34 <p id="notice"><%= notice %></p>
35 <h1>Signup</h1>

36

37 <%= form_for(@user, url: signups_path) do |f| %>
38 <% if @user.errors.any? %>

39      <div id="error_explanation">

40       <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

41

42       <ul>

43       <% @user.errors.full_messages.each do |message| %>

44           <li><%= message %></li>

45       <% end %>

46       </ul>

47      </div>

48  <% end %>

49

50  <div class="field">

51      <%= f.label :email %><br>
    Extract a form object                                      80

52      <%= f.text_field :email %>

53  </div>

54  <div class="field">

55      <%= f.label :name %><br>

56      <%= f.text_field :name %>

57  </div>

58  <div class="field">

59      <%= f.label :password %><br>

60      <%= f.password_field :password %>

61  </div>

62 <div class="actions">

63      <%= f.submit "Signup" %>

64  </div>

65 <% end %>

    Create Signup class under app/forms/ directory. It should have ActiveModel::Model included to
    support validations and follow other view-controller flow conventions.

1 class Signup
2 include ActiveModel::Model

3

4 attr_reader :name, :email, :password

5

6 def initialize(params = {})

7       @name  = params[:name]

8       @email = params[:email]

9       @password = params[:password]

10  end

11

12 validates :name, presence: true
13 validates :email, presence: true
14 validates :password, length: { within: 8..255 }

15

16  def persisted?

17      false

18  end

19 end

    As you can see, we used validations same as in User class but without conditionals. Our expectations
    are explicit, so the code is. Let¡¯s use our form object in controller and view. Signup#persisted?
    returning false indicates that our object is not persisted, since we won¡¯t persist form object itself.

    http://api.rubyonrails.org/classes/ActiveModel/Model.html
    Extract a form object                                                      81

1 class SignupsController < ApplicationController

2   def new

3       @user = User.new

4       @signup = Signup.new

5   end

6

7   def create

8       @user = User.new(signup_params)

9       @signup = Signup.new(signup_params)

10

11      respond_to do |format|

12       if @user.save

13           format.html { redirect_to @user, notice: 'Signup successfull.' }

14       if @signup.valid?

15           user = User.new(signup_params).save!(validate: false)

16           format.html { redirect_to user, notice: 'Signup successfull.' }

17       else

18           format.html { render new_signup_path }

19       end

20      end

21  end

22

23  private

24

25  def signup_params

26      params.require(:signup).permit(:name, :email, :password)

27  end

28 end

29

30 # app/views/signups/new.html.erb

31 <p id="notice"><%= notice %></p>

32 <h1>Signup</h1>

33 <%= form_for(@user) do |f| %>

34 <% if @user.errors.any? %>

35 <%= form_for(@signup) do |f| %>

36 <% if @signup.errors.any? %>

37      <div id="error_explanation">

38       <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

39       <h2><%= pluralize(@signup.errors.count, "error") %> prohibited this user from being saved:</h2>

40

41       <ul>

42       <% @signup.errors.full_messages.each do |message| %>

43       <% @user.errors.full_messages.each do |message| %>

44           <li><%= message %></li>

45       <% end %>

46       </ul>

47      </div>

48  <% end %>

49

50  <div class="field">

51      <%= f.label :email %><br>
    Extract a form object                                                                       82

52  <%= f.text_field :email %>

53  </div>

54  <div class="field">

55  <%= f.label :name %><br>

56  <%= f.text_field :name %>

57  </div>

58  <div class="field">

59  <%= f.label :password %><br>

60  <%= f.password_field :password %>

61  </div>

62 <div class="actions">

63  <%= f.submit "Signup" %>

64  </div>

65 <% end %>

    We used Signup class form object in our controller and view. We collect the data after submit and
    create User object if data are complete and return errors in other case.

    Now we can remove some of the validations from our User class:

1 class User < ActiveRecord::Base
2 attr_accessor :password
3 validates :name, presence: true
4 validates :email, presence: true
5 validates :password, presence: { on: :create}, length: { within: 8..255, allow_blank: true }
6 end

    We can make our form object more nifty and define attributes using Virtus gem which gives us
    Attributes on Steroids for Plain Old Ruby Objects.

1 class Signup
2 include ActiveModel::Model

3

4 attr_reader :name, :email, :password

5

6 def initialize(params = {})

7   @name     = params[:name]

8   @email = params[:email]

9   @password = params[:password]

10  end

11 include Virtus.model

12

13 attribute :name, String

14 attribute :email, String
15 attribute :password, String

16

17 validates :name, presence: true

    https://github.com/solnic/virtus
    Extract a form object                                               83

18 validates :email, presence: true

19 validates :password, length: { within: 8..255 }

20

21  def persisted?

22      false

23  end

24 end

    Benefits

    The biggest benefit of using form object is the fact that we can remove conditional validations from
    ActiveRecord::Base models like User in our example. Our codebase became more explicit and
    domain is expressed better. Form object collects the data within given domain context and verifies
    their corretness. We gain certainty that those data are what we expect.

  Warnings

   Older Rails versions

   ActiveModel::Model is available since Rails 4.x.x. In earlier versions you need to explicitly use:

1 class Signup
2 include ActiveModel::Conversion
3 include ActiveModel::Validations
4 extend ActiveModel::Naming
5 end

   Different examples over the Internet

   You can find many examples of form objects on popular blogs, forums, etc. Some of them contain
   #save method. You shouldn¡¯t follow that path. Those examples break one of the most important
   thing in Object Oriented Programming - Single Responsibility Principle. Persistence is a separate
   concern and a different object should take care of it, for example service object.

    Resources

         ActiveModel::Validations documentation
         ActiveModel::Conversion documentation

       http://api.rubyonrails.org/classes/ActiveModel/Model.html
       http://api.rubyonrails.org/classes/ActiveModel/Validations.html
       http://api.rubyonrails.org/classes/ActiveModel/Conversion.html
Extract a form object                                       84

 ActiveModel::Naming documentation
 ActiveModel::Model documentation
 ActiveModel::Errors documentation
 Virtus gem
 Form objects with Virtus

http://api.rubyonrails.org/classes/ActiveModel/Naming.html
http://api.rubyonrails.org/classes/ActiveModel/Model.html
http://api.rubyonrails.org/classes/ActiveModel/Errors.html
https://github.com/solnic/virtus
http://hawkins.io/2014/01/form_objects_with_virtus/
Example:

TripReservationsController#create

                                                         85
    Extract a service object

    We¡¯re going to start with a non-trivial controller action. The main purpose of this action is to let the
    user reserve a trip. The happy path succeeds if the user is allowed to book from this agency, there
    are available tickets and the user pays the price. In all other cases, the user should see an appropriate
    error message.

    It¡¯s also important to log some of the important events into the log file.

1 class TripReservationsController < ApplicationController

2   def create

3       reservation = TripReservation.new(params[:trip_reservation])

4       trip = Trip.find_by_id(reservation.trip_id)

5       agency = trip.agency

6

7       payment_adapter = PaymentAdapter.new(buyer: current_user)

8

9       unless current_user.can_book_from?(agency)

10       redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

11      end

12

13      unless trip.has_free_tickets?

14       redirect_to trip_reservations_page, notice: "No free tickets available"

15      end

16

17      begin

18       receipt = payment_adapter.pay(trip.price)

19       reservation.receipt_id = receipt.uuid

20

21       unless reservation.save

22           logger.info "Failed to save reservation: #{reservation.errors.inspect}"

23           redirect_to trip_reservations_page, notice: "Reservation error."

24       end

25

26       redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

27      rescue PaymentError

28       logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

29       redirect_to trip_reservations_page, notice: "Payment error."

30      end

31  end

32 end

    From my experience of reviewing hundreds of Rails applications, I know this is more or less a typical
    Rails action.

                                                     86
Extract a service object                                                   87

For the sake of simplicity, I didn¡¯t want to display other actions here.
Here is how it looks like with a flow diagram:

This code is not totally bad, I¡¯ve seen worse. However, it deals with so many concerns at the same
    Extract a service object                                           88

    time, that you need to jump in your brain between different layers to understand the whole flow.
    It exposes quite many things, including how the database is organised, how the business logic is
    implemented, how the views are created and where to log information.

    We can¡¯t fix all at once. We need to do small, safe steps.

    What¡¯s the best, first step here?

    Move the whole action code to the service, using

    SimpleDelegator

    We could start by creating a service class and moving the relevant bits from the controller into the
    class. This would be a safe step-by-step way of moving the functionality. However, this approach
    might be a bit slow. The thing is, in most cases we need to move almost all of the code into the
    service. The goal is to leave the Rails controller as thin as possible.

    We¡¯ll go with another approach - move all the code into the service and then just move the controller-
    related parts back into the controller.

    I recommend using SimpleDelegator class which helps us move all the content of the controller
    action into the service. You can think of it as a temporary hack. It¡¯s a good step in-between.

    SimpleDelegator basically delegates everything to the object that¡¯s passed in. In our case, we move
    all the code into the service, but in runtime it¡¯s all delegated back to the controller object.

    Thanks to that, we make a small step forward. This will put us into a position, where we can start
    moving only the appropriate things back to the controller. As we said before, service shouldn¡¯t handle
    any HTTP-related concerns. This all goes back to the controller.

    Here¡¯s how the code looks with SimpleDelegator-based service object:

1 class TripReservationsController < ApplicationController

2   def create

3   TripReservationService.new(self).execute

4   end

5

6 class TripReservationService < SimpleDelegator

7

8   def initialize(controller)

9        super(controller)

10  end

11

12  def execute

13       reservation = TripReservation.new(params[:trip_reservation])

14       trip = Trip.find_by_id(reservation.trip_id)

15       agency = trip.agency

16

17       payment_adapter = PaymentAdapter.new(buyer: current_user)
    Extract a service object                                                                             89

18

19       unless current_user.can_book_from?(agency)

20           redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

21       end

22

23       unless trip.has_free_tickets?

24           redirect_to trip_reservations_page, notice: "No free tickets available"

25       end

26

27       begin

28           receipt = payment_adapter.pay(trip.price)

29           reservation.receipt_id = receipt.uuid

30

31           unless reservation.save

32            logger.info "Failed to save reservation: #{reservation.errors.inspect}"

33            redirect_to trip_reservations_page, notice: "Reservation error."

34           end

35

36           redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

37       rescue PaymentError

38           logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

39           redirect_to trip_reservations_page, notice: "Payment error."

40       end

41      end

42  end

43 end

    It¡¯s worth noting, that we didn¡¯t create a separate file for the service object, yet. It¡¯s declared inside
    the controller. Thanks to that, we don¡¯t need to bother with jumping between files, when we do next
    refactorings. We also don¡¯t need to think just yet, where to put the new file.

    There are many conventions on how to call the service object. The one I suggested here is not perfect
    - TripReservationService. It¡¯s usually not a good idea to use a pattern name as part of a class name.
    For now, let¡¯s stick to that.

    A typical service object consists of the constructor and one public method that triggers the service.
    In our case, I called it #execute. Again, not the best name, but good enough for now.

    We haven¡¯t achieved much, yet. What can we do next?

    Explicit dependencies

    I like to be smell-driven. A code smell is a place in the code which doesn¡¯t look right. In this case,
    I want to get rid of the SimpleDelegator inheritance as quickly as possible. All the non-global
    calls could be made explicit. Let¡¯s make it clear that the service requires trip_reservation_params.
    Making it a parameter to #execute method sounds good.

    Another method that we now access magically is #current_user. Let¡¯s also make it a parameter.
    Extract a service object                                                                             90

1 class TripReservationsController < ApplicationController

2   def create

3       TripReservationService.new(self).execute(current_user, params[:trip_reservation])

4   end

5

6 class TripReservationService < SimpleDelegator

7

8       def initialize(controller)

9        super(controller)

10      end

11

12      def execute(current_user, trip_reservation_params)

13       reservation = TripReservation.new(trip_reservation_params)

14       trip = Trip.find_by_id(reservation.trip_id)

15       agency = trip.agency

16

17       payment_adapter = PaymentAdapter.new(buyer: current_user)

18

19       unless current_user.can_book_from?(agency)

20           redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

21       end

22

23       unless trip.has_free_tickets?

24           redirect_to trip_reservations_page, notice: "No free tickets available"

25       end

26

27       begin

28           receipt = payment_adapter.pay(trip.price)

29           reservation.receipt_id = receipt.uuid

30

31           unless reservation.save

32            logger.info "Failed to save reservation: #{reservation.errors.inspect}"

33            redirect_to trip_reservations_page, notice: "Reservation error."

34           end

35

36           redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

37       rescue PaymentError

38           logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

39           redirect_to trip_reservations_page, notice: "Payment error."

40       end

41      end

42  end

43 end

    Next, let¡¯s make it explicit that our service relies on the logger object.

    Do we make it another parameter to the #execute method? I suggest making it a parameter to the
    constructor. The thing is, logger is more of a static dependency. It doesn¡¯t change for every call to the
    service. The rule of thumb is to list all external dependencies and make them a proper (constructor)
    parameter.
    Extract a service object                                                                   91

    What¡¯s an external dependency?

    External dependency is something that doesn¡¯t belong to our main logic of application. For now,
    let¡¯s assume this are concerns, like external API, talking to the file system, storage etc.

1 class TripReservationsController < ApplicationController

2   def create

3   TripReservationService.new(self, logger).execute(current_user, params[:trip_reservation])

4   end

5

6 class TripReservationService < SimpleDelegator

7

8   attr_reader :logger

9

10  def initialize(controller, logger)

11       super(controller)

12       @logger = logger

13  end

    By creating an attr_reader, we don¡¯t need to change any code that accesses the logger inside the
    service object. There¡¯s a drawback, though. Since now, the logger is a public property of the object.
    We¡¯ll talk more about it, but for now I just want to highlight this problem. Good OOP is about
    sending messages not accessing properties.

    There¡¯s more that we could extract and make an explicit dependency. Let¡¯s stop for now (we can
    always come back) and look at how to communicate about ¡®problems¡¯ in the service object to the
    controller object.

    Resources

         SimpleDelegator documentation

       http://www.ruby-doc.org/stdlib-2.0/libdoc/delegate/rdoc/SimpleDelegator.html
Service - controller communication

The main purpose of a service object is to do a certain thing. Sometimes it¡¯s about registering a user,
sometimes it¡¯s about submitting an order. There¡¯s a clear expectation what service should do. In
most cases, a service object can succeed in one way - being able to do everything that was expected.
In most non-trivial situations, there¡¯s more than one reason to fail, though.

How do we deal with failures?

Look at the original messages, that we use to communicate a failure to a user:

     ¡°You¡¯re not allowed to book from this agency.¡±
     ¡°No free tickets available.¡±
     ¡°Reservation error.¡±
     ¡°Payment error.¡±

If we try to turn them into objects/classes, we¡¯d get:

     NotAllowedToBook
     NoTicketsAvailable
     ReservationError
     PaymentProblem

In fact, there are several ways to map those concepts into a code. For now, we¡¯ll go with one that is
based on exceptions. In another chapter, we¡¯ll see alternatives to them.

Extracting exceptions

Exceptions have a bad fame among programmers. We¡¯ll explain that in a dedicated chapter. For now,
let¡¯s focus on the code.
Let¡¯s start with NotAllowedToBook. Here¡¯s how the relevant code now looks like:

                                                         92
    Service - controller communication                                                               93

1 class TripReservationsController < ApplicationController

2   def create

3   begin

4        TripReservationService.new(self, logger).execute(current_user, params[:trip_reservation])

5   rescue TripReservationService::NotAllowedToBook

6        redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

7   end

8   end

 9

10 class TripReservationService < SimpleDelegator

11

12  class NotAllowedToBook < StandardError; end

13

14  attr_reader :logger

15

16  def initialize(controller, logger)

17       super(controller)

18       @logger = logger

19  end

20

21  def execute(current_user, trip_reservation_params)

22       reservation = TripReservation.new(trip_reservation_params)

23       trip = Trip.find_by_id(reservation.trip_id)

24       agency = trip.agency

25

26       payment_adapter = PaymentAdapter.new(buyer: current_user)

27

28       raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

29

30       unless trip.has_free_tickets?

31       redirect_to trip_reservations_page, notice: "No free tickets available"

32       end

    What are the changes?

     The controller part

    The controller that calls the service needed to be changed and prepared to rescue the exception.
    The exception name is prefixed with TripReservationService:: as it¡¯s part of this namespace. The
    controller needs to handle the exception. The relevant redirect_to part was moved back to the
    controller.

     The exception class

    The service now contains an internal class - the NotAllowedToBook class. It inherits from Standard-
    Error which is the best practice for custom exceptions.

     The service part

    The service object now makes a check before doing any further work.
    Service - controller communication                                                               94

1 raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

    This logic always happens after retrieving all the data required for this service (accessing ActiveRe-
    cord models).

    Let¡¯s now introduce the concept of NoTicketsAvailable:

1 class TripReservationsController < ApplicationController

2   def create

3   begin

4        TripReservationService.new(self, logger).execute(current_user, params[:trip_reservation])

5   rescue TripReservationService::NotAllowedToBook

6        redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

7   rescue TripReservationService::NoTicketsAvailable

8        redirect_to trip_reservations_page, notice: "No free tickets available."

9   end

10  end

11

12 class TripReservationService < SimpleDelegator

13

14  class NotAllowedToBook < StandardError; end

15  class NoTicketsAvailable < StandardError; end

16

17  attr_reader :logger

18

19  def initialize(controller, logger)

20       super(controller)

21       @logger = logger

22  end

23

24  def execute(current_user, trip_reservation_params)

25       reservation = TripReservation.new(trip_reservation_params)

26       trip = Trip.find_by_id(reservation.trip_id)

27       agency = trip.agency

28

29       payment_adapter = PaymentAdapter.new(buyer: current_user)

30

31       raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

32       raise NoTicketsAvailable.new unless trip.has_free_tickets?

    As can you see, it¡¯s the same refactoring algorithm applied:

     Add the exception class
     Raise the exception
     Catch the exception in the controller and move the redirect_to back to the controller

    Note, that we¡¯re left with 3 places, where the service still uses the controller method. It¡¯s time for
    the next exceptions, so that we can get rid of them:
    Service - controller communication                                                               95

     ReservationError
     PaymentProblem

1 class TripReservationsController < ApplicationController

2   def create

3   begin

4        TripReservationService.new(self, logger).execute(current_user, params[:trip_reservation])

5   rescue TripReservationService::NotAllowedToBook

6        redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

7   rescue TripReservationService::NoTicketsAvailable

8        redirect_to trip_reservations_page, notice: "No free tickets available"

9   rescue TripReservationService::PaymentProblem

10       redirect_to trip_reservations_page, notice: "Payment error."

11  rescue TripReservationService::ReservationError

12       redirect_to trip_reservations_page, notice: "Reservation error."

13  end

14  end

15

16 class TripReservationService < SimpleDelegator

17

18  class NotAllowedToBook < StandardError; end

19  class NoTicketsAvailable < StandardError; end

20  class ReservationError < StandardError; end

21  class PaymentProblem       < StandardError; end

22

23  attr_reader :logger

24

25  def initialize(controller, logger)

26       super(controller)

27       @logger = logger

28  end

29

30  def execute(current_user, trip_reservation_params)

31       reservation = TripReservation.new(trip_reservation_params)

32       trip = Trip.find_by_id(reservation.trip_id)

33       agency = trip.agency

34

35       payment_adapter = PaymentAdapter.new(buyer: current_user)

36

37       raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

38       raise NoTicketsAvailable.new unless trip.has_free_tickets?

39

40       begin

41       receipt = payment_adapter.pay(trip.price)

42       reservation.receipt_id = receipt.uuid

43

44       unless reservation.save

45         logger.info "Failed to save reservation: #{reservation.errors.inspect}"

46         raise ReservationError.new

47       end
    Service - controller communication                                                                96

48

49           redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

50       rescue PaymentError

51           logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

52           raise PaymentProblem.new

53       end

54      end

55  end

56 end

    Let¡¯s get rid of the last redirect_to, so that we can get rid of the controller dependency at all!

    What¡¯s the nature of this last redirection? This time it doesn¡¯t happen after a problem, it¡¯s the
    opposite. When all works ok, we redirect the user with a confirmation message. Do we need to
    communicate this to the controller somehow?

    The nice thing about exception-based flow is that you don¡¯t need to do anything, when the operation
    succeeded. Just the mere fact of not raising an exception means a success. We can simply move the
    redirect_to line to the controller:

1 class TripReservationsController < ApplicationController

2   def create

3       begin

4        TripReservationService.new(self, logger).execute(current_user, params[:trip_reservation])

5        redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

6       rescue TripReservationService::NotAllowedToBook

7        redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

8       rescue TripReservationService::NoTicketsAvailable

9        redirect_to trip_reservations_page, notice: "No free tickets available"

10      rescue TripReservationService::PaymentProblem

11       redirect_to trip_reservations_page, notice: "Payment error."

12      rescue TripReservationService::ReservationError

13       redirect_to trip_reservations_page, notice: "Reservation error."

14      end

15  end

16

17 class TripReservationService < SimpleDelegator

18

19      class NotAllowedToBook < StandardError; end

20      class NoTicketsAvailable < StandardError; end

21      class ReservationError < StandardError; end

22      class PaymentProblem  < StandardError; end

23

24      attr_reader :logger

25

26      def initialize(controller, logger)

27       super(controller)

28       @logger = logger

29      end

30
    Service - controller communication                                                                97

31      def execute(current_user, trip_reservation_params)

32       reservation = TripReservation.new(trip_reservation_params)

33       trip = Trip.find_by_id(reservation.trip_id)

34       agency = trip.agency

35

36       payment_adapter = PaymentAdapter.new(buyer: current_user)

37

38       raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

39       raise NoTicketsAvailable.new unless trip.has_free_tickets?

40

41       begin

42           receipt = payment_adapter.pay(trip.price)

43           reservation.receipt_id = receipt.uuid

44

45           unless reservation.save

46             logger.info "Failed to save reservation: #{reservation.errors.inspect}"

47             raise ReservationError.new

48           end

49       rescue PaymentError

50           logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

51           raise PaymentProblem.new

52       end

53      end

54  end

55 end

    No more controller dependency

    Finally, we get the chance to remove the controller dependency, it¡¯s no longer needed. At the same
    time, we¡¯re free to remove the trick with the SimpleDelegator. It served us well, but it¡¯s not a proper
    solution in a long term.

1 class TripReservationsController < ApplicationController

2   def create

3       begin

4        TripReservationService.new(logger).execute(current_user, params[:trip_reservation])

5        redirect_to trip_reservations_page(reservation), notice: "Thank your for your reservation!"

6       rescue TripReservationService::NotAllowedToBook

7        redirect_to trip_reservations_page, notice: "You're not allowed to book from this agency."

8       rescue TripReservationService::NoTicketsAvailable

9        redirect_to trip_reservations_page, notice: "No free tickets available"

10      rescue TripReservationService::PaymentProblem

11       redirect_to trip_reservations_page, notice: "Payment error."

12      rescue TripReservationService::ReservationError

13       redirect_to trip_reservations_page, notice: "Reservation error."

14      end

15  end

16
    Service - controller communication                                                 98

17 class TripReservationService

18

19      class NotAllowedToBook < StandardError; end

20      class NoTicketsAvailable < StandardError; end

21      class ReservationError < StandardError; end

22      class PaymentProblem    < StandardError; end

23

24      attr_reader :logger

25

26      def initialize(logger)

27       @logger = logger

28      end

29

30      def execute(current_user, trip_reservation_params)

31       reservation = TripReservation.new(trip_reservation_params)

32       trip = Trip.find_by_id(reservation.trip_id)

33       agency = trip.agency

34

35       payment_adapter = PaymentAdapter.new(buyer: current_user)

36

37       raise NotAllowedToBook.new unless current_user.can_book_from?(agency)

38       raise NoTicketsAvailable.new unless trip.has_free_tickets?

39

40       begin

41           receipt = payment_adapter.pay(trip.price)

42           reservation.receipt_id = receipt.uuid

43

44           unless reservation.save

45            logger.info "Failed to save reservation: #{reservation.errors.inspect}"

46            raise ReservationError.new

47           end

48       rescue PaymentError

49           logger.info "User #{current_user.name} failed to pay for a trip #{trip.name}: #{$!.message}"

50           raise PaymentProblem.new

51       end

52      end

53  end

54 end

    Move the service to its own file

    It was convenient to keep the service together with the controller in one file, when we did the
    refactorings. Now, we can make a better separation, by moving it to its own file.
    Where to put the services is an often discussed topic. We¡¯ll have a separate chapter for that.
    For now, let¡¯s create a new file in app/models, called trip_reservation_service.rb and move the
    service¡¯s code over there. Thanks to Rails autoloading, we don¡¯t need to do anything else.
    Remember, we can always move it somewhere else, later on.
Service - controller communication  99

Summary

We have isolated the service from the HTTP-related Rails parts (the controller).
The public interface of the service is the #execute method and the exceptions are being thrown.
This is not going to change in the next refactorings (more related to models than controllers) that
we could possibly go with.
Example: logging time

                                                         100
    The starting point

1     def create

2     @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :sp\

3 ent_on => User.current.today)

4     @time_entry.safe_attributes = params[:time_entry]

5

6     call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry \

7 })

8

9     if @time_entry.save

10    respond_to do |format|

11    format.html {

12             flash[:notice] = l(:notice_successful_create)

13             if params[:continue]

14              if params[:project_id]

15                  options = {

16                     :time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activ\

17 ity_id},

18                     :back_url => params[:back_url]

19                  }

20                  if @time_entry.issue

21                     redirect_to new_project_issue_time_entry_path(@time_entry.project, @time_entry.issue\

22 , options)

23                  else

24                     redirect_to new_project_time_entry_path(@time_entry.project, options)

25                  end

26              else

27                  options = {

28                     :time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issu\

29 e_id, :activity_id => @time_entry.activity_id},

30                     :back_url => params[:back_url]

31                  }

32                  redirect_to new_time_entry_path(options)

33              end

34             else

35              redirect_back_or_default project_time_entries_path(@time_entry.project)

36             end

37    }

38    format.api { render :action => 'show', :status => :created, :location => time_entry_url(@ti\

39 me_entry) }

40    end

41    else

42    respond_to do |format|

43    format.html { render :action => 'new' }

44    format.api { render_validation_errors(@time_entry) }

45    end

                                                       101
    The starting point                                                                            102

46  end

47  end

    First I applied some simple transformations to look at the problem from different perspective. After
    each change tests were run.

     Inline controller filters
     Explicitly render views with locals
     Extract Service Object with the help of SimpleDelegator
     Extract the ¡®if¡¯ conditional

    This turned previous code into this:

1   def create

2   CreateTimeEntryService.new(self).call()

3   end

4

5 class CreateTimeEntryService < SimpleDelegator

6   def initialize(parent)

7        super(parent)

8   end

9

10  def call

11       project = nil

12       begin

13       project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])

14       if project_id.present?

15            project = Project.find(project_id)

16       end

17       issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])

18       if issue_id.present?

19            issue = Issue.find(issue_id)

20            project ||= issue.project

21       end

22       rescue ActiveRecord::RecordNotFound

23       render_404 and return

24       end

25       if project.nil?

26       render_404

27       return

28       end

29

30       allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:act\

31 ion]}, project, :global => false)

32       if ! allowed

33       if project.archived?

34            render_403 :message => :notice_not_authorized_archived_project
    The starting point                                                                           103

35             return false

36     else

37             deny_access

38             return false

39     end

40     end

41

42     time_entry ||= TimeEntry.new(:project => project, :issue => issue, :user => User.current, :spe\

43 nt_on => User.current.today)

44     time_entry.safe_attributes = params[:time_entry]

45

46     call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => time_entry\

47 })

48

49     if time_entry.save

50     respond_to do |format|

51             format.html {

52                flash[:notice] = l(:notice_successful_create)

53                if params[:continue]

54                if params[:project_id]

55                      options = {

56                           :time_entry => {:issue_id => time_entry.issue_id, :activity_id => time_entry.act\

57 ivity_id},

58                           :back_url => params[:back_url]

59                      }

60                      if time_entry.issue

61                         redirect_to new_project_issue_time_entry_path(time_entry.project, time_entry.issue\

62 , options)

63                      else

64                         redirect_to new_project_time_entry_path(time_entry.project, options)

65                      end

66                else

67                      options = {

68                           :time_entry => {:project_id => time_entry.project_id, :issue_id => time_entry.is\

69 sue_id, :activity_id => time_entry.activity_id},

70                           :back_url => params[:back_url]

71                      }

72                      redirect_to new_time_entry_path(options)

73                end

74                else

75                redirect_back_or_default project_time_entries_path(time_entry.project)

76                end

77             }

78             format.api { render 'show', :status => :created, :location => time_entry_url(time_entry),\

79 :locals => {:time_entry => time_entry} }

80     end

81     else

82     respond_to do |format|

83             format.html { render :new, :locals => {:time_entry => time_entry, :project => project} }

84             format.api { render_validation_errors(time_entry) }

85     end
    The starting point  104

86       end

87  end

88  end

    As you can see the previous 40-lines block turned into 90 lines, temporarily.

    It¡¯s uglier.

    I basically made all dependencies inline so the code is more explicit now. Now I can look at it from
    the different perspective - without separation of concerns.

    What was previously hidden in different places is now in front of me in one place. The ¡®aha¡¯ moment
    is coming.

    The ¡®aha¡¯ moment

    Thanks to explicitness of code above I realised that the controller action is in fact responsible for
    two different user actions:

         CreateProjectTimeEntry
         CreateIssueTimeEntry

    The difference may not be huge but this explains the number of conditionals in this code. What may
    seem to be a clever code reuse (¡°I¡¯ll just add this if here and there and we can now create time entries
    for a project as well¡±), can be a problem for people to understand it in the future.
    Where do I go with this lesson now?
    I¡¯ve used these transformations to finish with the code below:

         Extract render/redirect methods
         Extract exception objects from a service object
         Change CRUD name to the domain one (CreateTimeEntry -> LogTime)
         Return entity from a service object
    The starting point                                                        105

1   def create

2   if issue_id.present?

3        log_time_on_issue

4   else

5        log_time_on_project

6   end

7   end

8

9 def log_time_on_project

10  log_time(nil, project_id) { do_log_time_on_project }

11  end

12

13 def log_time_on_issue

14  log_time(issue_id, project_id) { do_log_time_on_issue }

15  end

16

17 def do_log_time_on_project

18  time_entry = LogTime.new(self).on_project(project_id)

19  respond_to do |format|

20       format.html { redirect_success_for_project_time_entry(time_entry) }

21       format.api { render_show_status_created }

22  end

23  end

24

25 def do_log_time_on_issue

26  time_entry = LogTime.new(self).on_issue(project_id, issue_id)

27  respond_to do |format|

28       format.html { redirect_success_for_issue_time_entry(time_entry) }

29       format.api { render_show_status_created(time_entry) }

30  end

31  end

32

33 def log_time(issue_id, project_id)

34  begin

35       yield

36  rescue LogTime::DataNotFound

37       render_404

38  rescue LogTime::NotAuthorizedArchivedProject

39       render_403 :message => :notice_not_authorized_archived_project

40  rescue LogTime::AuthorizationError

41       deny_access

42  rescue LogTime::ValidationError => e

43       respond_to do |format|

44       format.html { render_new(e.time_entry, e.project) }

45       format.api { render_validation_errors(e.time_entry) }

46       end

47  end

48  end

49

50 class LogTime < SimpleDelegator

51  class AuthorizationError           < StandardError; end
    The starting point                                                  106

52  class NotAuthorizedArchivedProject < StandardError; end

53  class DataNotFound              < StandardError; end

54  class ValidationError                  < StandardError

55       attr_accessor :time_entry, :project

56       def initialize(time_entry, project)

57       @time_entry = time_entry

58       @project = project

59       end

60  end

61

62  def initialize(parent)

63       super(parent)

64  end

65

66  def on_issue(project_id, issue_id)

67       project, issue = find_project_and_issue(project_id, issue_id)

68       authorize(User.current, project)

69       time_entry = new_time_entry_for_issue(issue, project)

70       notify_hook(time_entry)

71       save(time_entry, project)

72       return time_entry

73  end

74

75  def on_project(project_id)

76       project = find_project(project_id)

77       authorize(User.current, project)

78       time_entry = new_time_entry_for_project(project)

79       notify_hook(time_entry)

80       save(time_entry, project)

81       return time_entry

82  end

83  end
Patterns

                                                         107
    Instantiating service objects

    Boring style

1 #!ruby

2 class ProductsController

3   def create

4       metrics = MetricsAdapter.new(METRICS_CONFIG.fetch(Rails.env))

5       service = CreateProductService.new(metrics)

6       product = service.call(params[:product])

7       redirect_to product_path(product), notice: "Product created"

8 rescue CreateProductService::Failed => failure

9       # ... probably render ...

10  end

11 end

    This is the simplest way, nothing new under the sun. When your needs are small, dependencies
    simple or non-existing (or created inside service, or you use globals, in other words: not passed
    explicitely) you might not need anything more.

    Testing

    Ideally we want to test our controllers in simplest possible way. In Rails codebase, unlike in desktop
    application, every controller action is an entry point into the system. Its our main() method. So we
    want our controllers to be very thin, instantiating the right kind of objects, giving them access to
    the input, and putting the whole world in motion. The simplest, the better, because controllers are
    the hardest beasts when it come to testing.

    Controller

                                                     108
    Instantiating service objects                                                  109

1 #!ruby

2

3 describe ProductsController do

4 specify "#create" do

5       product_attributes = {

6          "name" =>"Product Name",

7          "price"=>"123.45",

8       }

9

10      expect(MetricsAdapter).to receive(:new).with("testApiKey").and_return(

11         metrics = double(:metrics)

12      )

13      expect(CreateProductService).to receive(:new).with(metrics).and_return(

14         create_product_service = double(:register_user_service,

15            call: Product.new.tap{|p| p.id = 10 },

16         )

17      )

18

19      expect(create_product_service).to receive(:call).with(product_attributes)

20

21      post :create, {"product"=> product_attributes}

22

23      expect(flash[:notice]).to be_present

24      expect(subject).to redirect_to("/products/10")

25  end

26 end

    It¡¯s up to you whether you want to mock the service or not. Remember that the purpose of this test
    is not to determine whether the service is doing its job, but whether controller is. And the controller
    concers are

         passing params, request and session (subsets of) data for the services when they need it
         controlling the flow of the interaction by using redirect_to or render. In case of happy path

          as well as when something goes wrong.
         Updating the long-living parts of user interaction with our system such as session and

          cookies

         Optionally, notifying user about the achieved result of the actions. Often with the use of flash
          or flash.now. I wrote optionally because I think in many cases the communication of action
          status should actually be a responsibility of the view layer, not a controller one

    These are the things you should be testing, nothing less, nothing more.

    However mocking adapters might be necessary because we don¡¯t want to be sending or collecting
    our data from test environment.

       http://blog.robert.pankowecki.pl/2011/12/communication-between-controllers-and.html
    Instantiating service objects                                                               110

    Service
    When testing the service you need to instantiate it and its dependencies manually as well.

1 #!ruby

2

3 describe CreateProductService do

4 let(:metrics_adapter) do

5       FakeMetricsAdapter.new

6   end

7

8 subject(:create_product_service) do

9       described_class.new(metrics_adapter)

10  end

11

12 specify "something something" do

13      create_product_service.call(..)

14      expect(..)

15  end

16 end

    Modules

    When instantiating becomes more complicated I extract the process of creating the full object into
    an injector. The purpose is to make it easy to create new instance everywhere and to make it trivial
    for people to overwrite the dependencies by overwriting methods.

1 #!ruby

2 module CreateProductServiceInjector

3 def metrics_adapter

4       @metrics_adapter ||= MetricsAdapter.new( METRICS_CONFIG.fetch(Rails.env) )

5   end

6

7 def create_product_service

8       @create_product_service ||= CreateProductService.new(metrics_adapter)

9   end

10 end
    Instantiating service objects                                                  111

1 #!ruby
2 class ProductsController
3 include CreateProductServiceInjector

4

5   def create

6       product = create_product_service.call(params[:product])

7       redirect_to product_path(product), notice: "Product created"

8 rescue CreateProductService::Failed => failure

9       # ... probably render ...

10  end

11 end

    Testing

    The nice thing is you can test the instantiating process itself easily with injector (or skip it completely
    if you consider it to be typo-testing that provides very little value) and don¡¯t bother much with it
    anymore.

    Injector
    Here we only test that we can inject the objects and change the dependencies.

1 #!ruby
2 describe CreateProductServiceInjector do
3 subject(:injected) do

4       Object.new.extend(described_class)

5   end

6

7 specify "#metrics_adapter" do

8       expect(MetricsAdapter).to receive(:new).with("testApiKey").and_return(

9          metrics = double(:metrics)

10      )

11      expect(injected.metrics_adapter).to eq(metrics)

12  end

13

14 specify "#create_product_service" do

15      expect(injected).to receive(:metrics_adapter).and_return(

16         metrics = double(:metrics)

17      )

18      expect(CreateProductService).to receive(:new).with(metrics).and_return(

19         service = double(:register_user_service)

20      )

21

22      expect(injected.create_product_service).to eq(service)

23  end

24 end

    Is it worth it? Well, it depends how complicated setting your object is. Some of my colleagues just
    test that the object can be constructed (hopefully this has no side effects in your codebase):
    Instantiating service objects                                     112

1 #!ruby

2 describe CreateProductServiceInjector do

3 subject(:injected) do

4       Object.new.extend(described_class)

5   end

6

7 specify "can instantiate service" do

8       expect{ injected.create_product_service }.not_to raise_error

9   end

10 end

    Controller

    Our controller is only interested in cooperating with create_product_service. It doesn¡¯t care what
    needs to be done to fully set it up. It¡¯s the job of Injector. We can throw away the code for creating
    the service.

1 #!ruby

2

3 describe ProductsController do

4 specify "#create" do

5       product_attributes = {

6          "name" =>"Product Name",

7          "price"=>"123.45",

8       }

9

10      expect(controller.create_product_service).to receive(:call).

11         with(product_attributes).

12         and_return( Product.new.tap{|p| p.id = 10 } )

13

14      post :create, {"product"=> product_attributes}

15

16      expect(flash[:notice]).to be_present

17      expect(subject).to redirect_to("/products/10")

18  end

19 end

    Service Object

    You can use the injector in your tests as well. Just include it. Rspec is a DSL that is just creating
    classes and method for you. You can overwrite the metrics_adapter dependency using Rspec DSL
    with let or just by defining metrics_adapter method yourself.

    Just remember that let is adding memoization for you automatically. If you use your own method
    definition make sure to memoize as well (in some cases it is not necessary, but when you start
    stubbing/mocking it is).
    Instantiating service objects                                                113

1 #!ruby
2 describe CreateProductService do
3 include CreateProductServiceInjector

4

5 specify "something something" do

6       create_product_service.call(..)

7       expect(..)

8   end

9

10 let(:metrics_adapter) do

11      FakeMetricsAdapter.new

12  end

13

14  #or

15

16  def metrics_adapter

17      @adapter ||= FakeMetricsAdapter.new

18  end

19 end

    There is nothing preventing you from mixing classic ruby OOP with Rspec DSL. You can use it to
    your advantage.

    The downside that I see is that you can¡¯t easily say from reading the code that metrics_adapter is a
    dependency of our class under test (CreateProductService). As I said in simplest case it might not
    be worthy, in more complicated ones it might be however.

    Example

    Here is a more complicated example from one of our project.

1 #!ruby
2 require 'notifications_center/db/active_record_sagas_db'
3 require "notifications_center/schedulers/resque_scheduler"
4 require "notifications_center/clocks/real"

5

6 module NotificationsCenterInjector

7 def notifications_center

8       @notifications_center ||= begin

9        apns_adapter    = Rails.configuration.apns_adapter

10       policy          = Rails.configuration.apns_push_notifications_policy

11       mixpanel_adapter = Rails.configuration.mixpanel_adapter

12       url_helpers     = Rails.application.routes_url_helpers

13

14       db              = NotificationsCenter::DB::ActiveRecordSagasDb.new

15       scheduler       = NotificationsCenter::Schedulers::ResqueScheduler.new

16       clock           = NotificationsCenter::Clocks::Real.new

17

18       push = PushNotificationService.new(
    Instantiating service objects                                     114

19           url_helpers,

20           apns_adapter,

21           policy,

22           mixpanel_adapter

23       )

24       NotificationsCenter.new(db, push, scheduler, clock)

25      end

26  end

27 end

    Dependor

    You might also consider using dependor gem for this.

1 #!ruby
2 class Injector
3 extend Dependor::Let

4

5 let(:metrics_adapter) do

6       MetricsAdapter.new( METRICS_CONFIG.fetch(Rails.env) )

7   end

8

9 let(:create_product_service)

10      CreateProductService.new(metrics_adapter)

11  end

12 end

1 #!ruby

2 class ProductsController

3 extend Dependor::Injectable

4 inject_from Injector

5

6 inject :create_product_service

7   def create

8       product = create_product_service.call(params[:product])

9       redirect_to product_path(product), notice: "Product created"

10 rescue CreateProductService::Failed => failure

11      # ... probably render ...

12  end

13 end

    The nice thing about dependor is that it provides a lot of small APIs and doesn¡¯t force you to
    use any of them. Some of them do more magic (I am looking at you Dependor::AutoInject)
    and some of medium level (Dependor::Injectable) and some almost none magic whatso-
    ever(Dependor::Shorty). You can use only the parts that you like and are comfortable with.

       https://github.com/psyho/dependor
       https://github.com/psyho/dependor#dependorautoinject
       https://github.com/psyho/dependor#dependorinjectable
       https://github.com/psyho/dependor#dependorshorty
    Instantiating service objects                                             115

    Testing

    Injector
    The simple way that just checks if things don¡¯t crash and nothing more.

1 #!ruby

2 require 'dependor/rspec'

3

4 describe Injector do

5 let(:injector) { described_class.new }

6

7   specify do

8       expect{injector.create_product_service}.to_not raise_error

9   end

10 end

    Service

    For testing the service you go whatever way you want. Create new instance manually or use
    Dependor::Isolate.

1 #!ruby

2 require 'dependor/rspec'

3

4 describe CreateProductService do

5 let(:metrics_adapter) do

6       FakeMetricsAdapter.new

7   end

8 subject(:create_product_service) { isolate(CreateProductService) }

9

10 specify "something something" do

11      create_product_service.call(..)

12      expect(..)

13  end

14 end

    https://github.com/psyho/dependor#dependorisolate
    The repository pattern

    In typical Rails apps, all persistence is handled via the ActiveRecord pattern and library.

    ActiveRecord is a really good library for simple database access. Once your application grows, the
    ActiveRecord classes (models) start go get some logic. That¡¯s the clue of the Active Record pattern.

    This book shows how to deal with such situations and how to move some of that logic into other
    places, like service objects or domain objects.

    Even if you move all the logic out, there¡¯s still a pattern that can help you. It¡¯s called the Repository
    object pattern.

    ActiveRecord class as a repository

    In a way, ActiveRecord class (not an object), is already a repository. Thanks to classes being objects
    in Ruby, you can call methods on it.

    Treating AR classes as repositories has some limit, though. ActiveRecord makes it look, as if all tables
    are equally important. The typical example is Post and Comment. In most cases, it may make sense
    to treat the Post as a root object (thus it deserves its own repo), while the Comment class is almost
    always just a subtree of the Post objects.

    In practice, if you want to go this route, it¡¯s important to note, what is the scope of the repository.

    This is an example code of treating the Post class as a repository.

1 class Post < ActiveRecord::Base

2 end

3

4 class PostsController < ApplicationController

5   def create

6       CreatePostService.new(Post).call(title, content)

7       redirect_to :index

8   end

9 end

10

11 class CreatePostService

12 def initialize(posts_repo)

13      @posts_repo = posts_repo

14  end

15

16 def call(title, content)

17      @posts_repo.create(title: title, content: content)

18  end

19 end

                                                 116
    The repository pattern                           117

    Explicit repository object

1 class PostsRepository

2   def index

3       Post.all

4   end

5

6 def show(id)

7       Post.find(id)

8   end

9

10 def create(title, content)

11      Post.create(title: title, content: content)

12  end

13 end

    An explicit repository object usually wraps a certain subset of ActiveRecord objects. Once you start
    using repositories, the rule is to never talk to ActiveRecord outside of the repository object.

    In the ideal case, you want to have the ActiveRecord being hidden as a repository implementation
    detail.

    No logic in repos

    Repository objects should have no logic at all. Ideally, they don¡¯t deal with any typical validations.

    In practice, obviously you will need to deal with a temporary situation, where your AR classes still
    have validations, while being hidden behind the repository object.

    Repositories let you manipulate and retrieve the data. It¡¯s not a place for any authentication or
    authorisation. This needs to be handled higher.

    The repository object can call the ActiveRecord models, assuming they keep no logic on their own.
    It¡¯s obviously ¡°ok¡± to have some logic, as a step in-between. The goal is to make the whole data
    layer, logic-less. This also includes moving all callbacks out from the models.

    If your code relies on state-machine, you may have a hard time extracting things into a repository
    object.

    Ideally, a repo just has some CRUD operations.

    Transactions

    It¡¯s not the job of the repository object to wrap multiple operations into transaction. The repo
    object can expose a method, called in_transaction which takes a block. This way, the code above
    (usually the service object) can be explicit about the transaction boundary. It¡¯s the service object
    responsibility to draw the transaction lines.
    The repository pattern                               118

    The danger of too small repositories

    I¡¯ve seen one typical trap, that you can fall into. It¡¯s often very tempting to make a repo around
    every resource in your system. The repo classes seem to be nicely small in that case. This results in
    service objects, taking multiple repositories - a common code smell.

    In-memory repository

    At some point, all of your service objects operate on the data via logic-less repo objects. This is when
    you get your return on investment. You can easily replace the repository (the real one, talking to the
    real database), with one that operates in-memory. This is usually a huge performance win.

1 class InMemoryPostsRepo

2 def initialize

3       @posts = []

4   end

5

6 def create(title, content)

7       @posts << Post.new(generate_id, title, content)

8   end

9

10  def find(id)

11      @posts.detect{|post| post.id == id}

12  end

13 end
    Wrap external API with an adapter

    Introduction

    Most likely, your app contacts some kind of API. It may be Twitter, Facebook, some of your internal
    company API. In a way, contacting your DB is also like calling an API. Sending emails usually
    involves some other library and connecting to another port.

    There were many discussions in the Rails community, how to treat API, where to put the calls. The
    common consensus seems to be, keeping it in the models - often combined with model callbacks.
    It¡¯s also still popular to keep the API calls in the controller.

    This technique here assumes that the API call is in the controller or in the service object (if you used
    the ¡°Extract Service Object¡± technique before). If your API calls are still in the model, then it may
    worth considering applying the ¡°Move model callback to the controllers¡± technique.

    Example

    We¡¯ll use the Redmine admin part, in which the admin can send a test email to herself. In this
    example, we¡¯ll treat sending emails as an example API call.

1 def test_email

2   raise_delivery_errors = ActionMailer::Base.raise_delivery_errors

3   # Force ActionMailer to raise delivery errors so we can catch it

4   ActionMailer::Base.raise_delivery_errors = true

5   begin

6        @test = Mailer.test_email(User.current).deliver

7        flash[:notice] = l(:notice_email_sent, User.current.mail)

8   rescue Exception => e

9        flash[:error] = l(:notice_email_error, Redmine::CodesetUtil.replace_invalid_utf8(e.message))

10  end

11  ActionMailer::Base.raise_delivery_errors = raise_delivery_errors

12  redirect_to settings_path(:tab => 'notifications')

13  end

    This can be turned into:

                              119
    Wrap external API with an adapter                                 120

1 def test_email

2   begin

3        @test = EmailAdapter.new.send_email(User.current)

4        flash[:notice] = l(:notice_email_sent, User.current.mail)

5   rescue EmailAdapter::EmailNotSent => e

6        flash[:error] = l(:notice_email_error, Redmine::CodesetUtil.replace_invalid_utf8(e.message))

7   end

8   redirect_to settings_path(:tab => 'notifications')

9   end

    This is how the adapter looks like:

1 class EmailAdapter

2 EmailNotSent = Class.new(StandardError)

3

4 def send_email(user)

5   raise_delivery_errors = ActionMailer::Base.raise_delivery_errors

6   # Force ActionMailer to raise delivery errors so we can catch it

7   ActionMailer::Base.raise_delivery_errors = true

8   begin

9        @test = Mailer.test_email(user).deliver

10  rescue Exception => e

11       raise EmailNotSent.new(e.message)

12  end

13  ActionMailer::Base.raise_delivery_errors = raise_delivery_errors

14  return @test

15  end

    It¡¯s worth noting, that we don¡¯t let the internal API exceptions leak to the controller, we wrap them
    with our own protocol layer (EmailNotSent). This leads to more code, but it¡¯s a good isolation.

  Another long example

   Our second example will be about sending apple push notifications (APNS). Let¡¯s say in our system
   we are sending push notifications with text (alert) only (no sound, no badge, etc). Very simple and
   basic usecase. One more thing that we obviously need as well is device token. Let¡¯s have a simple
   interface for sending push notifications.

1 #!ruby
2 def notify(device_token, text)
3 end

   That¡¯s the interface that every one of our adapters will have to follow. So let¡¯s write our first
   implementation using the apns gem.
   Wrap external API with an adapter                            121

1 #!ruby

2 module ApnsAdapters

3  class Sync

4      def notify(device_token, text)

5       APNS.send_notification(device_token, 'Hello iPhone!' )

6      end

7  end

8 end

   Wow, that was simple, wasn¡¯t it? Ok, what did we achieve?

        We¡¯ve protected ourselves from the dependency on apns gem. We are still using it but no part
         of our code is calling it directly. We are free to change it later (which we will do)

        We¡¯ve isolated our interface from the implementation as Clean Code architecture teaches
         us. Of course in Ruby we don¡¯t have interfaces so it is kind-of virtual but we can make it a bit
         more explicit, which I will show you how, later.

        We designed API that we like and which is suitable for our app. Gems and 3rd party services
         often offer your a lot of features which you might not be even using. So here we explicitly
         state that we only use device_token and text. If it ever comes to dropping the old library
         or migrating to new solution, you are coverd. It¡¯s simpler process when the cooperation can
         be easily seen in one place (adapter). Evaluating and estimating such task is faster when you
         know exactly what features you are using and what not.

   Adapters and architecture

   Part of your app (probably a service) that we call client is relaying on some kind of interface
   for its proper behavior. Of course ruby does not have explicit interfaces so what I mean is a
   compatibility in a duck-typing way. Implicit interface defined by how we call our methods (what
   parameters they take and what they return). There is a component, an already existing one (adaptee)
   that can do the job our client wants but does not expose the interface that we would like to use.
   The mediator between these two is our adapter.
   The interface can be fulfilled by possibily many adapters. They might be wrapping another API or
   gem which we don¡¯t want our app to interact directly with.

   Multiple Adapters

   Let¡¯s move further with our task.
    Wrap external API with an adapter                                  122

    We don¡¯t wanna be sending any push notifications from our development environment
    and from our test environment. What are our options? I don¡¯t like putting code such as if
    Rails.env.test? || Rails.env.production? into my codebase. It makes testing as well as playing
    with the application in development mode harder. For such usecases new adapter is handy.

1 #!ruby

2 module ApnsAdapters

3   class Fake

4       attr_reader :delivered

5

6       def initialize

7          clear

8       end

9

10      def notify(device_token, text)

11         @delivered << [device_token, text]

12      end

13

14      def clear

15         @delivered = []

16      end

17  end

18 end

    Now whenever your service objects are taking apns_adapter as dependency you can use this one
    instead of the real one.

1 #!ruby
2 describe LikingService do
3 subject(:liking) { described_class.new(apns_adapter) }
4 let(:apns_adapter) { ApnsAdapters::Fake.new }

5

6 before{ apns_adapter.clear }

7 specify "delivers push notifications to friends" do

8       liking.painting_liked_by(user_id, painting_id)

9

10      expect(apns_adapter.delivered).to include(

11      [user_device_token, "Your friend 'Robert' liked 'The Kiss' "]

12      )

13  end

14 end

    I like this more then using doubles and expectations because of its simplicity. But using mocking
    techniques here would be apropriate as well. In that case however I would recommend using
    Verifying doubles from Rspec or to go with bogus. I recommend watching great video about

       https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/verifying-doubles
       https://github.com/psyho/bogus
    Wrap external API with an adapter                                     123

    possible problems that mocks and doubles introduce from the author of bogus and solutions for
    them. Integration tests are bogus.

    Injecting and configuring adapters

    Ok, so we have two adapters, how do we provide them to those who need these adapters to
    work? Well, I¡¯m gonna show you an example and not talk much about it because we are going to
    discuss it more deeply in other chapter.

1 #!ruby

2 module LikingServiceInjector

3 def liking_service

4       @liking_service ||= LikingService.new(Rails.config.apns_adapter)

5   end

6 end

7

8 class YourController

9 include LikingServiceInjector

10 end

11

12 #config/environments/development.rb

13 config.apns_adapter = ApnsAdapter::Fake.new

14

15 #config/environments/test.rb

16 config.apns_adapter = ApnsAdapter::Fake.new

    One more implementation

    Sending push notification takes some time (just like sending email or communicating with any
    remote service) so quickly we decided to do it asynchronously.

1 #!ruby

2

3 module ApnsAdapters

4 class Async

5       def notify(device_token, text)

6        Resque.enqueue(ApnsJob, device_token, text)

7       end

8   end

9 end

    And the ApnsJob is going to use our sync adapter.

    https://www.youtube.com/watch?v=7XI3H_rKmRU
    Wrap external API with an adapter                                                      124

1 #!ruby

2 class ApnsJob

3 def self.perform(device_token, text)

4       new(device_token, text).call

5 rescue => exc

6       HoneyBadger.notify(exc)

7       raise

8   end

9

10 def initialize(device_token, text)

11      @device_token = device_token

12      @text = text

13  end

14

15  def call

16      ApnsAdapter::Sync.new.notify(@device_token, @text)

17  end

18 end

    Did you notice that HoneyBadger is not hidden behind adapter? Bad code, bad code¡­ ;)
    What do we have now?

    The result

    We separated our interface from the implementations. Of course our interface is not defined (again,
    Ruby) but we can describe it later using tests. App with the interface it dependend is one component.
    Every implementation can be a separate component.
Wrap external API with an adapter  125

   Our goal here was to get closer to Clean Architecture . Use Cases (Interactors, Service Objects)
   are no longer bothered with implementation details. Instead they relay on the interface and
   accept any implementation that is consistent with it.

  Changing underlying gem

   In reality I no longer use apns gem because of its global configuration. I prefer grocer because I can
   more easily and safely use it to send push notifications to 2 separate mobile apps or even same iOS
   app but built with either production or development APNS certificate.
   So let¡¯s say that our project evolved and now we need to be able to send push notifications to 2
   separate mobile apps. First we can refactor the interface of our adapter to:

1 #!ruby
2 def notify(device_token, text, app_name)
3 end

   Then we can change the implementation of our Sync adapter to use grocer gem instead (we need
   some tweeks to the other implementations as well). In simplest version it can be:

        http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
    Wrap external API with an adapter                                  126

1 #!ruby

2 module ApnsAdapters

3   class Sync

4       def notify(device_token, text, app_name)

5        notification = Grocer::Notification.new(

6            device_token: device_token,

7            alert:       text,

8        )

9        grocer(app_name).push(notification)

10      end

11

12      private

13

14      def grocer(app_name)

15       @grocer ||= {}

16       @grocer[app_name] ||= begin

17           config = APNS_CONFIG[app_name]

18           Grocer.pusher(

19              certificate: config.fetch('pem']),

20              passphrase: config.fetch('password']),

21              gateway:     config.fetch('gateway_host'),

22              port:        config.fetch('gateway_port'),
                             2
23              retries:

24           )

25       end

26      end

27  end

28 end

    However every new grocer instance is using new conncetion to Apple push notifications service.
    But, the recommended way is to reuse the connection. This can be especially usefull if you are using
    sidekiq. In such case every thread can have its own connection to apple for every app that you need
    to support. This makes sending the notifications very fast.

1 #!ruby
2 require 'singleton'

3

4 class GrocerFactory
5 include Singleton

6

7 def pusher_for(app)

8       Thread.current[:pushers] ||= {}

9       pusher = Thread.current[:pushers][app] ||= create_pusher(app)

10      yield pusher

11  rescue

12      Thread.current[:pushers][app] = nil

13      raise

14  end

15

16  private
    Wrap external API with an adapter                                               127

17

18 def create_pusher(app_name)

19      config = APNS_CONFIG[app_name]

20      pusher = Grocer.pusher(

21         certificate: config.fetch('pem']),

22         passphrase: config.fetch('password']),

23         gateway:   config.fetch('gateway_host'),

24         port:      config.fetch('gateway_port'),

25         retries:   2

26      )

27  end

28 end

    In this implementation we kill the grocer instance when exception happens (might happen because
    of problems with delivery, connection that was unused for a long time, etc). We also reraise the
    exception so that higher layer (probably sidekiq or resque) know that the task failed (and can
    schedule it again).

    And our adapter:

1 #!ruby

2 module ApnsAdapters

3   class Sync

4       def notify(device_token, text, app_name)

5          notification = Grocer::Notification.new(

6             device_token: device_token,

7             alert:     text,

8          )

9          GrocerFactory.instance.pusher_for(app_name) do |pusher|

10            pusher.push(notification)

11         end

12      end

13  end

14 end

    The process of sharing instances of grocer between threads could be probably simplified with some
    kind of threadpool library.

    Adapters configuration

    I already showed you one way of configuring the adapter by using Rails.config.
    Wrap external API with an adapter                                              128

1 #!ruby
2 YourApp::Application.configure do
3 config.apns_adapter = ApnsAdapters::Async.new
4 end

   The downside of that is that the instance of adapter is global. Which means you might need to
   take care of it being thread-safe (if you use threads). And you must take great care of its state. So
   calling it multiple times between requests is ok. The alternative is to use proc as factory for creating
   instances of your adapter.

1 #!ruby

2

3 YourApp::Application.configure do
4 config.apns_adapter = proc { ApnsAdapters::Async.new }
5 end

   If your adapter itself needs some dependencies consider using factories or injectors for fully building
   it. From my experience adapters usually can be constructed quite simply. And they are building
   blocks for other, more complicated structures like service objects.

    Testing adapters

    I like to verify the interface of my adapters using shared examples in rspec.

1 #!ruby

2 shared_examples_for :apns_adapter do

3 specify "#notify" do

4       expect(adapter.method(:notify).arity).to eq(2)

5   end

6

7 # another way without even constructing instance

8 specify "#notify" do

9       expect(described_class.instance_method(:notify).arity).to eq(2)

10  end

11 end

    Of course this will only give you very basic protection.
    Wrap external API with an adapter                          129

 1 #!ruby

 2

 3 describe ApnsAdapter::Sync do
 4 it_behaves_like :apns_adapter
 5 end

 6

 7 describe ApnsAdapter::Async do
 8 it_behaves_like :apns_adapter
 9 end

10

11 describe ApnsAdapter::Fake do
12 it_behaves_like :apns_adapter
13 end

    Another way of testing is to consider one implementation as leading and correct (in terms
    of interface, not in terms of behavior) and another implementation as something that must stay
    identical.

1 #!ruby
2 describe ApnsAdapters::Async do
3 subject(:async_adapter) { described_class.new }

4

5 specify "can easily substitute" do

6       example = ApnsAdapters::Sync

7       example.public_instance_methods.each do |method_name|

8        method = example.instance_method(method_name)

9        copy = subject.public_method(method_name)

10

11       expect(copy).to be_present

12       expect([-1, method.arity]).to include(copy.arity)

13      end

14  end

15 end

    This gives you some very basic protection as well.

    For the rest of the test you must write something specific to the adapter implementation.
    Adapters doing http request can either stub http communication with webmock or vcr. Al-
    ternatively, you can just use mocks and expecations to check, whether the gem that you use for
    communication is being use correctly. However, if the logic is not complicated the test are quickly
    becoming typo test, so they might even not be worth writing.

    Test specific for one adapter:

    https://github.com/bblimke/webmock
    vcr
    Wrap external API with an adapter                                                 130

1 #!ruby
2 describe ApnsAdapter::Async do
3 it_behaves_like :apns_adapter

4

5 specify "schedules" do

6       described_class.new.notify("device", "about something")

7       ApnsJob.should have_queued("device", "about something")

8   end

9

10 specify "job forwards to sync" do

11      expect(ApnsAdapters::Sync).to receive(:new).and_return(apns = double(:apns))

12      expect(apns).to receive(:notify).with("device", "about something")

13      ApnsJob.perform("device", "about something")

14  end

15 end

    In many cases I don¡¯t think you should test Fake adapter because this is what we use for testing.
    And testing the code intended for testing might be too much.

    Dealing with exceptions

    Because we don¡¯t want our app to be bothered with adapter implementation (our clients don¡¯t care
    about anything except for the interface) our adapters need to throw the same exceptions. Because
    what exceptions are raised is part of the interface. This example does not suite us well to discuss it
    here because we use our adapters in fire and forget mode. So we will have to switch for a moment
    to something else.

    Imagine that we are using some kind of geolocation service which based on user provided address
    (not a specific format, just String from one text input) can tell us the longitude and latitude
    coordinates of the location. We are in the middle of switching to another provided which seems
    to provide better data for the places that our customers talk about. Or is simply cheaper. So we have
    two adapters. Both of them communicate via HTTP with APIs exposed by our providers. But both
    of them use separate gems for that. As you can easily imagine when anything goes wrong, gems are
    throwing their own custom exceptions. We need to catch them and throw exceptions which
    our clients/services except to catch.
    Wrap external API with an adapter                                                                 131

1 #!ruby
2 require 'hipothetical_gooogle_geolocation_gem'
3 require 'new_cheaper_more_accurate_provider_gem'

4

5 module GeolocationAdapters
6 ProblemOccured = Class.new(StandardError)

7

8 class Google

9       def geocode(address_line)

10       HipotheticalGoogleGeolocationGem.new.find_by_address(address_line)

11      rescue HipotheticalGoogleGeolocationGem::QuotaExceeded

12       raise ProblemOccured

13      end

14  end

15

16 class NewCheaperMoreAccurateProvider

17      def geocode(address_line)

18       NewCheaperMoreAccurateProviderGem.geocoding(address_line)

19      rescue NewCheaperMoreAccurateProviderGem::ServiceUnavailable

20       raise ProblemOccured

21      end

22  end

23 end

    This is something people often overlook which in many cases leads to leaky abstraction. Your
    services should only be concerned with exceptions defined by the interface.

1 #!ruby

2 class UpdatePartyLocationService

3 def call(party_id, address)

4       party = party_db.find_by_id(party_id)

5       party.coordinates = geolocation_adapter.geocode(address)

6       db.save(party)

7 rescue GeolocationAdapters::ProblemOccured

8       scheduler.schedule(UpdatePartyLocationService, :call, party_id, address, 5.minutes.from_now)

9   end

10 end

    Although some developers experiment with exposing exceptions that should be caught as part of
    the interface (via methods), I don¡¯t like this approach:
    Wrap external API with an adapter                                                                 132

1 #!ruby
2 require 'hipothetical_gooogle_geolocation_gem'
3 require 'new_cheaper_more_accurate_provider_gem'

4

5 module GeolocationAdapters
6 ProblemOccured = Class.new(StandardError)

7

8 class Google

9       def geocode(address_line)

10       HipotheticalGoogleGeolocationGem.new.find_by_address(address_line)

11      end

12

13      def problem_occured

14       HipotheticalGoogleGeolocationGem::QuotaExceeded

15      end

16  end

17

18 class NewCheaperMoreAccurateProvider

19      def geocode(address_line)

20       NewCheaperMoreAccurateProviderGem.geocoding(address_line)

21      end

22

23      def problem_occured

24       NewCheaperMoreAccurateProviderGem::ServiceUnavailable

25      end

26  end

27 end

    And the service

1 #!ruby

2 class UpdatePartyLocationService

3 def call(party_id, address)

4       party = party_db.find_by_id(party_id)

5       party.coordinates = geolocation_adapter.geocode(address)

6       db.save(party)

7 rescue geolocation_adapter.problem_occured

8       scheduler.schedule(UpdatePartyLocationService, :call, party_id, address, 5.minutes.from_now)

9   end

10 end

    But as I said I don¡¯t like this approach. The problem is that if you want to communicate something
    domain specific via the exception you can¡¯t relay on 3rd party exceptions. If it was adapter
    responsibility to provide in exception information whether service should retry later or give up,
    then you need custom exception to communicate it.
Wrap external API with an adapter  133

Adapters ain¡¯t easy

There are few problems with adapters. Their interface tends to be lowest common denominator
between features supported by implementations. That was the reason which sparkled big
discussion about queue interface for Rails which at that time was removed from it. If one technology
limits you so you schedule background job only with JSON compatibile attributes you are limited
to just that. If another technology let¡¯s you use Hashes with every Ruby primitive and yet another
would even allow you to pass whatever ruby object you wish then the interface is still whatever
JSON allows you to do. No only you won¡¯t be able to easily pass instance of your custom class as
paramter for scheduled job. You won¡¯t even be able to use Date class because there is no such type
in JSON. Lowest Common Denominator¡­

You won¡¯t easily extract Async adapter if you care about the result. I think that¡¯s obvious. You
can¡¯t easily substitute adapter which can return result with such that cannot. Async is architectural
decision here. And rest of the code must be written in a way that reflects it. Thus expecting to get
the result somehow later.

Getting the right level of abstraction for adapter might not be easy. When you cover api or a
gem, it¡¯s not that hard. But once you start doing things like NotificationAdapter which will let you
send notification to user without bothering the client whether it is a push for iOS, Android, Email
or SMS, you might find yourself in trouble. The closer the adapter is to the domain of adaptee, the
easier it is to write it. The closer it is to the comain of the client, of your app, the harder it is, the
more it will know about your usecases. And the more complicated and unique for the app, such
adapter will be. You will often stop for a moment to reflect whether given funcionality is the
responsibility of the client, adapter or maybe yet another object.

Summary

Adapters are puzzles that we put between our domain and existing solutions such as gems, libraries,
APIs. Use them wisely to decouple core of your app from 3rd party code for whatever reason you
have. Speed, Readability, Testability, Isolation, Interchangeability.
In-Memory Fake Adapters

There are two common techniques for specifying in a test the behavior of a 3rd party system:

     stubbing of an adapter/gem methods.
     stubbing the HTTP requests triggered by those adapters/gems.

I would like to present you a third option ¡ª In-Memory Fake Adapters and show an example of
one.

Why use them?

I find In-Memory Fake Adapters to be well suited into telling a full story. You can use them to
describe actions that might only be available on a 3rd party system via UI. But such actions often
configure the system that we cooperate with to be in a certain state. State that we depend on. State
that we would like to be present in a test case ¡ª showing how our System Under Test interacts with
the 3rd party external system.
Let¡¯s take as an example an integration with seats.io that I am working with recently. They provide
us with many features:

     building a venue map including sections, rows, and seats
     labeling the seats
     general admission areas with unnumbered (but limited in amount) seats
     a seat picker for customers to select a place
     real-time updates for selected seats during the sale process
     atomic booking of selected seats when they are available

So as a service provider they do a lot for us that we don¡¯t need to do ourselves.
On the other hand, a lot of those things are UI/Networking related. And it does not affect the core
business logic which is pretty simple:

     Don¡¯t let two people to buy the same seat
     Don¡¯t let customers to buy too many standing places, in a General Admission area.

In other words: Don¡¯t oversell. That¡¯s their job. To help us not oversell. Which is pretty important.
To have that feature working we need to communicate with them via API and they need to do their
job.
Let¡¯s see a simple exemplary test.

                                                         134
    In-Memory Fake Adapters                                                                         135

1 #!ruby

2 booking = BookingService.new(seats_adapter)

3 expect(seats_adapter).to receive(:book_entrance).with(

4 event_key: "concert",

5     places: [{

6     section_name: "Sector 1",

7     quantity: 3

8 }]).and_raise(SeatsIo::Error)

9

10 expect do

11 booking.book_standing_place(section_name: "Sector 1", quantity: 3)

12 end.to raise_error(BookingService::NotAllowed)

    When seats.io returns with HTTP 400, the adapter raises SeatsIo::Error. The tested service knows
    that the customer can¡¯t book those seats. It¡¯s OK code for a single class test.

    But I don¡¯t find this approach useful when writing more story-driven acceptance tests. Because
    this test does not say a story why the booking could not be finished. Is that because seats.io was
    configured via UI so that Sector 1 has only 2 places? Was it because it has 20 standing places, but
    more than 17 were already sold so there is not enough left for 3 people?

1 #!ruby
2 seats_adapter.add_event(event_key: "concert")
3 seats_adapter.add_general_admission(event_key: "concert", section_name: "Sector 1", quantity: 2)

4

5 organizer.import_season_pass(
6 name: "John Doe",
7 pass_type: :standing,

8 section_name: "Sector 1"

9)

10 organizer.import_season_pass(

11    name: "Mark Twain",

12 pass_type: :standing,

13 section_name: "Sector 1"

14 )

15

16 expect do
17 customer.buy_ticket(ticket_type: :standing, section_name: "Sector 1")
18 end.to raise_error(BookingService::NotAllowed)

    Now, this tells a bigger story. We know what was configured in seats.io using their GUI. When season
    passes are imported by the organizer, they took all the standing places in Sector 1. If a customer tries
    to buy a ticket there, it won¡¯t be possible, because there is no more space available.

    No need to stub every call

    When using In-Memory Fake Adapters you don¡¯t need to stub every call to the adapter (on method
    or HTTP level) separately. This is especially useful if the Unit that you tests is bigger than one
    In-Memory Fake Adapters                                                       136

    class. And when it communicates with the adapter in multiple places. To properly test a scenario
    that invokes multiple API calls it might be easier for you to plug in a fake adapter. Let the tests
    interact with it.

    Example

    Here is an example of a fake adapter for our seats.io integration. There are 3 categories of methods:

         Real adapter interface implemented: book_entrance. These can be called from the services
           that use our real Adapter in production and fake adapter in tests.

         UI fakers: add_event, add_general_admission, add_seat. They can only be called from a test
           setup. They show how the 3rd party API was configured using the web UI, without using the
           API. We use them to build the internal state of the fake adapter which represents the state of
           the 3rd party system.

         Test Helpers: clean. Useful for example to reset the state. Not always needed.

1 #!ruby

2

3 module SeatsIo
4 Error = Class.new(StandardError)
5 end

6

7 class FakeClient

8 class FakeEvent

9   def initialize

10  @seats = {}

11  @places = {}

12  end

13

14  def book_entrance(seats: [], places: [])

15  verify(seats, places)

16  update(seats, places)

17  end

18

19  def add_seat(label:)

20  @seats[label] = :released

21  end

22

23  def add_general_admission(section_name:, quantity:)

24  @places[section_name] = quantity

25  end

26

    http://blog.arkency.com/2014/09/unit-tests-vs-class-tests/
    http://blog.arkency.com/2013/09/services-what-they-are-and-why-we-need-them/
    In-Memory Fake Adapters                                         137

27  private

28

29  def update(seats, places)

30       seats.each do |seat|

31       @seats[seat] = :booked

32       end

33

34       places.each do |place|

35       place_name = place.fetch(:section_name)

36       quantity = place.fetch(:quantity)

37       @places[place_name] -= quantity

38       end

39  end

40

41  def verify(seats, places)

42       seats.all? do |seat|

43       @seats[seat] == :released

44       end or raise SeatsIo::Error

45

46       places.all? do |place|

47       place_name = place.fetch(:section_name)

48       quantity = place.fetch(:quantity)

49       @places[place_name] >= quantity

50       end or raise SeatsIo::Error

51  end

52  end

53

54  def initialize()

55  clear

56  end

57

58  # Test helpers

59

60  def clear

61  @events = {}

62  end

63

64  # UI Fakes

65

66 def add_event(event_key:)

67  raise "Event already exists" if @events[event_key]

68  @events[event_key] = FakeEvent.new

69  end

70

71 def add_general_admission(event_key:, section_name:, quantity:)

72  @events[event_key].add_general_admission(

73       section_name: section_name,

74       quantity: quantity

75  )

76  end

77
    In-Memory Fake Adapters                                                          138

78 def add_seat(event_key:, label:)

79      @events[event_key].add_seat(label: label)

80  end

81

82  # Real API

83

84 def book_entrance(event_key:, seats: [], places: [])

85      @events[event_key].book_entrance(

86         seats: seats,

87         places: places

88      )

89  end

90 end

    Seats.io has a lot of useful features for us. Despite it, the in-memory implementation of their core
    booking logic is pretty simple. For seats we mark them as booked: @seats[seat] = :booked. For
    general admission areas we lower their capacity: @places[place_name] -= quantity. That¡¯s it.

    In-memory adapters are often used as a step of building a walking skeleton. Where your system
    does not integrate yet with a real 3rd party dependency. It integrates with something that pretends
    to be the dependency.

    How to keep the fake adapter and the real one in sync?

    Use the same test scenarios. Stub HTTP API responses (based on what you observed while playing
    with the API) for the sake of real adapter. The fake one doesn¡¯t care. An oversimplified example
    below.

1 #!ruby

2

3 RSpec.shared_examples "TweeterAdapters" do |twitter_db_class|

4   specify do

5       twitter = twitter_db_class.new("@pankowecki")

6

7       stub_request(

8          :post,

9          'https://api.twitter.com/1.1/statuses/update.json?status=Hello%20world',

10         body: '{status: "status"}'

11      ).to_return(

12         status: 200, body: "[{text:"Hello world"}]"

13      )

14      twitter.tweet("Hello world")

15

16      stub_request(

17         :get,

    http://alistair.cockburn.us/Walking+skeleton
    In-Memory Fake Adapters                                                                          139

18         'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=pankowecki&count=1'

19      ).to_return(

20         status: 200, body: '[{text:"Hello world"}]'

21      )

22      expect(twitter.last_tweet).to include?("Hello world")

23  end

24 end

25

26

27 RSpec.describe FakeTwitterAdapter do
28 include_examples "TweeterAdapters", FakeTwitterAdapter
29 end

30

31 RSpec.describe RealTwitterAdapter do

32 include_examples "TweeterAdapters", RealTwitterAdapter
33 end

    You know how to stub the HTTP queries because you played the sequence and watched the results.
    So hopefully, you are stubbing with the truth.

    What if the external service changes their API in a breaking way? Well, that¡¯s more of a case for
    monitoring than testing in my opinion.

    The effect is that you can stub the responses only on real adapter tests. In all other places rely on the
    fact that fake client has the same behavior. Interact with it directly in services or acceptance tests.

    When to use Fake Adapters?

    The more your API calls and business logic depend on previous API calls and the state of the external
    system. So we don¡¯t want to just check that we called a 3rd party API. But that a whole sequence of
    calls made sense together and led to the desired state and result in both systems.

    There are many cases in which implementing Fake adapter would not be valuable and beneficial
    in your project. Stubbing/Mocking (on whatever level) might be the right way to go. But this is a
    useful technique to remember when your needs are different and you can benefit from it.

       /2015/11/monitoring-services-and-adapters-in-your-rails-app-with-honeybadger-newrelic-and-number-prepend/
    4 ways to early return from a rails
    controller

    When refactoring rails controllers you can stumble upon one gottcha. It¡¯s hard to easily extract
    code into methods when it escapes flow from the controller method (usually after redirecting and
    sometimes after rendering). Here is an example:

    1. redirect_to and return (classic)

1 #!ruby

2 class Controller

3   def show

4       unless @order.awaiting_payment? || @order.failed?

5        redirect_to edit_order_path(@order) and return

6       end

7

8       if invalid_order?

9        redirect_to tickets_path(@order) and return

10      end

11

12      # even more code over there ...

13  end

14 end

    So that was our first classic redirect_to and return way.

    Let¡¯s not think for a moment what we are going to do later with this code, whether some of it should
    landed in models or services. Let¡¯s just tackle the problem of extracting it into a controller method.

    2. extracted_method and return

                                         140
    4 ways to early return from a rails controller            141

1 #!ruby

2 class Controller

3   def show

4       verify_order and return

5       # even more code over there ...

6   end

7

8   private

9

10  def verify_order

11      unless @order.awaiting_payment? || @order.failed?

12       redirect_to edit_order_path(@order) and return true

13      end

14

15      if invalid_order?

16       redirect_to tickets_path(@order) and return true

17      end

18  end

19 end

    The problem with this technique is that after extracting the code into method you also need to fix
    all the returns so that they end with return true (instead of just return). If you forget about it you
    are going to introduce a new bug.

    The other thing is that verify_order and return does not feel natural. When this method returns
    true I would rather expect the order to be positively verified so escaping early from controller action
    does not seem to make sense here.

    So here is the alternative variant of it

    2.b extracted_method or return

1 #!ruby

2 class Controller

3   def show

4       verify_order or return

5       # even more code over there ...

6   end

7

8   private

9

10  def verify_order

11      unless @order.awaiting_payment? || @order.failed?

12       redirect_to edit_order_path(@order) and return

13      end

14

15      if invalid_order?

16       redirect_to tickets_path(@order) and return

17      end
    4 ways to early return from a rails controller         142

18

19      return true

20  end

21 end

    Now it sounds better verify_order or return. Either the order is verified or we return early. If
    you decide to go with this type of refactoring you must remember to add return true at the end
    of the extracted method. However the good side is that all your redirect_to and return lines can
    remain unchanged.

    3. extracted_method{ return }

1 #!ruby

2 class Controller

3   def show

4       verify_order{ return }

5       # even more code over there ...

6   end

7

8   private

9

10  def verify_order

11      unless @order.awaiting_payment? || @order.failed?

12       redirect_to edit_order_path(@order) and yield

13      end

14

15      if invalid_order?

16       redirect_to tickets_path(@order) and yield

17      end

18  end

19 end

    If we wanna return early from the top level method, why not be explicit about what we try to
    achieve. You can do that in Ruby if your callback block contains return. That way inner function
    can call the block and actually escape the outer function.

    But when you look at verify_order method in isolation you won¡¯t know that this yield is actually
    stopping the flow in verify_order as well. Next lines are not reached.

    I don¡¯t like when you need to look at outer function to understand the behavior of inner function.
    That¡¯s completely contrary to what we usually try to achieve in programming by splliting code into
    methods that can be understood on their own and provide us with less cognitive burden.

    4. extracted_method; return if performed?
    4 ways to early return from a rails controller         143

1 #!ruby

2 class Controller

3   def show

4       verify_order; return if performed?

5       # even more code over there ...

6   end

7

8   private

9

10  def verify_order

11      unless @order.awaiting_payment? || @order.failed?

12       redirect_to edit_order_path(@order) and return

13      end

14

15      if invalid_order?

16       redirect_to tickets_path(@order) and return

17      end

18  end

19 end

    With ActionController::Metal#performed? you can test whether render or redirect already
    happended. This seems to be a good solution for cases when you extract code into method solely
    responsible for breaking the flow after render or redirect. I like it because in such case as shown, I
    don¡¯t need to tweak the extracted method at all. The code can remain as it was and we don¡¯t care
    about returned values from the subroutine.

    throw :halt (sinatra bonus)

    In sinatra you could use throw :halt for that purpose (don¡¯t confuse throw (flow-control) with
    raise (exceptions)).

    There was a discussion about having such construction in Rails a few years ago happening
    automagically for rendering and redirecting but the discussion is inconclusive and looks like it was
    not implemented in the end in rails.

    It might be interesting for you to know that expecting render and redirect to break the flow of
    the method and exit it immediately is one of the most common mistake experienced by some Rails
    developers at the beginning of their career.

       http://api.rubyonrails.org/v4.1.4/classes/ActionController/Metal.html#method-i-performed-3F
       http://patshaughnessy.net/2012/3/7/learning-from-the-masters-sinatra-internals
       http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/
       https://groups.google.com/forum/#!topic/rubyonrails-core/EW7C5GoEZxw
4 ways to early return from a rails controller  144

throw :halt (rails?)

As Avdi wrote and his blogpost Rack is also internally using throw :halt. However I am not sure
if using this directly from Rails, deep, deep in your own controller code is approved and tesed. Write
me an email if you ever used it and it works correctly.

why not before filter?

Because in the end you probably want to put this code into service anyway and separate checking
pre-conditions from http concerns.

   http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/
   Service::Input

   When your app no longer starts being just a GUI for CRUD updates but rather becomes a set of
   multiple, complicated and overlapping business workflows; you might find it hard to understand
   some parts of it easily.

   This might often happen when you keep using old conventions that served you well during the
   initial phase of creating app but they are no longer doing their job well. Look at this code:

1 #!ruby

2

3 class OrderConfirmationService

4 def call(order_id, order_attributes)

5       o = Order.find(order_id)

6       o.attributes = order_attributes

7       o.proceed_to_payment!

8       # emails, notifications, analytics, reports etc...

9  end

10 end

   Do you know what it does exactly? No? Let¡¯s have a look at the controller:

1 #!ruby

2

3 class OrderConfirmationController

4  def update

5       OrderConfirmationService.new(dependencies).call(params[:id], params[:order])

6  end

7 end

   I guess you still have no clue. And that¡¯s exactly the problem :)

   To see what attributes are being updated here and for what reason we would have to have a look
   at the views. This is very often a problem when rails app gets bigger. The flow of the app is no
   longer based on one create/update form view that can change all attributes of the object (Order in
   our example). What often happens is that we have multiple controllers and forms operating on some
   specific parts of our domain objects. The flow of user operations is often broken into smaller steps
   for the convenience.

   So to understand what the code does you often need to have a look at the views. It takes time and
   it¡¯s troublesome to go through all the partials and understand the forms (especially nested ones) to
   have a little clue. Additionally, if you are using the state_machine gem you might even find out upon
   inspecting the views that this code can be used to trigger state transition along with all its callbacks

                                         145
    Service::Input                                                                                    146

    (by setting state_event attribute with a value from the form). Such code makes reasoning about
    the application harder. It takes more time to refactor it later every time you come back to it.

    It can also make your code more vulnerable to attacks when you accept attributes that were supposed
    to be provided in previous step or next step.

    So what¡¯s the solution?

    Be explicit about arguments that your service can take.

1 #!ruby

2 class OrderConfirmationService

3 class Input < Struct.new(:full_name, :email_address)

4   end

5

6 def call(order_id, order_input)

7       o = Order.find(order_id)

8       o.full_name  = order_input.full_name

9       o.email_address = order_input.email_address

10      o.proceed_to_payment!

11  end

12 end

    This code might be more verbose but is also more explicit on expected data that must be provided.
    Now you can easily see that our service wasn¡¯t for updating orders in any possible way of any
    allowed attribute. You can see that this step was only intended for customer to confirm their full
    name and email address that were provided in previous step. Editing the content of the order is not
    allowed at this step.

    You can use the Input class to provide basic validation. In trivial cases (like this one) it might be
    good idea to call the service and provide Form object as Input. In more complicated it might be good
    to keep them separated and map from one (Form) to another (Input). Such Input class is essential
    when you are using the technique of setting the system state in tests with your production services.
    Let¡¯s say you later add the additional third attribute that should be filled:

1 #!ruby

2 class OrderConfirmationService

3 class Input < Struct.new(:full_name, :email_address)

4   end

5

6 def call(order_id, order_input)

7       o = Order.find(order_id)

8       o.full_name  = order_input.full_name.presence        or raise ArgumentError

9       o.email_address = order_input.email_address.presence or raise ArgumentError

10      o.coupon_code = order_input.coupon_code              or raise ArgumentError # empty string is ok

11                                                                                   # nil is not ok

12      o.proceed_to_payment!

13  end

14 end
    Service::Input                                147

    Now all your tests which use the service to set the state will nicely crash with an error. If you
    extracted calling this service into one method with good defaults, you can just provide good default
    there (almost like with factory_girl). All other places will crash quickly as well so you will know
    to fix it. What won¡¯t happen is you setting the state of your tests incorrectly or in a way that can¡¯t
    happen in production.

    I¡¯ve seen many test cases diverge from real production situations because attributes are being added
    or removed from views but they are not cleaned in a similar way in test setups. So the setups either
    provide too many or not enough arguments compared to what is happening on production. Being
    explicit about it like in this example (contrary to using Hash structure all the time) makes it easier
    to avoid such mistakes.

    If you add new attribute to the service and throw error when it is missing, you can find easily all
    places that are now missing to provide it. If you remove an attribute those who try to set it on an
    instance of Input will trigger missing method error as well so you are nicely covered.

    Using with controller

1 #!ruby

2

3 class OrderConfirmationController

4   def update

5       OrderConfirmationService.

6        new(dependencies).

7        call(

8           params[:id],

9           OrderConfirmationService::Input.new(

10             params[:order][:full_name],

11             params[:order][:email_address]

12          )

13       )

14  end

15 end

    Nicer way to set multiple attributes
    Service::Input                                      148

1 #!ruby

2

3 class Input < Struct.new(:full_name, :email_address)

4 def initialize(*attributes)

5       super

6       yield self

7       freeze

8   end

9 end

    Then from controller you can do

1 #!ruby

2

3 order = params.fetch(:order)

4 OrderConfirmationService::Input.new do |input|

5 input.full_name   = order[:full_name]

6 input.email_address = order[:email_address]

7 # ... next attributes

8 end

    I like to freeze input objects when all the attributes are set (after creation they should be fully ready
    to be used) but that is completely optional. Performing some basic validation in input just after
    creation instead of in a service or in a ActiveRecord class is also acceptable:

1 #!ruby

2

3 class Input < Struct.new(:full_name, :email_address)

4 def initialize(*attributes)

5       super

6       yield self

7       full_name   or raise ArgumentError

8       email_address or raise ArgumentError

9       freeze

10  end

11 end

    One more thing

    I find Input classes like that or similar to be very valuable when user can provide very few attributes
    for your class. In one of our projects the OrderLine class has about 15 attributes out of which only 2
    can be set directly by the user via Form. The rest is computed by the system, filed based on other data,
    or duplicated from other records. Having a class like OrderLineInput < Struct.new(:product_id,
    :quantity) can be very intention revealing in such case and more secure.
Validations: Contexts

Many times Rails programmers ask How can I skip one (or more) validations in Rails. The common
usecase for it is that users with higher permissions are granted less strict validation rules. After all,
what¡¯s the point of being admin if admin cannot do more than normal user, right? With great power
comes great responsibility and all of that yada yada yada. But back to the topic. Let¡¯s start with
something simple and refactor it a little bit to show Rails feature that I rerly see in use in the real
world.

This is our starting point

  Where the fun begins

1 class User < ActiveRecord::Base
2 validates_length_of :slug, minimum: 3
3 end

   Our users can change the slug (/u/slug) under which their profiles will appear. However the most
   valuable short slugs are not available for them. Our business model dictates that we are going to sell
   them to earn a lot of money.
   So, we need to add conditional validation that will be different for admins and different for users.
   Nothing simpler, right?

Where the fun ends

1 class User < ActiveRecord::Base

2 attr_accessor: :edited_by_admin

3 validates_length_of :slug, minimum: 3, unless: Proc.new{|u| u.edited_by_admin? }

4 validates_length_of :slug, minimum: 1, if:  Proc.new{|u| u.edited_by_admin? }

5 end

                                              149
    Validations: Contexts                                                                       150

1 class Admin::UsersController

2   def edit

3       @user = User.find(params[:id])

4       @user.edited_by_admin = true

5       if @user.save

6        redirect # ...

7       else

8        render # ...

9       end

10  end

11 end

    Now this would work, however it is not code I would be proud about.

    But wait, you already know a way to mark validations to trigger only sometimes. Do you remember
    it?

1 class Meeting < ActiveRecord::Base
2 validate :starts_in_future, on: :create
3 end

    We¡¯ve got on: :create option which makes a validation run only when saving new record (#new_-
    record?).

    I wonder whether we could use it¡­

    Where it¡¯s fun again

1 class User < ActiveRecord::Base
2 validates_length_of :slug, minimum: 3, on: :user
3 validates_length_of :slug, minimum: 1, on: :admin
4 end

1 class Admin::UsersController

2   def edit

3       @user = User.find(params[:id])

4       if @user.save(context: :admin)

5        redirect # ...

6       else

7        render # ...

8       end

9   end

10 end

    Wow, now look at that. Isn¡¯t it cute?
    And if you want to only check validation without saving the object you can use:

       http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-new_record-3F
   Validations: Contexts                                                                                                           151

1 u = User.new
2 u.valid?(:admin)
3 # or
4 u.valid?(:user)

   This feature is actually even documented ActiveModel::Validations#valid?(context=nil)

   Now it is a good moment to remind ourselves of a nice API that can make it less redundant in case
   of multiple rules: Object#with_options

1 class User < ActiveRecord::Base

2 with_options({on: :user}) do |for_user|

3       for_user.validates_length_of :slug, minimum: 3

4       for_user.validates_acceptance_of :terms_of_service

5  end

6

7 with_options({on: :admin}) do |for_admin|

8       for_admin.validates_length_of :slug, minimum: 1

9  end

10 end

  When it¡¯s miserable again

   The problem with this approach is that you cannot supply multiple contexts.
   If you would like to have some validations on: :admin and some on: :create then it is probably
   not gonna work the way you would want.

1 class User < ActiveRecord::Base
2 validates_length_of :slug, minimum: 3, on: :user
3 validates_length_of :slug, minimum: 1, on: :admin
4 validate :something, on: :create
5 end

   When you run user.valid?(:admin) or user.save(context: admin) for new record, it¡¯s not gonna
   trigger the last validation because we substituted the default :create context with our own :admin
   context.
   You can see it for yourself in rails code:

   http://api.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-valid-3F
   http://api.rubyonrails.org/classes/Object.html#method-i-with_options
   https://github.com/rails/rails/blob/98b56eda5d7ebc595b6768d53ee12ad6296b4066/activerecord/lib/active_record/validations.rb#L68
    Validations: Contexts                                                                      152

 1 # Runs all the validations within the specified context. Returns +true+ if
 2 # no errors are found, +false+ otherwise.
 3#
 4 # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
 5 # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
 6#
 7 # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
 8 # some <tt>:on</tt> option will only run in the specified context.
 9 def valid?(context = nil)
10 context ||= (new_record? ? :create : :update)
11 output = super(context)
12 errors.empty? && output
13 end

    The trick with on: :create and on: :update works because Rails by default does the job of providing
    the most suitable context. But that does not mean you are only limited in your code to those two
    cases which work out of box.

    We could go with manual check for both contexts in our controllers but we would have to take
    database transaction into consideration, if our validations are doing SQL queries.

1 class Admin::UsersController

2   def edit

3       User.transaction do

4        @user = User.find(params[:id])

5        if @user.valid?(:admin) && @user.valid?(:create)

6            @user.save!(validate: false)

7            redirect # ...

8        else

9            render # ...

10       end

11      end

12  end

13 end

    I doubt that the end result is 100% awesome.

    When it might come useful

    I once used this technique to introduce new context on: :destroy which was doing something
    similar to:
    Validations: Contexts                                    153

1 class User < ActiveRecord::Base
2 has_many :invoices
3 validate :does_not_have_any_invoice, on: :destroy

4

5 def destroy

6       transaction do

7        valid?(:destroy) or raise RecordInvalid.new(self)

8        super()

9       end

10  end

11

12  private

13

14 def does_not_have_any_invoice

15      errors.add(:invoices, :present) if invoices.exists?

16  end

17 end

    The idea was, that it should not be possible to delete user who already took part of some important
    business activity.

    Nowdays we have has_many(dependent: :restrict_with_exception) but you might still find this
    technique beneficial in other cases where you would like to run some validations before destroying
    an object.

       http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
 Validations: Objectify

   In previous chapter I showed you how Rails validations might become context dependent and a few
   ways how to handle such situation. However none of them were perfect because our object had to
   become context-aware. The alternative solution that I would like to show you now is to extract the
   validations rules outside, making our validated object lighter.

  Not so far from our comfort zone

   For start we are gonna use the trick with SimpleDelegator that you might know from other chapters.

1 class UserEditedByAdminValidator < SimpleDelegator
2 include ActiveModel::Validations

3

4 validates_length_of :slug, minimum: 1
5 end

1 user = User.find(1)
2 user.attributes = {slug: "summertime-blues"}

3

4 validator = UserEditedByAdminValidator.new(user)
5 if validator.valid?
6 user.save!(validate: false)
7 else
8 puts validator.errors.full_messages
9 end

   So now you have external validator that you can use in one context and you can easily create another
   validator that would validate different business rules when used in another context.
   The context in your system can be almost everything. Sometimes the difference is just create vs
   update. Sometimes it is in save as draft vs publish as ready. And sometimes it based on the user role
   like admin vs moderator.

  One step further

   But let¡¯s go one step further and drop the nice DSL-alike methods such as validates_length_of
   that Rails used to bought us and that we all love, to see what¡¯s beneath them.

        http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_length_of
        https://github.com/rails/rails/blob/fe49f432c9a88256de753a3f2263553677bd7136/activemodel/lib/active_model/validations/length.rb#L119

                                                             154
   Validations: Objectify                                                                                                                155

1 class UserEditedByAdminValidator < SimpleDelegator
2 include ActiveModel::Validations

3

4 validates_with LengthValidator, attributes: [:slug], minimum: 1
5 end

   The DSL-methods from ActiveModel::Validations::HelperMethods are just tiny wrappers for
   a slightly more object oriented validators. And they just convert first argument to Array value of
   attributes key in a Hash.

  Almost there

   When you dig deeper you can see that one of validates_with responsibilities is to actually finally
   create an instance of validation rule.

1 class UserEditedByAdminValidator < SimpleDelegator
2 include ActiveModel::Validations

3

4 validate LengthValidator.new(attributes: [:slug], minimum: 1)
5 end

   Let¡¯s create an instance of such rule ourselves and give it a name.

   Rule as an object

   We are going to do it by simply assigning it to a constant. That is one, really global name, I guess :)

1 SlugMustHaveAtLeastOneCharacter =

2 ActiveModel::Validations::LengthValidator.new(

3     attributes: [:slug],

4     minimum: 1

5  )

6

7 class UserEditedByAdminValidator < SimpleDelegator
8 include ActiveModel::Validations

 9

10 validate SlugMustHaveAtLeastOneCharacter
11 end

   Now you can share some of those rules in different validators for different contexts.

      http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html
      http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates_with
      https://github.com/rails/rails/blob/bdf9141c039afc7ce56d6c69cfe50b60155e5359/activemodel/lib/active_model/validations/with.rb#L89
    Validations: Objectify                             156

    Reusable rules, my way

    The rules:

1 SlugMustStartWithU =

2 ActiveModel::Validations::FormatValidator.new(

3      attributes: [:slug],

4      with: /\Au/

5   )

6

7 SlugMustHaveAtLeastOneCharacter =

8 ActiveModel::Validations::LengthValidator.new(

9      attributes: [:slug],

10     minimum: 1

11  )

12

13 SlugMustHaveAtLeastThreeCharacters =

14 ActiveModel::Validations::LengthValidator.new(

15     attributes: [:slug],

16     minimum: 3

17  )

    Validators that are using them:

 1 class UserEditedByAdminValidator < SimpleDelegator
 2 include ActiveModel::Validations

 3

 4 validate SlugMustStartWithU
 5 validate SlugMustHaveAtLeastOneCharacter
 6 end

 7

 8 class UserEditedByUserValidator < SimpleDelegator
 9 include ActiveModel::Validations

10

11 validate SlugMustStartWithU
12 validate SlugMustHaveAtLeastThreeCharacters
13 end

    or the highway

    I could not find an easy way to register multiple instances of validation rules. So below is a bit
    hacky (although valid) way to work around the problem.
    It gives us a nice ability to group common rules in Array and add or subtract other rules.
    Rules definitions:
    Validations: Objectify                                      157

1 format_validator = ActiveModel::Validations::FormatValidator
2 length_validator = ActiveModel::Validations::LengthValidator

3

4 class SlugMustStartWithU < format_validator

5 def initialize(*)

6       super(attributes: [:slug], with: /\Au/)

7   end

8 end

9

10 class SlugMustEndWithZ < format_validator

11  def initialize(*)

12      super(attributes: [:slug], with: /z\Z/)

13  end

14 end

15

16 class SlugMustHaveAtLeastOneCharacter < length_validator

17  def initialize(*)

18      super(attributes: [:slug], minimum: 1)

19  end

20 end

21

22 class SlugMustHaveAtLeastThreeCharacters < length_validator

23  def initialize(*)

24      super(attributes: [:slug], minimum: 5)

25  end

26 end

    Validators using the rules:

1 CommonValidations = [SlugMustStartWithU, SlugMustEndWithZ]

2

3 class UserEditedByAdminValidator < SimpleDelegator

4 include ActiveModel::Validations

5

6 validates_with *(CommonValidations +

7       [SlugMustHaveAtLeastOneCharacter]

8   )

9 end

10

11 class UserEditedByUserValidator < SimpleDelegator

12 include ActiveModel::Validations

13

14 validates_with *(CommonValidations +

15      [SlugMustHaveAtLeastThreeCharacters]

16  )

17 end
    Validations: Objectify                                                           158

    Cooperation with rails forms

    The previous examples won¡¯t cooperate nicely with Rails features expecting list of errors validations
    on the validated object, because as I showed in first example, the #errors that are filled are defined
    on the validator object.

1 validator = UserEditedByAdminValidator.new(user)
2 unless validator.valid?
3 puts validator.errors.full_messages
4 end

    But you can easily overwrite the #errors that come from including ActiveModel::Validations,
    by delegating them to the validated object, which in our case is #user.

1 class UserEditedByAdminValidator
2 include ActiveModel::Validations

3

4 delegate :slug, :errors, to: :user

5

6 def initialize(user)

7       @user = user

8   end

9

10 validates_with *(CommonValidations +

11      [SlugMustHaveAtLeastOneCharacter]

12  )

13

14  private

15  attr_reader :user

16 end

    http://api.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-errors
Testing

                                                         159
Introduction

In the scope of the Rails applications we can talk about two kinds of tests:

   1. System tests
   2. Unit tests

There¡¯s a lot of confusion about testing in the Rails community. It¡¯s not totally clear what are unit
tests and at what level should we tests. It doesn¡¯t help that some of the terminology is not compatible
with the rest world (functional tests in Rails are actually unit tests of controllers).
By System Tests, I mean tests that cover the whole infrastructure. In particular, this includes hitting
the database. This also included checking the HTML format for the resulting webpages. Those tests
give a lot of confidence that everything is working. They¡¯re usually slow, but they integrate different
pieces and test them together. You can think of them as Black Box tests - you set some initial state,
you give it an input and you check the output.
By Unit Tests, I mean tests that don¡¯t hit the database, don¡¯t touch the file system. They¡¯re fast, but
they don¡¯t integrate all pieces together.
For the context of this book, our distinction is based on the stability of the tests. Refactoring is
a process of transforming the code, while not changing the overall behaviour. With the practices
described in this book, you will change the existing structure. New classes will be extracted, some
code will be inlined.
System tests are the stable tests - they are not meant to change, no matter how you change the
internals. No matter how differently the code will look like, at the end some database records are
created or some html is returned. This is not going to change, unless we consciously decide that
changing it is a good idea.
There¡¯s a common misconception, that it¡¯s expected to change unit tests, when the code changes. In
fact, this is an anti-pattern. An especially suspicious pattern is to have the test structure reflect the
production code, by overusing the should_receive-like calls.
It¡¯s important to note, that System Tests and Unit Tests have different goals, thus they have different
rules.
A good goal is to have a few System Tests, that cover the integration. They¡¯re slow, so it¡¯s better to
limit the number of them. Unit Tests are super fast and can cover all of the possible scenarios.

                                                         160
Good tests tell a story.

The most readable tests are the ones that resemble the actions which a user is doing. The use case is
a set of actions. You can test each action in isolation, but they don¡¯t tell a good story this way.
I often see tests, that run a single System operation and then check the internals to see if all is good.
We don¡¯t have the confidence that the action runs correctly, when it runs in production. Even if we
check that the product is added to the cart in the database in a specific way, we can¡¯t be sure if it¡¯s
retrieved in a way that¡¯s compatible.
The solution is to test with whole scenarios:

   1. User adds a product to the cart
   2. User looks at the cart to see the current total amount
   3. User changes the amount
   4. User goes to checkout

                                                         161
Unit tests vs class tests

There¡¯s a popular way of thinking that unit tests are basically tests for classes.
I¡¯d like to challenge this understanding.
When I work on a codebase that is heavily class-tested, I find it harder to do refactoring. If all I want
is to move some methods from one class to another, while preserving how the whole thing works,
then I need to change at least two tests, for both classes.

Class tests slow me down

Class tests are good if you don¡¯t do refactoring or if most of your refactorings are within 1 class.
This may mean, that once you come up with a new class, you know the shape of it.
I like a more light-weight approach. Feel free to create new classes, feel free to move the code
between them as easily as possible. It doesn¡¯t mean I¡¯m a fan of breaking the functionalities. Totally
the opposite. I feel paralysed, when I have to work on untested code. My first step in an unknown
codebase is to check how good is the test coverage.
How to combine such light-weight refactoring style with testing?

Test units, not classes

I was in the ¡°let¡¯s have a test file per a class¡± camp for a long time. If I created a OrderItem class, it
would probably have an equivalent OrderItemTest class. If I had a FriendPresenter, it would have a
FriendPresenterTest.
With this approach, changing any line of code, would result in failing tests.
Is that really a safety net?
It sounds more like cementing the existing design. It¡¯s like building a wall in front of the code. If
you want to change the code, you need to rebuild the wall.
In a team, where collective ownership is an accepted technique, this may mean that whoever first
works on the module, is also the one who decides on the structure of it. It¡¯s not really a conscious
decision. It¡¯s just a result of following the class-tests approach. Those modules are hard to change.
They often stay in the same shape, even when the requirement change. Why? Because it¡¯s so hard
to change the tests (often full of mocks). Sounds familiar?
What¡¯s the alternative?

                                                         162
Unit tests vs class tests  163

The alternative is to think in units, more than in classes. What¡¯s a unit? I already touched on this
subject in TDD and Rails - what makes a good unit?. Let me quote the most important example:

You¡¯ve got an Order, which can have many OrderLines, a ShippingAddress and a Customer.

Do we have 4 units here, representing each class? It depends, but most likely it may be easier to treat
the whole thing as a Unit. You can write a test which test the whole thing through the Order object.
The test is never aware of the existence of the ShippingAddress. It¡¯s an internal implementation
detail of the Order unit.

A class doesn¡¯t usually make a good Unit, it¡¯s usually a collection of classes that is interesting.

The Billing example

In one of our projects, which is a SaaS service, we need to handle billing, paying, licenses. We¡¯ve put
it in one module. (BTW, the ¡®module¡¯ term is quite vague nowadays, as well). It has the following
classes:

     Billing (the facade)
     Subscription
     License
     Purchase
     Pricing
     PurchasingNotEnoughLicenses
     BillingDB
     BillingInMemoryDB
     BillingNotificationAdapter
     ProductSerializer

It¡¯s not a perfect piece code (is there any in the world?), but it¡¯s a good example for this topic. We¡¯ve
got about 10 classes. How many of them have their own test? Just the Billing (the facade). What¡¯s
more, in the tests we don¡¯t reference and use any of those remaining classes. We test the whole
module through the Billing class. The only other class, that we directly reference is a class, that
doesn¡¯t belong to this module, which is more of a dependency (shared kernel). Obviously, we also
use some stdlib classes, like Time.

BTW, did you notice, how nicely isolated is this module? It uses the payment/billing domain
language and you can¡¯t really tell for what kind of application it¡¯s designed for. In fact, it¡¯s not
unlikely that it could be reused in another SaaS project. To be honest, I¡¯ve never been closer to
reusing certain modules between Rails apps, than with this approach. The reusability wasn¡¯t
the goal here, it¡¯s a result of following good modularisation.

Some requirements here include:

   http://andrzejonsoftware.blogspot.com/2014/04/tdd-and-rails-what-makes-good-unit.html
Unit tests vs class tests                 164

 licences for multiple products
 changing licences within a certain date
 terminating licenses
 license counter

It¡¯s nothing really complicated - just an example.

What do I gain, by having the tests for the whole unit, instead of per-class?

I have the freedom of refactoring - I can move some methods around and as long as it¡¯s correct,
the tests pass. I tend to separate my coding activities - when I¡¯m adding a new feature, I¡¯m test-
driven. I try to pass the test in the simplest way. Then I¡¯m switching to refactoring-mode. I¡¯m no
longer adding anything new, I¡¯m just trying to find the best structure, for the current needs. It¡¯s
about seconds/minutes, not hours. When I have a good shape of the code, I can go to implement the
next requirement.

I can think about the whole module as a black-box. When we talk about Billing in this project,
we all know what we mean. We don¡¯t need to go deeper into the details, like licenses or purchases.
Those are implementation details.

When I add a new requirement to this module, I can add it as a test at the higher-level scope. When
specifying the new test, I don¡¯t need to know how it¡¯s going to be implemented. It¡¯s a huge win,
as I¡¯m not blocked with the implementation structure yet. Writing the test is decoupled from the
implementation details.

Other people can enter the module and clearly see the requirements at the higher level.

Now, would I see value in having a test for the Pricing class directly? Having more tests is good,
right? Well, no - tests are code. The more code you have the more you need to maintain. It makes
a bigger cost. It also builds a more complex mental model. Low-level tests are often causing more
troubles than profit.

Let me repeat and rephrase - by writing low-level tests, you may be damaging the project.
Techniques

Service objects as a way of testing Rails apps (without

factory_girl)

There¡¯s been recently an interesting discussion about setting up the initial state of your tests. Some
are in favor of using built-in Rails fixtures (because of speed and simplicity). Others are in favor of
using factory_girl or similar gems. I can¡¯t provide definite numbers but judging based on the apps
that we review, in terms of adoption, factory_girl seems to have won.

I would like to present you a third alternative ¡°Setting up tests with services¡± (the same ones you
use in your production code, not ones crafted specifically for tests) and compare it to factory_girl to
show where it might be beneficial to go with such approach.

Let¡¯s start with a little background from an imaginary application for teaching languages in schools.

There is a school in our system which decided to use our software and buy a license. Teacher can
create classes to teach a language (or use existing one created by someone else). During the procedure
multiple pupils can be imported from file or added manually on the webui. The teacher will be
teaching a class. The school is having a native language and the class is learning a foreign language.
Based on that we provide them with access to school dictionaries suited to kids¡¯ needs.

Everything is ok

Let¡¯s think about our tests for a moment.

1 #!ruby         { create(:school, native_language: "en") }
2 let!(:school)  { create(:klass, school: school) }
3 let!(:klass)   { create(:pupil, klass: klass) }
4 let!(:pupil)

5

6 let!(:teacher) { create(:teacher,
7 school: school,
8 languages: %w(fr it),
9 )}

10

11 let!(:dictionary) { create(:dictionary,
12 native_language: "en",
13 learning_language: "fr",
14 ) }

15

16 let!(:assignment) { create(:assignment,

                                            165
    Techniques                                           166

17  klass:      klass,

18  teacher: teacher,

19 dictionary: dictionary,

20 ) }

21

22

23 specify "pupil can learn from class dictionaries" do

24  expect(

25      teaching.dictionaries_for(pupil.id)

26 ).to include(dictionary)

27 end

    So far so good. Few months pass by, we have more tests we setup like that or in a similar way
    and then we start to stumble upon more of the less common usecases during the conversations
    with our client. And as it always is with such features, they force use to rethink the underlying
    architecture of our app.

    One of our new requirements is that when teacher is no longer assigned to a class this doesn¡¯t
    mean that a class is not learning the language anymore. In other words in our domain once pupils
    are assigned to a class that is learning French it is very unlikely that at some point they stopped
    learning French (at least in that educational system which domain we are trying to reflect in our
    code). It might be that the class no longer has a french teacher for a moment (ex. before the school
    finds replacement for her/him) but that doesn¡¯t mean they no longer learn French.

    Because we try to not delete data (soft delete all the way) we could have keep getting this
    knowledge about dictionaries from Assignments. But since we determined very useful piece of
    knowledge domain (the fact of learning a language is not directly connected to the fact
    of having teacher assigned) we decided to be explicit about it on our code. So we added new
    KlassLanguage class which is created when a class is assigned a new language for the first time.

    You don¡¯t even know what hit you

    We changed the implementation so it creates KlassLanguage whenever necessary. And we changed
    #dictionaries_for method to obtain the dictionaries from KlassLanguage instead of Assignment.
    We migrated old data. We can click through our webapp and see that everything works
    correctly. But guess what. Our tests fail. Why is that so?

    Our tests fail because we must add one more piece of data to them. The KlassLanguage that we
    introduced.
    Techniques                                             167

1 #!ruby
2 let!(:klass_language) { create(:klass_language,
3 klass: klass,
4 dictionary: dictionary,
5 )}

   Imagine adding that to dozens or hundred tests that you already wrote. No fun. It would be as if
   almost all those tests that you wrote discouraged you from refactorings instead of begin there
   for you so you can feel safely improving your code.

   Consider that after introducing our change to code, some tests are not even properly testing what
   they used to test. Like imagine you had a test like this:

1 #!ruby

2

3 let!(:assignment) { create(:assignment,

4   klass:      klass,

5 teacher: teacher,

6 dictionary: french_dictionary

7 )}

8

9 specify "pupil cannot learn from other dictionaries" do

10  expect(

11      teaching.dictionaries_for(pupil.id)

12 ).not_to include(german_dictionary)

13 end

    This test doesn¡¯t even make sense anymore because we no longer look for the dictionaries that are
    available for a pupil in Assignments but rather in KlassLanguages in our implementation.

    When you have hundreds of factory_girl-based test like that they are (imho) preventing you from
    bigger changes to your app. From making changes to your db structure, from moving the logic
    around. It¡¯s almost as if every step you wanna make in a different direction was not permitted.

    We draw parallel

    Before we tackle our problem let¡¯s for a moment talk about basics of TDD and testing. Usually
    when they try to teach you testing you start with simple data structure such as Stack and you try
    to implement it using existing language structure and verify its correctness.
    Techniques                                                            168

1 #!ruby

2

3 class Stack

4 Empty = Class.new(StandardError)

5

6 def initialize

7       @collection = []

8   end

9

10  def push(obj)

11      @collection.push(obj)

12  end

13

14  def pop

15      @colllection.empty? and raise Empty

16      @collection.pop

17  end

18 end

    So you put something on the stack, you take it back and you verify that it is in fact the same thing.

1 #!ruby

2

3 describe Stack do
4 subject(:stack) { described_class.new }
5 specify "last put element is first to pop" do

6       stack.push(pushed = Object.new)

7       expect(popped = stack.pop).to eq(pushed)

8   end

9 end

    Why am I talking about this?

    Because I think that what many rails projects started doing with factory_girl is no longer
    similar to our basic TDD technique.

    I cannot but think we started to turn our test more into something like:

1 #!ruby

2

3 describe Stack do

4 subject(:stack) { described_class.new }

5 specify "last put element is first to pop" do

6       stack.instance_variable_set(:@collection, [pushed = Object.new])

7       expect(popped = stack.pop).to eq(pushed)

8   end

9 end
    Techniques                                                      169

    So instead of interacting with our SUT (System under Test) through set of exposed methods we
    violate its boundaries and directly set the state. In this example this is visible at first sight because
    we use instance_variable_set and no one would do such thing in real life. Right?

    But the situation with factories is not much different in fact from what we just saw. Instead of
    building the state through set of interactions that happened to system we tried to build the state
    directly.

    With factories we build the state as we know/imagine it to be at the very moment of writing the test.
    And we rarely tend to revisit them later with the intent to verify the setup and fix it. Given
    enough time it might be even hard to imagine what sequence of events in system the original test
    author imagined leading to a state described in a test.

    This means that we are not protected in any way against changes to the internal implementation that
    happen in the future. Same way you can¡¯t just rename @collection in the stack example because
    the test is fragile.

    In other words, we introduced a third element into Command/Query separation model for our
    tests. Instead of issuing Commands and testing the result with Queries we issue commands and test
    what¡¯s in db. And for Queries we set state in db and then we run Queries. But we usually have no
    way to ensure synchronization of those test. We are not sure that what Commands created is the
    same for what we test in Queries.

    You take revenge

    What can we do to mitigate this unfortunate situation? Go back to the basic and setup our tests
    by directly interacting with the system instead of building its state. In case of our original school
    example it might look like.

1 #!ruby

2

3 registration = SchoolRegistration.new

4 registration.call(SchoolRegistration::Input.new.tap do |i|

5 i.school_attributes = attributes(:school, native_language: "en")

6 i.teacher_attributes = teacher_attributes = attributes(:teacher,

7      id: "f154cc85-0f0d-4c5a-9be1-f71aa217b2c0",

8      languages: %w(fr it)

9   )

10 end)

11

12 class_creation = ClassCreation.new

13 class_creation.call(ClassCreation::Input.new.tap do |i|

14 i.id = "5c7a1aa9-72ca-46b2-bf8c-397d62e7db19"

15  i.klass_number = "1"

16  i.klass_letter = "A"

    http://ruby-doc.org/core-2.1.2/Object.html
    /assets/sounds/right.mp3
    Techniques                                            170

17  i.klass_pupils = [{

18     id: "6d805bdd-79ff-4357-88cc-45baf103965a",

19     first_name: "John",

20     last_name: "Doe",

21  }]

22 end)

23

24 assignment = ClassAssignment.new

25 assignment.call(ClassAssignment::Input.new.tap do |i|

26 i.klass_id = "5c7a1aa9-72ca-46b2-bf8c-397d62e7db19"

27 i.teacher_id = teacher_attributes.id

28 i.learning_language = "fr"

29 end)

    This setup is way longer because in some places we decided to go with longer syntax and set some
    attribute by hand (although) we didn¡¯t have to. This example mixes two approaches so you can see
    how you can do things longer-way and shorter-way (by using attributes). We didn¡¯t take a single
    step to refactor it into shorter expression and to be more reusable in multiple tests because I wanted
    you to see a full picture of it. But extracting it into smaller test helpers, so that the test setup
    would be as short and revealing in our factory girl example would be trivial. For now let¡¯s keep
    focus on our case.

    What can we see from this test setup? We can see the interactions that led to the state of the
    system. There were 3 of them and are similar to how I described the entire story for you. First teacher
    registered (first teacher creates the school as well and can invite the rest of the teachers). Teacher
    created a class with pupils (well, one pupil to be exact). Teacher assigned the class to himself/herself
    as a French teacher.

    It¡¯s the last step implementation that we had to change to for our new feature. It had to store
    KlassLanguage additionally and required our tests to change, which we didn¡¯t want to.

    It doesn¡¯t have to be all about DB.

    Let¡¯s recall our test:

1 #!ruby

2

3 specify "pupil can learn from class dictionaries" do

4   expect(

5      teaching.dictionaries_for(pupil.id)

6 ).to include(dictionary)

7 end

    I didn¡¯t tell you what teaching was in our first version of the code. It doesn¡¯t matter much for our
    discussion or to see the point of our changes but let¡¯s think about it for a moment. It had to be
   Techniques                          171

   some kind of Repository object implementing #dictionaries_for method. Or a Query object.
   Something definitely related and coupled to DB because we set the state with factories deep down
   creating AR objects.

   It can be the same in our last example. But it doesn¡¯t have to! All those services can build and store
   AR objects and communicate with them and teaching would be just a repository object querying
   the db for dictionaries of class that the pupil is in. And that would be fine.

   But teaching could be a submodule of our application that the services are communicating with.
   Maybe the key Commands/Services in our system communicate with multiple modules such as
   Teaching, Accounting, Licensing and in this test we are only interested in what happened in one
   of them. So we could stub other dependencies except for teaching if they were explicitly passed in
   constructor.

1 #!ruby

2 teaching = Teaching.new

3 class_creation = ClassCreation.new(

4   teaching,

5 double(:accounting),

6 double(:licensing)

7)

   So with this kind of test setup you are way more flexible and less constrained. Having data in db is
   no longer your only option.

   TL;DR;

   In some cases you might wanna consider setting up the state of your system using Services/Com-
   mands instead of directly on DB using factory_girl. The benefit will be that it will allow you to more
   freely change the internal implementation of your system without much hassle for changing
   your tests.

   For me one of the main selling points for having services is the knowledge of what can happen in
   my app. Maybe there are 10 things that the user can do in my app, maybe 100 or maybe 1000. But
   I know all of them and I can mix and match them in whatever order I wish to create the setup of
   situation that I wish to test. It¡¯s hard to set incorrect state that way that would not have happened
   in your real app, because you are just using your production code.

      http://martinfowler.com/eaaCatalog/repository.html
      http://martinfowler.com/eaaCatalog/queryObject.html
Related topics

                                                         172
Service controller communication

The communication between two objects can happen in many different ways. Here we review the
possible ways that are especially possible in the communication between a controller and a service.

     True/false

This is the technique used in ActiveRecord. You call the .save method and it returns true/false. In
case of ¡®false¡¯ the client need to call additional methods (.errors) to understand what exactly went
wrong. This approach is good for simple cases. It suffers from breaking the ¡®Tell, don¡¯t Ask¡± rule.
With a Service Object, we don¡¯t operate on the AR model directly, we need to expose the model if
it was created, through an accessor.

     return the object created/updated

This can be used together with the technique above or just return nil, when things went wrong.

     return a response object that contains all the data and/or errors

An example object can be called UserCreationResponse and may contain the user object. It can have
methods like ¡®successful?¡¯, ¡®failed?¡¯ that help the controller code understand the result.

     carry data through exceptions

This is the most ¡°Tell, don¡¯t ask¡± approach, however it introduces a protocol based on additional
(exceptions) classes. Many people worry about the performance of exceptions. However, if we raise
exceptions only in the unhappy paths, it¡¯s less likely to impact the performance.

     controller passes callback methods

This approach is characterised, by passing ¡®self¡¯ to the service object, which uses a small ¡°interface¡±
to expect methods like ¡°success¡±, ¡°failure¡± etc.

                                                         173
 Naming Conventions

   When you create a service object you need to decide on the name of the class and on the name of
   the main method.
   I¡¯ve seen the following naming conventions for the name of the class:

         RegisterUserService
         RegisterUserUseCase
         RegisterUser
         UserRegistrator

   When it comes to the method names, the following names are popular:

         execute
         process
         call
         perform
         run
         custom name, like ¡®register¡¯

  The special .call method

   There¡¯s an interesting situation with the special .call method. When you have a .call method, then
   you can call it like this:

1 RegisterUser.new.call(foo, bar)

   or like this:

1 RegisterUser.new.(foo, bar)

   which is a bit shorter, but may be surprising for people unaware of this special syntax case.

                                                             174
Where to keep services

When you just start with services, I¡¯d recommend keeping them in the app/models directory. Just to
keep things simpler. Once you become more familiar with this concept, you may experiment with
other places:
Physical location

     app/models
     app/services
     app/feature_name/
     lib/feature_name/

The app/services location seems to be most popular in Rails apps. What I like to experiment with, is
to create a highest-level directory that contains the whole world of code related to one part of the
app:

     time_tracking/models
     time_tracking/services

while leaving the core app in its ¡®app¡¯ directory.
Even more important than the physical location is the proper usage of namespaces.
Namespaces

     FeatureName::Service_1
     global

I think it¡¯s a good idea to group your services according to the feature and then surround them with
a proper namespace, like:

     TimeTracking::LogTimeService
     Reporting::MonthlyReport
     Forum::CreateNewTopic

                                                         175
   Routing constraints

   Routing constraints is a relatively less known Rails feature. Its basic usage is to define certain
   requirements which are defined along the routes.

   Now, those requirements may vary. When I did my research I asked many experienced Rails
   developers where they use it. This is the list:

    Different page for guests/logged in
    Slugs validation
    Authentication
    Authorization
    Subdomains in SaaS apps
    Determine the right action based on a certain param

   You can do most of that with filters, however in some cases routing constraints may be better.

   My personal preference is to use routing constraints for situations where you need to choose different
   actions based on some params. This makes the controller thinner and it¡¯s more explicit what action
   is happening.

   In one of the refactorings before, we ended with the following code:

1  def create

2  if issue_id.present?

3       log_time_on_issue

4  else

5       log_time_on_project

6  end

7  end

   This a ¡®create¡¯ action, which is responsible for two different business procedures. I think, that a
   controller action should handle just one type of a business procedure.

   We could move this code to the routing constraint and create two different controller actions,
   explicitly called: log_time_on_issue and log_time_on_project with their appropriate service objects.

   I¡¯ve found this pattern useful recently in two situations.

                             176
Routing constraints                                                                   177

 Refactoring controller without touching frontend

  Similarly, when you are in power over backend but not in power over frontend in your
  application. You notice that a certain action should be split in two. But you lack the power to
  force such split on callers of your application. It might be that another colleague or company
  is working on the Javascript frontend or mobile frontend. They will change their code as well
  in the future probably. But in the meantime you have to make your changes compatible with
  existing code.

 No power over incoming webhook

  Say your controller is called by incoming webhook from a 3rd party app. In such cases we
  usually have very little configuration options. Some payment gateways allow you to configure
  differnt URLs for successful and failed transactions. But in many other cases data will be sent
  always to the same URL exposed by your app. So no matter what type of webhook notification
  you receive it is hitting the same controller. The can by annoying and lead to complicated code.

Resources

     Inside book - Technique: Extract routing constraint
     How to use Rails route constraints
     Using Routing Constraints to Root Your App
     Pretty, short urls for every route in your Rails app

http://blog.8thlight.com/ben-voss/2013/01/12/how-to-use-rails-route-constraints.html
http://viget.com/extend/using-routing-constraints-to-root-your-app
http://blog.arkency.com/2014/01/short-urls-for-every-route-in-your-rails-app/
   Rails controller - the lifecycle

   It¡¯s important to understand, how a controller gets called in a request lifetime and how it accesses
   the views.

   When a request comes, the routes engine is called with:

1 # rails/railties/lib/rails/engine.rb

2 def routes

3 @routes ||= ActionDispatch::Routing::RouteSet.new

4 @routes.append(&Proc.new) if block_given?

5  @routes

6 end

   Afterwards, the right route is found and the controller action is called in an unusual way:

1 # rails/actionpack/lib/action_dispatch/routing/route_set.rb
2 def dispatch(controller, action, env)
3 controller.action(action).call(env)
4 end

   The controller parameter is a class reference (like ProductsController). The action parameter is
   a symbol that represents the action name (like :index).

   This is what it looks like, in the runtime:

1 ProductsController.action(:index).call(env)

   What that means is that the action is treated as a Proc here. In fact it¡¯s trying to behave like a Rack
   app (with the usual call method).

   What happens afterwards?

1 # rails/actionpack/lib/action_controller/metal.rb

2 def self.action(name, klass = ActionDispatch::Request)

3 middleware_stack.build(name.to_s) do |env|

4      new.dispatch(name, klass.new(env))

5  end

6 end

   This is the place, where the controller gets initialized (via the .new call). A controller instance is
   created and the dispatch method is called.

   It goes to:

                                               178
    Rails controller - the lifecycle                                                              179

1 # rails/actionpack/lib/action_controller/metal/rack_delegation.rb
2 def dispatch(action, request)
3 set_response!(request)
4 super(action, request)
5 end

    Here, a new instance of a Response class is created. The super method looks like this:

1 # rails/actionpack/lib/action_controller/metal.rb

2 def dispatch(name, request)

3 @_request = request

4 @_env = request.env

5 @_env['action_controller.instance'] = self

6 process(name)

7   to_a

8 end

    The to_a call at the end is responsible for returning the [status, headers, response_body]
    collection that is compatibile with Rack interface.

    Now, the process call goes to:

1 # rails/actionpack/lib/abstract_controller/base.rb
2 def process(action, *args)
3 @_action_name = action_name = action.to_s

4

5 unless action_name = method_for_action(action_name)

6       raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"

7   end

 8

 9 @_response_body = nil
10 process_action(action_name, *args)
11 end

    After basic validation, the flow now goes to:

1 # rails/actionpack/lib/abstract_controller/callbacks.rb

2 module AbstractController

3 module Callbacks

4       include ActiveSupport::Callbacks

5

6       def process_action(*args)

7        run_callbacks(:process_action) do

8            super

9        end

10      end

11

12  end

13 end
    Rails controller - the lifecycle                                             180

    This just wraps the action with the callbacks (filters are callbacks, too).
    Let¡¯s look at how the callbacks work:

1       def run_callbacks(kind, &block)

2        cbs = send("_#{kind}_callbacks")

3        if cbs.empty?

4            yield if block_given?

5        else

6            runner = cbs.compile

7            e = Filters::Environment.new(self, false, nil, block)

8            runner.call(e).value

9        end

10      end

    Then main flow then goes to:

1 # rails/actionpack/lib/abstract_controller/base.rb
2 def process_action(method_name, *args)
3 send_action(method_name, *args)
4 end

    which leads to:

1 # rails/actionpack/lib/action_controller/metal/implicit_render.rb

2 module ActionController

3 module ImplicitRender

4

5       def send_action(method, *args)

6        ret = super

7        default_render unless response_body

8        ret

9       end

10

11  ...

12

13  end

14 end

    Now, you can see why we don¡¯t need to call render directly. It¡¯s handled here with the default_-
    render call.

    BTW, the send_action is just an alias for send, so this is where the story ends. In our case, the
    controller.index method/action would get called.

    Accessing instance variables in the view

    It¡¯s worth learning, how the ¡°magic¡± works, so that the @ivars set in the controllers are automatically
    available in the views.

    It starts with collecting controller instance variables:
   Rails controller - the lifecycle                                                          181

1 # actionpack/lib/abstract_controller/rendering.rb

2 def view_assigns

3  hash = {}

4 variables = instance_variables

5 variables -= protected_instance_variables

6 variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES

7 variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }

8  hash

9 end

   which are then passed to the view:

1 def view_context
2 view_context_class.new(view_renderer, view_assigns, self)
3 end

   and then, they are all set in the view:

1 # actionpack/lib/action_view/base.rb
2 def assign(new_assigns) # :nodoc:
3 @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
4 end

   Resources

        Rails from Request to Response: Part 2 - Routing
        Rails from Request to Response: Part 3 - ActionController

   http://andrewberls.com/blog/post/rails-from-request-to-response-part-2--routing
   http://andrewberls.com/blog/post/rails-from-request-to-response-part-3--actioncontroller
Appendix

                                                         182
Thank you

All feedback is welcome.
Andrzej Krzywda
andrzej@arkency.com

                                                         183
Bonus

                                                         184
    Thanks to repositories¡­

    by Piotr Macuk

    I am working in Arkency for 2+ months now and building a tender documentation system for our
    client. The app is interesting because it has a dynamic data structure constructed by its users. I would
    like to tell you about my approaches to the system implementation and why the repository pattern
    allows me to be more safe while data structure changes.

    System description

    The app has users with its tender projects. Each project has many named lists with posts. The post
    structure is defined dynamically by the user in project properties. The project property contains
    its own name and type. When the new project is created it has default properties. For example:
    ProductId(integer), ElementName(string), Quantity(float) Unit(string), PricePerUnit(price). User can
    change and remove default properties or add custom ones (i.e. Color(string)). Thus all project posts
    on the lists have dynamic structure defined by the user.

    The first solution

    I was wondering the post structure implementation. In my first attempt I had two tables. One
    for posts and one for its values (fields) associated with properties. The database schema looked
    as follows:

1 #!ruby
2 create_table "properties" do |t|
3 t.integer "project_id", null: false
4 t.string "english_name"
5 t.string "value_type"
6 end

7

8 create_table "posts" do |t|

9 t.integer "list_id",           null: false

10 t.integer "position", default: 1, null: false

11 end

12

13 create_table "values" do |t|

14 t.integer "post_id",        null: false

15 t.integer "property_id", null: false

16  t.text  "value"

17 end

                                                  185
   Thanks to repositories¡­                                                                   186

   That implementation was not the best one. Getting data required many SQL queries to the database.
   There were problems with performance while importing posts from large CSV files. Also large posts
   lists were displayed quite slow.

   The second attempt

   I have removed the values table and I have changed the posts table definition as follows:

1 #!ruby

2 create_table "posts" do |t|

3 t.integer "list_id",            null: false

4 t.integer "position", default: 1, null: false

5  t.text  "values"

6 end

   Values are now hashes serialized in JSON into the values column in the posts table.

   The scary solution

   In the typical Rails application with ActiveRecord models placed all around that kind of change
   involve many other changes in the application code. When the app has some code that solution is
   scary :(

   But I was lucky :) At that time I was reading the Fearless Refactoring Book by Andrzej Krzywda
   and that book inspired me to prepare data access layer as a set of repositories. I have tried to cover all
   ActiveRecord objects with repositories and entity objects. Thanks to that approach I could change
   database structure without pain. The changes was only needed in database schema and in PostRepo
   class. All application logic code stays untouched.

   The source code

   ActiveRecords

   Placed in app/models. Used only by repositories to access the database.

   http://rails-refactoring.com/
    Thanks to repositories¡­           187

1 #!ruby

2 class Property < ActiveRecord::Base

3 belongs_to :project

4 end

5

6 class List < ActiveRecord::Base

7 belongs_to :project

8 has_many :posts

9 end

10

11 class Post < ActiveRecord::Base

12  belongs_to :list

13 serialize :values, JSON

14 end

    Entities

    Placed in app/entities. Entities are simple PORO objects with Virtus included. These objects are
    the smallest system building blocks. The repositories use these objects as return values and as input
    parameters to persist them in the database.

 1 #!ruby
 2 class PropertyEntity
 3 include Virtus.model

 4

 5 attribute :id, Integer
 6 attribute :symbol, Symbol
 7 attribute :english_name, String
 8 attribute :value_type, String
 9 end

10

11 class ListEntity
12 include Virtus.model

13

14 attribute :id, Integer
15 attribute :name, String
16 attribute :position, Integer
17 attribute :posts, Array[PostEntity]
18 end

19

20 class PostEntity
21 include Virtus.model

22

23 attribute :id, Integer
24 attribute :number, String # 1.1, 1.2, ..., 2.1, 2.2, ...
25 attribute :values, Hash[Symbol => String]
26 end
    Thanks to repositories¡­                                        188

    Post repository

    Placed in app/repos/post_repo.rb. PostRepo is always for single list only. The API is quite small:

     all ¨C get all posts for the given list,
     load ¨C get single post by its id from the given list,
     create ¨C create post in the list by given PostEntity object,
     update ¨C update post in the list by given PostEntity object,
     destroy ¨C destroy post from the list by its id.

    The properties array is given in initialize parameters. Please also take a note that ActiveRecord don¡¯t
    leak outside the repo. Even ActiveRecord exceptions are covered by the repo exceptions.

1 #!ruby
2 class PostRepo
3 ListNotFound = Class.new(StandardError)
4 PostNotUnique = Class.new(StandardError)

5 PostNotFound = Class.new(StandardError)

6

7 def initialize(list_id, properties)

8   @list_id = list_id

9   @ar_list = List.find(list_id)

10  @properties = properties

11 rescue ActiveRecord::RecordNotFound => error

12  raise ListNotFound, error.message

13  end

14

15  def all

16  ar_list.posts.order(:position).map do |ar_post|

17       build_post_entity(ar_post)

18  end

19  end

20

21  def load(post_id)

22  ar_post = find_ar_post(post_id)

23  build_post_entity(ar_post)

24  end

25

26  def create(post)

27  fail PostNotUnique, 'post is not unique' if post.id

28  next_position = ar_list.posts.maximum(:position).to_i + 1

29  attributes = { position: next_position, values: post.values }

30  ar_post = ar_list.posts.create!(attributes)

31  ar_post.id

32  end

33

34  def update(post)

35  ar_post = find_ar_post(post.id)
    Thanks to repositories¡­                                                      189

36      ar_post.update!(values: post.values)

37      nil

38  end

39

40 def destroy(post_id)

41      ar_post = find_ar_post(post_id)

42      ar_post.destroy!

43      ar_list.posts.order(:position).each_with_index do |post, idx|

44       post.update_attribute(:position, idx + 1)

45      end

46      nil

47  end

48

49  private

50

51 attr_reader :ar_list, :properties

52

53 def find_ar_post(post_id)

54      ar_list.posts.find(post_id)

55 rescue ActiveRecord::RecordNotFound => error

56      raise PostNotFound, error.message

57  end

58

59 def build_post_entity(ar_post)

60      number = "#{ar_list.position}.#{ar_post.position}"

61      values_hash = {}

62      if ar_post.values

63       properties.each do |property|

64           values_hash[property.symbol] = ar_post.values[property.symbol.to_s]

65       end

66      end

67      PostEntity.new(id: ar_post.id, number: number, values: values_hash)

68  end

69 end

    Sample console session

1 #!ruby

2 # Setup

3 > name = PropertyEntity.new(symbol: :name,

4                             english_name: 'Name',

5                             value_type: 'string')

6 > age = PropertyEntity.new(symbol: :age,

7                             english_name: 'Age',

8                             value_type: 'integer')

9 > properties = [name, age]

10

11 > post_repo = PostRepo.new(list_id, properties)

12

13 # Post creation
    Thanks to repositories¡­                                              190

14 > post = PostEntity.new(values: { name: 'John', age: 30 })

15 => #<PostEntity:0x00000006ae93f8 @values={:name=>"John", :age=>"30"},

16  => #                               @id=nil, @number=nil>

17 > post_id = post_repo.create(post)

18  => 3470

19

20 # Get single post by id (notice that the number is set by the repo)

21 > post = post_repo.load(post_id)

22 => #<PostEntity:0x00000005e52248 @values={:name=>"John", :age=>"30"},

23  => #                               @id=3470, @number="1.1">

24

25 # Get all posts from the list
26 > posts = post_repo.all
27 => [#<PostEntity:0x00000005eba0a0 ...]

28

29 # Post update

30 > post.values = { age: 31 }

31 => {:age=>31}

32 > post_repo.update(post)

33  => nil

34 > post = post_repo.load(post_id)

35 => #<PostEntity:0x00000005ffc828 @values={:name=>nil, :age=>"31"},

36  => #                               @id=3470, @number="1.1">

37

38 # Post destroy

39 > post_repo.destroy(post_id)

40  => nil
Pretty, short urls for every route in
your Rails app

One of our recent project had the requirement so that admins are able to generate short top level
urls (like /cool) for every page in our system. Basically a url shortening service inside our app. This
might be especially usefull in your app if those urls are meant to appear in printed materials (like
/productName or /awesomePromotion). Let¡¯s see what choices we have in our Rails routing.

Top level routing for multiple resources

If your requirements are less strict, you might be in a better position to use a simpler solution. Let¡¯s
say that your current routing rules are like:

1 #!ruby                              authors#show
2 resources :authors                  posts#show
3 resources :posts

4

5 #author GET /authors/:id(.:format)
6 # post GET /posts/:id(.:format)

We assume that :id might be either resource id or its slug and you handle that in your controller
(using friendly_id gem or whatever other solution you use).

And you would like to add route like:

1 #!ruby
2 match '/:slug'

that would either route to AuthorsController or PostController depending on what the slug points
to. Our client wants Pretty Urls:

1 /rails-team
2 /rails-4-0-2-have-been-released

Well, you can solve this problem with constraints.

                                      191
   Pretty, short urls for every route in your Rails app  192

1 #!ruby

2 class AuthorUrlConstrainer

3 def matches?(request)

4       id = request.path.gsub("/", "")

5       Author.find_by_slug(id)

6  end

7 end

 8

 9 constraints(AuthorUrlConstrainer.new) do
10 match '/:id', to: "authors#show", as: 'short_author'
11 end

1 #!ruby

2 class PostUrlConstrainer

3 def matches?(request)

4       id = request.path.gsub("/", "")

5       Post.find_by_slug(id)

6  end

7 end

8

9 constraints(PostUrlConstrainer.new) do

10 match '/:id', to: "posts#show", as: 'short_post'

11 end

   This will work fine but there are few downsides to such solution and you need to remember about
   couple of things.

   First, you must make sure that slugs are unique across all your resources that you use this for. In
   our project this is the responsibility of <%= service_landing_link(¡°services¡±) %> which first try to
   reserve the slug across the whole application, and assign it to the resource if it succeeded. But you
   can also implement it with a hook in your ActiveRecord class. It¡¯s up to you whether you choose
   more coupled or decoupled solution.

   The second problem is that adding more resources leads to more DB queries. In your example the
   second resource (posts) triggers a query for authors first (because the first constraint is checked first)
   and only if it does not match, we try to find the post. N-th resource will trigger N db queries before
   we match it. That is obviously not good.

   Render or redirect

   One of the thing that you are going to decide is whether visiting such short url should lead to
   rendering the page or redirection.
   What we saw in previous chapter gives us rendering. So the browser is going to display the visited
   url such as /MartinFowler . In such case there might be multiple URLs pointing to the same resource
   Pretty, short urls for every route in your Rails app                193

   in your application and for best SEO you probably should standarize which url is the canonical:
   /authors/MartinFowler or /MartinFowler/ ? Eventually you might also consider dropping the
   longer URL entirely in your app to have a consistent routing.

   You won¡¯t have such dillemmas if you go with redirecting so that /MartinFowler simply redirects
   to /authors/MartinFowler. It is not hard with Rails routing. Just change

1 #!ruby
2 constraints(AuthorUrlConstrainer.new) do
3 match '/:id', to: "authors#show", as: 'short_author'
4 end

   into

1 #!ruby

2 constraints(AuthorUrlConstrainer.new) do

3 match('/:id', as: 'short_author', to: redirect do |params, request|

4      Rails.application.routes_url_helpers.author_path(params[:id])

5  end)

6 end

   Top level routing for everything

   But we started with the requirement that every page can have its short version if admins generate
   it. In such case we store the slug and the path that it was generated based on in Short::Url class. It
   has the slug and target attributes.

1 #!ruby

2 class Vanity::Url < ActiveRecord::Base

3 validates_format_of  :slug, with: /\A[0-9a-z\-\_]+\z/i

4 validates_uniqueness_of :slug, case_sensitive: false

5

6  def action

7      [:render, :redirect].sample

8  end

9 end

10

11 url = Short::Url.new
12 url.slug = "fowler"
13 url.target = "/authors/MartinFowler"
14 url.save!

   Now our routing can use that information.

      https://support.google.com/webmasters/answer/139394?hl=en
    Pretty, short urls for every route in your Rails app                    194

1 #!ruby

2 class ShortDispatcher

3 def initialize(router)

4       @router = router

5   end

6 def call(env)

7       id   = env["action_dispatch.request.path_parameters"][:id]

8       slug = Short::Url.find_by_slug(id)

9       strategy(slug).call(@router, env)

10  end

11

12  private

13

14  def strategy(url)

15      {redirect: Redirect, render: Render }.fetch(url.action).new(url)

16  end

17

18  class Redirect

19      def initialize(url)

20          @url = url

21      end

22      def call(router, env)

23          to = @url.target

24          router.redirect{|p, req| to }.call(env)

25      end

26  end

27

28  class Render

29      def initialize(url)

30          @url = url

31      end

32      def call(router, env)

33          routing = Rails.application.routes.recognize_path(@url.target)

34          controller = (routing.delete(:controller) + "_controller").

35           classify.

36           constantize

37          action      = routing.delete(:action)

38          env["action_dispatch.request.path_parameters"] = routing

39          controller.action(action).call(env)

40      end

41  end

42 end

43

44 match '/:id', to: ShortDispatcher.new(self)

    You can simplify this code greatly (and throw away most of it) if you go with either render or redirect
    and don¡¯t mix those two approaches. I just wanted to show that you can use any of them.

    Let¡¯s focus on the Render strategy for this moment. What happens here. Assuming some visited
    /fowler in the browser, we found the right Short::Url in the dispatcher, now in our Render#call
    we need to do some work that usually Rails does for us.
Pretty, short urls for every route in your Rails app  195

   First we need to recognize what the long, target url (/authors/MartinFowler) points to.

1 #!ruby
2 routing = Rails.application.routes.recognize_path(@url.target)
3 # => {:action=>"show", :controller=>"authors", :id=>"1"}

   Based on that knowledge we can obtain the controller class.

1 #!ruby
2 controller = (routing.delete(:controller) + "_controller").classify.constantize
3 # => AuthorsController

   And we know what controller action should be processed.

1 #!ruby
2 action = routing.delete(:action)
3 # => "show"

   No we can trick rails into thinking that the actual parameters coming from recognized url were
   different

1 #!ruby
2 env["action_dispatch.request.path_parameters"] = routing
3 # => {:id => "MartinFowler"}

   If we generated the slug url based on nested resources path, we would have here two hash keys with
   ids, instead of just one.
   And at the and we create new instance of rack compatible application based on the #show() method
   of our controller. And we put everything in motion with #call() and pass it env (the Hash with
   Rack environment).

1 #!ruby
2 controller.action(action).call(env)
3 # AuthorsController.action("show").call(env)

   That¡¯s it. You delegated the job back to the rails controller that you already have had implemented.
   Great job! Now our admins can generate those short urls like crazy for the printed materials.

Is it any good?

Interestingly, after prooving that this is possible, I am not sure whether we should be actually doing
it  . What¡¯s your opinion? Would you rather render or redirect? Should we be solving this on
application level (render) or HTTP level (redirect) ?

   https://github.com/rails/rails/blob/64226302d82493d9bf67aa9e4fa52b4e0269ee3d/actionpack/lib/action_controller/metal.rb#L244
   http://rack.rubyforge.org/doc/SPEC.html
