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

Add Touch method to ActiveRelation #8343

Closed
wants to merge 2 commits into from
Closed
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
28 changes: 27 additions & 1 deletion activerecord/lib/active_record/relation.rb
Expand Up @@ -519,7 +519,33 @@ def to_sql
end end
end end


# Returns a hash of where conditions. # Touches multiple records.
#
# === Examples
#
# # Touch all records
# Person.all.touch
# # => "UPDATE \"persons\" SET \"updated_at\" = '2063-04-04 22:55:23.132670'"
#
# # Touch multiple records with a custom attribute
# Person.all.touch(:created_at)
# # => "UPDATE \"persons\" SET \"updated_at\" = '2012-11-27 23:12:25.745411', \"created_at\" = '2012-11-27 23:12:25.745411'"
#
# # Touch records with scope
# Person.where(:name => 'David').touch
# # => "UPDATE \"persons\" SET \"updated_at\" = '2012-11-27 23:12:30.087110' WHERE \"persons\".\"name\" = 'David'"
def touch(name = nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't it be called touch_all, for consistency with update_all, destroy_all, etc.?

attributes = [:updated_at]
attributes << name if name
Copy link
Member

Choose a reason for hiding this comment

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

This always sets updated_at, so you can't touch(:other_timestamp) without changing updated_at. Not sure if that's a good idea. It's not demonstrated in the test coverage, in any case.

Copy link
Contributor

Choose a reason for hiding this comment

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

The behavior of this method closely follows the behavior of ActiveRecord#touch as shown here: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb#L338-L357

The only caveat is that the :udpated_at attribute is something that is being derived form an instance method in ActiveRecord which of course is inaccessible in this case.

Copy link
Member

Choose a reason for hiding this comment

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

Good point. That feels weird to me too :)

now = klass.default_timezone == :utc ? Time.now.utc : Time.now
value_hash = attributes.inject({}) do |values, column|
values[column] = now
values
end
update_all(value_hash)
end

# Returns a hash of where conditions
# #
# User.where(name: 'Oscar').where_values_hash # User.where(name: 'Oscar').where_values_hash
# # => {name: "Oscar"} # # => {name: "Oscar"}
Expand Down
18 changes: 18 additions & 0 deletions activerecord/test/cases/relations_test.rb
Expand Up @@ -1391,6 +1391,24 @@ def test_presence
assert topics.loaded? assert topics.loaded?
end end


def test_touching_multiple_record_updates_their_timestamps
Developer.all.touch
assert Developer.first.updated_at >= 1.second.ago, "First Developer was not updated"
assert Developer.last.updated_at >= 1.second.ago, "Last Developer was not updated"
end

def test_touching_multiple_record_with_custom_timestamp
Developer.all.touch(:created_at)
assert Developer.first.created_at >= 1.second.ago
assert Developer.last.created_at >= 1.second.ago
end

def test_touching_records_from_a_scope
Developer.where(:name => 'David').touch
assert Developer.first.updated_at >= 1.second.ago, "First Developer was not updated"
assert Developer.last.updated_at <= 1.second.ago, "Last Developer was updated"
end

test "find_by with hash conditions returns the first matching record" do test "find_by with hash conditions returns the first matching record" do
assert_equal posts(:eager_other), Post.order(:id).find_by(author_id: 2) assert_equal posts(:eager_other), Post.order(:id).find_by(author_id: 2)
end end
Expand Down
14 changes: 13 additions & 1 deletion activerecord/test/fixtures/developers.yml
Expand Up @@ -2,20 +2,32 @@ david:
id: 1 id: 1
name: David name: David
salary: 80000 salary: 80000
created_at: <%= 10.minutes.ago.to_s(:db) %>
updated_at: <%= 5.minutes.ago.to_s(:db) %>
updated_on: <%= 5.minutes.ago.to_s(:db) %>


jamis: jamis:
id: 2 id: 2
name: Jamis name: Jamis
salary: 150000 salary: 150000
created_at: <%= 15.minutes.ago.to_s(:db) %>
updated_at: <%= 12.minutes.ago.to_s(:db) %>
updated_on: <%= 12.minutes.ago.to_s(:db) %>


<% (3..10).each do |digit| %> <% (3..10).each do |digit| %>
dev_<%= digit %>: dev_<%= digit %>:
id: <%= digit %> id: <%= digit %>
name: fixture_<%= digit %> name: fixture_<%= digit %>
salary: 100000 salary: 100000
created_at: <%= (digit+5).minutes.ago.to_s(:db) %>
updated_at: <%= digit.minutes.ago.to_s(:db) %>
updated_on: <%= digit.minutes.ago.to_s(:db) %>
<% end %> <% end %>


poor_jamis: poor_jamis:
id: 11 id: 11
name: Jamis name: Jamis
salary: 9000 salary: 9000
created_at: <%= 20.minutes.ago.to_s(:db) %>
updated_at: <%= 16.minutes.ago.to_s(:db) %>
updated_on: <%= 16.minutes.ago.to_s(:db) %>