module Carto # Rails 3 stores nil strings with JSON serialization as 'null', so we need to replace it with a valid value. # See https://github.com/rails/rails/issues/13144 class CartoJsonSerializer def self.dump(hash) hash.try(&:to_json) end def self.load(value) value.try(&:with_indifferent_access) end end # CartoJsonSerializer is closer to Rails Json serializer, using with_indifferent_access # for load. The problem with that is that although you can _access_ first # level keys both with symbols and keys, other common operations, such as # key comparison, fail, and it only applies to the first level. class CartoJsonSymbolizerSerializer < CartoJsonSerializer def self.load(value) value && symbolize(value) end def self.symbolize(value) if value.is_a?(Hash) value.deep_symbolize_keys elsif value.is_a?(Array) value.map { |v| symbolize(v) } else value end end private_class_method :symbolize end end class CartoJsonSymbolizerValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value && !(value.is_a?(Hash) || value.is_a?(Array)) record.errors[attribute] << 'wrongly formatted (not a Hash or invalid JSON)' end end end class JsonSchemaValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return unless value value_for_json = prepare_for_json(value) schema = Carto::Definition.instance.load_from_file("lib/formats/#{record.class.to_s.underscore}/#{attribute}.json") errors = JSON::Validator::fully_validate(schema, value_for_json, strict: true) record.errors[attribute] << errors.join(', ') if errors.any? end private def prepare_for_json(value) if value.is_a? Hash value.with_indifferent_access elsif value.respond_to? :map value.map { |v| prepare_for_json(v) } else value end end end