Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Allow fragment cache to accept :if and :unless options #8371

Merged
merged 1 commit into from

4 participants

@freegenie

No description provided.

@freegenie

This issue was discussed here #5396

@rafaelfranca

I think the implementation on #5396 is better.

@freegenie

@rafaelfranca please tell me more of your point of view so that I can learn what's wrong

@rafaelfranca

I think this is simpler.

def options_pass_conditions?(options) #:nodoc:
  !(options && (!options.fetch(:if, true) || options.fetch(:unless, false)))
end

Or this doesn't work with your implementation?

@freegenie

well, you are right, it's much simpler, I fixed it and pushed again.

@rafaelfranca

Why not use the same logic?

@freegenie

yes you are right, I used fetch because it's much dryier and readable to me, pushed again. I'm using --force to keep it in one commit.

@freegenie

@pixeltrix any opinion on this one too?

@pixeltrix
Owner

I'm definitely :+1: on it - I've done something similar in my apps where for example I've cached the rendering of an empty shopping basket but not anything else as the cache hit rate would be low so anything which is user customisable but has a common default is definitely a good match for this.

I'll have a closer look later but it'll need a CHANGELOG entry and the commits squashing on first glance.

@rafaelfranca
Owner

I didn't undertand why did you open a new pull request with the same code of #5396 without ask to the original author if it is still interested in the code.

Besides this, (both) pull requests look good and only need the CHANGELOG entry and the commits squashed.

@pixeltrix
Owner

Does the test still fail in the way mentioned in #5396?

@freegenie

@rafaelfranca sorry about that, I should have asked, you are right. I didn't mean to cheat anybody, I just thought it was easier for me to start from a white page on a branch of my own. And maybe I considered two months inactivity as enough time for anybody to be no longer interested in the code. Please go ahead with whatever you feel to be more correct to resolve this pull request.

@rafaelfranca
Owner

@freegenie no problem. You did an awesome work and deserves recognition. Also, your tests are passing.

To be fair change you commit message and put this in the last line:

[Stephen Ausman + Fabrizio Regini]

And we can merge this one.

Thank you so much for the pull request.

@rafaelfranca
Owner

Ah, if you could put the Stephen Ausman name in the CHANGELOG too

@freegenie

@rafaelfranca done, thanks for teaching!

@rafaelfranca rafaelfranca merged commit 6ed4ad1 into from
@rafaelfranca
Owner

Thank you so much for the pull request. :heart:

@pixeltrix
Owner

@rafaelfranca hey, you stole my merge commit :trollface:

@bogdan bogdan commented on the diff
actionpack/lib/action_view/helpers/cache_helper.rb
@@ -136,6 +143,11 @@ def cache_fragment_name(name = {}, options = nil)
end
private
+
+ def conditions_match?(options)
+ !(options && (!options.fetch(:if, true) || options.fetch(:unless, false)))
@bogdan
bogdan added a note

I thought there is :force => true option supported by ActiveSupport::CacheStore to do this. I believe it is supported at this layer as well.

@pixeltrix Owner

@bogdan that's slightly different - it forces a cache miss so it will still write the newly rendered fragment. This will not cache the fragment at all.

@bogdan
bogdan added a note

@pixeltrix clear enough

Than maybe we should support :if and :unless in CacheStore. wdyt?

@pixeltrix Owner

Possibly - can you think of a use case?

@bogdan
bogdan added a note

Sure.

Test environment can create object with same id many times during test suite

Now I do:

  Rails.cache.fetch(object.id, :force => Rails.env.test?) do

  end

But using :if will be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rafaelfranca
Owner

@pixeltrix so sorry :bow:

@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit in acapilleri/rails
@acapilleri acapilleri Removed :if and :unless from fragment cache option in favour of
cache_if(condition, option, &block) and cache_unless(condition, option, &block).

In the PR #8371 was introduced  conditional options :if and :unless in
the cache method.

    Example:

      <%= cache @model, if: some_condition(@model) do %>
        ...
      <%end%>

This is a good feature but *cache_if* and and *cache_unless*
are more concise and close to the standard of rails view helpers
(ex: link_to_if and link_to_unless).

    Example:

      <%= cache_if condition, @model do %>
      ...
      <%end%>
4a3293e
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@acapilleri acapilleri referenced this pull request from a commit in acapilleri/rails
@acapilleri acapilleri Removed :if and :unless from fragment cache option in favour of
cache_if(condition, option, &block) and cache_unless(condition, option, &block).

