Permalink
Browse files

Drop JsonSchema classes and use Membrane instead.

 - Drop all JsonSchema classes.
 - Change classes: JsonMessage and VCAP::Config to use Membrane.
 - Modify tests to suit the change.
 - Improve unit tests in class JsonMessage to check exceptions.

Change-Id: I7430bc735352ef628ac53f20e7744d9f6b2b40ce
  • Loading branch information...
kowshik committed Jul 10, 2012
1 parent b7e3508 commit 5334b662238f14ed2ddbb80f524dcf8cdd0eaadf
View
@@ -1,9 +1,7 @@
# Copyright (c) 2009-2011 VMware, Inc
require 'rubygems'
-
require 'yajl'
-
-require 'json_schema'
+require 'membrane'
class JsonMessage
# Base error class that all other JsonMessage related errors should
@@ -21,42 +19,49 @@ class ParseError < Error
# One or more field's values didn't match their schema
class ValidationError < Error
- def initialize(field_errs)
- @field_errs = field_errs
+ attr_reader :errors
+
+ def initialize(errors)
+ @errors = errors
end
def to_s
- err_strs = @field_errs.map{|f, e| "Field: #{f}, Error: #{e}"}
+ err_strs = @errors.map { |f, e| "Field: #{f}, Error: #{e}" }
err_strs.join(', ')
end
end
class Field
attr_reader :name, :schema, :required, :default
- def initialize(name, schema, required = true, default = nil)
- if required && default
- msg = "Cannot define a default value: #{default}"
- msg << " for the required field: #{name}."
- raise DefinitionError.new(msg)
- end
+ def initialize(name, options = {}, &blk)
+ blk ||= lambda { |*_| options[:schema] || any }
@name = name
- @schema = schema.is_a?(JsonSchema) ? schema : JsonSchema.new(schema)
- @required = required
- if default
- errs = @schema.validate(default)
- raise ValidationError.new({name => errs}) if errs
+ @schema = Membrane::SchemaParser.parse(&blk)
+ @required = options[:required] || false
+ @default = options[:default]
+
+ if @required && @default
+ raise DefinitionError, \
+ "Cannot define a default value for required field #{name}"
+ end
+
+ validate(@default) if @default
+ end
+
+ def validate(value)
+ begin
+ @schema.validate(value)
+ rescue Membrane::SchemaValidationError => e
+ raise ValidationError.new( { name => e.message } )
end
- @default = default
end
end
class << self
- attr_reader :fields
-
- def schema(&blk)
- instance_eval &blk
+ def fields
+ @fields ||= {}
end
def decode(json)
@@ -79,14 +84,18 @@ def from_decoded_json(dec_json)
dec_json.delete_if {|k, v| v == nil}
# Collect errors by field
- @fields.each do |name, field|
+ fields.each do |name, field|
err = nil
- name_s = name.to_s
- if dec_json.has_key?(name_s)
- err = field.schema.validate(dec_json[name_s])
+ if dec_json.has_key?(name.to_s)
+ begin
+ field.validate(dec_json[name.to_s])
+ rescue ValidationError => e
+ err = e.errors[name]
+ end
elsif field.required
err = "Missing field #{name}"
end
+
errs[name] = err if err
end
@@ -95,51 +104,51 @@ def from_decoded_json(dec_json)
new(dec_json)
end
- def required(field_name, schema = JsonSchema::WILDCARD)
- define_field(field_name, schema, true)
+ def required(name, schema = nil, &blk)
+ define_field(name, :schema => schema, :required => true, &blk)
end
- def optional(field_name, schema = JsonSchema::WILDCARD, default = nil)
- define_field(field_name, schema, false, default)
+ def optional(name, schema = nil, default = nil, &blk)
+ define_field(name, :schema => schema, :default => default, &blk)
end
protected
- def define_field(name, schema, required, default = nil)
+ def define_field(name, options = {}, &blk)
name = name.to_sym
- @fields ||= {}
- @fields[name] = Field.new(name, schema, required, default)
+ fields[name] = Field.new(name, options, &blk)
- define_method name.to_sym do
+ define_method(name) do
set_default(name)
@msg[name]
end
- define_method "#{name}=".to_sym do |value|
+ define_method("#{name}=") do |value|
set_field(name, value)
end
end
end
def initialize(fields={})
@msg = {}
- fields.each {|k, v| set_field(k, v)}
+ fields.each { |name, value| set_field(name, value) }
set_defaults
end
def encode
- if self.class.fields
- set_defaults
- missing_fields = {}
- self.class.fields.each do |name, field|
- unless (!field.required || @msg.has_key?(name))
- missing_fields[name] = "Missing field #{name}"
- end
+ set_defaults
+
+ missing_fields = {}
+
+ self.class.fields.each do |name, field|
+ if field.required && !@msg.has_key?(name)
+ missing_fields[name] = "Missing field #{name}"
end
- raise ValidationError.new(missing_fields) unless missing_fields.empty?
end
+ raise ValidationError.new(missing_fields) unless missing_fields.empty?
+
Yajl::Encoder.encode(@msg)
end
@@ -149,31 +158,29 @@ def extract
protected
- def set_field(field, value)
- field = field.to_sym
- unless self.class.fields && self.class.fields.has_key?(field)
- raise ValidationError.new({field => "Unknown field #{field}"})
+ def set_field(name, value)
+ name = name.to_sym
+ field = self.class.fields[name]
+
+ unless field
+ raise ValidationError.new( { name => "Unknown field: #{name}" } )
end
- errs = self.class.fields[field].schema.validate(value)
- raise ValidationError.new({field => errs}) if errs
- @msg[field] = value
+ field.validate(value)
+ @msg[name] = value
end
def set_defaults
- if self.class.fields
- self.class.fields.each do |name, field|
- set_default(name)
- end
+ self.class.fields.each do |name, _|
+ set_default(name)
end
end
def set_default(name)
- if !@msg.include?(name)
- if self.class.fields
- if self.class.fields.include?(name) && self.class.fields[name].default
- @msg[name] = self.class.fields[name].default
- end
+ unless @msg.has_key?(name)
+ field = self.class.fields[name]
+ if field
+ @msg[name] = field.default if field.default
end
end
end
View
@@ -1,84 +0,0 @@
-# Copyright (c) 2009-2011 VMware, Inc.
-# This class provides dead simple declarative validation for decoded json using
-# a fairly intuitive DSL like syntax.
-#
-# For example, the following is a sample schema that exercises all functionality
-#
-# {'foo' => [String], # 'foo' must be a list of strings
-# 'bar' => {'baz' => Fixnum, # 'bar' must be a hash where
-# 'jaz' => /foo/, # 'baz' is a Fixnum, and
-# } # 'jaz' matches the regex /foo/
-# }
-#
-class JsonSchema
- WILDCARD = Object
-
- # TODO(mjp): validate that schema is syntatically correct
-
- def initialize(schema)
- @schema = schema
- end
-
- def validate(json)
- _validate(json, @schema)
- end
-
- protected
-
- def _validate(json, schema)
- case schema
- when Class
- # Terminal case, type check
- klass = json.class
- if json.is_a? schema
- nil
- else
- "Type mismatch (expected #{schema}, got #{klass})"
- end
-
- when Hash
- # Recursive case, check for required params, recursively check them against the supplied schema
- missing_keys = schema.keys.select {|k| !json.has_key?(k)} if json.is_a? Hash
-
- if !(json.is_a? Hash)
- "Type mismatch (expected hash, got #{json.class})"
- elsif missing_keys.length > 0
- "Missing params: '#{missing_keys.join(', ')}'"
- else
- errs = nil
- schema.each_key do |k|
- sub_errs = _validate(json[k], schema[k])
- if sub_errs
- errs ||= {}
- errs[k] = sub_errs
- end
- end
- errs
- end
-
- when Array
- # Recursive case, check that array isn't empty, recursively check array against supplied schema
- if !(json.is_a? Array)
- "Type mismatch (expected array, got #{json.class}"
- else
- errs = nil
- json.each do |v|
- errs = _validate(v, schema[0])
- break if errs
- end
- errs
- end
-
- when Regexp
- if schema.match(json)
- nil
- else
- "Invalid value (doesn't match '#{schema.source})"
- end
-
- else
- # Terminal case, value check
- "Value mismatch (expected '#{schema}', got #{json})" unless json == schema
- end
- end
-end
@@ -2,6 +2,7 @@
require 'uri'
require 'services/api/const'
+require 'membrane'
require 'json_message'
module VCAP
@@ -49,7 +50,7 @@ class HandleUpdateRequest < JsonMessage
end
class ListHandlesResponse < JsonMessage
- required :handles, [::JsonSchema::WILDCARD]
+ required :handles, [Object]
end
class ListBrokeredServicesResponse < JsonMessage
@@ -137,7 +138,7 @@ class Snapshot < JsonMessage
end
class SnapshotList < JsonMessage
- required :snapshots, [::JsonSchema::WILDCARD]
+ required :snapshots, [Object]
end
class Job < JsonMessage
@@ -146,7 +147,7 @@ class Job < JsonMessage
required :start_time, String
optional :description, String
optional :complete_time, String
- optional :result, ::JsonSchema::WILDCARD
+ optional :result, Object
end
class SerializedURL < JsonMessage
View
@@ -2,21 +2,21 @@
require 'yaml'
require 'vcap/common'
-require 'vcap/json_schema'
+require 'membrane'
module VCAP
class Config
class << self
attr_reader :schema
def define_schema(&blk)
- @schema = VCAP::JsonSchema.build(&blk)
+ @schema = Membrane::SchemaParser.parse(&blk)
end
def from_file(filename, symbolize_keys=true)
config = YAML.load_file(filename)
- @schema.validate(config)
config = VCAP.symbolize_keys(config) if symbolize_keys
+ @schema.validate(config)
config
end
@@ -27,6 +27,5 @@ def to_file(config, out_filename)
end
end
end
-
end
end
Oops, something went wrong.

0 comments on commit 5334b66

Please sign in to comment.