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

Don't show unpersisted has_one associations #1794

Merged

Conversation

rwehresmann
Copy link
Contributor

Showing unpersisted has_one associations can be misleading and break the application is some cases.

Let's consider the following example:

class Product < ApplicationRecord
  has_one :product_meta_tag

  def product_meta_tag
    super || build_product_meta_tag
  end
end

That opens for a scenario where a product.product_meta_tag will return a non-persisted instance of a ProductMetaTag. In the _show partial we're just checking if field.data (i.e., ProductMetaTag instance) exists to display it, and a non-persisted instance would be displayed. The link displayed in the partial (link_to( field.display_associated_resource, [namespace, field.data]) would be pointing to the index page of product meta tags, what is not desired. Also, in the absence of an index route, the page would break.

The solution: just check if data is persisted to display it or not.

Copy link
Collaborator

@pablobm pablobm left a comment

Choose a reason for hiding this comment

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

Thank you for your PR! On one hand, I agree that we shouldn't be displaying links to unpersisted resources. On the other hand, I wonder if the problem here is not so much with Administrate as with the underlying model, forcing an unpersisted association and confusing the code that enters in contact with it.

I'd like a second opinion on this one. Perhaps @nickcharlton or @c4lliope have thoughts?

spec/administrate/views/fields/has_one/_show_spec.rb Outdated Show resolved Hide resolved
spec/administrate/views/fields/has_one/_show_spec.rb Outdated Show resolved Hide resolved
spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
@rwehresmann rwehresmann force-pushed the avoid_displaying_unpersisted_records branch from 75c6139 to 71ad3c4 Compare October 17, 2020 13:39
@rwehresmann
Copy link
Contributor Author

@pablobm I understand your point. From what I saw until now in rails projects is quite common to find has_one relationships described in the model like in my example. However, that doesn't mean it's the best approach and I'm also not sure if this is enough reason to merge the PR (I'll keep doing these rewritings in my projects, though). In any case, I updated the commit with the test names you suggested to keep.

@pablobm
Copy link
Collaborator

pablobm commented Oct 22, 2020

@rwehresmann - Can you also solve Hound's lint warnings, please?

@c4lliope
Copy link
Contributor

Can we add a check like...

<%= @product.product_meta_tag.persisted? ? link_to(@product.product_meta_tag) : @product.product_meta_tag.name %>

So the link is only rendered for persisted models, and otherwise we only display plain text on the page?

Nice catch here, this seems like a use case that I wouldn't want to break peoples' applications over.

@rwehresmann rwehresmann force-pushed the avoid_displaying_unpersisted_records branch 2 times, most recently from 688897e to 3d11211 Compare October 26, 2020 00:52
@rwehresmann
Copy link
Contributor Author

@pablobm PR updated with the corrections.

@c4lliope I'm thinking if it's worthy. We actually don't have how to control the name of the model fields (we don't have the warranty that there is a name field, for instance), so the alternative would be to print the model instance as a string, what I think it isn't visually useful.

What we're showing now for unpersisted models:
Screenshot from 2020-10-25 21-55-09

What we would be showing printing as a string:
Screenshot from 2020-10-25 21-59-38

@c4lliope
Copy link
Contributor

Could you access product_meta_tag_dashboard.display_resource(...) ?
...[as described here])

describe "#display_resource" do
it "returns the customer's name" do
customer = double(name: "Example Customer")
dashboard = CustomerDashboard.new
rendered_resource = dashboard.display_resource(customer)
expect(rendered_resource).to eq(customer.name)
end
end
).

Or, i believe, there is dashboard.display_associated_resource(), doing similar.

@c4lliope
Copy link
Contributor

Ah, here;
Administrate::Field::HasOne < Administrate::Field::Associative
and ::Associative has...

def display_associated_resource
associated_dashboard.display_resource(data)
end

@rwehresmann
Copy link
Contributor Author

rwehresmann commented Oct 29, 2020

@c4lliope Oh, I see what you mean. I checked and display_resource called inside display_associated_resource is implemented in Administrate::BaseDashboard as

    def display_resource(resource)
      "#{resource.class} ##{resource.id}"
    end

The output for the situation we're treating in this PR would be ProductMetaTag #, but if I haven't a product_meta_tag as super || build_product_meta_tag, it would be nil and break when trying to access resource.id.

I could change the display_resource implementation to avoid this, of course. Do you think it's worth it?

@c4lliope
Copy link
Contributor

c4lliope commented Oct 29, 2020 via email

@c4lliope
Copy link
Contributor

Sorry, I guess email breaks comment formatting.

@rwehresmann rwehresmann force-pushed the avoid_displaying_unpersisted_records branch 3 times, most recently from dab4454 to d74aa43 Compare November 2, 2020 01:53
@rwehresmann
Copy link
Contributor Author

@c4lliope I still think that from a user perspective display nothing at all is the best option. It's possible that they don't even understand what is "persistence" in the context of the application. We could use "unrecord" for instance, but it seems redundant to me. However, if we should display something, I think your suggestion is the way to go.

I updated the PR with the described solution.

Just to record: I'm calling always try(:persisted?). I never used another ORM then ActiveRecord, and I'm not sure if Administrate works just fine with another. In any case, if the ORM doesn't implement persisted? these alterations will not break the application, we'll just display text instead. If that becomes a problem, it'll be easy to change it to make it work with another ORM.

@c4lliope
Copy link
Contributor

c4lliope commented Nov 2, 2020 via email

spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
spec/lib/fields/has_one_spec.rb Outdated Show resolved Hide resolved
@nickcharlton
Copy link
Member

We don't need to worry about other ORMs, thankfully, so persisted? is fine!

@nickcharlton nickcharlton added feature new functionality that’s not yet implemented fields new fields, displaying and editing data labels Nov 9, 2020
Showing unpersisted `has_one` associations can be misleading and break the application is some cases.

Let's consider the following example:

```
class Product < ApplicationRecord
  has_one :product_meta_tag

  def product_meta_tag
    super || build_product_meta_tag
  end
end
```

That opens for a scenario where a `product.product_meta_tag` will return a non-persisted instance of a ProductMetaTag. In the `_show` partial we're just checking if `field.data`
(i.e., ProductMetaTag instance) exists to display it, and a non-persisted instance would be displayed. The link displayed in the partial (`link_to( field.display_associated_resource, [namespace, field.data]`) would be pointing to the index page of product meta tags, what is not desired. Also, in the absence of an index route, the page would break.

The solution: just check if data is persisted to display it or not.
@rwehresmann rwehresmann force-pushed the avoid_displaying_unpersisted_records branch from d74aa43 to 90ebf9a Compare November 22, 2020 20:00
@rwehresmann
Copy link
Contributor Author

rwehresmann commented Nov 22, 2020

Sorry for the delay on this.

@c4lliope done: display a link if persisted, or blank if not.

@nickcharlton great. Thanks for the tips!

Co-authored-by: Nick Charlton <nick@nickcharlton.net>
@pablobm pablobm merged commit 4318044 into thoughtbot:master Dec 3, 2020
pablobm added a commit that referenced this pull request Dec 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature new functionality that’s not yet implemented fields new fields, displaying and editing data
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants