This project is archived and is in readonly mode.

#4547 ✓invalid
siplux

ActiveSupport #to_json doesn't work with DM

Reported by siplux | May 6th, 2010 @ 10:55 PM

object#to_json will fail with "ActiveSupport::JSON::Encoding::CircularReferenceError: object references itself"
This is due to the usage of "instance_values" in the #as_json method. DM objects carry a _@repository ivar which carries a reference to the original object.

This can be fixed by adding "dm-serializer" to the Gemfile, which implements its own #to_json method.

on line 137 of activesupport/lib/active_support/json/encoding

  def as_json(options = nil)

if respond_to?(:to_hash)
  to_hash
else
  instance_values
end



end

The following will reproduce the issue

require 'dm-core'
require 'active_support'
require 'active_support/json'
class Foo
  include DataMapper::Resource
  property :id, Serial
  property :name, String
end
DataMapper.setup(:default, 'sqlite3::memory:')
DataMapper.auto_migrate!
Foo.create(:name => "foo").to_json

Comments and changes to this ticket

  • siplux

    siplux May 8th, 2010 @ 11:46 PM

    • Tag changed from rails3 compatibility, datamapper, rails3 to rails3 compatibility, activesupport, datamapper, rails3
  • Dan Pickett

    Dan Pickett May 9th, 2010 @ 06:36 PM

    • Tag changed from rails3 compatibility, activesupport, datamapper, rails3 to rails3 compatibility, activesupport, bugmash, datamapper, rails3
  • Lawrence Pit

    Lawrence Pit May 11th, 2010 @ 12:09 PM

    I also have this with ActiveRecord

  • Neeraj Singh

    Neeraj Singh May 11th, 2010 @ 02:44 PM

    @lawrence Can you provide an example where you are running into same problem with ActiveRecord.

    I am able to reproduce this error with DM.

    I guess if one of the instance variables refers back to the original object as is the case with the first example provided then I see two choices.

    1) Let use pass an option :ignore_circular_reference => true. In this case in stead of raising an error, to_json method will ignore the circular reference and will move on.

    2) Let user pass an option :ignore_items => _my_intance_variable. This will provide more control to user and rather than ignoring all circular references, user is asking to_json to ignore just this variable.

  • blackgold9

    blackgold9 May 12th, 2010 @ 06:03 AM

    I hit the same thing with my models.
    In my case, it fails if i attempt to call to_json on an activerelation object (i.e. church.where().to_json), but works if i do church.where().to_a.to_json

    my model is:

    class Church < ActiveRecord::Base
    has_many :people has_many :groups validates_presence_of :name end

    The only property it has is name.

  • Santiago Pastorino

    Santiago Pastorino May 12th, 2010 @ 03:25 PM

    Can someone provide a failing test using ActiveRecord? i've tried the thing that blackgold9 said but works for me ...
    http://pastie.org/957044

  • Ryan Bigg

    Ryan Bigg May 13th, 2010 @ 11:56 PM

    • State changed from “new” to “incomplete”

    Please provide a test case and patch for this issue.

  • Bernerd Schaefer

    Bernerd Schaefer May 25th, 2010 @ 12:35 AM

    The issue here is that ActiveSupport defines Object#to_json, and therefore Post.where.to_json tries to dump the ActiveRecord::Relation object instead of passing the method call on to it's internal collection. Note, this also applies to Object#to_yaml:

    # script/rails console
    include Test::Unit::Assertions
    
    relation = Post.where # or Post.where(:author_id => 1), etc.
    
    assert_nothing_raised(ActiveSupport::JSON::Encoding::CircularReferenceError) { relation.to_json } # fails
    assert_nothing_raised(TypeError) { relation.to_yaml } # fails
    
    ActiveRecord::Relation.send(:undef_method, :to_json)
    ActiveRecord::Relation.send(:undef_method, :to_yaml)
    
    assert_equal relation.to_a.to_json, relation.to_json # and now passes
    assert_equal relation.to_a.to_yaml, relation.to_yaml # and now passes
    

    This is important because it causes the following code to generate a 500 when requesting /posts/mine.json:

    class PostsController < ApplicationController
      respond_to :html, :xml, :json
      def mine
        @posts = Posts.where(:author_id => 1)
        respond_with(@clients)
      end
    end
    
  • Marcin Kulik

    Marcin Kulik June 10th, 2010 @ 10:53 AM

    Requiring "json/pure" solves the problem for DataMapper, you just need to implement to_json for your models (or for DataMapper::Model).

  • Lawrence Pit

    Lawrence Pit June 10th, 2010 @ 12:28 PM

    What Bernerd says.. exactly the issue I have. None of my controllers work when a json request is done. You'd think this workaround would work:

       def mine
         @posts = Posts.where(:author_id => 1).all
         respond_with(@posts)
       end
    

    But that too fails in rails edge since a while, with "ArgumentError: wrong number of arguments (2 for 1)". Posts.where(:author_id => 1).to_yaml does work, Posts.where(:author_id => 1).to_json doesn't. I attempted to create a patch, but for some reason the delegation of to_json to to_a in ActiveRecord::Relation doesn't work. Anybody else have an idea?

    (see attached what I tried; I also tried what Bernerd did, undef_method of to_json, same result)

  • Neeraj Singh

    Neeraj Singh June 10th, 2010 @ 04:07 PM

    • Tag changed from rails3 compatibility, activesupport, bugmash, datamapper, rails3 to rails3 compatibility, activesupport, bugmash, datamapper, patch, rails3

    Attached is a patch along with test which makes it possible to have code like

    User.where(:name => 'John').to_json
    
  • José Valim

    José Valim June 10th, 2010 @ 06:59 PM

    I'm not sure if we should allow Post.where(:omg).to_json. If so, what about to_xml? And to_yaml?

  • Ola Tuvesson

    Ola Tuvesson June 22nd, 2010 @ 02:20 AM

    • Tag changed from rails3 compatibility, activesupport, bugmash, datamapper, patch, rails3 to json to_json, rails3 compatibility, activesupport, bugmash, datamapper, patch, rails3

    I am having this same issue with a named scope.

    Given this model:

    class Listing < ActiveRecord::Base
      belongs_to :company
      scope :approved, lambda {
        where("approved = ?", true)
      }
    end
    

    And this controller:

    def index
      @listings = Listing.all
      @approved = Listing.approved
      ...
    end
    

    This works:

    respond_to do |format|
      format.json { render :json => @listings }
    end
    

    Whereas using the named scope fails with a CircularReferenceError:

    respond_to do |format|
      format.json { render :json => @approved }
    end
    

    Note: this is in Rails3b3

  • Dan Sully

    Dan Sully June 22nd, 2010 @ 11:22 PM

    This is still an issue with DataMapper 1.0.0 and Rails3 beta4. The above work-arounds using json/pure or dm-serializers don't work for me.

    The test script by the original poster still fails.

  • Neeraj Singh

    Neeraj Singh June 23rd, 2010 @ 03:09 PM

    • Assigned user set to “José Valim”

    Attached is patch with test.

  • Neeraj Singh

    Neeraj Singh June 23rd, 2010 @ 04:56 PM

    Attached is patch with to_xml support. This patch needs be applied in addition to the patch previously mentioned.

  • Repository

    Repository June 23rd, 2010 @ 05:17 PM

    • State changed from “incomplete” to “resolved”

    (from [eb04408a20628a49296e0859425940b39a83ec63]) ActiveRecord's relation object should respond to to_json and to_yaml

    [#4547 state:resolved]

    Signed-off-by: José Valim jose.valim@gmail.com
    http://github.com/rails/rails/commit/eb04408a20628a49296e0859425940...

  • Marcin Kulik

    Marcin Kulik June 23rd, 2010 @ 07:20 PM

    How above ActiveRecord patch is supposed to solve serializing DataMapper objects??

  • José Valim

    José Valim June 23rd, 2010 @ 07:25 PM

    • State changed from “resolved” to “open”

    Hahahaha! The patch was not supposed to close this issue, it just solves another issue raise in this same thread. Reopening.

  • José Valim

    José Valim June 26th, 2010 @ 10:49 AM

    • State changed from “open” to “invalid”
    • Importance changed from “” to “Low”

    About Datamapper: you need to define a as_json implementation for your models, otherwise Rails will use the default implementation causing the errors you mentioned. Maybe, dm-rails guys, could include a default as_json implementation. But I cannot see anything Rails could do here.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>