jcon 0.1

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.
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
+