to_json and Rails 2
Published by Andre February 13th, 2008 in Fixes, JavaScript, Ruby on Rails, The BookRails 2 changed the way to_json works on ActiveRecord objects. Prior to Rails 2, to_json returned a hash of hashes — the first level hash had the key ‘attributes’, which in turn had another hash with your field name/values.
With Rails 2, calling to_json on an AR object just gives you a hash of field name/values. The new way is easier and more intuitive. However, in breaks our code in places where we use to_json to pass AR data into JavaScript.
The changes are straightforward. There were two ways we dealt with the intermediate “attributes” hash prior to Rails 2:
- we used map/collect before calling to_json, i.e.: cities.collect{|c|c.attributes}.to_json
- we dealt with ‘attributes’ in JavaScript, i.e., var marker=markers[i].attributes
Fixing it just means removing references to the ‘attributes’ and using the ActiveRecord instance directly.
Here are all the places changes need to be made:
- 3.8: var marker=markers[i].attributes to var marker=markers[i]
- 4.6: all instances of result.attributes to just result
- 4.15: var stores=<%=(@stores.collect {|s|s.attributes}).to_json%>; to var stores=<%=@stores.to_json%>;
- 5-12: remove the to_json method from class Tower
- 7-2: render :text=>cities.collect{|c|c.attributes}.to_json to render :text=>cities.to_json
- 7-5: render :text=>cities.collect{|c|c.attributes}.to_json to render :text=>cities.to_json
- 7.15: var points=<%=@towers.collect{|c|c.attributes}.to_json %>; to var points=<%=@towers.to_json %>;




I use json and have no problem! This is very good think =)
In Rails 2.1, to_json seems to return hash of hashes by default.
# Include Active Record class name as root for JSON serialized output.
# ActiveRecord::Base.include_root_in_json = true
3.8: var marker=markers[i].attributes to var marker=markers[i].marker
I am trying to test your code in example 4-15 with rails 2.0.
I am seeing a syntax error when using json.
In your updated example: you said to change the code to var stores=; to var stores=;. This is the same thing. :) I also used the new update on this site and it is different.
var points=;
This is also throwing an error.
To see what Rails to_json function returns put this statement
in javascript file
alert(’request.responseText :’ + request.responseText);
@jeremy
Put
var stores = ;
in map.html.erb (as it is explained above). Next see Page Source in a web browser to see what is the value of stores.
You should link this from the errata– I only found this page after spending a bunch of time trying to figure out why listMarkers() in chapter 3 didn’t work and eventually figuring it out myself. I even came and looked at the errata page but it’s not there. I only got here after googling “rails to_json” once I realized there was a difference in the structure in the book vs. what I was getting back.
The upside: I learned more how to debug javascript and its interactions with rails.
the downside: it took more time than I would have liked because I don’t know much javascript and the way it deals with objects.
joinlee, thanks for your August 16th post. I was completely stumped and unable to persuade my map to retrieve the points from the DB. I had pinned it down to a to_json type problem, but got lost working out how to debug. In the end it was a case of replacing .attributes with .marker. thank you so much for that gem!
I am using rails 2.2.2 and am only on chapter 3. I had to make this change to application.js
var marker=markers[i].attributes to var marker=markers[i].marker
and then it would pull the Markers already in the DB. Thanks, I am thrilled with this book.
PS I do have a question. If I only wanted to allow ONE geocach site what would you recommend. This is what I did, but do not think it is a good idea.
(the goal is to allow a user to select where they are from, but I only
want to capture and store a single geocode - no addresses can be stored in the system)
added to bottom of appl.js
function oneTime() {
removeListener(clicker)
}
window.onload = init;
window.onclick = oneTime;
window.onunload = GUnload;
in init() I changed the GEvent.addListener….
to
clicker = GEvent.addlistener…
I must admit that I’m a little confused, too, with regards to the example for 4-15. I’ve changed it from:
var stores=;
to
var stores=;
But it keeps giving me an “invalid argument” error. Any ideas?
Actually, never mind. I figured it out. Apparentally, the error I was getting was due to the loop that placed the markers on the map. I had to change the loop to look like:
for (i = 0; i < stores.length; i++) {
var store=stores[i].store;
addMarker(store.lat, store.lng, store.name);
}
anybody who is trying to get the examples running on rails 3 beta:
rails 3 escapes erb by default. so make sure to use the raw method like this:
var spots = ;
“var spots = ;”
is what I meant…