Kane
Kane

Reputation: 924

How to share code between two controllers

I have an instance variable @posts_by_month defined in two controllers and is used in two views:

Posts controller:

class PostsController < ApplicationController

def index
    @posts = Post.all
    @posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%L") }

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @posts }
    end
  end  
.
.
end

Archives controller:

class ArchivesController < ApplicationController
  def index  
    @posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%B") }
  end  
end

Posts index view:

<h1>Listing posts</h1>
.
.
.
<div>
  <% @posts_by_month.each do |millisecond, posts| %>
    <p><%= millisecond %>milliseconds <%= posts.count %></p>
  <% end %>
</div>

Archives index view:

<% @posts_by_month.each do |monthname, posts| %>
<%= monthname %>
<ul>
   <% posts.each do |post| %>
     <h3><%= post.title %></h3>
     <p><%= post.body %></p>
   <% end %>
</ul>
<% end %>

I have two questions. Is there any way I can define the @posts_by_month instance variable so that I can have it available to both views without repeating it in each respective controller?

Secondly, is there any way that the millisecond part of <p><%= millisecond %>milliseconds <%= posts.count %></p> can be made into a link that leads to the archive view?

Note: In my app millisecond will be replaced by month as in the archive view.

Upvotes: 0

Views: 1952

Answers (2)

Aman Garg
Aman Garg

Reputation: 4218

Yes you can reduce duplicacy of same variable. One way is to use filters:

Define a mthod inside application controllers:

class ApplicationController < ActionController::Base
  private
  def find_post_by_month
    @posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%L") }
  end
end

Then inside archive and posts controllers:

class ArchivesController < ApplicationController
  before_filter :find_post_by_month, :only => :index  
  ...    
end


class PostsController < ApplicationController
  before_filter :find_post_by_month, :only => :index  
  ...
end

This will give you value in @posts_by_month variable.

And, for making link of mentioned text, you should use this code:

<p><%= link_to "#{millisecond} milliseconds", path %></p>   # Replace path with your url

Upvotes: 1

Billy Chan
Billy Chan

Reputation: 24815

When an action was executed, aka rendered, the instance is over. There is no more instance variable.

The instance variables in View are not real instance variables. View and Controller are in different classes, how can they share instance? The reality is, what Rails does is to copy those instance variable from Controller instance to View instance exactly.

So the answer in your question is: No.

But you can still dry your code by a private method in application controller, to share with PostsController and ArchiveController.

class ApplicationController
  private
  def posts_by_time(arg)
    Post.all.group_by { |post| post.created_at.strftime(arg) }
  end
end

class PostsController < ApplicationController
  def index
    @posts = posts_by_time "%L"
    # ...
  end
end

class ArchievesController < ApplicationController
  def index
    @posts = posts_by_time "%B"
    # ...
  end
end

Upvotes: 2

Related Questions