8

Let link_to infer link name from Model#to_s by olivierlacan · Pull Request #4223...

 2 years ago
source link: https://github.com/rails/rails/pull/42234
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Conversation

Copy link

Contributor

olivierlacan commented on May 16

edited

Summary

Ever since I've started using Rails I've yearned to be able to link to models like this:

link_to @profile

And have the following rendered:

<a href="/profiles/1">Eileen</a>

This PR makes this a reality. And you'll no longer have to type the following if your model defines a to_s method:

link_to @profile, @profile

Or even:

link_to @profile, @profile.name

Other Information

I initially implemented this a Railtie plugin gem but the idea of touching the internals of url_for in a gem gives me the heebee jeebees as you might understand. I'd much rather this exist as an option, even if rarely used, within Action View than within a likely soon-to-be out-of-date gem.

I'm aware this amounts to me telling the Rails team to maintain this neato feature, but I'll gladly support maintenance.

@@ -146,6 +148,12 @@ def _filtered_referrer # :nodoc:

# link_to nil, "http://example.com"

# # => <a href="http://www.example.com">http://www.example.com</a>

#

# Pithier yet, when name is an ActiveRecord model that defines a

Copy link

Member

@zzak zzak on May 16

I'm not sure I understand the use of "pithier" here but perhaps my English skills are deteriorating thinking

Copy link

Contributor

Author

@olivierlacan olivierlacan on May 16

Pithy means concise or forcefully expressive. Something concise and full of meaning, which IMO is exactly what a model is in relation to a URL helper. hugs

Copy link

Contributor

Author

@olivierlacan olivierlacan on May 16

That said we can change it to "concise" to be more ESL friendly. I'll just point out I'm the ESL learner and you're the native English speaker. stuck_out_tongue

Copy link

Contributor

Author

@olivierlacan olivierlacan on May 17

Mind you I think I just used the same adjective I found earlier in the docs:

@@ -146,6 +148,12 @@ def _filtered_referrer # :nodoc:

# link_to nil, "http://example.com"

# # => <a href="http://www.example.com">http://www.example.com</a>

#

# More concise yet, when +name+ is an ActiveRecord model that defines a

# +to_s+ method returning a default value or a model instance attribute

Copy link

Contributor

Author

@olivierlacan olivierlacan on May 17

@zzak I changed this from:

+to_s+ method returning a name attribute or a default value

To this:

+to_s+ method returning a default value or a model instance attribute

I feel like the mention of a "name attribute" could have been misunderstood as the +name+ argument to link_to when really I meant a completely different model instance attribute called name. That was too confusing, the implementation of whatever custom #to_s is irrelevant.

Copy link

Member

pixeltrix commented on May 17

edited

In principle I'm 👍🏻 on the feature, but not keen on overloading to_s for this since models may be using it for other purposes already. We have to_param for the id used in a url so seems like we should come up with a similar convention - unsure what this should be of the top of my head so open to ideas.

Copy link

Contributor

Author

olivierlacan commented on May 17

@pixeltrix Good point, I'll change the docs to reference to_param.

Copy link

Contributor

Author

olivierlacan commented on May 18

edited

Actually @pixeltrix, I don't think I agree. I read your response too fast.

to_s is the convention for implicitly converting a Ruby object into its String representation and it is already used in the tests as you can see here:

In my experience with Rails apps, most often there is no implementation of to_s on many models and people are instead verbosely telling link_to how to represent their model as a string by typing: link_to(@article, @article.title) over and over again, instead of a much more straightforward link_to(@article) where link_to can abstract away the fetching of the model route to url_for while we can rely on the model itself to find its string/display representation.

We could recommend alternatives but there's nothing in my implementation that calls to_s. I'm simply relying on implicit to_s behavior which is common throughout Rails and Ruby. In my opinion, alternatives would be breaking convention, which is not what we should do. If people have their own implementations of to_s that are specific, they can always opt out of this behavior by using the common explicit two-argument form of link_to.

Copy link

Member

tenderlove commented on May 18

If people have their own implementations of to_s that are specific, they can always opt out of this behavior by using the common explicit two-argument form of link_to.

I don't think the problem is with link_to necessarily. The issue is that other parts of the system could depend on the implementation of to_s without even calling the to_s method. For example:

irb(main):001:0> class Foo; def to_s; "bar"; end; end
=> :to_s
irb(main):002:0> "foo #{Foo.new}"
=> "foo bar"

In this case, interpolation has a dependency on the to_s method and we can't really "see" it in the code. I think this is the type of implicit dependency that @pixeltrix (and I) am worried about.

