Search code examples
ruby-on-railsjsonapiactiveresourcerecursive-datastructures

How to flatten a JSON response when using ActiveResource?


I have an API and a client app, and I am using rails with ActiveResource.

I have a Recruiter model that inherits from ActiveResource::Base

Let's say on the client side I write:

dave = Recruiter.new(email: "[email protected]", password: "tyu678$--è", full_name:      "David Blaine", company: "GE") 
dave.save

The request I send is formatted like so:

{"recruiter":{
    "email": "[email protected]",
    "password": "tyu678$--è",
    "full_name": "David Blaine",
    "company": "GE"
    }
}

and the Json response I get from the API is formatted like:

   {"recruiter":{
    "email": "[email protected]",
    "password": "tyu678$--è",
    "full_name": "David Blaine",
    "company": "GE",
    "foo": "bar"
    },
    "app_token":"ApfXy8YYVtsipFLvJXQ"
}

The problem is that this will let me access the app token with dave.app_token but I can't for instance write dave.foo, which raises an error.

Is there a way to flatten the response or read through it recursively si that I can access all of my instance's attributes while keeping the API response formatted as it is?


Solution

  • Looking through the whole ActiveResource process, you can overwrite the load method in your Recruiter model.

    I just added the code in the #HACK section which "flatten" your attributes.

    def load(attributes, remove_root = false, persisted = false)
      raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
      @prefix_options, attributes = split_options(attributes)
    
      # HACK
      if !attributes[:app_token].nil?
        attributes[:recruiter]["app_token"] = attributes[:app_token]
        attributes = attributes[:recruiter]
      end
      # /HACK
    
      if attributes.keys.size == 1
        remove_root = self.class.element_name == attributes.keys.first.to_s
      end
    
      attributes = ActiveResource::Formats.remove_root(attributes) if remove_root
    
      attributes.each do |key, value|
        @attributes[key.to_s] =
        case value
        when Array
          resource = nil
          value.map do |attrs|
            if attrs.is_a?(Hash)
              resource ||= find_or_create_resource_for_collection(key)
              resource.new(attrs, persisted)
            else
              attrs.duplicable? ? attrs.dup : attrs
            end
          end
        when Hash
          resource = find_or_create_resource_for(key)
          resource.new(value, persisted)
        else
          value.duplicable? ? value.dup : value
        end
      end
      self
    end