Skip to content

Commit

Permalink
Add expires_at option to signed_id
Browse files Browse the repository at this point in the history
Problem:

Though, the `MessageVerifier` that powers `signed_id` supports both
`expires_in` and `expires_at`, `signed_id` only supports `expires_in`.
Because of this, generating signed_id that expires at a certain time is
somewhat tedious. Imagine issuing a coupon that is valid only for a
day.

Solution:

Add `expires_at` option to `signed_id` to generate signed ids that
expire at the given time.
  • Loading branch information
shouichi authored and byroot committed Sep 22, 2022
1 parent 93b2a2c commit 364939c
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 6 deletions.
4 changes: 4 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
* Add `expires_in` option to `signed_id`.

*Shouichi Kamiya*

* Allow applications to set retry deadline for query retries.

Building on the work done in #44576 and #44591, we extend the logic that automatically
Expand Down
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/signed_id.rb
Expand Up @@ -109,8 +109,8 @@ def combine_signed_id_purposes(purpose)
#
# And you then change your +find_signed+ calls to require this new purpose. Any old signed ids that were not
# created with the purpose will no longer find the record.
def signed_id(expires_in: nil, purpose: nil)
self.class.signed_id_verifier.generate id, expires_in: expires_in, purpose: self.class.combine_signed_id_purposes(purpose)
def signed_id(expires_in: nil, expires_at: nil, purpose: nil)
self.class.signed_id_verifier.generate id, expires_in: expires_in, expires_at: expires_at, purpose: self.class.combine_signed_id_purposes(purpose)
end
end
end
18 changes: 14 additions & 4 deletions activerecord/test/cases/signed_id_test.rb
Expand Up @@ -67,11 +67,11 @@ class SignedIdTest < ActiveRecord::TestCase
assert_nil Account.find_signed("this won't find anything")
end

test "find signed record within expiration date" do
test "find signed record within expiration duration" do
assert_equal @account, Account.find_signed(@account.signed_id(expires_in: 1.minute))
end

test "fail to find signed record within expiration date" do
test "fail to find signed record within expiration duration" do
signed_id = @account.signed_id(expires_in: 1.minute)
travel 2.minutes
assert_nil Account.find_signed(signed_id)
Expand All @@ -83,6 +83,16 @@ class SignedIdTest < ActiveRecord::TestCase
assert_nil Account.find_signed signed_id
end

test "find signed record within expiration time" do
assert_equal @account, Account.find_signed(@account.signed_id(expires_at: 1.minute.from_now))
end

test "fail to find signed record within expiration time" do
signed_id = @account.signed_id(expires_at: 1.minute.from_now)
travel 2.minutes
assert_nil Account.find_signed(signed_id)
end

test "find signed record with purpose" do
assert_equal @account, Account.find_signed(@account.signed_id(purpose: :v1), purpose: :v1)
end
Expand All @@ -99,11 +109,11 @@ class SignedIdTest < ActiveRecord::TestCase
end
end

test "find signed record with a bang within expiration date" do
test "find signed record with a bang within expiration duration" do
assert_equal @account, Account.find_signed!(@account.signed_id(expires_in: 1.minute))
end

test "finding signed record outside expiration date raises on the bang" do
test "finding signed record outside expiration duration raises on the bang" do
signed_id = @account.signed_id(expires_in: 1.minute)
travel 2.minutes

Expand Down

0 comments on commit 364939c

Please sign in to comment.