Can I use CoffeeScript in the views executed on render.js? - ruby-on-rails

What do I need to do so that I can use CoffeeScript in the Rails JS views? For example:
def index
format.js { render :layout => false }
end
What would I need to do in order for Rails to use index.js.coffee?

Johnny's answer is correct. If you look at the pull request linked to from the CoffeeBeans page, you have dhh saying
Once we have a fast, clean implementation, it's welcome in core. 3.2 is a more likely target, though.
I briefly talked with Sam Stephenson and Josh Peek about this at Railsconf, since this was a missing feature people had asked me about after my CoffeeScript talk. After all, Rails 3.1 is pushing CoffeeScript as a default pretty hard; it seems odd that there are places where pure JS has to be used. Sam's reaction was that this wouldn't be efficient, because you'd have to fire up the CoffeeScript compiler on every page request, even in production. That's because code like
<%= coffee_script_tag do %>
alert "coffee script is #{verb}!"
<% end %>
creates an ERB interpolation (not a CoffeeScript interpolation—unfortunate that both use the same syntax), potentially yielding a different string of CoffeeScript code on every request. And there's no way to tell, from the coffee_script_tag implementation, whether the given code is going to be the same every time (i.e. whether there's an ERB interpolation or not).
Now, the CoffeeScript compiler is very fast, but compiling to JavaScript is still going to add a little extra time to each request. So the Rails team is hesitant to encourage the practice.
For the sake of efficiency, and to avoid the ambiguity between ERB interpolations and CoffeeScript interpolations, you should probably keep your CoffeeScript somewhere (perhaps as a .coffee file in the same directory as your view) and compile it to JavaScript by hand.

It's not yet supported in 3.1. You will need to use Coffeebeans.
Update: It is now supported in rails 3.2

This is now working in Rails 3.2. For example, I have a resource named book. This resource has a file at app/views/books/index.html.erb with the following:
<%= link_to 'test me', new_book_path(color: 'blue'), remote: true %>
Then I have a file at app/views/books/new.js.coffee at ≈ with the following code:
test = ->
'this is a test'
console.log test()
console.log( "<%= params[:color] %>" )
I see:
'this is a test'
'blue'
in my browser console.

if you dont want to install coffeebeans, heres a sort of quick and dirty way to do it by hacking into the erb outputter a bit :
<%
require 'coffee-script';
def coffee_script; CoffeeScript.compile yield '' end %>
<script type="text/javascript">
<% compiled = coffee_script do |_erbout|%>
->
console.log 'works! this part is coffeescript!'
<% end %>
<% _erbout.concat compiled %>
</script>

Related

rails, make render add a HTML comment of the filename being rendered for development

