Customize ActiveResource to handle upper case JSON key

19 Jun 2020

Recently I use activeressource to wrap an 3rd API service. But the response JSON keys are all upper case like this:

{
  "NAME" => "Card 1"
}

Then, when you using activeresource to wrap it, you have to use upper case key to get the result like this:

card = API::Card.find(1)
card.NAME

It’s not very suit for ruby-like code. It should be:

card = API::Card.find(1)
card.name

So I did some customizaton works base on ActiveResource

module API
  class Base < ActiveResource::Base

    def method_missing(method_symbol, *arguments)
      method_symbol = method_symbol.upcase if downcased_known_attributes_setter_or_getter?(method_symbol)

      super
    end

    private

    def downcased_known_attributes_setter_or_getter?(attr_name)
      downcased_known_attribute_getters_setters.include?(attr_name.to_s)
    end

    def downcased_known_attribute_getters_setters
      @downcased_known_attribute_getters_setters ||= (
        downcased_known_attributes_setters + downcased_known_attributes
      )
    end

    def downcased_known_attributes_setters
      @downcased_known_attributes_setters ||= downcased_known_attributes.map{ |attr| "#{attr.downcase}=" }
    end

    def downcased_known_attributes
      @downcased_known_attributes ||= known_attributes.map(&:downcase)
    end
  end
end

Now you can use:

card = API::Card.find(1)
card.name

But sometiems I want do this:

card = API::Card.new(name: 'card name')
card.save

If you do some tests, you will find that the post payload using name as the JSON key but not NAME. And the request will fail, because the 3rd server only support upper case keys. So we have to do more customization works:

module API
  class Base < ActiveResource::Base

    def initialize(attributes = {}, persisted = false)
      attributes.transform_keys! do |key|
        key.upcase
      end

      super
    end

    # ...
  end
end

See, we transform all attribute keys to upper case. Now everything works fine.

Back to top