Skip to content

Commit b467a00

Browse files
committed
Review styles for controller/models
1 parent 4e908de commit b467a00

File tree

1 file changed

+16
-35
lines changed

1 file changed

+16
-35
lines changed

STYLE.md

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -135,54 +135,35 @@ class SomeModule
135135
end
136136
```
137137

138-
## CRUD operations from controllers
139138

140-
In general, we favor a vanilla Rails approach to CRUD operations. We create and update models from Rails controllers passing the parameters directly to the model constructor or update method. We do not use services or form objects to handle these operations.
139+
## Controller and model interactions
141140

142-
There are exceptional scenarios where we need to perform more complex operations, and we use form objects or higher-level service methods to handle them. We use the same pattern for both creations and updates.
141+
In general, we favor a [vanilla Rails](https://dev.37signals.com/vanilla-rails-is-plenty/) approach with thin controllers directly invoking a rich domain model. In general, we don't use services or other artifacts to connect the two.
143142

144-
Related to this, we prefer to avoid [nested attributes](https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html). If you find yourself wanting to use `accepts_nested_attributes_for`, that's a good smell that you might want to consider using a form object instead.
145-
146-
As an example, you can check how we create and update messages in HEY's: `MessagesController`:
143+
Invoking plain Active Record operations is totally fine:
147144

148145
```ruby
149-
class MessagesController < ApplicationController
146+
class Cards::CommentsController < ApplicationController
150147
def create
151-
@entry = Entry.enter \
152-
new_message,
153-
on: new_topic,
154-
status: :drafted,
155-
address: entry_addressed_param,
156-
scheduled_delivery_at: entry_scheduled_delivery_at_param,
157-
scheduled_bubble_up_on: entry_scheduled_bubble_up_on_param
158-
159-
respond_to_saved_entry @entry
148+
@comment = @card.comments.create!(comment_params)
160149
end
150+
end
151+
```
161152

162-
def update
163-
previously_scheduled = @entry.scheduled_delivery
164-
165-
@entry.revise \
166-
message_params,
167-
status: :drafted,
168-
is_delivery_imminent: !entry_status_param.drafted?,
169-
address: entry_addressed_param,
170-
scheduled_delivery_at: entry_scheduled_delivery_at_param,
171-
scheduled_bubble_up_on: entry_scheduled_bubble_up_on_param
153+
For more complex behavior, we prefer clear, intention-revealing model APIs that controllers call directly:
172154

173-
respond_to_saved_entry(@entry, previously_scheduled: previously_scheduled)
155+
```ruby
156+
class Cards::GoldnessesController < ApplicationController
157+
def create
158+
@card.gild
174159
end
175160
end
161+
```
176162

177-
class Entry < ApplicationRecord
178-
def self.enter(*args, **kwargs)
179-
Entry::Enter.new(*args, **kwargs).perform
180-
end
163+
When justified, it is fine to use services or form objects, but don't treat those as special artifacts:
181164

182-
def revise(*args, **kwargs)
183-
Entry::Revise.new(self, *args, **kwargs).perform
184-
end
185-
end
165+
```ruby
166+
Signup.new(email_address: email_address).create_identity
186167
```
187168

188169
## Run async operations in jobs

0 commit comments

Comments
 (0)