In the PR #8371 was introduced  conditional options :if and :unless in
the cache method.

    Example:

      <%= cache @model, if: some_condition(@model) do %>
        ...
      <%end%>

This is a good feature but *cache_if* and and *cache_unless*
are more concise and close to the standard of rails view helpers
(ex: link_to_if and link_to_unless).

    Example:

      <%= cache_if condition, @model do %>
      ...
      <%end%>
8322342
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 5, 2012
  1. @freegenie

    Allow fragment cache to accept :if and :unless options

    freegenie authored
    [Stephen Ausman + Fabrizio Regini]
This page is out of date. Refresh to see the latest.
View
6 actionpack/CHANGELOG.md
@@ -1,4 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* Add :if / :unless conditions to fragment cache:
+
+ <%= cache @model, if: some_condition(@model) do %>
+
+ *Stephen Ausman + Fabrizio Regini*
+
* Add filter capability to ActionController logs for redirect locations:
config.filter_redirect << 'http://please.hide.it/'
View
14 actionpack/lib/action_view/helpers/cache_helper.rb
@@ -110,8 +110,15 @@ module CacheHelper
# <%= some_helper_method(person) %>
#
# Now all you'll have to do is change that timestamp when the helper method changes.
+ #
+ # ==== Conditional caching
+ #
+ # You can pass :if and :unless options, to conditionally perform or skip the cache.
+ #
+ # <%= cache @model, if: some_condition(@model) do %>
+ #
def cache(name = {}, options = nil, &block)
- if controller.perform_caching
+ if controller.perform_caching && conditions_match?(options)
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
else
yield
@@ -136,6 +143,11 @@ def cache_fragment_name(name = {}, options = nil)
end
private
+
+ def conditions_match?(options)
+ !(options && (!options.fetch(:if, true) || options.fetch(:unless, false)))
@bogdan
bogdan added a note

I thought there is :force => true option supported by ActiveSupport::CacheStore to do this. I believe it is supported at this layer as well.

@pixeltrix Owner

@bogdan that's slightly different - it forces a cache miss so it will still write the newly rendered fragment. This will not cache the fragment at all.

@bogdan
bogdan added a note

@pixeltrix clear enough

Than maybe we should support :if and :unless in CacheStore. wdyt?

@pixeltrix Owner

Possibly - can you think of a use case?

@bogdan
bogdan added a note

Sure.

Test environment can create object with same id many times during test suite

Now I do:

  Rails.cache.fetch(object.id, :force => Rails.env.test?) do

  end

But using :if will be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ end
+
def fragment_name_with_digest(name) #:nodoc:
if @virtual_path
[
View
64 actionpack/test/controller/log_subscriber_test.rb
@@ -46,6 +46,22 @@ def with_fragment_cache_and_percent_in_key
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
+ def with_fragment_cache_and_if_true_condition
+ render :inline => "<%= cache('foo', :if => true) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_if_false_condition
+ render :inline => "<%= cache('foo', :if => false) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_unless_false_condition
+ render :inline => "<%= cache('foo', :unless => false) { 'bar' } %>"
+ end
+
+ def with_fragment_cache_and_unless_true_condition
+ render :inline => "<%= cache('foo', :unless => true) { 'bar' } %>"
+ end
+
def with_exception
raise Exception
end
@@ -203,6 +219,54 @@ def test_with_fragment_cache
@controller.config.perform_caching = true
end
+ def test_with_fragment_cache_and_if_true
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_if_true_condition
+ wait
+
+ assert_equal 4, logs.size
+ assert_match(/Read fragment views\/foo/, logs[1])
+ assert_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_if_false
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_if_false_condition
+ wait
+
+ assert_equal 2, logs.size
+ assert_no_match(/Read fragment views\/foo/, logs[1])
+ assert_no_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_unless_true
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_unless_true_condition
+ wait
+
+ assert_equal 2, logs.size
+ assert_no_match(/Read fragment views\/foo/, logs[1])
+ assert_no_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
+ def test_with_fragment_cache_and_unless_false
+ @controller.config.perform_caching = true
+ get :with_fragment_cache_and_unless_false_condition
+ wait
+
+ assert_equal 4, logs.size
+ assert_match(/Read fragment views\/foo/, logs[1])
+ assert_match(/Write fragment views\/foo/, logs[2])
+ ensure
+ @controller.config.perform_caching = true
+ end
+
def test_with_fragment_cache_and_percent_in_key
@controller.config.perform_caching = true
get :with_fragment_cache_and_percent_in_key
Something went wrong with that request. Please try again.