Search code examples
cssrubyerbtheforemandeface

Insert after closing ERB tag with spree/deface


there is a project called TheForeman (v3.3.0) for which I want to override some ERB templates with spree/deface (v1.5.3), for example _managed.html.erb, and add something at the end of it.

# in app/overrides/nic/manageds/_managed/add_my_partial.html.erb.deface
<!-- insert_after "erb[loud]:contains('nic/provider_specific_form')" -->
<%= render 'my/partial', :f => f %>

Works great! Now, there are 5 more templates of the same kind, where the same approach works. But then I have to supply the identical .deface for each template. It would be more sensible and maintainable if I were to put my partial at the end of the other partial, which is rendered in each template (my current deface select target), right? Sadly, I failed to achieve that with deface, which brought me here.

What I've tried so far...

  • deface cannot go directly to the end of a file
  • deface insert_after cannot use closing_selector and it looks like it doesn't recognize which ERB elements start and end Ruby blocks
    • selecting erb[silent]:contains('if') inserts as second line in the file
    • selecting erb[silent]:contains('end') inserts three times, once after each <% end %>
  • Using :root or :last as CSS selectors yield very strange results I cannot explain. Both select more than the root and final elements, which makes no sense.
  • The CSS selector last-of-type seems to be completely ignored and therefor erb[silent]:last-of-type selects every silent ERB element
  • nth-child seems to be processed, but...
    • erb[silent]:contains('end'):nth-child(3) hits nothing
    • erb[silent]:nth-child(5) hits the second <% end %>
    • erb[silent]:nth-child(6) hits nothing
  • erb[silent]:nth-last-child(1) after each <% end %>
  • :root>erb[silent]:contains('end') inserts after the second <% end %>
  • Since I failed to find any example for deface's cut action, I wouldn't know how to paste whatever was cut back for other ludicrous tests like...
    1. Cut everything
    2. insert my partial
    3. insert before my partial what was cut

Maybe, if there was a way to let deface show me the concrete pseudo HTML markup of an ERB file, I could experiment with CSS selectors against that. At this point I cannot extrapolate that in my imagination far enough.

I welcome any hints!

Thank you for your time, Xavier.


Solution

  • if there was a way to let deface show me the concrete pseudo HTML markup of an ERB file (...)

    That, in fact, wasn't so difficult:

    require 'deface'
    raw_erb = File.open(path_to_erb_template).read
    puts Deface::Parser.erb_markup!(raw_erb)
    

    Which produces...

    <erb silent> if provider_partial_exist?(@host.compute_resource, &amp;#39;network&amp;#39;) </erb>
      <erb silent> hidden = f.object.virtual? ? &amp;quot;hidden&amp;quot; : &amp;quot;&amp;quot; </erb>
      <fieldset  data-erb-class="compute_attributes &lt;%= hidden %&gt;">
        <h3 class="col-md-offset-1">
          <erb loud> @host.compute_resource.provider_friendly_name </erb>
          <erb silent> if f.object.compute_attributes[&amp;quot;from_profile&amp;quot;] </erb>
            <span class="profile">( <erb loud> _(&amp;quot;from profile %s&amp;quot;) % f.object.compute_attributes[&amp;quot;from_profile&amp;quot;] </erb> )</span>
          <erb silent> end </erb>
        </h3>
    
        <erb silent> new_vm = @vm ? @vm.persisted? : new_vm?(@host) </erb>
    
        <erb loud> f.fields_for &amp;#39;compute_attributes&amp;#39;, OpenStruct.new(f.object.compute_attributes) do |f| </erb>
          <erb loud> render provider_partial(@host.compute_resource, &amp;#39;network&amp;#39;), :f =&amp;gt; f, :disabled =&amp;gt; f.object.persisted?, :compute_resource =&amp;gt; @host.compute_resource, :new_host =&amp;gt; new_vm, :new_vm =&amp;gt; new_vm </erb>
        <erb silent> end </erb>
      </fieldset>
    <erb silent> end </erb>

    On www.videlibri.de I could interactively test CSS selectors against that. The solution then was found quickly and is rather simple: :root:last-child

    [foreman@host ~]# foreman-rake deface:get_result[nic/_provider_specific_form] 
                                                                              ---------------- Before ----------------
    <% if provider_partial_exist?(@host.compute_resource, 'network') %>
      <% hidden = f.object.virtual? ? "hidden" : "" %>
      <fieldset class="compute_attributes <%= hidden %>">
        <h3 class="col-md-offset-1">
          <%= @host.compute_resource.provider_friendly_name %>
          <% if f.object.compute_attributes["from_profile"] %>
            <span class="profile">( <%= _("from profile %s") % f.object.compute_attributes["from_profile"] %> )</span>
          <% end %>
        </h3>
    
        <% new_vm = @vm ? @vm.persisted? : new_vm?(@host) %>
    
        <%= f.fields_for 'compute_attributes', OpenStruct.new(f.object.compute_attributes) do |f| %>
          <%= render provider_partial(@host.compute_resource, 'network'), :f => f, :disabled => f.object.persisted?, :compute_resource => @host.compute_resource, :new_host => new_vm, :new_vm => new_vm %>
        <% end %>
      </fieldset>
    <% end %>
    
    ---------------- Overrides (1)--------
    - 'add_my_partial' will be applied.
    
    ---------------- After ----------------
    <% if provider_partial_exist?(@host.compute_resource, 'network') %>
      <% hidden = f.object.virtual? ? "hidden" : "" %>
      <fieldset class="compute_attributes <%= hidden %>">
        <h3 class="col-md-offset-1">
          <%= @host.compute_resource.provider_friendly_name %>
          <% if f.object.compute_attributes["from_profile"] %>
            <span class="profile">( <%= _("from profile %s") % f.object.compute_attributes["from_profile"] %> )</span>
          <% end %>
        </h3>
    
        <% new_vm = @vm ? @vm.persisted? : new_vm?(@host) %>
    
        <%= f.fields_for 'compute_attributes', OpenStruct.new(f.object.compute_attributes) do |f| %>
          <%= render provider_partial(@host.compute_resource, 'network'), :f => f, :disabled => f.object.persisted?, :compute_resource => @host.compute_resource, :new_host => new_vm, :new_vm => new_vm %>
        <% end %>
      </fieldset>
    <% end %>
    <%= render 'foreman_cnames/hosts/host_aliases', :f => f %>
    
    
    ---------------- Diff -----------------
     <% if provider_partial_exist?(@host.compute_resource, 'network') %>
       <% hidden = f.object.virtual? ? "hidden" : "" %>
       <fieldset class="compute_attributes <%= hidden %>">
         <h3 class="col-md-offset-1">
           <%= @host.compute_resource.provider_friendly_name %>
           <% if f.object.compute_attributes["from_profile"] %>
             <span class="profile">( <%= _("from profile %s") % f.object.compute_attributes["from_profile"] %> )</span>
           <% end %>
         </h3>
    
         <% new_vm = @vm ? @vm.persisted? : new_vm?(@host) %>
    
         <%= f.fields_for 'compute_attributes', OpenStruct.new(f.object.compute_attributes) do |f| %>
           <%= render provider_partial(@host.compute_resource, 'network'), :f => f, :disabled => f.object.persisted?, :compute_resource => @host.compute_resource, :new_host => new_vm, :new_vm => new_vm %>
         <% end %>
       </fieldset>
     <% end %>
    +<%= render 'my/partial', :f => f %>
    +