jcon 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2008 Oliver Steele
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,15 @@
1
+ lib/jcon/conformance.rb
2
+ lib/jcon/dictionary.rb
3
+ lib/jcon/matchers/jcon_matchers.rb
4
+ lib/jcon/parser.rb
5
+ lib/jcon/types.rb
6
+ lib/jcon.rb
7
+ LICENSE
8
+ Manifest
9
+ README
10
+ spec/conformance_spec.rb
11
+ spec/jcon_spec.rb
12
+ spec/matchers_spec.rb
13
+ spec/parser_spec.rb
14
+ spec/spec_helper.rb
15
+ TODO
data/README ADDED
@@ -0,0 +1,41 @@
1
+ = JCon -- JavaScript Type Conformance Checking
2
+
3
+ Test whether a parsed JSON type conforms to a type specification, read
4
+ from a string or file that matches the proposed ECMAScript 4.0 type
5
+ syntax (PDF[http://www.ecmascript.org/es4/spec/overview.pdf]).
6
+
7
+
8
+ == Install
9
+
10
+ gem install rcon
11
+
12
+ == Usage
13
+
14
+ type = JCON::parse "[string, int]"
15
+ type.contains?(['a', 1]) # => true
16
+ type.contains?(['a', 'b']) # => false
17
+ type.contains?(['a', 1, 2]) # => true
18
+
19
+ type = JCON::parse "type S = (string, int); {a: [S], b: int}"
20
+ type.contains?({:a => [1, 'b'], :b => 2}) # => true
21
+
22
+ == RSpec Matcher
23
+
24
+ [1, 'xyzzy'].should conform_to_js('[int, string]')
25
+ [1, 2, 'xyzzy'].should_not conform_to_js('[int, string]')
26
+ {:x => 1}.should conform_to_js('{x: int}')
27
+
28
+ Use this with the "JavaScriptFu"[http://osteele.com/archives/2008/04/javascript-fu-rails-plugin] plugin to test JSON arguments:
29
+
30
+ '<script>fn("id", {x:1, y:2}, true)</script>'.should call_js('fn') do |args|
31
+ args[0].should conform_to_js('string')
32
+ args[1].should conform_to_js('{x:int, y:int}')
33
+ args[2].should conform_to_js('boolean')
34
+ # or:
35
+ args.should conform_to_js('[string, {x:int, y:int}, boolean]')
36
+ end
37
+
38
+ == License
39
+
40
+ Copyright 2008 by Oliver Steele. All rights reserved. Released under
41
+ the MIT License.
data/TODO ADDED
@@ -0,0 +1,26 @@
1
+ = JCon TODO
2
+
3
+ Next:
4
+ * docs -- add the README
5
+ * check the spec
6
+ * add method to display path to mismatch
7
+ * rename SimpleType
8
+
9
+ Error detection:
10
+ * circular references
11
+ * duplicate definitions
12
+
13
+ Test cases:
14
+ * recursive types
15
+
16
+ Parser:
17
+ * can property name be string?
18
+
19
+ Conformance:
20
+ * byte
21
+
22
+ Future:
23
+ * function(this:T | T | T= | ... | ...[T]) : T | void
24
+ * 'class'
25
+ * js implementation
26
+ * includes
@@ -0,0 +1,64 @@
1
+
2
+ # Gem::Specification for Jcon-0.1
3
+ # Originally generated by Echoe
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{jcon}
7
+ s.version = "0.1"
8
+ s.date = %q{2008-04-16}
9
+ s.summary = %q{Test JSON values against a schema.}
10
+ s.email = %q{steele@osteele.com}
11
+ s.homepage = %q{http://jcon.rubyforge.org}
12
+ s.rubyforge_project = %q{jcon}
13
+ s.description = %q{Test JSON values for conformance with ECMAScript 4.0 types.}
14
+ s.has_rdoc = true
15
+ s.authors = ["Oliver Steele"]
16
+ s.files = ["lib/jcon/conformance.rb", "lib/jcon/dictionary.rb", "lib/jcon/matchers/jcon_matchers.rb", "lib/jcon/parser.rb", "lib/jcon/types.rb", "lib/jcon.rb", "LICENSE", "Manifest", "README", "spec/conformance_spec.rb", "spec/jcon_spec.rb", "spec/matchers_spec.rb", "spec/parser_spec.rb", "spec/spec_helper.rb", "TODO", "jcon.gemspec"]
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Jcon", "--main", "README", "--title", "JCON: Test JSON values against a schema", "--main", "README", "--exclude", "spec/.*"]
18
+ s.extra_rdoc_files = ["README", "TODO", "LICENSE"]
19
+ end
20
+
21
+
22
+ # # Original Rakefile source (requires the Echoe gem):
23
+ #
24
+ # require 'rubygems'
25
+ # require 'echoe'
26
+ # require 'spec/rake/spectask'
27
+ #
28
+ # PKG_VERSION = '0.1'
29
+ #
30
+ # task :test => :spec
31
+ #
32
+ # Echoe.new('jcon', PKG_VERSION) do |p|
33
+ # p.summary = "Test JSON values against a schema."
34
+ # p.url = 'http://jcon.rubyforge.org'
35
+ # p.description = <<-EOF
36
+ # Test JSON values for conformance with ECMAScript 4.0 types.
37
+ # EOF
38
+ # p.author = 'Oliver Steele'
39
+ # p.email = 'steele@osteele.com'
40
+ # p.ignore_pattern = /^(.git|.*#.*#)$/
41
+ # p.test_pattern = 'test/*_test.rb'
42
+ # p.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGES|^TODO|^LICENSE$/
43
+ # p.eval = proc do |s|
44
+ # s.rdoc_options <<
45
+ # '--title' << "JCON: #{s.summary.sub(/.$/,'')}" <<
46
+ # '--main' << 'README' <<
47
+ # '--exclude' << 'spec/.*'
48
+ # s.extra_rdoc_files = ['README', 'TODO', 'LICENSE']
49
+ # end
50
+ # end
51
+ #
52
+ # desc "Really a replacement for echoe's 'install', which bombs on my machine"
53
+ # task :reinstall => [:clean, :package, :uninstall] do
54
+ # system "sudo gem install pkg/*.gem"
55
+ # end
56
+ #
57
+ # desc "Run all specs"
58
+ # Spec::Rake::SpecTask.new do |t|
59
+ # t.spec_files = FileList['spec/*_spec.rb']
60
+ # if ENV['RCOV']
61
+ # t.rcov = true
62
+ # t.rcov_opts = ['--exclude', 'spec\/spec']
63
+ # end
64
+ # end
@@ -0,0 +1,5 @@
1
+ require "jcon/types"
2
+ require "jcon/conformance"
3
+ require "jcon/dictionary"
4
+ require "jcon/parser"
5
+ require "jcon/matchers/jcon_matchers"
@@ -0,0 +1,57 @@
1
+ module JCON
2
+ module Types
3
+ class SimpleType < Type
4
+ def contains?(value)
5
+ if not @test and context and@context[name]
6
+ return context[name].contains?(value)
7
+ end
8
+ raise "No definition for #{self}" unless @test
9
+ @test.call(value)
10
+ end
11
+ end
12
+
13
+ class OptionalType < Type
14
+ def contains?(value)
15
+ value.nil? or type.contains?(value)
16
+ end
17
+ end
18
+
19
+ class RequiredType < Type
20
+ def contains?(value)
21
+ !value.nil? and type.contains?(value)
22
+ end
23
+ end
24
+
25
+ class ListType < Type
26
+ def contains?(value)
27
+ return false unless value.is_a?(Array)
28
+ return false unless value.length >= types.length
29
+ value.each_with_index do |x, i|
30
+ return false unless types[[i, types.length-1].min].contains?(x)
31
+ end
32
+ true
33
+ end
34
+ end
35
+
36
+ class UnionType < Type
37
+ def contains?(value)
38
+ types.any? do |type| type.contains?(value) end
39
+ end
40
+ end
41
+
42
+ class RecordType < Type
43
+ def contains?(value)
44
+ return false unless value.is_a?(Hash)
45
+ value.each do |k, v|
46
+ type = properties["#{k}".intern]
47
+ return false unless type
48
+ return false unless type.contains?(v)
49
+ end
50
+ properties.each do |k, _|
51
+ return false unless value.include?(k) or value.include?(k.to_s)
52
+ end
53
+ true
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,69 @@
1
+ require File.join(File.dirname(__FILE__), 'types')
2
+
3
+ module JCON
4
+ class Dictionary
5
+ include Types
6
+
7
+ attr_reader :parent, :definitions
8
+ attr_accessor :start
9
+
10
+ def initialize(parent=BUILTINS)
11
+ @parent = parent
12
+ @definitions = {}
13
+ end
14
+
15
+ def deftype(name, type=nil, &block)
16
+ return deftype(name, SimpleType.new(name, &block)) if block
17
+ case type
18
+ when nil
19
+ deftype(name, Kernel.const_get(name))
20
+ when Class
21
+ deftype(name) do |x| x.nil? or x.is_a?(type) end
22
+ when Array
23
+ deftype(name) do |x| type.include?(x) end
24
+ when Type
25
+ name = name.intern if name.is_a?(String)
26
+ @definitions[name] = type
27
+ @start ||= type
28
+ else
29
+ raise "invalid arguments"
30
+ end
31
+ end
32
+
33
+ def [](name)
34
+ @definitions[name] || (parent && parent[name])
35
+ end
36
+ end
37
+
38
+ class DefaultDictionary < Dictionary
39
+ def initialize
40
+ super(nil)
41
+ add_builtin_definitions
42
+ end
43
+
44
+ def add_builtin_definitions
45
+ deftype(:*) do true end
46
+ deftype(:null) do |x| x.nil? end
47
+ deftype(:undefined) do |x| x.nil? end
48
+ deftype(:Object) do true end
49
+ deftype(:Array)
50
+ deftype(:Date)
51
+ deftype(:RegExp, Regexp)
52
+ deftype(:Boolean, [false,true,nil])
53
+ deftype(:String)
54
+ deftype(:Number, Numeric)
55
+ deftype(:boolean, [false,true])
56
+ deftype(:string) do |x| x.is_a?(String) end
57
+ deftype(:int) do |x| x.is_a?(Integer) end
58
+ deftype(:uint) do |x| x.is_a?(Integer) and x > 0 end
59
+ deftype(:double) do |x| x.is_a?(Numeric) end
60
+ deftype(:decimal) do |x| x.is_a?(Numeric) end
61
+ deftype :AnyString, union(:string, String)
62
+ deftype :AnyBoolean, union(:boolean, :Boolean)
63
+ deftype :AnyNumber, union(:byte, :int, :uint, :double, :decimal,:Number)
64
+ deftype :FloatNumber, union(:double, :decimal)
65
+ end
66
+ end
67
+
68
+ BUILTINS = DefaultDictionary.new
69
+ end
@@ -0,0 +1,26 @@
1
+ module JCON
2
+ module Matchers
3
+ class ConformToJSType #:nodoc:
4
+ def initialize(type_or_text)
5
+ type = type_or_text
6
+ type = JCON.parse(type) if String === type
7
+ @expected = type
8
+ end
9
+
10
+ def matches?(actual)
11
+ @expected.contains?(actual)
12
+ end
13
+
14
+ def failure_message; "should #{description}, but did not"; end
15
+ def negative_failure_message; "should not #{description}, but did"; end
16
+
17
+ def description
18
+ "conform to type #{@expected}"
19
+ end
20
+ end
21
+
22
+ def conform_to_js(type_or_text)
23
+ return ConformToJSType.new(type_or_text)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,127 @@
1
+ require 'strscan'
2
+
3
+ module JCON
4
+ def self.parse(source)
5
+ Parser.parse(source).start
6
+ end
7
+
8
+ class Parser < StringScanner
9
+ include Types
10
+
11
+ COMMENT = %r{//.*|/\*(?:.|[\r\n])*?\*/}
12
+ WS_CHAR = /[\s\r\n]/
13
+ IGNORE = /(?:#{COMMENT}|#{WS_CHAR})+/
14
+ IDENTIFIER = /[\w_$][\w\d_$]*/
15
+
16
+ def self.parse(source)
17
+ self.new(source).parse
18
+ end
19
+
20
+ attr_reader :dictionary
21
+
22
+ def initialize(source)
23
+ super(source)
24
+ @dictionary = Dictionary.new
25
+ end
26
+
27
+ def parse
28
+ reset
29
+ until eos?
30
+ case
31
+ when skip(IGNORE)
32
+ when skip(/;/)
33
+ ;
34
+ when scan(/type/)
35
+ parse_deftype
36
+ else
37
+ type = parse_type
38
+ dictionary.start = type
39
+ while skip(IGNORE) || skip(/;/); end
40
+ parse_error "expected end of input" unless eos?
41
+ end
42
+ end
43
+ dictionary
44
+ end
45
+
46
+ def parse_deftype
47
+ name = expect('identifier', IDENTIFIER)
48
+ expect('=', /=/)
49
+ type = parse_type
50
+ dictionary.deftype(name.intern, type)
51
+ sscan(/;/)
52
+ end
53
+
54
+ def parse_type
55
+ skip(IGNORE)
56
+ type = case
57
+ when id = scan(IDENTIFIER)
58
+ simple_type(id)
59
+ when scan(/\*/)
60
+ simple_type('*')
61
+ when scan(/\[/)
62
+ types = parse_types_until(/\]/)
63
+ list(*types)
64
+ when scan(/\(/)
65
+ types = parse_types_until(/\)/)
66
+ union(*types)
67
+ when scan(/\{/)
68
+ parse_structure_type
69
+ else
70
+ parse_error
71
+ end
72
+ type.context = dictionary
73
+ add_modifiers(type)
74
+ end
75
+
76
+ def add_modifiers(type)
77
+ while sscan(/[!?]/)
78
+ case self[0]
79
+ when '!'
80
+ type = required(type)
81
+ when '?'
82
+ type = optional(type)
83
+ end
84
+ type.context = dictionary
85
+ end
86
+ type
87
+ end
88
+
89
+ def parse_types_until(stop_pattern)
90
+ types = []
91
+ while true
92
+ break if sscan(stop_pattern)
93
+ expect('comma', /,/) if types.any?
94
+ types << parse_type
95
+ end
96
+ types
97
+ end
98
+
99
+ def parse_structure_type
100
+ map = {}
101
+ while true
102
+ break if sscan(/\}/)
103
+ expect('comma', /,/) if map.any?
104
+ name = expect('id', IDENTIFIER)
105
+ expect(':', /:/)
106
+ type = parse_type
107
+ map[name.intern] = type
108
+ end
109
+ RecordType.new(map)
110
+ end
111
+
112
+ def expect(name, pattern)
113
+ value = sscan(pattern)
114
+ parse_error("expected #{name}") unless value
115
+ value
116
+ end
117
+
118
+ def sscan(pattern)
119
+ skip(IGNORE)
120
+ scan(pattern)
121
+ end
122
+
123
+ def parse_error(msg='unexpected token')
124
+ raise "#{msg} at '#{skip(IGNORE); peek(20)}'"
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,67 @@
1
+ module JCON
2
+ module Types
3
+ def self.to_type(value)
4
+ value = SimpleType.new(value) unless value.is_a?(Type)
5
+ value
6
+ end
7
+
8
+ def self.to_types(values)
9
+ values.map { |value| to_type(value) }
10
+ end
11
+
12
+ class Type
13
+ attr_accessor :context
14
+ def inspect; to_s; end
15
+ end
16
+
17
+ class SimpleType < Type
18
+ attr_reader :name
19
+ def initialize(name, &block)
20
+ name = name.intern if name.is_a?(String)
21
+ @name = name
22
+ @test = block
23
+ end
24
+
25
+ def to_s; name.to_s; end
26
+ end
27
+
28
+ class OptionalType < Type
29
+ attr_reader :type
30
+ def initialize(type); @type = type; end
31
+ def to_s; "#{type}?"; end
32
+ end
33
+
34
+ class RequiredType < Type
35
+ attr_reader :type
36
+ def initialize(type); @type = type; end
37
+ def to_s; "#{type}!"; end
38
+ end
39
+
40
+ class ListType < Type
41
+ attr_reader :types
42
+ def initialize(types); @types = Types.to_types(types); end
43
+ def to_s; "[#{types.join(', ')}]"; end
44
+ end
45
+
46
+ class UnionType < Type
47
+ attr_reader :types
48
+ def initialize(types); @types = Types.to_types(types); end
49
+ def to_s; "(#{types.join(', ')})"; end
50
+ end
51
+
52
+ class RecordType < Type
53
+ attr_reader :properties
54
+ def initialize(properties);
55
+ @properties = properties
56
+ end
57
+
58
+ def to_s; "{#{properties.map { |k,v| "#{k}: #{v}"}.join(', ')}}"; end
59
+ end
60
+
61
+ def simple_type(*args); SimpleType.new(*args); end
62
+ def required(*args); RequiredType.new(*args); end
63
+ def optional(*args); OptionalType.new(*args); end
64
+ def list(*args); ListType.new(args); end
65
+ def union(*args); UnionType.new(args); end
66
+ end
67
+ end
@@ -0,0 +1,162 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe JCON do
4
+ describe :conforms? do
5
+ it "should test the Any type" do
6
+ type = JCON::parse('*')
7
+ type.contains?(false).should == true
8
+ type.contains?(0).should == true
9
+ type.contains?(1).should == true
10
+ end
11
+
12
+ it "should test the null type" do
13
+ type = JCON::parse('null')
14
+ type.contains?(false).should == false
15
+ type.contains?(0).should == false
16
+ type.contains?(nil).should == true
17
+ end
18
+
19
+ it "should test the undefined type" do
20
+ type = JCON::parse('undefined')
21
+ type.contains?(false).should == false
22
+ type.contains?(0).should == false
23
+ type.contains?(nil).should == true
24
+ end
25
+
26
+ it "should test string types" do
27
+ type = JCON::parse('string')
28
+ type.contains?(1).should == false
29
+ type.contains?('s').should == true
30
+ type.contains?(nil).should == false
31
+
32
+ type = JCON::parse('String')
33
+ type.contains?(1).should == false
34
+ type.contains?('s').should == true
35
+ type.contains?(nil).should == true
36
+ end
37
+
38
+ it "should test numeric types" do
39
+ type = JCON::parse('Number')
40
+ type.contains?(1).should == true
41
+ type.contains?('s').should == false
42
+ type.contains?(nil).should == true
43
+
44
+ type = JCON::parse('int')
45
+ type.contains?(1).should == true
46
+ type.contains?(1.5).should == false
47
+ type.contains?('s').should == false
48
+ type.contains?(nil).should == false
49
+
50
+ type = JCON::parse('uint')
51
+ type.contains?(1).should == true
52
+ type.contains?(1.5).should == false
53
+ type.contains?(-1).should == false
54
+ type.contains?(nil).should == false
55
+
56
+ type = JCON::parse('double')
57
+ type.contains?(1).should == true
58
+ type.contains?(1.5).should == true
59
+ type.contains?(nil).should == false
60
+
61
+ type = JCON::parse('decimal')
62
+ type.contains?(1).should == true
63
+ type.contains?(1.5).should == true
64
+ type.contains?(nil).should == false
65
+ end
66
+
67
+ it "should test object types" do
68
+ type = JCON::parse('Object')
69
+ type.contains?(1).should == true
70
+ type.contains?('s').should == true
71
+ type.contains?([]).should == true
72
+ type.contains?({}).should == true
73
+ type.contains?(nil).should == true
74
+
75
+ type = JCON::parse('Array')
76
+ type.contains?(1).should == false
77
+ type.contains?('s').should == false
78
+ type.contains?([]).should == true
79
+ type.contains?({}).should == false
80
+ type.contains?(nil).should == true
81
+
82
+ type = JCON::parse('Date')
83
+ type.contains?(1).should == false
84
+ type.contains?('s').should == false
85
+ type.contains?(Date.new).should == true
86
+ type.contains?({}).should == false
87
+ type.contains?(nil).should == true
88
+
89
+ type = JCON::parse('RegExp')
90
+ type.contains?(1).should == false
91
+ type.contains?('s').should == false
92
+ type.contains?(/s/).should == true
93
+ type.contains?({}).should == false
94
+ type.contains?(nil).should == true
95
+ end
96
+
97
+ it "should test optional types" do
98
+ type = JCON::parse('int?')
99
+ type.contains?(1).should == true
100
+ type.contains?(nil).should == true
101
+ end
102
+
103
+ it "should test required types" do
104
+ type = JCON::parse('Object!')
105
+ type.contains?(1).should == true
106
+ type.contains?(nil).should == false
107
+
108
+ type = JCON::parse('Number!')
109
+ type.contains?(1).should == true
110
+ type.contains?(nil).should == false
111
+ end
112
+
113
+ it "should test union types" do
114
+ type = JCON::parse('(string, boolean)')
115
+ type.contains?('s').should == true
116
+ type.contains?(true).should == true
117
+ type.contains?(1).should == false
118
+ type.contains?(nil).should == false
119
+ end
120
+
121
+ it "should test list types" do
122
+ type = JCON::parse('[string, boolean]')
123
+ type.contains?('s').should == false
124
+ type.contains?([]).should == false
125
+ type.contains?([1]).should == false
126
+ type.contains?([1,2]).should == false
127
+ type.contains?([1,2,3]).should == false
128
+ type.contains?(['s']).should == false
129
+ type.contains?(['s',2]).should == false
130
+ type.contains?([1,true]).should == false
131
+ type.contains?(['s',true]).should == true
132
+ type.contains?(['s',true,true]).should == true
133
+ type.contains?(['s',true,3]).should == false
134
+ end
135
+
136
+ it "should test record types" do
137
+ type = JCON::parse('{a:int, b:string}')
138
+ type.contains?('s').should == false
139
+
140
+ type.contains?({:a => 1}).should == false
141
+ type.contains?({:b => 's'}).should == false
142
+ type.contains?({:a => 1, :b => 's'}).should == true
143
+ type.contains?({:a => 's', :b => 's'}).should == false
144
+ type.contains?({:a => 1, :b => 2}).should == false
145
+ type.contains?({:a => 1, :b => 's', :c => false}).should == false
146
+
147
+ type.contains?({'a' => 1}).should == false
148
+ type.contains?({'b' => 's'}).should == false
149
+ type.contains?({'a' => 1, 'b' => 's'}).should == true
150
+ type.contains?({'a' => 's', 'b' => 's'}).should == false
151
+ type.contains?({'a' => 1, 'b' => 2}).should == false
152
+ type.contains?({'a' => 1, 'b' => 's', :c => false}).should == false
153
+ end
154
+
155
+ it "should test type definitions"
156
+
157
+ it "should raise an error when for unknown" do
158
+ type = JCON::parse('S')
159
+ lambda { type.contains?('s') }.should raise_error('No definition for S')
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe JCON do
4
+ describe :parse do
5
+ it "parse should return a type" do
6
+ type = JCON::parse('string')
7
+ type.should be_an_instance_of(JCON::Types::SimpleType)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe JCON::Matchers do
4
+ include JCON::Matchers
5
+
6
+ describe :conforms_to_js do
7
+ it "should test basic types" do
8
+ 1.should conform_to_js('int')
9
+ 1.should_not conform_to_js('string')
10
+ 's'.should conform_to_js('string')
11
+ 's'.should_not conform_to_js('int')
12
+ lambda { 1.should conform_to_js('string') }.should raise_error('should conform to type string, but did not')
13
+ lambda { 1.should_not conform_to_js('int') }.should raise_error('should not conform to type int, but did')
14
+ end
15
+
16
+ it "should test nullability" do
17
+ 1.should conform_to_js('int')
18
+ nil.should_not conform_to_js('int')
19
+ 1.should conform_to_js('Number')
20
+ nil.should conform_to_js('Number')
21
+ 1.should conform_to_js('int!')
22
+ nil.should_not conform_to_js('int!')
23
+ 1.should conform_to_js('Number!')
24
+ nil.should_not conform_to_js('Number!')
25
+ 1.should conform_to_js('int?')
26
+ nil.should conform_to_js('int?')
27
+ 1.should conform_to_js('Number?')
28
+ nil.should conform_to_js('Number?')
29
+ end
30
+
31
+ it "should test more complex cases" do
32
+ [[1], 2].should conform_to_js('[Array, (int, boolean)]')
33
+ [[1], true].should conform_to_js('[Array, (int, boolean)]')
34
+ [[1], 2, true].should conform_to_js('[Array, (int, boolean)]')
35
+ [].should_not conform_to_js('[Array, (int, boolean)]')
36
+ [1, 2].should_not conform_to_js('[Array, (int, boolean)]')
37
+ [[1], nil].should_not conform_to_js('[Array, (int, boolean)]')
38
+ {'x' => 1, 'y' => 2, 'z' => 3}.should conform_to_js('{x: double, y: double, z: double?}')
39
+ [[[1], 2], {'x' => 1, 'y' => 2, 'z' => 3}].should conform_to_js('[[Array, (int, boolean)], {x: double, y: double, z: double?}]')
40
+ end
41
+
42
+ it "should work with the examples in the README" do
43
+ [1, 'xyzzy'].should conform_to_js('[int, string]')
44
+ [1, 2, 'xyzzy'].should_not conform_to_js('[int, string]')
45
+ {:x => 1}.should conform_to_js('{x: int}')
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,110 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe JCON::Parser do
4
+ describe :parse do
5
+ it "should parse a bare type" do
6
+ type = JCON::Parser.parse('string').start
7
+ type.should be_an_instance_of(JCON::Types::SimpleType)
8
+ end
9
+
10
+ it "should parse a union type" do
11
+ type = JCON::Parser.parse('(string, String)').start
12
+ type.should be_an_instance_of(JCON::Types::UnionType)
13
+ type.types.length.should == 2
14
+ type.types[0].name.should == :string
15
+ type.types[1].name.should == :String
16
+ end
17
+
18
+ it "should parse a list type" do
19
+ type = JCON::Parser.parse('[string, String]').start
20
+ type.should be_an_instance_of(JCON::Types::ListType)
21
+ type.types.length.should == 2
22
+ type.types[0].name.should == :string
23
+ type.types[1].name.should == :String
24
+ end
25
+
26
+ it "should parse a required type" do
27
+ type = JCON::Parser.parse('string!').start
28
+ type.should be_an_instance_of(JCON::Types::RequiredType)
29
+ end
30
+
31
+ it "should parse an optional type" do
32
+ type = JCON::Parser.parse('type T = string?')[:T]
33
+ type.should be_an_instance_of(JCON::Types::OptionalType)
34
+ end
35
+
36
+ it "should parse a record type" do
37
+ type = JCON::Parser.parse('type T = {a:string?, b:int}')[:T]
38
+ type.should be_an_instance_of(JCON::Types::RecordType)
39
+ end
40
+
41
+ it "should parse a basic type definition" do
42
+ type = JCON::Parser.parse('type T = string')[:T]
43
+ type.should be_an_instance_of(JCON::Types::SimpleType)
44
+ end
45
+
46
+ it "should parse two type definitions" do
47
+ dict = JCON::Parser.parse('type T = string; type U = String;')
48
+ dict.definitions.length.should == 2
49
+ dict[:U].should be_an_instance_of(JCON::Types::SimpleType)
50
+ dict[:T].should be_an_instance_of(JCON::Types::SimpleType)
51
+ end
52
+
53
+ it "should parse a union type definition" do
54
+ type = JCON::Parser.parse('type T = (string, String)')[:T]
55
+ type.should be_an_instance_of(JCON::Types::UnionType)
56
+ type.types.length.should == 2
57
+ type.types[0].name.should == :string
58
+ type.types[1].name.should == :String
59
+ end
60
+
61
+ it "should ignore extraneous semicolons" do
62
+ dict = JCON::Parser.parse(';type T = string')
63
+ dict.definitions.length.should == 1
64
+ dict = JCON::Parser.parse(';;type T = string')
65
+ dict.definitions.length.should == 1
66
+ dict = JCON::Parser.parse('type T = string;')
67
+ dict.definitions.length.should == 1
68
+ dict = JCON::Parser.parse('type T = string;;')
69
+ dict.definitions.length.should == 1
70
+ dict = JCON::Parser.parse('type T = string;type U = String;')
71
+ dict.definitions.length.should == 2
72
+ dict = JCON::Parser.parse(';;string;;')
73
+ dict.start.should be_an_instance_of(JCON::Types::SimpleType)
74
+ end
75
+
76
+ it "should ignore comments" do
77
+ JCON::Parser.parse("// comment\nstring").start.should be_an_instance_of(JCON::Types::SimpleType)
78
+ JCON::Parser.parse("string// comment\n").start.should be_an_instance_of(JCON::Types::SimpleType)
79
+ JCON::Parser.parse("string\n// comment\n").start.should be_an_instance_of(JCON::Types::SimpleType)
80
+
81
+ JCON::Parser.parse("type T = // comment\nstring").definitions.length.should == 1
82
+ JCON::Parser.parse("type T = // comment\nstring").definitions.length.should == 1
83
+ JCON::Parser.parse("type T = /* comment */ string").definitions.length.should == 1
84
+ JCON::Parser.parse("type T = /* multiline\ncomment */ string").definitions.length.should == 1
85
+ end
86
+
87
+ it "should report parse errors" do
88
+ lambda { JCON::Parser.parse('type') }.should raise_error("expected identifier at ''")
89
+ lambda { JCON::Parser.parse('type S') }.should raise_error("expected = at ''")
90
+ lambda { JCON::Parser.parse('[1') }.should raise_error("expected comma at ''")
91
+ lambda { JCON::Parser.parse('[1,,') }.should raise_error("unexpected token at ','")
92
+ lambda { JCON::Parser.parse('[1,]') }.should raise_error("unexpected token at ']'")
93
+ lambda { JCON::Parser.parse('[1]]') }.should raise_error("expected end of input at ']'")
94
+ lambda { JCON::Parser.parse('[1 2]') }.should raise_error("expected comma at '2]'")
95
+ lambda { JCON::Parser.parse('{a') }.should raise_error("expected : at ''")
96
+ lambda { JCON::Parser.parse('{a:') }.should raise_error("unexpected token at ''")
97
+ lambda { JCON::Parser.parse('{a:1') }.should raise_error("expected comma at ''")
98
+ lambda { JCON::Parser.parse('{a:1,') }.should raise_error("expected id at ''")
99
+ lambda { JCON::Parser.parse('{a:1}}') }.should raise_error("expected end of input at '}'")
100
+ end
101
+ end
102
+
103
+ describe :start do
104
+ it "should return the first definition" do
105
+ type = JCON::Parser.parse('type T = string; type U = String;').start
106
+ type.should be_an_instance_of(JCON::Types::SimpleType)
107
+ type.name.should == :string
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,2 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ require 'jcon'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: jcon
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2008-04-16 00:00:00 -04:00
8
+ summary: Test JSON values against a schema.
9
+ require_paths:
10
+ - lib
11
+ email: steele@osteele.com
12
+ homepage: http://jcon.rubyforge.org
13
+ rubyforge_project: jcon
14
+ description: Test JSON values for conformance with ECMAScript 4.0 types.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Oliver Steele
31
+ files:
32
+ - lib/jcon/conformance.rb
33
+ - lib/jcon/dictionary.rb
34
+ - lib/jcon/matchers/jcon_matchers.rb
35
+ - lib/jcon/parser.rb
36
+ - lib/jcon/types.rb
37
+ - lib/jcon.rb
38
+ - LICENSE
39
+ - Manifest
40
+ - README
41
+ - spec/conformance_spec.rb
42
+ - spec/jcon_spec.rb
43
+ - spec/matchers_spec.rb
44
+ - spec/parser_spec.rb
45
+ - spec/spec_helper.rb
46
+ - TODO
47
+ - jcon.gemspec
48
+ test_files: []
49
+
50
+ rdoc_options:
51
+ - --line-numbers
52
+ - --inline-source
53
+ - --title
54
+ - Jcon
55
+ - --main
56
+ - README
57
+ - --title
58
+ - "JCON: Test JSON values against a schema"
59
+ - --main
60
+ - README
61
+ - --exclude
62
+ - spec/.*
63
+ extra_rdoc_files:
64
+ - README
65
+ - TODO
66
+ - LICENSE
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ requirements: []
72
+
73
+ dependencies: []
74
+