jsexp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ jsexp
2
+ =====
3
+
4
+ json schemas with s-expressions
5
+
6
+ This is a tool to do common json validations. `jsexp` factors out validations into a schema.
7
+
8
+ Unlike [JSON Schema](http://tools.ietf.org/html/draft-zyp-json-schema-03), `jsexp` is not written in json and does not specify media types.
9
+
10
+ Example
11
+ -------
12
+
13
+ schema = {
14
+ 'name' => String, # case expression
15
+ 'email' => /@/, # case expression
16
+ 'p' => (0.0..1.0), # case expression
17
+ 'id' => [:range, [0, :inf]], # indefinite range
18
+ 'color' => [:enum, ['red', 'green', 'blue']], # enum
19
+ 'offset' => [:tuple, [Float, Float]], # exact-size array
20
+ 'samples' => [:array, [:tuple, [Float, Float]]] # indefinite array
21
+ 'description' => [:optional, String] # optional field
22
+ }
23
+ obj = {
24
+ 'name' => '',
25
+ 'email' => 'user@example.com',
26
+ 'p' => 0.5,
27
+ 'id' => 0,
28
+ 'color' => 'red',
29
+ 'offset' => [0.0, 0.0],
30
+ 'samples' => [
31
+ [0.0, 0.0],
32
+ [0.0, 0.0]
33
+ ]
34
+ }
35
+
36
+ puts JSExp.valid?(schema, obj)
37
+ # true
38
+
39
+ Non-features
40
+ ------------
41
+
42
+ - default values: use defaults.merge(input) prior to validation
43
+ - string length bounds: use /^.{3,5}$/
44
+ - array/map size bounds
45
+ - key expressions
46
+ - nested schemas: This is a source of arbitrary complexity.
47
+ - specific error messages: Refer to the schema to see what's wrong.
48
+ - lambdas: Do more complex validation yourself.
@@ -0,0 +1,26 @@
1
+ require 'jsexp'
2
+
3
+ schema = {
4
+ 'name' => String,
5
+ 'email' => /@/,
6
+ 'p' => (0.0..1.0),
7
+ 'id' => [:range, [0, :inf]],
8
+ 'color' => [:enum, ['red', 'green', 'blue']],
9
+ 'offset' => [:tuple, [Float, Float]],
10
+ 'samples' => [:array, [:tuple, [Float, Float]]],
11
+ 'description' => [:optional, String]
12
+ }
13
+ obj = {
14
+ 'name' => '',
15
+ 'email' => 'user@example.com',
16
+ 'p' => 0.5,
17
+ 'id' => 0,
18
+ 'color' => 'red',
19
+ 'offset' => [0.0, 0.0],
20
+ 'samples' => [
21
+ [0.0, 0.0],
22
+ [0.0, 0.0]
23
+ ]
24
+ }
25
+
26
+ puts JSExp.valid?(schema, obj)
@@ -0,0 +1,35 @@
1
+ module JSExp
2
+
3
+ def JSExp.valid?(schema, obj)
4
+ if schema.is_a?(Hash)
5
+ obj.is_a?(Hash) && schema.map {|key, schema_|
6
+ if schema_.is_a?(Array) && schema_[0] == :optional
7
+ !obj.key?(key) || valid?(schema_[1], obj[key])
8
+ else
9
+ obj.key?(key) && valid?(schema_, obj[key])
10
+ end
11
+ }.all?
12
+ elsif schema.is_a?(Array)
13
+ case schema[0]
14
+ when :array
15
+ obj.is_a?(Array) && obj.map {|x| valid?(schema[1], x)}.all?
16
+ when :enum
17
+ schema[1].include?(obj)
18
+ when :range
19
+ min, max = schema[1]
20
+ (min == :inf ? true : obj.class == min.class && obj >= min) &&
21
+ (max == :inf ? true : obj.class == max.class && obj <= max)
22
+ when :tuple
23
+ obj.is_a?(Array) && obj.size == schema[1].size && schema[1].each_index.map {|i| valid?(schema[1][i], obj[i])}.all?
24
+ end
25
+ else
26
+ case obj
27
+ when schema
28
+ true
29
+ else
30
+ false
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,64 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
2
+ require 'jsexp'
3
+ require 'minitest/autorun'
4
+
5
+ describe JSExp do
6
+ it 'validates case expressions' do
7
+ JSExp.valid?(String, '').must_equal true
8
+ JSExp.valid?(String, 0).must_equal false
9
+
10
+ JSExp.valid?(Integer, 0).must_equal true
11
+ JSExp.valid?(Integer, 0.0).must_equal false
12
+
13
+ JSExp.valid?(Float, 0.0).must_equal true
14
+ JSExp.valid?(Float, 0).must_equal false
15
+
16
+ JSExp.valid?(/@/, 'user@example.com').must_equal true
17
+ JSExp.valid?(/@/, '').must_equal false
18
+
19
+ JSExp.valid?((0..10), 5).must_equal true
20
+ JSExp.valid?((0..10), 11).must_equal false
21
+
22
+ JSExp.valid?((0.0..1.0), 0.5).must_equal true
23
+ JSExp.valid?((0.0..1.0), 1.1).must_equal false
24
+ end
25
+
26
+ it 'validates indefinite arrays' do
27
+ JSExp.valid?([:array, Integer], []).must_equal true
28
+ JSExp.valid?([:array, Integer], [0]).must_equal true
29
+ JSExp.valid?([:array, Integer], [0, 0]).must_equal true
30
+ JSExp.valid?([:array, Integer], [0.0]).must_equal false
31
+ JSExp.valid?([:array, Integer], [0, 0.0]).must_equal false
32
+ end
33
+
34
+ it 'validates enums' do
35
+ JSExp.valid?([:enum, ['red', 'green', 'blue']], 'red').must_equal true
36
+ end
37
+
38
+ it 'validates optional fields' do
39
+ schema = {
40
+ 'id' => [:optional, String]
41
+ }
42
+ JSExp.valid?(schema, {}).must_equal true
43
+ JSExp.valid?(schema, {'id' => ''}).must_equal true
44
+ JSExp.valid?(schema, {'id' => 0}).must_equal false
45
+ end
46
+
47
+ it 'validates indefinite ranges' do
48
+ JSExp.valid?([:range, [0, :inf]], 0).must_equal true
49
+ JSExp.valid?([:range, [0, :inf]], -1).must_equal false
50
+ JSExp.valid?([:range, [0, :inf]], 0.0).must_equal false
51
+
52
+ JSExp.valid?([:range, [:inf, 0]], 0).must_equal true
53
+ JSExp.valid?([:range, [:inf, 0]], 1).must_equal false
54
+
55
+ JSExp.valid?([:range, [:inf, :inf]], 0).must_equal true
56
+ end
57
+
58
+ it 'validates tuples' do
59
+ JSExp.valid?([:tuple, [Integer, Float]], [0, 0.0]).must_equal true
60
+ JSExp.valid?([:tuple, [Integer, Float]], 0).must_equal false
61
+ JSExp.valid?([:tuple, [Integer, Float]], [0]).must_equal false
62
+ JSExp.valid?([:tuple, [Integer, Float]], [0, 0.0, 0]).must_equal false
63
+ end
64
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsexp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matthew King
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: json schemas with s-expressions
15
+ email: matthew.y.king@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - examples/example.rb
22
+ - lib/jsexp.rb
23
+ - test/jsexp_test.rb
24
+ homepage: http://github.com/mking/jsexp
25
+ licenses: []
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 1.8.11
45
+ signing_key:
46
+ specification_version: 3
47
+ summary: json schemas
48
+ test_files:
49
+ - test/jsexp_test.rb