Ruby


Move fast, move early.

And we are moving again. Since we will move now to the official developer blog of my current employer TravelIQ, this site will not be continued. But feel free to join us at our shiny new place around trafikant.de.

And of cource all articles published here are available as well at our new home.

For the communication between client and server in an AJAX environment JSON became a very important part. It is easy to use, to understand and supported from the most important programming languages (if not native like in Javascript, at least there is a framefork provided). Usually one would use the default encoders to translate the objects provided on the one side to data represented using JSON and then again decode to extract the objects.

But in some cases using the usual encoders would not be enough. For these cases Rails provides a very easy framework to plug you own encoders for you own class. The process to achieve that is simple and again easy to understand, but the procedure can leave some obstacles on the road that need to be survived.

The following sample will explain, what can happen and will show a solution to avoid the most problematic JSON CircularReferenceError.

class MyArray < Array
  attr_accessor :providers
end

The above noted class provides a new type of Arrays containing a new attribute to better identify the providers of the array. Using this array in Ruby is now problem, since the runtime will always identify this object correctly. But what about serialization? Here the problem starts. The serialization will not uses the attribute and simply ignore it. So the next step would be to write an own encoder to provied a specific JSON encoding.

module ActiveSupport
  module JSON
    module Encoders
      define_encoder MyArray do | fra |
        myfra = fra.compact
        returning( result = '{' ) do
          i = -1
          result << '"providers": ' << myfra.providers.to_json << ', '
          result << '"length": ' << "#{myfra.size}" << ', '
     
            result << myfra.map do | val |
              [ i += 1, val ].map { |value| value.to_json } * ': '
            end * ', '

          result << '}'
        end

This code provides a simple Encoder for our MyArray class and adds the provider to our data string. Of course on the client side (or the other side), this will not result in an array but more an object that has integers as attributes and the values as values for these attributes. But in Javascript this would feel like an array.

// ...
// data is the variable containing the parsed object fromm our ruby side.
for (var i=0; i < data.length; i++){
  alert(data[i]);
}

for (i=0; i < data.providers.length; i++){
  alert(data.providers[i]);
}

Well, and this is what we wanted to achieve. An user defined array class containing additional attributes to carry additional data. So far, so good. But it is not as simple as it seems. Just imagine the following example that creates an empty instance of MyArray containing an emtpty providers Array. In the beginning everything is fine, but as soon as you want to serialize the instance using our user defined JSON Encoder, we will receive a circular reference error. Before I will explain a solution for our problem let’s have a look on what happens while serializeing. At first for each object a new stack is created, this stack contains all objects that are to be serialized. The reason for this stack is simple: If the object contains an instance of itself, this would result in a circular reference and an endless loop.

But why do we get an circular reference error? We put at first an instance of our MyArray class, and then an instance of an normal Array into the stack list. It looks like there is no circular reference! But there is, or at least for Ruby it looks like there is! To explain, have a look at the following piece of code:

a = MyArray.new
b = Array.new # Or simply []
puts a == b

Actually a == b is what the include? call is doing when recursing the object stack. The result of this piece of code is true. The instance of MyArray has the same == method as Array, they share it. Since Array overwrite the default == method of Object, the result is true. And this is the simple and short reason why we receive a CircularReferenceError message.

To avoid receiving this error, the first and most simple reason would be to define an own == method for MyArray and actually this is the most safe method. Another idea would be to extend the include? method used by the JSON Encoder. Instead of simply calling == for all objects, we could combine this with an instance check.

  # This method is directly taken from Rails json.rb
  def raise_on_circular_reference(value)
          stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
          raise CircularReferenceError, 'object references itself' if
            stack.include? value && stack.find {|e| e.instance_of? value.class}
          stack << value
          yield
        ensure
          stack.pop
  end

The include condition is now extended using the stack.find {|e| e.instance_of? value.class} fragment. The circular reference error will only be raised if the referenced stack element is an instance of the current value class.

The second possibility may have side effects of that I am not sure. But I think I will investigate in that direction. If you have any ideas, please tell me!