membrane 0.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.
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Any do
4
+ describe "#validate" do
5
+ it "should always return nil" do
6
+ schema = Membrane::Schema::Any.new
7
+ # Smoke test more than anything. Cannot validate this with 100%
8
+ # certainty.
9
+ [1, "hi", :test, {}, []].each do |o|
10
+ schema.validate(o).should be_nil
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Bool do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Bool.new }
7
+
8
+ it "should return nil for {true, false}" do
9
+ [true, false].each { |v| schema.validate(v).should be_nil }
10
+ end
11
+
12
+ it "should return an error for values not in {true, false}" do
13
+ ["a", 1].each do |v|
14
+ expect_validation_failure(schema, v, /true or false/)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Class do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Class.new(String) }
7
+
8
+ it "should return nil for instances of the supplied class" do
9
+ schema.validate("test").should be_nil
10
+ end
11
+
12
+ it "should return nil for subclasses of the supplied class" do
13
+ class StrTest < String; end
14
+
15
+ schema.validate(StrTest.new("hi")).should be_nil
16
+ end
17
+
18
+ it "should return an error for non class instances" do
19
+ expect_validation_failure(schema, 10, /instance of String/)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane do
4
+ let(:schema) do
5
+ Membrane::SchemaParser.parse do
6
+ { "ints" => [Integer],
7
+ "tf" => bool,
8
+ "any" => any,
9
+ "1_or_2" => enum(1, 2),
10
+ "str_to_str_to_int" => dict(String, dict(String, Integer)),
11
+ optional("optional") => bool,
12
+ }
13
+ end
14
+ end
15
+
16
+ let(:valid) do
17
+ { "ints" => [1, 2],
18
+ "tf" => false,
19
+ "any" => nil,
20
+ "1_or_2" => 2,
21
+ "optional" => true,
22
+ "str_to_str_to_int" => { "ten" => { "twenty" => 20 } },
23
+ }
24
+ end
25
+
26
+ it "should work with complex nested schemas" do
27
+ schema.validate(valid).should be_nil
28
+ end
29
+
30
+ it "should complain about missing keys" do
31
+ required_keys = schema.schemas.keys.dup
32
+ required_keys.delete("optional")
33
+
34
+ required_keys.each do |k|
35
+ invalid = valid.dup
36
+
37
+ invalid.delete(k)
38
+
39
+ expect_validation_failure(schema, invalid, /#{k} => Missing key/)
40
+ end
41
+ end
42
+
43
+ it "should validate nested maps" do
44
+ invalid = valid.dup
45
+
46
+ invalid["str_to_str_to_int"]["ten"]["twenty"] = "invalid"
47
+
48
+ expect_validation_failure(schema, invalid, /twenty => Expected/)
49
+ end
50
+ end
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Dictionary do
4
+ describe "#validate" do
5
+ let (:data) { { "foo" => 1, "bar" => 2 } }
6
+
7
+ it "should return an error if supplied with a non-hash" do
8
+ schema = Membrane::Schema::Dictionary.new(nil, nil)
9
+
10
+ expect_validation_failure(schema, "test", /instance of Hash/)
11
+ end
12
+
13
+ it "should validate each key against the supplied key schema" do
14
+ key_schema = mock("key_schema")
15
+
16
+ data.keys.each { |k| key_schema.should_receive(:validate).with(k) }
17
+
18
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema,
19
+ Membrane::Schema::Any.new)
20
+
21
+ dict_schema.validate(data)
22
+ end
23
+
24
+ it "should validate the value for each valid key" do
25
+ key_schema = Membrane::Schema::Class.new(String)
26
+ val_schema = mock("val_schema")
27
+
28
+ data.values.each { |v| val_schema.should_receive(:validate).with(v) }
29
+
30
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
31
+
32
+ dict_schema.validate(data)
33
+ end
34
+
35
+ it "should return any errors for keys or values that didn't validate" do
36
+ bad_data = {
37
+ "foo" => "bar",
38
+ :bar => 2,
39
+ }
40
+
41
+ key_schema = Membrane::Schema::Class.new(String)
42
+ val_schema = Membrane::Schema::Class.new(Integer)
43
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
44
+
45
+ errors = nil
46
+
47
+ begin
48
+ dict_schema.validate(bad_data)
49
+ rescue Membrane::SchemaValidationError => e
50
+ errors = e.to_s
51
+ end
52
+
53
+ errors.should match(/foo/)
54
+ errors.should match(/bar/)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Enum do
4
+ describe "#validate" do
5
+ let (:int_schema) { Membrane::Schema::Class.new(Integer) }
6
+ let (:str_schema) { Membrane::Schema::Class.new(String) }
7
+ let (:enum_schema) { Membrane::Schema::Enum.new(int_schema, str_schema) }
8
+
9
+ it "should return an error if none of the schemas validate" do
10
+ expect_validation_failure(enum_schema, :sym, /doesn't validate/)
11
+ end
12
+
13
+ it "should return nil if any of the schemas validate" do
14
+ enum_schema.validate("foo").should be_nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::List do
4
+ describe "#validate" do
5
+ it "should return an error if the validated object isn't an array" do
6
+ schema = Membrane::Schema::List.new(nil)
7
+
8
+ expect_validation_failure(schema, "hi", /instance of Array/)
9
+ end
10
+
11
+ it "should invoke validate each list item against the supplied schema" do
12
+ item_schema = mock("item_schema")
13
+
14
+ data = [0, 1, 2]
15
+
16
+ data.each { |x| item_schema.should_receive(:validate).with(x) }
17
+
18
+ list_schema = Membrane::Schema::List.new(item_schema)
19
+
20
+ list_schema.validate(data)
21
+ end
22
+ end
23
+
24
+ it "should return an error if any items fail to validate" do
25
+ item_schema = Membrane::Schema::Class.new(Integer)
26
+ list_schema = Membrane::Schema::List.new(item_schema)
27
+
28
+ errors = nil
29
+
30
+ begin
31
+ list_schema.validate([1, 2, "hi", 3, :there])
32
+ rescue Membrane::SchemaValidationError => e
33
+ errors = e.to_s
34
+ end
35
+
36
+ errors.should match(/index 2/)
37
+ errors.should match(/index 4/)
38
+ end
39
+
40
+ it "should return nil if all items validate" do
41
+ item_schema = Membrane::Schema::Class.new(Integer)
42
+ list_schema = Membrane::Schema::List.new(item_schema)
43
+
44
+ list_schema.validate([1, 2, 3]).should be_nil
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Record do
4
+ describe "#validate" do
5
+ it "should return an error if the validated object isn't a hash" do
6
+ schema = Membrane::Schema::Record.new(nil)
7
+
8
+ expect_validation_failure(schema, "test", /instance of Hash/)
9
+ end
10
+
11
+ it "should return an error for missing keys" do
12
+ key_schemas = { "foo" => Membrane::Schema::ANY }
13
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
14
+
15
+ expect_validation_failure(rec_schema, {}, /foo => Missing/)
16
+ end
17
+
18
+ it "should validate the value for each key" do
19
+ data = {
20
+ "foo" => 1,
21
+ "bar" => 2,
22
+ }
23
+
24
+ key_schemas = {
25
+ "foo" => mock("foo"),
26
+ "bar" => mock("bar"),
27
+ }
28
+
29
+ key_schemas.each { |k, m| m.should_receive(:validate).with(data[k]) }
30
+
31
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
32
+
33
+ rec_schema.validate(data)
34
+ end
35
+
36
+ it "should return all errors for keys or values that didn't validate" do
37
+ key_schemas = {
38
+ "foo" => Membrane::Schema::ANY,
39
+ "bar" => Membrane::Schema::Class.new(String),
40
+ }
41
+
42
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
43
+
44
+ errors = nil
45
+
46
+ begin
47
+ rec_schema.validate({ "bar" => 2 })
48
+ rescue Membrane::SchemaValidationError => e
49
+ errors = e.to_s
50
+ end
51
+
52
+ errors.should match(/foo => Missing key/)
53
+ errors.should match(/bar/)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Regexp do
4
+ let(:schema) { Membrane::Schema::Regexp.new(/bar/) }
5
+
6
+ describe "#validate" do
7
+ it "should raise an error if the validated object isn't a string" do
8
+ expect_validation_failure(schema, 5, /instance of String/)
9
+ end
10
+
11
+ it "should raise an error if the validated object doesn't match" do
12
+ expect_validation_failure(schema, "invalid", /match regex/)
13
+ end
14
+
15
+ it "should return nil if the validated object matches" do
16
+ schema.validate("barbar").should be_nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,249 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::SchemaParser do
4
+ let(:parser) { Membrane::SchemaParser.new }
5
+
6
+ describe "#deparse" do
7
+ it "should call inspect on the value of a Value schema" do
8
+ val = "test"
9
+ val.should_receive(:inspect).twice
10
+ schema = Membrane::Schema::Value.new(val)
11
+
12
+ parser.deparse(schema).should == val.inspect
13
+ end
14
+
15
+ it "should return 'any' for instance of Membrane::Schema::Any" do
16
+ schema = Membrane::Schema::Any.new
17
+
18
+ parser.deparse(schema).should == "any"
19
+ end
20
+
21
+ it "should return 'bool' for instances of Membrane::Schema::Bool" do
22
+ schema = Membrane::Schema::Bool.new
23
+
24
+ parser.deparse(schema).should == "bool"
25
+ end
26
+
27
+ it "should call inspect on the class of a Membrane::Schema::Class schema" do
28
+ klass = String
29
+ klass.should_receive(:inspect).twice
30
+ schema = Membrane::Schema::Class.new(klass)
31
+
32
+ parser.deparse(schema).should == klass.inspect
33
+ end
34
+
35
+ it "should deparse the k/v schemas of a Membrane::Schema::Dictionary schema" do
36
+ key_schema = Membrane::Schema::Class.new(String)
37
+ val_schema = Membrane::Schema::Class.new(Integer)
38
+
39
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
40
+
41
+ parser.deparse(dict_schema).should == "dict(String, Integer)"
42
+ end
43
+
44
+ it "should deparse the element schemas of a Membrane::Schema::Enum schema" do
45
+ schemas =
46
+ [String, Integer, Float].map { |c| Membrane::Schema::Class.new(c) }
47
+
48
+ enum_schema = Membrane::Schema::Enum.new(*schemas)
49
+
50
+ parser.deparse(enum_schema).should == "enum(String, Integer, Float)"
51
+ end
52
+
53
+ it "should deparse the element schema of a Membrane::Schema::List schema" do
54
+ key_schema = Membrane::Schema::Class.new(String)
55
+ val_schema = Membrane::Schema::Class.new(Integer)
56
+ item_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
57
+
58
+ list_schema = Membrane::Schema::List.new(item_schema)
59
+
60
+ parser.deparse(list_schema).should == "[dict(String, Integer)]"
61
+ end
62
+
63
+ it "should deparse elem schemas of a Membrane::Schema::Record schema" do
64
+ str_schema = Membrane::Schema::Class.new(String)
65
+ int_schema = Membrane::Schema::Class.new(Integer)
66
+ dict_schema = Membrane::Schema::Dictionary.new(str_schema, int_schema)
67
+
68
+ int_rec_schema = Membrane::Schema::Record.new({:str => str_schema,
69
+ :dict => dict_schema})
70
+
71
+ rec_schema = Membrane::Schema::Record.new({"str" => str_schema,
72
+ "rec" => int_rec_schema,
73
+ "int" => int_schema})
74
+ exp_deparse =<<EOT
75
+ {
76
+ "str" => String,
77
+ "rec" => {
78
+ :str => String,
79
+ :dict => dict(String, Integer),
80
+ },
81
+ "int" => Integer,
82
+ }
83
+ EOT
84
+ parser.deparse(rec_schema).should == exp_deparse.strip
85
+ end
86
+
87
+ it "should call inspect on regexps for Membrane::Schema::Regexp" do
88
+ schema = Membrane::Schema::Regexp.new(/test/)
89
+ schema.regexp.should_receive(:inspect)
90
+ parser.deparse(schema)
91
+ end
92
+
93
+ it "should deparse the element schemas of a Membrane::Schema::Tuple schema" do
94
+ schemas = [String, Integer].map { |c| Membrane::Schema::Class.new(c) }
95
+ schemas << Membrane::Schema::Value.new("test")
96
+
97
+ enum_schema = Membrane::Schema::Tuple.new(*schemas)
98
+
99
+ parser.deparse(enum_schema).should == 'tuple(String, Integer, "test")'
100
+ end
101
+
102
+ it "should raise an error if given a non-schema" do
103
+ expect do
104
+ parser.deparse({})
105
+ end.to raise_error(ArgumentError, /Expected instance/)
106
+ end
107
+ end
108
+
109
+ describe "#parse" do
110
+ it "should leave instances derived from Membrane::Schema::Base unchanged" do
111
+ old_schema = Membrane::Schema::ANY
112
+
113
+ parser.parse { old_schema }.should == old_schema
114
+ end
115
+
116
+ it "should translate 'any' into Membrane::Schema::Any" do
117
+ schema = parser.parse { any }
118
+
119
+ schema.class.should == Membrane::Schema::Any
120
+ end
121
+
122
+ it "should translate 'bool' into Membrane::Schema::Bool" do
123
+ schema = parser.parse { bool }
124
+
125
+ schema.class.should == Membrane::Schema::Bool
126
+ end
127
+
128
+ it "should translate 'enum' into Membrane::Schema::Enum" do
129
+ schema = parser.parse { enum(bool, any) }
130
+
131
+ schema.class.should == Membrane::Schema::Enum
132
+
133
+ schema.elem_schemas.length.should == 2
134
+
135
+ elem_schema_classes = schema.elem_schemas.map { |es| es.class }
136
+
137
+ expected_classes = [Membrane::Schema::Bool, Membrane::Schema::Any]
138
+ elem_schema_classes.should == expected_classes
139
+ end
140
+
141
+ it "should translate 'dict' into Membrane::Schema::Dictionary" do
142
+ schema = parser.parse { dict(String, Integer) }
143
+
144
+ schema.class.should == Membrane::Schema::Dictionary
145
+
146
+ schema.key_schema.class.should == Membrane::Schema::Class
147
+ schema.key_schema.klass.should == String
148
+
149
+ schema.value_schema.class.should == Membrane::Schema::Class
150
+ schema.value_schema.klass.should == Integer
151
+ end
152
+
153
+ it "should translate 'tuple' into Membrane::Schema::Tuple" do
154
+ schema = parser.parse { tuple(String, any, Integer) }
155
+
156
+ schema.class.should == Membrane::Schema::Tuple
157
+
158
+ schema.elem_schemas[0].class.should == Membrane::Schema::Class
159
+ schema.elem_schemas[0].klass.should == String
160
+
161
+ schema.elem_schemas[1].class == Membrane::Schema::Any
162
+
163
+ schema.elem_schemas[2].class.should == Membrane::Schema::Class
164
+ schema.elem_schemas[2].klass.should == Integer
165
+ end
166
+
167
+ it "should translate classes into Membrane::Schema::Class" do
168
+ schema = parser.parse { String }
169
+
170
+ schema.class.should == Membrane::Schema::Class
171
+
172
+ schema.klass.should == String
173
+ end
174
+
175
+ it "should translate regexps into Membrane::Schema::Regexp" do
176
+ regexp = /foo/
177
+
178
+ schema = parser.parse { regexp }
179
+
180
+ schema.class.should == Membrane::Schema::Regexp
181
+
182
+ schema.regexp.should == regexp
183
+ end
184
+
185
+ it "should fall back to Membrane::Schema::Value" do
186
+ schema = parser.parse { 5 }
187
+
188
+ schema.class.should == Membrane::Schema::Value
189
+ schema.value.should == 5
190
+ end
191
+
192
+ describe "when parsing a list" do
193
+ it "should raise an error when no element schema is supplied" do
194
+ expect do
195
+ parser.parse { [] }
196
+ end.to raise_error(ArgumentError, /must supply/)
197
+ end
198
+
199
+ it "should raise an error when supplied > 1 element schema" do
200
+ expect do
201
+ parser.parse { [String, String] }
202
+ end.to raise_error(ArgumentError, /single schema/)
203
+ end
204
+
205
+ it "should parse '[<expr>]' into Membrane::Schema::List" do
206
+ schema = parser.parse { [String] }
207
+
208
+ schema.class.should == Membrane::Schema::List
209
+
210
+ schema.elem_schema.class.should == Membrane::Schema::Class
211
+ schema.elem_schema.klass.should == String
212
+ end
213
+ end
214
+
215
+ describe "when parsing a record" do
216
+ it "should raise an error if the record is empty" do
217
+ expect do
218
+ parser.parse { {} }
219
+ end.to raise_error(ArgumentError, /must supply/)
220
+ end
221
+
222
+ it "should parse '{ <key> => <schema> }' into Membrane::Schema::Record" do
223
+ schema = parser.parse do
224
+ { "string" => String,
225
+ "ints" => [Integer],
226
+ }
227
+ end
228
+
229
+ schema.class.should == Membrane::Schema::Record
230
+
231
+ str_schema = schema.schemas["string"]
232
+ str_schema.class.should == Membrane::Schema::Class
233
+ str_schema.klass.should == String
234
+
235
+ ints_schema = schema.schemas["ints"]
236
+ ints_schema.class.should == Membrane::Schema::List
237
+ ints_schema.elem_schema.class.should == Membrane::Schema::Class
238
+ ints_schema.elem_schema.klass.should == Integer
239
+ end
240
+
241
+ it "should handle keys marked with 'optional()'" do
242
+ schema = parser.parse { { optional("test") => Integer } }
243
+
244
+ schema.class.should == Membrane::Schema::Record
245
+ schema.optional_keys.to_a.should == ["test"]
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,7 @@
1
+ require "membrane"
2
+
3
+ def expect_validation_failure(schema, object, regex)
4
+ expect do
5
+ schema.validate(object)
6
+ end.to raise_error(Membrane::SchemaValidationError, regex)
7
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Tuple do
4
+ let(:schema) do
5
+ Membrane::Schema::Tuple.new(Membrane::Schema::Class.new(String),
6
+ Membrane::Schema::ANY,
7
+ Membrane::Schema::Class.new(Integer))
8
+ end
9
+
10
+ describe "#validate" do
11
+ it "should raise an error if the validated object isn't an array" do
12
+ expect_validation_failure(schema, {}, /Array/)
13
+ end
14
+
15
+ it "should raise an error if the validated object has too many/few items" do
16
+ expect_validation_failure(schema, ["foo", 2], /element/)
17
+ expect_validation_failure(schema, ["foo", 2, "bar", 3], /element/)
18
+ end
19
+
20
+ it "should raise an error if any of the items do not validate" do
21
+ expect_validation_failure(schema, [5, 2, 0], /0 =>/)
22
+ expect_validation_failure(schema, ["foo", 2, "foo"], /2 =>/)
23
+ end
24
+
25
+ it "should return nil when validation succeeds" do
26
+ schema.validate(["foo", "bar", 5]).should be_nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Value do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Value.new("test") }
7
+
8
+ it "should return nil for values that are equal" do
9
+ schema.validate("test").should be_nil
10
+ end
11
+
12
+ it "should return an error for values that are not equal" do
13
+ expect_validation_failure(schema, "tast", /Expected test/)
14
+ end
15
+ end
16
+ end