Unnecessary Instance Variables
Note: This is part of the Production vs Tutorial Code Series.
Let’s revisit instance variables as a source of confusion.
Sometimes it doesn’t make sense to have a single memoized helper method. You want to set a bunch of instance variables all at once.
But is that really true?
What if, instead, there is a value object or process object hiding in your controller?
Consider this example:
class NotesController < ApplicationController
def create
if create_note
redirect_to note_path(@note)
else
render :new
end
end
private
def create_note
if current_user.can?(:write_rich_text)
@note = RichTextNote.new(note_params)
@editable_text = @note.markdown
@renderable_content = @note.html
else
@note = Note.new(note_params)
@renderable_content = @editiable_text = @note.text
end
@note.save
end
def note_params
params.require(:note).permit(:id, :user_id, :text)
end
end
Why set these @editable_text
and @renderable_content
instance variables in the controller?
It was more convenient to only check for rich text in the controller vs the view.
I see. What if Note and RichTextNote had a better shared interface?
class NoteDecorator < SimpleDelegator
def editable_text
text
end
def renderable_content
text
end
end
class RichTextNoteDecorator < SimpleDelegator
def editable_text
markdown
end
def renderable_content
html
end
end
Then in the controller
class NotesController < ApplicationController
def create
if new_note.save
redirect_to note_path(new_note)
else
render :new
end
end
protected
def new_note
@new_note ||=
if current_user.can?(:write_rich_text)
RichTextNoteDecorator.new(RichTextNote.new(note_params))
else
NoteDecorator(Note.new(note_params))
end
end
private
def note_params
return {} unless params.key?(:note)
params.require(:note).permit(:id, :user_id, :text)
end
end
There are a few changes here.
- We have extended the memoized
new_note
helper from an earlier refactor. It now decorates the note object and passes in optional form params. - We have made calling
note_params
safer, it can now be called fromnew
instead of justcreate
. - Because of these two changes,
create_note
would have been a single line (new_note.save
), so we inlined it intocreate
. - In the views, you can now safely call
new_note.editable_text
andnew_note.renderable_content
without worrying about extra instance variables.