Ruby interpolation inside :javascript filter

5,598 views
Skip to first unread message

Les Nightingill

unread,
May 21, 2013, 12:22:27 PM5/21/13
to ha...@googlegroups.com
I have this in a rails view file:

...
:javascript
  var departments = new Object()
  - @faculties.each do  |fac|
    departments['#{ fac.id }']= new Object()

And the error message I get is:
undefined local variable or method `fac' for #<#<Class:0x00000104e1b3d8>:0x000001074bb420>
I'm clearly missing something very basic. Can anyone see what the problem is, please. Thanks in advance.

Les

Duncan Beevers

unread,
May 21, 2013, 12:29:57 PM5/21/13
to ha...@googlegroups.com
You're in a javascript filter. The code that you think is iterating through @faculties is actually just being inlined as javascript into a script tag.



Les

--
You received this message because you are subscribed to the Google Groups "Haml" group.
To unsubscribe from this group and stop receiving emails from it, send an email to haml+uns...@googlegroups.com.
To post to this group, send email to ha...@googlegroups.com.
Visit this group at http://groups.google.com/group/haml?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Duncan Beevers

unread,
May 21, 2013, 12:38:38 PM5/21/13
to ha...@googlegroups.com
You might be happier using Ruby to map your data to JSON, and then feeding that to your JavaScript, rather than using Ruby to dynamically generate JavaScript.

For example, if you wanted to map the data directly in the view, could do so like this.

- mapped_faculties = @faculties.inject({}) { |a, fac| a[fac.id] = {} }
:javascript
  var departments = #{mapped_faculties.to_json};

In general, I would recommend doing these types of transformations in the controller rather than in the view itself, but the principle is the same.

Duncan Beevers

unread,
May 21, 2013, 12:42:29 PM5/21/13
to ha...@googlegroups.com
Oh, forgot to hand the accumulator back in that inject call. Should look like:
  mapped_faculties = @faculties.inject({}) { |a, fac| a[fac.id] = {}; a }

Les Nightingill

unread,
May 21, 2013, 1:00:07 PM5/21/13
to ha...@googlegroups.com
Thanks for your suggestions Duncan.

I would most definitely be happier mapping the Ruby variables to JSON in the controller. But I'm in the middle of a large Rails upgrade and I'm hoping to defer such refactoring improvements, at least until I get this view basically working and (important) get the tests all green. My first step is the conversion to haml.

So... given that it's a suboptimal architecture, how can I take this working erb:

<script>
var departments = new Object()
<% @faculties.each do  |fac| %>
departments['<%= fac.id -%>']= new Object()
<% end %>
...
</script>

and convert it to haml?

Duncan Beevers

unread,
May 21, 2013, 1:24:49 PM5/21/13
to ha...@googlegroups.com
The code I posted before should do that. The inject call does the same object initialization as the javascript you originally posted.

To be clear, the old haml generated client-side code that looked like this:
<script>
var departments = new Object();
departments[9] = new Object();
departments[67] = new Object();
...
</script>

After this code was run, the resultant `departments` object would look like this:

departments = { 9: {}, 67: {} };

The inject code I provided does the same thing except it bypasses generation of the client-side javascript and instead generates the resultant object as a JavaScript object literal. The new code would look like this:

<script>
var departments = { 9: {}, 67: {} };
...
</script>

Les Nightingill

unread,
May 21, 2013, 2:02:14 PM5/21/13
to ha...@googlegroups.com
Yes, you're right, Duncan, your previous code does achieve the desired outcome. Thanks for that.

What I posted, though, is a simplified version of the problem. And the js here does not have tests (yet), so I'm trying to take very small steps to get green tests. I'm curious if you know a way to directly translate the erb/js script snippet into haml? If not, I'll follow your suggestion.

The problem with poorly tested legacy code is you sometimes have to live with suboptimal architecture for a while until you can get a good test suite in place!

Thanks for your help

Les

Duncan Beevers

unread,
May 21, 2013, 2:12:42 PM5/21/13
to ha...@googlegroups.com
Well, if the old code works and you're just trying to go from erb->haml, I wouldn't use the `javascript:` filter.

Instead, just translate the tags to haml and treat the javascript like text. Erb interpolation sigils get swapped out for ruby string interpolation sigils, block is automatically closed.

%script
  var departments = new Object();
  - @faculties.each do  |fac|
  departments['#{fac.id}']= new Object();

Les Nightingill

unread,
May 21, 2013, 3:01:18 PM5/21/13
to ha...@googlegroups.com
Yes, good idea, that works, except for the error message "illegal nesting: nesting within plain text is illegal". This refers to the indentation within the js code.

I have ended up with the Ruby code under %script and js code under :javascript. Not clean, but a workable interim step. I'll definitely clean this up when I can get some jasmine tests running.

Many thanks for your assistance, Duncan.

Les
Reply all
Reply to author
Forward
0 new messages