Today I Learned: Deface Selectors Work on ERB Code, not HTML
Are you looking for quick solutions to your problems with code? You'll like our new post cycle: Today I Learned.
We’re going to present quick tips which our developers during their everyday work. The first tip comes from Marcin and concerns Deface - the gem used in Spree e-commerce platform. Enjoy!
Are you looking for quick solutions to your problems with code? Or want to find clever tricks you can read about in a few minutes? You’re in the right place! This is the beginning of the new post cycle: Today I Learned. In these posts, we’re going to present quick tips from our developers which they came across during their working day. The first tip comes from Marcin and concerns Deface - the gem used in Spree e-commerce platform.
What exactly does Deface do?
When working with Spree, you can use a gem called Deface which lets the developers inject code into a Spree view without overwriting the whole file. It works with HTML (ERB, Haml and Slim). In short, you select whether it should insert your piece before/after an element, replace it/remove it altogether, etc. Finding the right element to replace is easy because Deface uses Nokogiri's implementation of CSS selectors.
The problem with Deface
Our client asked us to replace
mailto: links. Instead, we were supposed to put an email in a certain table and link them to a user's profile. Take a quick look at this ERB code:
<td> had no class, so we needed a pseudo-selector to find it. Here is the selector that I thought would work.
tr[data-hook=admin_orders_index_rows] td:nth-of-type(6) a
However, for some reason the selector couldn’t find the necessary element. Here is what Deface had to say about it:
Why didn’t it work?
After a lot of frustration, trying different selectors, theories that Deface doesn't find ‘a’ elements because of a bug, it turns out that Deface uses selectors on ERB files, not compiled HTML files. Moreover, you can write Deface overrides in Haml/Slim, but it will still:
Convert them to Erb.
Convert Erb blocks into HTML-ish tags...
<% some_method %> converts to
<erb silent> some_method </erb>
<%= some_method %> converts to
<erb loud> some_method </erb>
- Do all the overrides there.
So, since we want to replace an ERB block, our selector will have to contain
erb[loud] instead of
The final solution
The trick that worked was to use a selector directly on the erb code:
As you can see, instead of matching an anchor element, my override matches an
erb[loud] tag that is generated by Deface in an intermediate step, before printing the final HTML.
So, here’s the trick - hope you’ll find it useful! If you have any other tips and tricks for Spree, feel free to share them in comments - or link to your GitHub page.
In the next episode of Today I Learned you’ll find out how to render templates to strings in EmberJS, a tip brought to you by our developer Kuba. Hope you’ll get back to our blog soon!