70

Is there any way to use a Rails helper method, more specifically, a path helper method within a javascript asset file. This file foo.js.coffee.erb

$('#bar').val("<%= create_post_path %>")

I would love it if I could get from erubis

$('#bar').val("path/to/create")
axsuul
  • 7,370
  • 9
  • 54
  • 71

5 Answers5

77

You can include any helper/module/class in an erb template with:

<% environment.context_class.instance_eval { include MyHelper } %>

See: https://github.com/rails/sprockets/blob/master/lib/sprockets/environment.rb and https://github.com/rails/sprockets/blob/master/lib/sprockets/context.rb

To use the url helpers you have to include your specific applications' helpers.

They are available at Rails.application.routes.url_helpers so:

<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>

EDIT: Fixed links to moved sprockets repo but not really sure this still makes sense so many years later.

eirc
  • 1,654
  • 14
  • 14
  • But how would you include the UrlHelpers module? And the bad thing with that is that is the url helpers need a model at runtime, buy-buy cowboy – Nikos D Nov 15 '11 at 09:19
  • 14
    To use the url helpers you have to include your specific applications' helpers. They are available at `Rails.application.routes.url_helpers` so: `<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>` – eirc Nov 15 '11 at 09:39
  • 3
    Fantastic answer! How did you figure this out? is this present at the Rails Guides? I thought I maybe saw something similar in a RailsCast, this magic is so powerful, you gotta tell me man! – jlstr Dec 13 '12 at 19:11
  • 1
    Not sure exactly how I came up with it but it's either by navigating through the Rails code (IntelliJ the IDE (http://www.jetbrains.com/idea/) makes this possible and very easy) or by running in debug mode (praise the IDE again :) – eirc Dec 24 '12 at 10:30
  • github links return 404 :( – pedrorijo91 Nov 10 '17 at 21:40
  • Fixed links to moved sprockets repo but not really sure this still makes sense so many years later. – eirc Nov 12 '17 at 13:30
27

This will also do the trick in an initializer:

Sprockets::Context.send :include, MyHelper

In development mode the helper will not be reloaded on every request.

Your approach with UrlHelpers will work until you need to find post_path(...um...we don't know what post id we'll need at compile time...). There is a ruby gem which reimplements all of your Rails UrlHelpers in JavaScript: https://github.com/railsware/js-routes

user1158559
  • 1,954
  • 1
  • 18
  • 23
  • 1
    I had to add a railtie to my application.rb file since I was using config.assets.initialize_on_precompile = false . https://stackoverflow.com/questions/9235292/how-can-i-run-some-initializers-when-doing-a-rails-assetsprecompile/12700183#12700183 on how to set up the railtie. – HannesBenson Apr 30 '14 at 11:20
19

I'm not quite sure, why you want to do this.

  • You would prevent the javascript file from being cached, as it contains a dynamic value

  • You would get a coupling between your javascript/coffeescript and rails

If possible I would advice you to abstract your problem away by providing your target path in the view and retrieve the value from the DOM-Element when the specific event occurs. Just like 'Unobtrusive Scripting Adapters' for Rails do. E.g.: https://github.com/rails/jquery-ujs/blob/master/src/rails.js#L157-173

topek
  • 18,609
  • 3
  • 35
  • 43
  • 20
    this wouldn't prevent caching. the assets get compiled on deploy and never change that path doesn't change (unless it actually does and the assets get recompiled) this is particularly useful if, say you wanted to use an image path in your JS that is hosted from your `asset_path` – brad Oct 19 '12 at 13:27
  • 4
    As mentioned above I would not want to do this, because it introduces coupling between your app and your JavaScript. Every part of your app should serve a different purpose. You wouldn't want your view to have model logic and I personally do not want Rails code leaking into my JavaScript. Of course this is kind of personal preference. JavaScript has many possibilities to retrieve information from the DOM without being directly injected into it. You would not want to hard code these values. So why would it be better to have a helper do this for you? – topek Mar 23 '13 at 21:24
  • At where in the code did the github link originally point when it was first posted? Having links to "master" branch makes them difficult to follow after some time. – valscion Dec 20 '13 at 14:45
8

Indeed I agree with @eirc answer (taking into account Sprockets documentation). But I would consider including helper modules at rails boot time. As per

# config/initializers/sprockets.rb
Rails.application.assets.context_class.instance_eval do
  include ActionView::Helpers
  include Rails.application.routes.url_helpers
  include MyModule
end

this is because of: https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/railtie.rb#L23

Andrea Amantini
  • 480
  • 4
  • 11
5

Actually, not sure if this helps but there is a way to define your own helpers for use during rake precompile

Create a new initializer and put this in it:

module Sprockets
  module Helpers
    module RailsHelper

      def my_helper_method
       ...
      end

    end
  end
end

And then you can:

<%= my_helper_method %>

in your .erb assets

Matthew
  • 12,892
  • 6
  • 42
  • 45