10

I'm pretty sure I can page cache the vast majority of my site but the one thing preventing me from doing so is that my flash messages will not show, or they'll show at the wrong time.

One thing I'm considering is writing the flash message to a cookie, reading it and displaying it via javascript and clearing the cookie once the message has been displayed. Has anyone had any success doing this or are there better methods?

Thanks.

John Topley
  • 113,588
  • 46
  • 195
  • 237
KJF
  • 2,083
  • 4
  • 21
  • 38

7 Answers7

8

Cacheable flash do this:

in your application controller:

after_filter :write_flash_to_cookie


def write_flash_to_cookie
    cookie_flash = cookies['flash'] ? JSON.parse(cookies['flash']) : {}

    flash.each do |key, value|
        if cookie_flash[key.to_s].blank?
            cookie_flash[key.to_s] = value
        else
            cookie_flash[key.to_s] << "<br/>#{value}"
        end
     end

    cookies['flash'] = cookie_flash.to_json
    flash.clear
end

and then read "flash" cookie via Javascript and insert the message inside the HTML

VMOrtega
  • 1,948
  • 3
  • 18
  • 22
4

I'm dealing with the same problem and I found cacheable-flash plugin that does exactly what KJF described in the question.

I think this is simpler and nicer solution than making excessive ajax calls.

Anton Mironov
  • 488
  • 5
  • 13
  • I think this is the best approach for Rails. Most of the other answers to this question suggest building your own, but the cacheable-flash gem already solves this and solves it well. Thanks Anton and Pivotal Labs. – GregT Sep 19 '13 at 01:17
2

One solution would be to cache the page, but include a javascript snippet that will make another small request just for the section you want to be dynamic. So the user will download the page fully, and then when javascript executes, it will pull down the dynamic page element.

I wrote a short blog post about this a while back. http://chase.ratchetsoftware.com/2008/12/rails-caching-dynamic-fragments/

Also, Greg Pollack of RailsEnvy did a screencast where he focuses on having dynamic data in cached pages. http://railslab.newrelic.com/2009/02/05/episode-5-advanced-page-caching

Hope this helps,

Chase Gray

Chase M Gray
  • 330
  • 3
  • 14
  • I like this idea. Seems like it could be the ticket. – KJF Feb 26 '10 at 21:16
  • 1
    I think it's worth noting that this won't realise the full benefit of page caching, because the Rails process will still be hit every time instead of not at all. I can't think of a better solution though. – John Topley Feb 27 '10 at 10:08
  • 3
    Fragment caching is better than serving a second HTTP request that only requests data which could be included in the first one. This strategy would double the number of HTTP requests you serve. – Jesse Dhillon Jul 12 '10 at 19:20
0

Old question... but I got around this by including the flash message into my cache key.

caches_action :show, cache_path: proc { |c|
  most_recent_update_time = MyClass.order('updated_at DESC').limit(1).first.try(:updated_at).to_i
  { tag: most_recent_update_time.to_s + c.flash.collect{|x| x}.join }
}

If you have flash messages on your show action this will obviously break the cache often, but works well if you aren't doing a lot of messages.

cpuguy83
  • 5,794
  • 4
  • 18
  • 24
0

Unobtrusive Flash puts flash message into cookie, and displays it via JavaScript. It provides vanilla and Bootstrap flavored JS display logics. It works in normal and ajax requests. It is also easy to hook into frameworks such as AngularJS.

lulalala
  • 17,572
  • 15
  • 110
  • 169
0

You don't have to cache entire page. Try fragment caching API

Kylo
  • 2,300
  • 1
  • 19
  • 24
-1

I don't use Rails but this is how I did it in Python using UUIDs:

# set flash messages like this
def flash(self, title, body):
  session['flash_messages'].append({
    'title': title,
    'body': body,
    'uuid': uuid().hex  # stores a UUID as a string
  })

...

self.flash('foo', 'bar')

Then in the base template I have this:

<script type="text/javascript">
  {% for m in session.flash_messages %}
    if(!Cookies.get('{{m.uuid}}')) {
      Notify('{{m.title}}', '{{m.body}}');
      Cookie.set('{{m.uuid}}', 'true', 86400); // key, value, expiry seconds
    }
  {% endfor %}
</script>

I'll break it down for the Pythonically-challenged:

  1. When you add a flash message, you create a unique ID and store it with that message.
  2. Before you display the message, you check to see if a cookie named with the message's unique ID has been set.
  3. If that cookie has not been set, flash the message and set the cookie. Expire the cookie in a day, or as brief as you think is wise.

Now if this page is pulled from cache, it will be okay. At step 2 the test for the cookie will pass because it has already been set, and the message will not be displayed.

Jesse Dhillon
  • 7,841
  • 1
  • 34
  • 34