views:

198

answers:

2

Hi people

I have an issue with trying to keep AR finders DRY in my application. I have created a blogging application which fetches all the related pages,posts,links,tags and categories for a blog when a user first views it. A sample show action for the Blog controller is shown below:

def show
    #find blog by user name
    @user= User.find_by_login(params[:id])
    @blog= @user.blog
    @posts = Post.status("publish",@user).find(:all, :order => "created_at DESC")
    @tags = @user.tags
    @pages = Page.status("publish",@user).find(:all, :order => "created_at DESC")
    @links = @user.links.public_link.find(:all, :order => 'created_at DESC')
    @archives = @posts.group_by(&:month)
    @categories = @user.categories.group_by(&:name)
    session[:found_user][email protected]
    render :layout=>false
  end

As you can see it is not very DRY as there are other actions which call upon the same instance variables such as @tags etc further in the controller.

How could I make this more DRY? I tried moving it into the Blog model but I still need to call the various instance variables such as @tags etc in the controller.

Is there a way to store all these variables when the blog is first called and re-use them across controllers and actions?

Thank you for any advice. I'm using Rails 2.1

+3  A: 

I read on a blog somewhere to simply replace before filters (or loading all kinds of data in the controller method) with helper methods. Something like this:

class BlogsController < ApplicationController
  def show
    session[:found_user][email protected]
    render :layout=>false
  end

  helper_method :user, :blog, :posts, :tags, :pages, :links, :archives, :categories

  protected
  def user
    @user ||= User.find_by_login(params[:id])
  end

  def blog
    @blog ||= user.blog
  end

  def posts
    @posts ||= Post.status("publish", user).find(:all, :order => "created_at DESC")
  end

  def tags
    @tags ||= user.tags
  end

  def pages
    @pages ||= Page.status("publish", user).find(:all, :order => "created_at DESC")
  end

  def links
    @links ||= user.links.public_link.find(:all, :order => 'created_at DESC')
  end

  def archives
    @archives ||= posts.group_by(&:month)
  end

  def categories
    @categories ||= user.categories.group_by(&:name)
  end
end

## app/views/blogs/show.html.erb
<p>Name: <%=h user.name %></p>
<h3><%= posts.length %> Posts</h3>
<% posts.each do |post| %>
  ...
<% end %>
<ul>
  <% categories.each do |category| %>
    <li><%=h category %></li>
  <% end %>
</ul>

See how in the view using anything simply calls on the database. An advantage of this solution is that helper methods that aren't called won't take time from the action.

If necessary, abstract the helper methods into a module and include the module in ApplicationController.

François Beausoleil
A: 

I don't fully understnad these (OK, i'm a cargo cult), but these guys have applied metamagic to setting instance vars and doing the #find's

http://gilesbowkett.blogspot.com/2007/11/put-this-in-your-applicationcontroller.html

http://tumble.lukeredpath.co.uk/post/11530172/more-sneaky-to-proc-tricks

Gene T