Possible to use ActionView in asset pipeline? #218

Closed
jasonm opened this Issue Oct 5, 2011 · 12 comments

Projects

None yet

10 participants

jasonm commented Oct 5, 2011

I'm curious if it's possible to use ActionView helpers in the asset pipeline. For example, to use Rails form helpers to build form markup that is then available to JavaScript code via JST (with EJS). Contrived, but humor me?

Consider app/templates/users/form.jst.ejs.erb:

<% form_for(User.new) do |form| %>
  <li>
    <%= form.label :email %>
    <%= form.input :email %>
  </li>
  <li>
    <%= form.label :password %>
    <%= form.password :password %>
  </li>
<% end %>

If I want to intermingle EJS and ERb interpolation, I could modify the EJS interpolation regex to, say, {% ... %}

EJS.evaluation_pattern = /\{%([\s\S]+?)%\}/
EJS.interpolation_pattern = /\{%=([\s\S]+?)%\}/

If I mix ActionView::Helpers and the routing helpers into the Sprockets environment's context_class, I get access to these helpers:

Rails.application.assets.context_class.class_eval do
  include ActionView::Helpers
  include Rails.application.routes.url_helpers
end

but get stuck at:

undefined method `output_buffer=' for #<#<Class:0x000001014a9208>:0x00000102e8a190>
  (in /Users/jason/dev/example_app/app/assets/templates/users/new.jst.ejs.erb)

Should it be possible to use Rails helpers in a Tilt::ERBTemplate via Sprockets?

Contributor
josh commented Oct 5, 2011

I think that Rails helper depends on Erubis + Rails hacks to get it working. Sprocket just uses ERB. So I don't think this going to work. Other Rails helpers depend on being in a real ActionView::Base object which sprockets doesn't use. So Rails helpers aren't truly modular.

I'd ask around the Rails mailing list, maybe someone has a replacement form_for helper module.

@josh josh closed this Oct 5, 2011
jnimety commented Nov 30, 2011

jasonm, did you find a solution?

I've been googling around for this as well, but without much luck- are there other pockets of discussion regarding this?

inkstak commented Apr 15, 2012

Hi, we've got the same problem here : http://github.com/jamesotron/hamlbars/issues/9

Did someone found any solution ?

jkeen commented Aug 20, 2012

I used that solution mentioned above, and it worked for me.

module ViewContext
  attr_accessor :output_buffer

  def output_buffer_with_sprockets=(buffer)
    unless is_sprockets?
      output_buffer_without_sprockets=(buffer)
    end
  end

  def is_sprockets?
    self.try(:environment).class == Sprockets::Index
  end

  def self.included(klass)
    klass.instance_eval do
      alias_method_chain :output_buffer=, :sprockets
    end
  end
end
Rails.application.assets.context_class.class_eval do
   include ViewContext
end
jhubert commented Oct 2, 2012

Hi Jeff, thanks for sharing. Would you mind clarifying where you put that module and the associated Rails class eval?

jkeen commented Oct 2, 2012

@jhubert The bottom block of code is in engine.rb, and the top block is in the /lib/view_context.rb

jhubert commented Oct 3, 2012

@jkeen Thanks. I ended up just adding the whole thing as an initializer, which worked well enough. Very handy bit of code. :)

Pickachu commented Dec 4, 2013

👍 @jkeen
Also used on initializer, rails 4, ruby 2 here.
Thanks!

@pitr pitr referenced this issue in pitr/angular-rails-templates May 6, 2014
Closed

How do I access my helpers? #45

ProGM commented Apr 21, 2015

The previous solutions didn't worked for me. Instead I'm using this:

module HTMLAssets
  class LookupContext < ActionView::LookupContext
    def initialize(context, path)
      super(path)
      @view_context = context
    end

    def find_template(*args)
      super.tap do |r|
        @view_context.depend_on(r.identifier)
      end
    end
  end

  module ViewContext
    attr_accessor :output_buffer

    def view_renderer
      @_view_renderer ||= ActionView::Renderer.new(lookup_context)
    end

    def lookup_context
      @_lookup_context ||= LookupContext.new(self, environment.paths.to_a)
    end

    def output_buffer_with_sprockets=(buffer)
      unless is_sprockets?
        output_buffer_without_sprockets=(buffer)
      end
    end

    def is_sprockets?
      self.try(:environment).class == Sprockets::Index
    end

    def self.included(klass)
      klass.instance_eval do
        include Rails.application.routes.url_helpers
        include Rails.application.routes.mounted_helpers
        include ActionView::Helpers
        alias_method_chain :output_buffer=, :sprockets
      end
    end
  end
end

Rails.application.assets.context_class.class_eval do
  include HTMLAssets::ViewContext
end

I wrote this starting from this repo:
https://github.com/carezone/haml_assets/blob/21300cde3c99bb13c569cbe02c994fa33ec9ff65/lib/haml_assets/haml_sprockets_engine.rb

voondo commented Sep 8, 2015

@ProGM thanks a lot !

ProGM commented Mar 31, 2016

To make it work with newer versions of sprockets, I had to wrap the class_eval in an after_initialize block:

Rails.application.config.assets.configure do |env|
  env.context_class.send :include, HTMLAssets::ViewContext
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment