Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept a collection in fresh_when and stale? #18374

Merged
merged 1 commit into from Feb 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions actionpack/CHANGELOG.md
@@ -1,3 +1,21 @@
* Expand `ActionController::ConditionalGet#fresh_when` and `stale?` to also
accept a collection of records as the first argument, so that the
following code can be written in a shorter form.

# Before
def index
@article = Article.all
fresh_when(etag: @articles, last_modified: @articles.maximum(:created_at))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be maximum(:updated_at)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's fixed on master.

end

# After
def index
@article = Article.all
fresh_when(@articles)
end

*claudiob*

* Explicitly ignored wildcard verbs when searching for HEAD routes before fallback

Fixes an issue where a mounted rack app at root would intercept the HEAD
Expand Down
46 changes: 36 additions & 10 deletions actionpack/lib/action_controller/metal/conditional_get.rb
Expand Up @@ -57,15 +57,25 @@ def etag(&etagger)
# This will render the show template if the request isn't sending a matching ETag or
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
#
# You can also just pass a record where +last_modified+ will be set by calling
# +updated_at+ and the +etag+ by passing the object itself.
# You can also just pass a record. In this case +last_modified+ will be set
# by calling +updated_at+ and +etag+ by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
# fresh_when(@article)
# end
#
# When passing a record, you can still set whether the public header:
# You can also pass an object that responds to +maximum+, such as a
# collection of active records. In this case +last_modified+ will be set by
# calling +maximum(:updated_at)+ on the collection (the timestamp of the
# most recently updated record) and the +etag+ by passing the object itself.
#
# def index
# @articles = Article.all
# fresh_when(@articles)
# end
#
# When passing a record or a collection, you can still set the public header:
#
# def show
# @article = Article.find(params[:id])
Expand All @@ -77,8 +87,8 @@ def etag(&etagger)
#
# before_action { fresh_when @article, template: 'widgets/show' }
#
def fresh_when(record = nil, etag: record, last_modified: nil, public: false, template: nil)
last_modified ||= record.try(:updated_at)
def fresh_when(object = nil, etag: object, last_modified: nil, public: false, template: nil)
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)

if etag || template
response.etag = combine_etags(etag: etag, last_modified: last_modified,
Expand Down Expand Up @@ -121,8 +131,8 @@ def fresh_when(record = nil, etag: record, last_modified: nil, public: false, te
# end
# end
#
# You can also just pass a record where +last_modified+ will be set by calling
# +updated_at+ and the +etag+ by passing the object itself.
# You can also just pass a record. In this case +last_modified+ will be set
# by calling +updated_at+ and +etag+ by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
Expand All @@ -135,7 +145,23 @@ def fresh_when(record = nil, etag: record, last_modified: nil, public: false, te
# end
# end
#
# When passing a record, you can still set whether the public header:
# You can also pass an object that responds to +maximum+, such as a
# collection of active records. In this case +last_modified+ will be set by
# calling +maximum(:updated_at)+ on the collection (the timestamp of the
# most recently updated record) and the +etag+ by passing the object itself.
#
# def index
# @articles = Article.all
#
# if stale?(@articles)
# @statistics = @articles.really_expensive_call
# respond_to do |format|
# # all the supported formats
# end
# end
# end
#
# When passing a record or a collection, you can still set the public header:
#
# def show
# @article = Article.find(params[:id])
Expand All @@ -155,8 +181,8 @@ def fresh_when(record = nil, etag: record, last_modified: nil, public: false, te
# super if stale? @article, template: 'widgets/show'
# end
#
def stale?(record = nil, etag: record, last_modified: nil, public: nil, template: nil)
fresh_when(record, etag: etag, last_modified: last_modified, public: public, template: template)
def stale?(object = nil, etag: object, last_modified: nil, public: nil, template: nil)
fresh_when(object, etag: etag, last_modified: last_modified, public: public, template: template)
!request.fresh?(response)
end

Expand Down
50 changes: 49 additions & 1 deletion actionpack/test/controller/render_test.rb
Expand Up @@ -58,6 +58,27 @@ def conditional_hello_with_record
end
end

class Collection
def initialize(records)
@records = records
end

def maximum(attribute)
@records.max_by(&attribute).public_send(attribute)
end
end

def conditional_hello_with_collection_of_records
ts = Time.now.utc.beginning_of_day

record = Struct.new(:updated_at, :cache_key).new(ts, "foo/123")
old_record = Struct.new(:updated_at, :cache_key).new(ts - 1.day, "bar/123")

if stale?(Collection.new([record, old_record]))
render action: 'hello_world'
end
end

def conditional_hello_with_expires_in
expires_in 60.1.seconds
render :action => 'hello_world'
Expand Down Expand Up @@ -288,7 +309,6 @@ def test_request_modified
assert_equal @last_modified, @response.headers['Last-Modified']
end


def test_responds_with_last_modified_with_record
get :conditional_hello_with_record
assert_equal @last_modified, @response.headers['Last-Modified']
Expand Down Expand Up @@ -318,6 +338,34 @@ def test_request_modified_with_record
assert_equal @last_modified, @response.headers['Last-Modified']
end

def test_responds_with_last_modified_with_collection_of_records
get :conditional_hello_with_collection_of_records
assert_equal @last_modified, @response.headers['Last-Modified']
end

def test_request_not_modified_with_collection_of_records
@request.if_modified_since = @last_modified
get :conditional_hello_with_collection_of_records
assert_equal 304, @response.status.to_i
assert @response.body.blank?
assert_equal @last_modified, @response.headers['Last-Modified']
end

def test_request_not_modified_but_etag_differs_with_collection_of_records
@request.if_modified_since = @last_modified
@request.if_none_match = "234"
get :conditional_hello_with_collection_of_records
assert_response :success
end

def test_request_modified_with_collection_of_records
@request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
get :conditional_hello_with_collection_of_records
assert_equal 200, @response.status.to_i
assert @response.body.present?
assert_equal @last_modified, @response.headers['Last-Modified']
end

def test_request_with_bang_gets_last_modified
get :conditional_hello_with_bangs
assert_equal @last_modified, @response.headers['Last-Modified']
Expand Down