Search code examples
ruby-on-railspartial-viewsinstance-variablesrailstutorial.org

Michael Hartl Rails Tutorial 4, micropost Partial and instance variables


following his tutorial, the home page from Static-Pages Controller renders a partial_form:

<%= render 'shared/micropost_form' %> 

StaticPagesController.rb

def home
  @micropost  = current_user.microposts.build #for the form_form 
  @feed_items = current_user.feed.paginate(page: params[:page])
end

Then, in the MicropostController.rb, an action for the partial micropost_form is declared:

class MicropostsController < ApplicationController
  def create
    @micropost = current_user.microposts.build(micropost_params)
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end
end

Notice how, if micropost_form success, it gets redirected static_pages's index view, but, if failed, it renders static_pages home view (which also keeps the params in the form, so the user doesn't have to writype all the data), BUT, it needs to initialize static_pages's home controller instance variables (namely, feed_items).

Now, suppose StaticPages Controller's home has a long list of complex instances variables, how can MicropostController render (to keep form data upon failure) static_pages/home without creating that many instance variables?

note: this question is related to my previous one,but I wanted to understand this basic example before.


Solution

  • how can MicropostController render (to keep form data upon failure) static_pages/home without creating that many instance variables?

    The simple answer is that it can't. In Rails you cannot redirect internally - which is an intentional design. What that means you cannot "patch through" a request to StaticPagesController#home once the router has matched the request.

    So in order for MicropostsController to render static_pages/home it needs to do the same job as StaticPagesController.

    However if setting up the instance variables is arduous or you want to avoid repetition then a good technique is to use a module to extract the shared functionality.

    # app/controllers/concerns/feedable.rb
    module Feedable
      protected
    
        def set_feed_items!
          @feed_items = FeedItem.includes(:comments).all
        end
    end
    
    class StaticPagesController < ActiveRecord::Base
      include Feedable
    
      # ...
      before_action :set_feed_items!, only: [:home]
    end
    
    class MicropostsController < ApplicationController
      include Feedable
    
      # ...
      def create
        @micropost = current_user.microposts.build(micropost_params)
        if @micropost.save
          flash[:success] = "Micropost created!"
          redirect_to root_url
        else
          set_feed_items!
          render 'static_pages/home'
        end
      end
    end