Skip to content

Instantly share code, notes, and snippets.

@bloudermilk
Last active March 9, 2023 02:28
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save bloudermilk/8345597 to your computer and use it in GitHub Desktop.
Save bloudermilk/8345597 to your computer and use it in GitHub Desktop.
Explaining the rationale behind using memoized helper methods for controller resources

Last year I started playing around with using memoized private helper methods in my controllers instead of the traditional instance variable assigns we see in RoR controllers. Here's an example:

class PostsController < ApplicationController
  helper_method :new_post, :post, :posts
  
  def new; end
  def show; end
  def edit; end
  def index; end
  
  def create
    new_post.attributes = post_params
    
    if new_post.save
      redirect_to post
    else
      render :new
    end
  end
  
  def update
    if post.update_attributes(post_params)
      redirect_to post
    else
      render :edit
    end
  end
  
  def destroy
    post.destroy!
    redirect_to :index
  end
  
  private
  
  def new_post
    @new_post ||= Post.new
  end
  
  def post
    @post ||= posts.find(params[:id])
  end
  
  def posts
    @posts ||= current_user.posts
  end
  
  def post_params
    @post_params ||= params.require(:post).permit(:title, :body)
  end
end

Here's what I like about it:

  • Actions #new, #show, #edit, #index have no unique logic and as such, nothing in their methods (in fact, we don't even have to define methods here but I usually define stub methods to make it clear to readers that those actions do exist)
  • Actions #update, #create, and #destroy contain only the logic that is unique and important to them
  • Avoids hard-to-maintain before_filter :only and :except lists or repetative resource instantiation code
  • No more "called method on nil" errors. If you misspell something, you'll get an "undefined method" exception which includes your misspelling.
  • No more weird ass Rails magic copying ivars from one context to another

I also got into the habit of abstracting the logic in #update and #create into another private method (not helper) but I've since stopped doing that because I think it adds more indirection than value. Here's an example of that:

class PostsController < ApplicationController
  helper_method :new_post, :post, :posts
  
  def new; end
  def show; end
  def edit; end
  def index; end
  
  def create
    save_or_render(:new)
  end
  
  def update
    save_or_render(:edit)
  end
  
  def destroy
    post.destroy!
    redirect_to :index
  end
  
  private
  
  def save_or_render(action)
    if post.update_attributes(post_params)
      redirect_to post
    else
      render action
    end
  end
  
  def new_post
    @new_post ||= Post.new
  end
  
  def post
    @post ||= posts.find(params[:id])
  end
  
  def posts
    @posts ||= current_user.posts
  end
  
  def post_params
    @post_params ||= params.require(:post).permit(:title, :body)
  end
end
@kevinmanurip
Copy link

That was really good explaination and thanks for all the code. You've show many unpredictable things..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment