jsexp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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