-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Make the output of ActiveRecord::Core#inspect
configurable.
#49765
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
Conversation
a419cda
to
6c384a2
Compare
@@ -132,6 +132,8 @@ class SymbolIgnoredDeveloper < ActiveRecord::Base | |||
class AuditLog < ActiveRecord::Base | |||
belongs_to :developer, validate: true | |||
belongs_to :unvalidated_developer, class_name: "Developer" | |||
|
|||
self.attributes_to_inspect = [:id, :message] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes AssociationsTest#test_inspect_does_not_reload_a_not_yet_loaded_target
@@ -51,9 +65,9 @@ def test_inspect_class_without_table | |||
assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect | |||
end | |||
|
|||
def test_inspect_relation_with_virtual_field | |||
relation = Topic.limit(1).select("1 as virtual_field") | |||
assert_match(/virtual_field: 1/, relation.inspect) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to remove this test. Relation#inspect
calls inspect
on each of the records. It seems unnatural that virtual columns would be included in attributes_to_expect
so I didn't want to add that as a test case.
I think we could add a full_inspect
method to Relation
if we want to preserve this behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can rewrite this test in a way that makes sure virtual attributes are inside the loaded record and can be inspected. You could call full_inspect
in relation.first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be value in adding a global config, e.g. config.active_record.attributes_to_inspect
? If we opted for an :all
option, might be useful to be able to set this across all Active Records in order to maintain the existing behaviour.
I think the argument is less strong if we stick with #full_inspect
instead of an :all
option, although you could make the argument that users may always want inspect to include certain fields (e.g. timestamps).
activerecord/CHANGELOG.md
Outdated
Post.first.inspect #=> "#<(Post id: 1)>" | ||
``` | ||
|
||
The attributes to be included in the out of `inspect` can be configured with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The attributes to be included in the out of `inspect` can be configured with | |
The attributes to be included in the output of `inspect` can be configured with |
activerecord/test/cases/core_test.rb
Outdated
assert_match(/virtual_field: 1/, relation.inspect) | ||
def test_full_inspect_instance | ||
topic = topics(:first) | ||
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_fs(:inspect)}", bonus_time: "#{topic.bonus_time.to_fs(:inspect)}", last_read: "#{topic.last_read.to_fs(:inspect)}", content: "Have a nice day", important: nil, binary_content: nil, approved: false, replies_count: 1, unique_replies_count: 0, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_fs(:inspect)}", updated_at: "#{topic.updated_at.to_fs(:inspect)}">), topic.full_inspect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could pull this out into a HEREDOC to make it easier to read?
3053798
to
e22000f
Compare
@@ -680,21 +683,18 @@ def connection_handler | |||
self.class.connection_handler | |||
end | |||
|
|||
# Returns the contents of the record as a nicely formatted string. | |||
# Returns the attributes specified by #attributes_for_inspect as a nicely formatted string. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Returns the attributes specified by #attributes_for_inspect as a nicely formatted string. | |
# Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string. |
@@ -51,9 +65,9 @@ def test_inspect_class_without_table | |||
assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect | |||
end | |||
|
|||
def test_inspect_relation_with_virtual_field | |||
relation = Topic.limit(1).select("1 as virtual_field") | |||
assert_match(/virtual_field: 1/, relation.inspect) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can rewrite this test in a way that makes sure virtual attributes are inside the loaded record and can be inspected. You could call full_inspect
in relation.first
e22000f
to
9289e5b
Compare
end | ||
end.join(", ") | ||
if attributes_for_inspect == :all | ||
inspect_with_attributes(attribute_names) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inspect_with_attributes(attribute_names) | |
full_inspect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, thanks. I think I left it like this when I was playing with a version that had inspect(:all)
instead of full_inspect
We also already have a |
It doesn't overlap. filter_attributes tell active Record to show BTW, maybe we should implement another improvement on this entire pipeline because it doesn't make sense to:
Just to show |
Ah sorry, my mistake. I thought it would bypass the parameter filter, but instead it creates a customer filter if |
9289e5b
to
add9776
Compare
Currently there doesn't seem to be a distinction between local/remote here, which the original issue mentions. I have accidentally printed much more than I intended in production and because of slow connections that took quite a while. IRB had its autocomplete disabled in production because of similar reasons. In development however I want to see everything. I know I can set the required config to do that, but it really should be the default in my opinion. |
I do agree with that. Let's automatically set it to Add a test to make sure it is set to |
848bfe7
to
a80564a
Compare
c3fbfc4
to
62e9693
Compare
why is this not opt in? I imagine this is a massive annoyance to most people who have upgraded to 7.2. If I opened a PR to make this opt in instead of opt out, would it be accepted, or is rails core taking a hard stance on this? |
This is opt-out, not opt-in. |
Sorry, maybe I got that reversed...why did this not default to |
Because |
It is explained in the issue #49707 (comment). |
This seems like a very specific problem in a very specific rails app that most rails apps will not experience. Thanks for the explanation. |
Wait a sec, I just now realized this that changed for existing applications without given them any warning. I do think |
The problem came up for us when we upgraded to 7.2, so it really caught us off guard even when upgrading. I can see what I can do in the next week or so. |
Yeah, that wasn't not the intention. New apps having that new default is fair, but for existing apps this can break a lot of behavior. I'll try to fix today. |
I appreciate that! Thanks for talking this out with me!! |
Where might this live? I can try to fix it, but I also know you probably know exactly where to do this. |
There are a few things that I was planning to do:
I think if we do those 3 things, all apps, even new ones will be able to see all attribute when inspecting on the Rails console. New apps will not suffer the performance problem that can take down apps when calling a |
Got it! I'll give this a shot, and put up a PR. |
@levicole Thanks for the report and sorry this is causing you issues. I am happy to help if you run into any trouble with the PR. I agree with Rafael's suggestions except for one thing. Altho it is not the original motivation for the feature, something I like about this feature is that it allows the console output for models to be less noisey when you using the development console or debugging a test. So, I would not like to have an IRB inspector calling |
@andrewn617 I wondered about the IRB inspector as well. I'm definitely tackling the defaults, and adding it to the framework defaults. I will probably skip the IRB inspector. It's super interesting though! This would be my first PR to rails. Would this PR need to be based on the v7.2 tag? I saw that this file doesn't exist on main. |
Got it. Not sure how to bring that old file back since it's not present on main. |
@levicole Yeah, its a bit of an uncommon situation to add a framework default after the release. I believe @rafaelfranca will have to add it himself when he merges your PR. So it's ok just to open a PR on main for the other changes. |
Got it! Ok, almost done here. I just need to find where there might be tests for the configuration and update/add. |
Just FYI that I see this behavior in development as well. |
@andrewn617 / @rafaelfranca this is probably going to take me longer than I expected due to me getting my the dev environment set up. But I do want to do this. If you need to get the change in sooner than I'm able to, then I understand! |
PR is opened. We can move the discussion there. |
Just want to be clear that I did make a mistake with environments (ugh long week), but I am seeing this behavior in my test environment despite setting this to |
@andrewn617 hey, thanks for this PR! Is there a good way to exclude a column from this config? I basically want :all - some columns. I can't just use |
@MatheusRich I think you can just define that method on your model: class Foo < AR::Base
def attributes_for_inspect
super - ['undesired']
end
end Something to that affect, maybe. |
@zzak I totally missed that overriding the instance method was possible. TY! |
Motivation / Background
Fixes #49707
This Pull Request has been created because we noticed that
ActiveRecord::Core#inspect
was taking >9s to run for some large objects in production. This is because the parameter filter is be executed for every attribute.Detail
This Pull Request introduces configuration for
ActiveRecord::Core#inspect
. By default,inspect
just returns the object with its id (eg#<Post id: 1>
. The attributes to include can be configured withPost.attributes_to_inspect=
. If you want to full output with all the attributes you can callPost.full_inspect
.Additional information
In my first draft I had a config to disable this, if you just always want to use
full_inspect
. But it seems easy enough just to doalias_method :inspect, :full_inspect
on the model, or inApplicationRecord
if you want to disable it globally. So the config didn't seem worth it to me.Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]