Findit
Tired of writing fat controllers? But you must do all these queries.. There is a solution, move it to a Finder class.
Don't write this:
class SomeController
  def index
    @cache_key = (
      # many_values
    )
    return if fragment_exists?(@cache_key)
    search_scope = SearchEngine.scope
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    search_scope.add(some_conditions)
    result = search_scope.search_and_return_ids
    @scope = scope.where(ids: result)
    ....
  end
end
Just do this:
# /app/controllers/some_controller.rb
class SomeController
  def index
    @scope = SomeFinder.new(params)
  end
end
# app/finders/some_finder.rb
class SomeFinder
  include Findit::Collections
  cache_key do
    # calculate you cache_key from params
  end
  def initialize(params)
    # some initialize, maybe params parse
  end
  def call
    # put here you find logic
  end
end
And that it! Now you can iterate over finder results by simple each:
@scope = SomeFinder.new(params)
@scope.each do |d|
  print d.description
end
or perform caching like ActiveRecord::Base
# app/some_view
<% cache @scope do %>
  <%scope.each do |res|%>
    ...
  <%end%>
<%end%>
Installation
Add this line to your application's Gemfile:
gem 'findit'
And then execute:
$ bundle
Or install it yourself as:
$ gem install findit
Per module documentation
Collections
It makes Finder work as Enumerator.
Result can be accessed with each, [] and size methods, but to make things work you must implement call method.
  class PostFinder
    incliude Findit::Collections
    def call
      Post.where(user_id: 1)
    end
  end
  @posts = PostFinder.new
  # load all matching posts and iterate over collection
  @posts.each do |post|
    print post.title
  end
  @posts[10] # get 10 element of posts
  @posts.size # size of PostFinder results
  # Also you can access result direcly by using `data` method.
  @posts.data # access to all posts
For easier caching expirience we provide DSL to define you custom cache_key
#/app/finders/posts_finders.rb
class PostFinder
  include Findit::Collections
  cache_key do
    [@user.id, @query] # here you put any stuff that result of finder depend on it
  end
  # custom initializer, do whatever you want here
  def initialize(user, options = {})
    @user = user
    @query = options[:query]
  end
  # Here we fetch results. You MUST implement it
  def call
    scope = scope.where(user_id: @user.id)
    scope = scope.where('description like :query', query: @query) if @query.present?
    scope
  end
end
#/app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = PostFinder.new(user: current_user)
  end
end
#/app/views/posts/index.html.haml
<% cache(@posts, expire_in: 30.minutes) do %>
   <%=render 'post' colection: @posts, as: :post%> # it will automaticly iterate over finder results by each method
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/findit.