I'm working with an application whose is a bit complicated and was not designed by me. As it uses A LOT of partials, It would be really helpful if I could automatically add an html template each time a new erb/rhtml file is rendered.
So for exmaple if code have this:
<%= render(:partial => 'personal_details', :object => #auser) %>
the ouput html add something like:
<!-- Rendering: views/users/_personal_details.rhtml called from other_file.rhtml -->
How to acoomplish that?
First of all, are you already using xray-rails to visualize your partials? It is an incredibly useful tool.
If you need something more than that, take a look at the xray-rails source code for some pointers. Essentially what you'll need to do is monkey-patch ActionView::Template#render as shown here.
To summarize:
ActionView::Template.class_eval do
def render_with_prepend_comment(*args, &block)
# Defer to original implementation to do the actual render
source = render_without_prepend_comment(*args, &block)
# Then augment it as desired
"<!-- prepended comment -->\n" + source
end
alias_method_chain :render, :prepend_comment
end
There's a little more to it; see how xray-rails does its augmentation.
This gem claims to support the functionality you desire.
Rails >= 6.1 includes an option for ActionView called annotate_rendered_view_with_filenames which determines whether to annotate rendered view with template file names. This defaults to false.
In your case, you probably want to add the following to your development.rb configuration file:
# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true
This results in commented annotations similar to these in your rendered HTML:
<!-- BEGIN app/views/layouts/partials/_header.html.erb -->
<header></header>
<!-- END app/views/layouts/partials/_header.html.erb -->
Here is the description from the guides and here is the implemenation in Rails itself.

Rails path-helpers doesn't work in js.coffee.erb

In my Rails 3.2 app (Ruby 1.9) I get following error when using path helpers in Coffeescript.
undefined local variable or method `new_user_session_path'
In my partial _usermenu.html.haml that works fine:
= link_to t('user.login'), new_user_session_path
In my app/assets/javascripts/metamenu.js.coffee.erb that throws above error:
$.get("<%= new_user_session_path %>")
Isn't it possible to use x_path and x_url helpers in coffeescript erb's?
This is because you are not within the view context inside of your assets. Adding an erb extension to the file doesn't change this, it simply allows you to evaluate embedded ruby.
If this is a one-off scenario, your best bet is to simply use the string itself.
$.get("/sign_in")
If you really wanted to you could create a partial that output a script tag that output your helper methods into js variables and access them that way.
# in your layout
<%= render 'url_helpers' %>
# in app/views/layouts/_url_helpers.html.erb
<script>
window.new_user_session_path = "<%= new_user_session_path %>";
# add more if necessary
</script>
# in your coffeescript
$.get(#new_user_session_path)
Also worth keeping in mind that this will obviously never work for member routes where your passing an instance of a model to the url helper as that is definitely not available to coffeescript. Remember, in production assets are precompiled so you can't use anything dynamic. For that you can only really rely on setting up actions in your controller to respond to JS calls.
Old post, but still accessible from Google.
In rails 4 (and certainly at least 3 too) you can use the route helpers to insert your js files easily:
assets/javascript/my_file.js.coffee.erb
<% self.class.include Rails.application.routes.url_helpers %>
window.index_route = '<%= index_path %>'

Possible to use ERB/slim template instead of EJS (JST) after Ajax update?

I've seen a lot of questions around this topic but no definitive solutions. I have a Rails 3.2 app that leverages ERB/slim and Coffeescript/EJS/Backbone. I inherited this codebase so some of these peripherals are a little over my head.
Problem
My venue view has a section that displays tips that were submitted to the venue. The list of tips has a "sort" function facilitated by JavaScript. The associated CoffeeScript file has event listeners for clicks on links for "recent" and "popular". On click, the JS does some work and utilizes Rails scopes to resort the list. I'm building out this tips list to include a little more data, specifically including the Rails helper time_ago_in_words. The original code updated the div containing the tips using a JST/EJS template in the Javascripts/templates asset directory.
These are the specific problems I encounter:
If I add .erb to the file chain (after updating the EJS interpreter to look for a different evaluation and interpolation pattern as to not conflict with ERB), I can evaluate basic Ruby expressions. The partial, however, fails to have access to the same tip variable that the JavaScript references. I know this is because Rails is server side and JS is client side. Consensus seems to be no, but is there a way to get that data to Rails?
While basic Ruby expressions can be evaluated (Time.now, for example), Rails helpers such as time_ago_in_words fail, complaining that the method is undefined for the class, though I am absolutely passing in the proper date object. Can Rails/helpers not be evaluated in this .EJS.ERB chain?
I can circumvent all of these problems if there is a way to reference the original ERB/slim partial used on load after the sort is performed. Is this possible? Right now the Coffeescript file called uses render and JST to call on the EJS template. Is it possible to somehow reference the original partial but with the updated sort?
Here's the relevant code.
##show.html.erb (venue)##
#tips
p.sort.span4
| Sort by:
= link_to 'Recent', '#', class: 'selected', id: 'sort-tips-recent'
| |
= link_to 'Popularity', '#', id: 'sort-tips-popularity'
#tip-list
= render resource.tips.by_recent
##tip_list_view.js.coffee##
class CS.TipListView extends Backbone.View
el: "#tip_module"
events:
"click #sort-tips-recent": "sortByRecent"
"click #sort-tips-popularity": "sortByPopularity"
initialize: ->
console.log("ERROR: View must be initialized with a model") unless #model
#model.bind('reset', #render)
render: =>
tips = #model.map (tip) -> JST['templates/tip'](tip: tip)
#$('#tip-list').html(tips.join("\n"))
sortByRecent: (e) ->
e.preventDefault()
#model.enableSortRecent()
#updateSelectedFilter('recent')
sortByPopularity: (e) ->
e.preventDefault()
#model.enableSortPopularity()
#updateSelectedFilter('popularity')
updateSelectedFilter: (sort) ->
#$('p.sort a').removeClass('selected')
#$("p.sort a#sort-tips-#{sort}").addClass('selected')
##tip.jst.ejs.erb (called from tip_list_view.js.coffee after sort change##
<div class="tip">
<p class="tip-author">
<$= tip.get('user').username $>
</p>
<p class="tip-timestamp">
Eventually, time goes here
</p>
<p class="tip-likes pull-right">
<$= tip.upvoteCountText() $>
</p>
<p id="<$= tip.domId() $>">
<$= tip.get('text') $>
<$ if(tip.get('can_upvote')) { $>
upvote
<$ } $>
</p>
<div>
Definitely at a loss here - any and all help is greatly appreciated. Let me know if there is any other detail or code I can provide for background. Thanks in advance!
With your ejs.erb, Rails is generating the template, but has no idea what you will use it for. Then your Backbone app is fetching the tip from Rails via ajax, and populate the model accordingly. Then it's your backbone app that populates and renders the template. Rails cannot chime in here, at least not in this configuration where backbone expects json and then renders the template. Everything is happening in the front-end.
The only thing you can do is ask Rails to give you the result of your helpers inside the json. I am guessing you have a Tip.rb model and it should implement:
include ActionView::Helpers::DateHelper
...
def as_json(options = {})
{
:name => self.name,
... (all the model attributes that you want to expose)
:date_age => time_ago_in_words self.created_at
}
end
That being said, I would def use a client side solution, also because: http://rails-bestpractices.com/posts/105-not-use-time_ago_in_words

Using reCaptcha with Rails 2.3.12

So I'm trying to get reCaptcha to render on a partial form view that uses HAML. I have tried using the :ruby filter and then adding <%= recaptcha_tags %> but that didn't work, neither has anything else that I've found. Is there a way to implement this?
*Revision
Ahem, more specifically, can anyone tell me what I need to have for the <%= recaptcha_tags %> helper? Every thing I find on this subject just says "Add <%= recaptcha_tags %> wherever you want it to appear!" and absolutely nothing on what the helper should contain.
*Another Revision
I am indeed trying to use Ambethia. I tried using just = recaptcha_tags but that didn't work, I got an error saying it was an undefined variable or method. I installed the Ambethia/reCaptcha as a plugin using script/plugin install git://github.com/ambethia/recaptcha.git and I put config.gem "ambethia-recaptcha", :lib => "recaptcha/rails", :source => "http://gems.github.com" in environment.rb along with my public/private keys.
*Started Over
Okay, got rid of everything I had done initially. Can anyone help me with this? I follow all of the tutorials I can find on it, but none of them explain how to implement/create the helpers for <%= recaptcha_tags %> or <%= verify_recaptcha %>. I'm obviously new to RoR and implementing reCaptcha of any kind, so I'm sorry I'm asking for my hand to be held but I am honestly lost and am not finding any guidance anywhere! Thanks so much anyone and everyone.
did you try simply:
= recaptcha_tags
You don't mention the plugin you're using. I'm assuming this one. If that's the case, the recaptcha_tags helper will return the HTML for the captcha, and you'd insert it into whichever forms you wanted the captcha to appear on.
The <%= %> around recaptcha_helper aren't part of the helper, but rather the way you insert content into erb templates (and other templating languages resembling erb). In Haml you don't need the surrounding tag. It's just =.
I had the same problem and I finally solved it realizing that my form was called asynchronously.
I added:
= recaptcha_tags :ajax => true
and captcha appeared.
Hope this could help the original question.

Rails 3: Simple AJAXy Page updates?

I can't believe I've been looking four hours for this simple task, but I have.
In Rails 2.3, I could replace one section of a page with this simple code:
render :update do |page|
page.replace_html "div_id", :partial => "new_content",...
end
In Rails 3, Ryan Bates has me writing entire new javascript functions, switching from Prototype (rails default) to jQuery, and otherwise not enjoying life. The other tutes are no more straightforward.
What am I missing? How do we replace a <div> these days?
Thanks, guys. The official answer seems to be that, yes, the team felt simple is the enemy of good and made it more complicated.
The first key is to create a .js.erb file NAMED for the method CALLING the ajax update. So if the index method handles the update, put the raw javascript in index.js.erb. This goes in the views folder.
Second, the code that worked in index.js.erb was
m = $('list_users');
m.innerHTML = "<%= escape_javascript(render :partial => "reload_users") %>";
Then to make the call, add in the respond_to block of the controller method, add:
format.js
Finally, the calling view has:
<%= link_to "Update User List", #reload_users_path, :remote => true %>
By the way, supposedly all the old pages using page.replace will work if you install a plugin. The plugin download page suggests that it broke in the last releases of Rails 3 and has not been fixed. Also, various bloggers will come to your home and birch-switch you if you use it.
The whole RJS stuff makes the javascript inline and makes the dom very obtrusive. Also, by avoiding inline javascript you could open up other possible ways of optimizing you javascript by compressing and caching those files in browser. Thats the reason why RJS is getting out of scope from rails 3. A little bit of getting around with jQuery or Prototype for a day should get you on gears with these kind of small stuff and will help the project on long run.
Do you still have jQuery in there? I'd recommend it over Prototype any day...
If it's still there you can just use the following in your Javascript:
$.get("<%= url_for path/to/partial %>",
function(response) {
$("#div_id").html(response);
});
This gets the partial via AJAX and just dumps it into the div with id div_id.
Hope this helps!
I'm not even sure you need to make an AJAX call to load that partial. I believe that in a js.erb file, a call to render(:partial => object_or_path) will just return a string with all the html, which you can wrap in a jQuery object and append. Example:
$('#div_id').html($('<%= render :partial => #object %>'))
As far as I know, along the same line as the answer above, you can do something like this in your template:
<%= link_to "Update User List", #reload_users_path, :remote => true %>
And in controller, do this:
respond_to do |format|
format.js {
render :text => "alert('reloaded')"
}
end
This way you can have controller "execute" client side JS much the same as as render :update used to do. This is equivalent to doing the following in Rails 2:
render :update do |page|
page << "alert('reloaded')"
end
Is there any reason why this approach is not advisable?
Try this:
page.call "$('#div_id').html", render(:partial => 'new_content')

Resources