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
HTML tag dynamic proxy - modern syntax for tag helpers, related to issue #25195 #25289
Conversation
Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @matthewd (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Is there any reason why not to use already built builder for this implementaion? |
@simi good question. One doubt I have is that builder is external dependency (https://github.com/jimweirich/builder) and second I am not sure which is better starting point - current tag helper code base or xmlbuilder code base. Current tag helper already have html related logic implemented (boolean attributes, tag prefixes, etc). On the other hand Builder::XMLMarkup has XML generation implemented. Not sure yet how to combine both. |
VOID_ELEMENTS.merge(VOID_ELEMENTS.map(&:to_sym)) | ||
|
||
private | ||
|
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.
Snip newline
If we're going to base the tag proxy off the existing helpers, then I don't think it makes sense to add Builder to the mix. As the code shows in this patch, that's really simple. The only argument could be if we wanted to go the other way around. Reimplement everything to sit on top of a builder-backed tag proxy and have content_tag and friends call that. But I don't see a compelling reason to do that, primarily because I don't see a compelling reason to hard deprecate the existing usage of tag and content_tag. There's so much code written with that already that wouldn't be materially better off by being rewritten with the proxy tag. The proxy tag is really just to make new code a little better. So I think the approach taken in this patch is sound. |
@marekkirejczyk builder is already actionview dependency, so I'm not sure if there's any reason to duplicate that. |
@simi Feel free to work on an alternative patch that uses Builder and see if it turns out better on the A/B than this patch. I'd consider that a refactoring that can happen after this has been merged in any case. They should be functionally equivalent. |
I added implementation along with tests and documentation. I'll appreciate feedback :) Notes and considerations:
|
…ake builder a singleton
Small fixes, typos and improvements added. |
end | ||
|
||
private | ||
|
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.
Snip newline.
# void[https://www.w3.org/TR/html5/syntax.html#void-elements] element. | ||
# | ||
# ==== Options | ||
# Like with traditional syntax the options hash can be used 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.
Why are we referencing the traditional syntax here? Wouldn't it be best to assume people only know the new way?
# in options to disable attribute value escaping. The tag will be | ||
# generated with related closing tag unless tag represents a | ||
# void[https://www.w3.org/TR/html5/syntax.html#void-elements] element. | ||
# |
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 wanted to see if interspersing the examples inbetween documentation would read nicer.
# === Building HTML tags
# Builds HTML5 compliant tags with a tag proxy. Every tag can be built with:
#
# tag.<tag name>(optional content, options)
#
# where tag name can be e.g. br, div, section, article, or any tag really.
#
# ==== Passing content
# Tags can pass content to embed within it:
#
# tag.h1 'All shit fit to print' # => <h1>All shit fit to print</h1>
#
# tag.div tag.p('Hello world!') # => <div><p>Hello world!</p></div>
#
# Content can also be captured with a block. Great for ERB templates:
#
# <%= tag.p do %>
# The next great American novel starts here.
# <% end %>
# # => <p>The next great American novel starts here.</p>
#
# ==== Options
# Any passed options becomes attributes on the generated tag.
#
# tag.section class: %w( kitties puppies )
# # => <section class="kitties puppies"></section>
#
# tag.section id: dom_id(@post)
# # => <section id="<generated dom id>"></section>
#
# Pass true for any attributes that can render with no values like +disabled+.
#
# tag.input type: 'text', disabled: true
# # => <input type="text" disabled="disabled">
#
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
# pointing to a hash of sub-attributes.
#
# To play nicely with JavaScript conventions sub-attributes are dasherized.
#
# tag.article data: { user_id: 123 }
# # => <article data-user-id="123"></article>
#
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
#
# Data attribute values are encoded to JSON, with the exception of strings, symbols and
# BigDecimals.
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
# from 1.4.3.
#
# tag.div data: { city_state: %w( Chigaco IL ) }
# # => <div data-city-state="["Chicago","IL"]"></div>
#
# The generated attributes are escaped by default, but it can be turned off with
# +escape_attributes+.
#
# tag.img src: 'open & shut.png'
# # => <img src="open & shut.png">
#
# tag.img src: 'open & shut.png', escape_attributes: false
# # => <img src="open & shut.png">
#
# The tag builder respects
# [HTML5 void elements](https://www.w3.org/TR/html5/syntax.html#void-elements)
# if no content is passed, and omits closing tags for those elements.
#
# # A standard element:
# tag.div # => <div></div>
#
# # A void element:
# tag.br # => <br>
What do you think, @marekkirejczyk, @rafaelfranca? 😁
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.
But I think we can look into that in a later PR which I'd be happy to open.
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.
Looks awsome!
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.
Fantastic! Feel free to incorporate it into this PR if you want ❤️
@marekkirejczyk can you squash your commits down to 1? 😁 |
Just realized we likely want to dasherize tag names to support multi-word web components natively: tag.img_slider # => <img-slider></img-slider> |
@kaspth I'll find some time tomorrow to squash commits, do the dasherize and include documentation. And ... if you find anything else hopefully that too :) Thanks for feedback again :D |
@kaspth I created new pull request with commits squashed. Documentation is updated. Tag names dasherized. Please check documentation one more time. Also added you to changelog :) Let continue there if needed. |
@marekkirejczyk you don't have to open a new pr, you can just force push to this branch :) Though I went ahead and merged since it's ready #25543. Thanks so much for this! It was a blast helping here and I hope we get to see you contributing again. In fact, you could check if @vipulnsward is interested in getting help with #25197 😁 |
Sweet! |
Is anyone else having trouble with this new syntax? With a brand new Rails 5.0.1 app, adding the following to <%= tag.br %> I get an
All other examples from |
This is a Rails 5.1 feature. That's why. |
Ack, I'm sorry. I should've realized that! Thanks for the quick reply. Should helpers (e.g. |
Sure, you be able to write: tag.link rel: "stylesheet", type: "text/css", href: "theme.css" However |
@marekkirejczyk Thanks for the quick reply!
To satisfy my own curiosity, why won't |
@marekkirejczyk I think @jgarber623 means the underlying implementation of @jgarber623 I'd be willing to entertain a PR on that if we also update other helpers 👍 — though be wary as the PR teeters close to cosmetic commit grounds, which we don't accept. Here's more: #13771 (comment) |
@kaspth Fair enough and good to know! Given the team's stance on cosmetic commits, it seems more appropriate to change Thanks all for the info and hard work maintaining and improving Rails! |
Yup, that's even better 👍 — removing deprecated code is something our release managers do, so (unfortunately) please don't send a PR :)
Thanks for jumping in and helping out! ❤️ |
Prototype for new tag helper syntax
This is prototype implementation for @dhh's proposal in issue #25195 for HTML tag dynamic proxy.
The purpose of this PR is to discuss and gather feedback rather then being merged.
Example usage: