-
Feature: partial's expose
local_assigns
+locals
alias<%# app/views/articles/show.html.erb %> <%= render "section", id: "an_article" %> <%# app/views/application/_section.html.erb %> <%# We can access the passed `id:` like this: %> <% partial.local_assigns[:id] %> <% partial.locals[:id] %>
Note: this is equal to the default partial local variable of
local_assigns
, but it becomes more useful with the next feature below. -
Feature: partial helpers can access
partial
<%# app/views/articles/show.html.erb %> <% render "section", id: "an_article" do |section| %> <%= tag.h1 "An Article", id: section.labelledby %> <% end %> <%# app/views/application/_section.html.erb %> <% partial.helpers do def aria partial.locals.fetch(:aria, {}).with_defaults(labelledby:) end def labelledby id = partial.locals[:id] and "#{id}_label" end end %> <%= tag.section partial.yield, id:, aria: partial.aria %>
-
Feature: declare contents via
required
andoptional
<% if partial.title? %> <h1 class="text-xl"> <%= partial.title %> </h1> <% end %> <div><%= partial.body %></div>
Can now become:
<%= partial.title.optional.h1 class: "text-xl" %><%# Will not output any HTML element if no content has been provided. %> <div><%= partial.body.required %></div> <%# Raises when this line is hit if no content has been provided %>
See the README for more.
-
Fixed: section predicates not respecting
local_assigns
contentPreviously, when doing something like this:
<%= render "card", title: "Hello there" %>
If the inner card partial had this,
<% if partial.title? %> <%= partial.title %> <% end %>
The
title?
predicate would fail, because it didn't look up content from the passedlocal_assigns
. Now it does.
-
Changed: view methods don't clobber section names
Previously, we'd eagerly delegate to the view context so if the view had a
label
method,partial.label
would call the view'slabel
instead of making alabel
section.This was to support
partial.helpers
but we've changed the implementation to support the above.partial.helpers
still works the same too. -
Changed:
partial.helpers
no longer automatically callspartial
methodsPreviously, if a user defined a partial helper like this:
partial.helpers do def some_helper some_section end end
If
some_section
wasn't a view method, it would automatically callpartial.some_section
thereby adding a new content section to the partial.Now
partial.helpers
behaves exactly like view helpers — making it easier to copy code directly when migrating — so users would have to explicitly callpartial.some_section
.
- Fix Ruby 2.7 compatibility
-
Fix rendering with special characters in a view path.
Ref: #70
-
Seed Nice Partials content from
local_assigns
Previously, the only way to assign content to a Nice Partial was through passing a block:
# app/views/posts/show.html.erb <%= render "posts/post", byline: "Some guy" %> # app/views/posts/_post.html.erb <%= render "card" do |partial| %> <% partial.title "Hello there" %> <% partial.byline byline %> <%# `byline` comes from the outer `render` call above. %> <% end %> Now, Nice Partials will automatically use Rails' `local_assigns`, which contain any `locals:` passed to `render`, as the seed for content. So this works: ```erb <%= render "card", title: "Hello there", byline: byline %>
And the
card
partial is now oblivious to whether itstitle
orbyline
were passed as renderlocals:
or through the usual assignments in a block.# app/views/_card.html.erb <%= partial.title %> written by <%= partial.byline %>
Previously to get this behavior you'd need to write:
# app/views/_card.html.erb <%= partial.title.presence || local_assigns[:title] %> written by <%= partial.byline.presence || local_assigns[:byline] %>
Passing extra content via a block appends:
<%= render "card", title: "Hello there" do |partial| %> <% partial.title ", and welcome!" %> # Calling `partial.title` outputs `"Hello there, and welcome!"` <% end %>
-
Add
NicePartials::Partial#slice
Returns a Hash of the passed keys with their contents, useful for passing to other render calls:
<%= render "card", partial.slice(:title, :byline) %>
-
Fix
partial.helpers
accidentally adding methods toActionView::Base
When using
partial.helpers {}
, internallyclass_eval
would be called on the Partial instance, and throughdelegate_missing_to
passed on to the view context and thus we'd effectively have a global method, exactly as if we'd just used regular Rails view helpers. -
Let partials respond to named content sections
<% partial.content_for :title, "Title content" %> # Before <% partial.title "Title content" %> # After # Which can then be output <% partial.title %> # => "Title content" <% partial.title? %> # => true
Note,
title
responds topresent?
so rendering could also be made conditional with:<% partial.title if partial.title? %> # Instead of this… <% partial.title.presence %> # …you can do this
Procs and objects that implement
render_in
, like ViewComponents, can also be appended as content:<% partial.title { "some content" } %> <% partial.title TitleComponent.new(Current.user) %>
Options can also be captured and output:
<% partial.title class: "text-m4" %> # partial.title.options # => { class: "text-m4" } # When output `to_s` is called and options automatically pipe through `tag.attributes`: <h1 <% partial.title.options %>> # => <h1 class="text-m4">
A content section appends to its content when calling any view context method on it, e.g.:
<% partial.title.t ".title" %> <% partial.title.link_to @document.name, @document %> <% partial.title.render "title", user: Current.user %> <% partial.title.render TitleComponent.new(Current.user) do |component| %> <% … %> <% end %>
These
tag
calls let you generate elements based on the stored content and options:<% partial.title "content", class: "post-title" %> # Adding some content and options… <% partial.title.h2 %> # => <h2 class="post-title">content</h2> <% partial.title.h2 "more" %> # => <h2 class="post-title">contentmore</h2>
-
Add
NicePartials#t
to aid I18n.When using NicePartials with I18n you end up with lots of calls that look like:
<% partial.title t(".title") %> <% partial.description t(".header") %> <% partial.byline t("custom.key") %>
With NicePartials'
t
method, you can write the above as:<% partial.t :title, description: :header, byline: "custom.key" %>
Clarifying what keys get converted to what content sections on the partial rather than the syntax heavy
partial.… t(".…")
.Like the Rails built-in
t
method, it's just a shorthand alias fortranslate
so that's available too. -
Add
Partial#content_from
content_from
lets a partial extract contents from another partial. Additionally, contents can be renamed by passing a hash:<% partial.title "Hello there" %> <% partial.byline "Somebody" %> <%= render "shared/title" do |cp| %> # Here the inner partial `cp` accesses the outer partial through `partial` # extracting the `title` and `byline` contents. # `byline` is renamed to `name` in `cp`. <% cp.content_from partial, :title, byline: :name %> <% end %>
-
Remove need to insert
<% yield p = np %>
in partials.Nice Partials now automatically captures blocks passed to
render
. Instead ofp
, apartial
method has been added to access the currentNicePartials::Partial
object.Here's a script to help update your view code:
files_to_inspect = [] Dir["app/views/**/*.html.erb"].each do |path| if contents = File.read(path).match(/(<%=? yield\(?.*? = np\)? %>\n+)/m)&.post_match files_to_inspect << path if contents.match?(/render.*?do \|/) contents.gsub! /\bp\.(?=yield|helpers|content_for|content_for\?)/, "partial." File.write path, contents end end if files_to_inspect.any? puts "These files had render calls with a block parameter and likely require some manual edits:" puts files_to_inspect else puts "No files with render calls with a block parameter found, you're likely all set" end
-
Support manual
yield
s in partials.Due to the automatic yield support above, support has also been added for manual
yield some_object
calls.Nice Partials automatically appends the
partial
to the yielded arguments, so you can changerender … do |some_object|
torender … do |some_object, partial|
. -
Deprecate
p
as the partial object access. Usepartial
instead. -
Expose
partial.yield
to access the captured output buffer.Lets you access what a
<%= yield %>
call returned, like this:<%= render "card" do %> This is the content of the internal output buffer <% end %>
# app/views/cards/_card.html.erb # This can be replaced with `partial.yield`. <%= yield %> # Will output "This is the content of the internal output buffer"
- Rely on
ActiveSupport.on_load :action_view
- Add support for Ruby 3.0
- Initial release