Make this view helper.
def breadcrumb(&)
render(layout: 'common/breadcrumb', &)
end
Add this partial 'common/_breadcrumb.html.erb' (do whatever html you want): <li class="breadcrumb-item">
<%= yield %>
</li>
Add this to your layout: <% if content_for?(:breadcrumbs) %>
<ol class="breadcrumbs">
<%= yield :breadcrumbs %>
</ol>
<% else %>
Then this is how you use it in your views: <% content_for :breadcrumbs do %>
<%= breadcrumb { link_to 'Foo', foo_url } %>
<%= breadcrumb { link_to 'Bar', bar_url } %>
<%= breadcrumb { 'you are here' } %>
<% end %>
For minitest tests I add this helper: module AssertBreadcrumbs
Crumb = Struct.new(:text, :href)
# Note: the block must have 1 argument per breadcrumb. It asserts their count.
def assert_breadcrumbs(&blk)
assert_select '.breadcrumb-item', blk.parameters.size do |items|
structs = items.map { |item|
if (link = item.css('a')[0])
Crumb.new(link.text, link['href'])
else
Crumb.new(item.text.strip)
end
}
yield(*structs)
end
end
end
Which you can use in tests like this: assert_breadcrumbs do |item1, item2, item3|
assert_equal 'Foo', item1.text
assert_equal foo_url, item1.href
assert_equal 'Bar', item2.text
assert_equal bar_url, item2.href
assert_equal 'you are here', item3.text
assert_nil item3.href
end class BooksController < ApplicationController
def show
@book = Book.find(params[:id])
add_breadcrumb("Home", path: root_path)
add_breadcrumb("Books", path: books_path)
add_breadcrumb(@book.title)
end
end
Only the title is specific to the show method. Home should be set by the application controller and Books by the books controller code.Here is what I like about this code:
1. It is explicit
2. Breadcrumbs are information that this action needs to set. You can set them in the views or in the controller via these helpers. But no matter where you put the data it is custom data that you as developer set and it is specific to this controller.
The information about how to navigate from homepage to this show method is something that either: you can use meta-programming to try to get it if you would for example scope controllers based on paths (not sure it is a good idea) or you have to provided as Rails cannot know if your controllers/views are in the top namespace.
But users of it will also never (unless they're doing something very weird) unintentionally break styling somewhere else while editing it later or have it broken by changes made elsewhere, have to come up with names for one-off element-specific styles, or have to jump between multiple files for their pseudo, colour scheme-specific and responsive styling.
Them's the breaks.
> have to come up with names for one-off element-specific styles
You never have to “name styles”. You have to identify the target element as different from other similar ones. There’s a subtle difference. I wonder if acknowledgement of that difference leads developers to pick different tools to help them manage their CSS.
> have to jump between multiple files for their pseudo, colour scheme-specific and responsive styling.
I’m gonna infer from this you’ve worked on large CSS codebases and know what you’re doing. I’m not trying to discourage you from whatever your doing: use Tailwind if it helps.
I’m just really interested in the problems and mental models that lead people towards Tailwind and similar tools. I’ve never felt drawn to them, despite also feeling the pain of sprawling CSS codebases. (I’ve worked on legacy CSS files so large that versions of IE stopped parsing them; I’ve refactored CSS repeated-but-not-quite across multiple teams’ reimplenetations of the main menu widget, etc).
Inlining it however, I'm with you.
Tailwind folks will tell you you’re holding it wrong, but every tailwind codebase I’ve seen winds up like this.
This person would write bad CSS, let's not put the blame on tailwind.
Also so much repetition instead of pulling each breadcrumb link out into a shared component. I understand it's just demo code for an article, but if all code bases end up like this that you've seen, the issue isn't tailwind.