2

I'm working on a hotel aggregation site that lets the user adjust filters (e.g. price or number of guests) and watch the availability of accommodations refresh via ajax. It's built on Rails 4 with CoffeeScript and Jquery.

Here is the JQuery call (Coffeescript):

$.ajax '/accommodations/show',
      type: 'GET'
      dataType : 'script'
      data:
        privates: include_privates
        shared: include_shared
        homes: include_homes
        price_range: price_range
        start_date: start_date
        end_date: end_date
        num_guests: num_guests
      success: (data) ->
        #RESPONSE DOES NOT HAPPEN HERE, IS HANDLED IN SHOW.JS.ERB
        console.log "Success!"
        console.log data
      error: (data, status, error) ->
        console.log 'How embarassing! Something went wrong - please try your search again. '
        console.log "Status: #{status}"
        console.log "Error: #{error}"

90% of the time, this code works. The other 10% of the time, the server returns 200 status (okay), but the Jquery fails and the console displays the following:

How embarassing! Something went wrong - please try your search again. 
Status: parsererror
Error: SyntaxError: Unexpected identifier

Every other stackoverflow question with this issue looks like it was invalid JSON being passed back to Jquery. I identified the problem line in show.js.coffee.erb. It is where we convert the rails models into JSON so it can be passed to javascript.

$('.refresh-loading').hide()
//Adding the accommodation modules to the left-hand side
$('#accommodation-modules-wrapper').html("<%= escape_javascript(render partial: 'accommodations/accomm_modules', locals: {properties: @accommodations, slider_min: @price_low, slider_max: @price_high})%>")
window.init_isotope()

//Removing the loading icon
$('.refresh-loading').hide()

//THIS IS WHERE THE ERROR IS
//Removing this line fixes the issue
marker_string = '<script>window.accommodation_markers = <%= raw @accommodations.to_json %>;</script>'

The output of raw @accommodations.to_json is the following:

[{
    "id": 741580,
    "name": "Gamer's Paradise in Brooklyn!",
    "google_place_id": "ChIJOwg_06VPwokRYv534QaPC8g",
    "type_code": "hotel",
    "external_id": 2243038,
    "lat": 40.694426,
    "lng": -73.94636,
    "location_rating": 9.0,
    "overall_rating": 9.0,
    "review_count": 13,
    "currency_code": "USD",
    "average_nightly_price": 30.0,
    "image_url": "https://a2.muscache.com/im/pictures/asdfa-4cf7-4222-9204-619200def457.jpg?aki_policy=small",
    "url": "https://www.test.com/rooms/13396285",
    "review_url": "https://www.test.com/rooms/13396285#reviews",
    "accommodation_cluster_id": null,
    "created_at": "2016-12-07T11:22:21.319-08:00",
    "updated_at": "2016-12-14T08:48:51.073-08:00",
    "usd_average_nightly_price": "30.0",
    "city_rank": 0,
    "city_rank_price": 15,
    "city_rank_reviews": 511,
    "cluster_rank": 0,
    "cluster_rank_price": 0,
    "cluster_rank_reviews": 1,
    "shared_room": true,
    "private_room": false,
    "entire_home": false,
    "external_uid": null
}]

This output is valid according to JSONLint. How do I go about further debugging and solving this issue?

6
  • 1
    @PaulRoub seems correct on this one. Although you should be probably converting to json before assigning it to the instance variable and validating there if you still have issues. Commented Jan 11, 2017 at 19:18
  • @Jeff - If I do the conversion (i.e. 'raw') in the controller method instead, I still have the same issue. Commented Jan 11, 2017 at 19:22
  • 1
    how did you validate it in the controller action? Is the hash you copied an instance when it did not work? Commented Jan 11, 2017 at 19:29
  • I assigned an instance variable and ran raw in the controller. @test = raw @accommodations.to_json. Then in show.js.coffee, I have <script>window.accommodation_markers = <%= @test %>;</script>. The hash I copied is the exact text when it did not work. Commented Jan 11, 2017 at 19:32
  • 1
    The "name": "Gamer's Paradise in Brooklyn!" part is suspicious given the single quotes around marker_string's value. Commented Jan 11, 2017 at 19:56

1 Answer 1

2

Let's start with a simplified example and see what happens. If you have this in Ruby:

@thing = { :id => 741580, :name => "Gamer's Paradise in Brooklyn!" }

and then in some ERB you say:

thing = '<%=raw @thing.to_json %>'

then you'll see this as the output:

thing = '{"id":741580,"name":"Gamer's Paradise in Brooklyn!"}'

and there's your problem: neither raw nor <%= ... %> will quote/escape/encode the '. That raw call simply tells the ERB engine not to HTML-encode its argument and you're not targeting HTML so you the raw functionality in the mix. But you want more, you want those quotes escaped.

Probably the easiest thing to do is to use String#html_safe and escape_javascript. But you have JavaScript inside a JavaScript string being generated by ERB so you need to double escape things; something unpleasant like:

marker_string = '<script>window.accommodation_markers = <%= escape_javascript(escape_javascript(@accommodations.to_json.html_safe)) %>;</script>'

I'd strongly lean towards refactoring things so that the "<script> inside a JavaScript string" hackery wasn't needed. Three layers of encoding/escaping is a bit much and very error prone.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.