Maybe we could add a link_text method? For example link_to(model) would be equivalent to link_to(model, model.link_text).

Copy link

Member

matthewd commented on May 18

I think to_s is the right thing here, exactly because of that implicit behaviour.

<%= my_model %> will already call to_s; having <%= link_to(my_model) %> produce a link with the same text seems entirely natural to me.

Copy link

Member

tenderlove commented on May 18

<%= my_model %> will already call to_s; having <%= link_to(my_model) %> produce a link with the same text seems entirely natural to me.

Ah, that's a good point and does seem very natural. I'm sold on to_s.

Copy link

Member

pixeltrix commented on May 18

Ah, that's a good point and does seem very natural. I'm sold on to_s.

I guess that's a consensus then upside_down_face

My other concern is that what's the out-of-the-box experience? Because currently they'll just get #<Post:0x00007f9e0e866470>, so do we need some kind of default implementation that looks for certain attribute names?

Copy link

Member

matthewd commented on May 18

@pixeltrix to me the argument still holds: that's what you'd already get from <%= model %>.

I don't really see this as providing new deep link-label-guessing magic, and more just about making link_to(model) equivalent to existing link_to(model, model) -- if you're following the right conventions (defining a sensible to_s), then you can type a slightly-shorter, slightly-less-redundant, thing to get the same result.

Copy link

Contributor

Author

olivierlacan commented on May 18

edited

My other concern is that what's the out-of-the-box experience? Because currently they'll just get #<Post:0x00007f9e0e866470>, so do we need some kind of default implementation that looks for certain attribute names?

Speaking of good points. smiley

Maybe exposing a default to_s in ApplicationRecord could help make this feature discoverable. Otherwise if that feels a bit too intrusive, ActiveRecord::Base could have def to_s; "#{self} #{id}"; end. It'll print the class name and the ID attribute (if there's one, Post 1 or Post <uuid>). That would take care of the concern raised about people having their own to_s overload, since theirs would take precedence still.

It's not perfect for printing the identity of non-AR-models or models with no primary keys (or one not called id) but it feels like a slightly better default than printing the hex Ruby object ID. That said, @matthewd has me thinking it will be too backward incompatible for anyone who relied on the previous default to_s behavior.

It's almost like scaffolds if you think about it. The default behavior isn't great but you can easily improve it by bringing your own implementation.

Copy link

rails-bot bot commented on Aug 16

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

Copy link

Member

ghiculescu commented on Aug 16

Could you add a CHANGELOG entry?

Copy link

Contributor

Author

olivierlacan commented on Aug 16

@ghiculescu Sure thing.

olivierlacan

force-pushed the actionview_inferred_model_url

branch 2 times, most recently from 9df31a9 to dbe7099 last month

Copy link

Contributor

Author

olivierlacan commented on Aug 17

@ghiculescu Pending CI but I think we're good to merge after this. Added the CHANGELOG entry at the top. Let me know if you think the entry is helpful:

*   Allow `link_to` helper to infer link name from `Model#to_s` when it 
    is used with a single argument:

        link_to @profile
        #=> <a href="/profiles/1">Eileen</a>

    Previously you had to supply a second argument even if the `Profile`
    model implemented a `#to_s` method that called the `name` method.

        link_to @profile, @profile.name
        #=> <a href="/profiles/1">Eileen</a>

    *Olivier Lacan*

Copy link

Member

ghiculescu commented on Aug 17

Maybe the changelog should show how to_s is implemented on the model, to complete the picture.

Copy link

Contributor

Author

olivierlacan commented on Aug 17

Maybe the changelog should show how to_s is implemented on the model, to complete the picture.

Yeah I think that's helpful. Updated to:

*   Allow `link_to` helper to infer link name from `Model#to_s` when it 
    is used with a single argument:

        link_to @profile
        #=> <a href="/profiles/1">Eileen</a>

    This assumes the model class implements a `to_s` method like this: 

        class Profile < ApplicationRecord 
          # ...
          def to_s
            name
          end
        end

    Previously you had to supply a second argument even if the `Profile`
    model implemented a `#to_s` method that called the `name` method.

        link_to @profile, @profile.name
        #=> <a href="/profiles/1">Eileen</a>

    *Olivier Lacan*

rafaelfranca

merged commit 8e86e87 into

rails:main 6 days ago

3 of 4 checks passed

Copy link

Contributor

Author

olivierlacan commented 5 days ago

Thanks @rafaelfranca blush

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Assignees

No one assigned

Projects

None yet

Milestone

No milestone

Linked issues

Successfully merging this pull request may close these issues.

None yet

7 participants

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK