6

Is there a way to pre-build a page cache without calling the actual page via a http request?

I looked at solutions like this and this, but these don't generate the cache.

I have a relatively complicated view, and want to cache the entire thing. I want to pre-build this cached version in the application so when a user actually hits it, it will already be there.

Thanks

Community
  • 1
  • 1
user99168
  • 173
  • 2
  • 11

2 Answers2

2

We had a need to do something similar from a rake task -- we had a partial that would need to display a very long list of entities (~700) which were somewhat context specific and which, due to a series of database structure issues and custom sorting criteria, would easily take > 25 seconds to render the first time before going into cache> This would often time out because our HTTP servers were set to terminate HTTP requests with no response after 30 seconds, and pre-caching this custom list was a solution.

What you need to do is create an instance of ActiveController::Base, or of one of your controllers if you need helper methods or other entities, then pass its lookup_context reference to a new instance of ActionView.Renderer.

In our rake task, we did the following

namespace :rake_for_time_consuming_nonsense do
  task :pre_cache_long_list do
    PreCacher.pre_fetch_partials
  end
end

class PreCacher
  def self.pre_fetch_partials
    the_controller = ActionController::Base.new
    # Set any instance variables required by your partial in the controller, 
    # they will be passed to the partial with the view_context reference
    the_controller.instance_variable_set "@cache_key", cache_key
    the_controller.instance_variable_set "@the_object", MyModel.first

    view_renderer = ActionView::Renderer.new the_controller.lookup_context
    view_renderer.render the_controller.view_context, {partial: 'my_model/the_partial', layout: false}
  end
end

This works in Rails 3.2.13.

jfrprr
  • 543
  • 4
  • 11
0

I think following link should give you a good start. How do I get the rendered output of a controller's action without visiting the web page?

I try to accomplish the same and as far i can see, your fake request should have the correct host, because the cache-key includes host informations.

I accomplished caching by using ActionController::Integration::Session

ais = ActionController::Integration::Session.new
ais.host = host
ais.xml_http_request(:post, url, params, headers)

I'v got another one:

class FakeRequest
  include ActionController::UrlWriter

  def initialize(url, params, session, host)
    @url = url
    @params = params
    @session = session
    default_url_options[:host] = URI.parse(host).host
  end

  def post
    process(:post)
  end

  def get
    process(:get)
  end

  def xhr
    process(:post, true)
  end

  def process(method, ajax = false)
    uri = URI.parse(url_for(@url))
    request = ActionController::TestRequest.new({'HTTP_HOST' => uri.host,'rack.input' => '','rack.url_scheme' => 'http'})
    request.query_parameters = @params
    request.path = uri.path
    request.host = uri.host
    request.env['REQUEST_METHOD'] = method.to_s.upcase
    if ajax
      request.headers['X-Requested-With'] = 'XMLHttpRequest'
    end
    @session.each_pair do |k,v|
      request.session[k] = v
    end

    response = ActionController::TestResponse.new

    controller = ActionController::Routing::Routes.recognize(request).new
    return controller.process(request, response)
  end
end

This will also return the response object.

Community
  • 1
  • 1
  • This did not work, or at least I can't get it to generate the view. ais.response did return the generated code, but nto the partials, and not the cache. Any way to render the response with HAML from the console? – user99168 Aug 04 '11 at 22:08
  • This only generated the cache, i don't know what cache backend you use. You will have to use FileStore Backend, because MemoryStore will live in the Memory and when you close rake (or console) all cached partials are lost. – metaphalon Aug 05 '11 at 13:33