typed 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/lib/typed.rb +0 -1
- data/lib/typed/default.rb +1 -1
- data/lib/typed/hash.rb +25 -44
- data/lib/typed/schema.rb +78 -17
- data/lib/typed/version.rb +1 -1
- data/spec/hash_spec.rb +82 -15
- data/spec/schema_spec.rb +37 -0
- data/typed.gemspec +1 -1
- metadata +8 -7
data/Gemfile
CHANGED
data/lib/typed.rb
CHANGED
data/lib/typed/default.rb
CHANGED
data/lib/typed/hash.rb
CHANGED
@@ -11,7 +11,7 @@ module Typed
|
|
11
11
|
def initialize(options = {})
|
12
12
|
@hash = {}
|
13
13
|
@options = DEFAULT_OPTIONS.merge(options.must(::Hash))
|
14
|
-
@schema = Schema.new
|
14
|
+
@schema = Schema.new
|
15
15
|
@default = Default.new(self)
|
16
16
|
end
|
17
17
|
|
@@ -54,38 +54,25 @@ module Typed
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def []=(key, val)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
when [], {}
|
76
|
-
# ambiguous
|
77
|
-
@schema[key] = val
|
78
|
-
when nil, true, false
|
79
|
-
# no information
|
80
|
-
else
|
81
|
-
struct = Must::StructInfo.new(val).compact
|
82
|
-
@schema[key] = struct
|
83
|
-
# when schema format, just declare schema and return
|
84
|
-
return if struct == val
|
85
|
-
end
|
86
|
-
end
|
57
|
+
declare = @schema.declare_method(val)
|
58
|
+
case declare
|
59
|
+
when Schema::LazyValue
|
60
|
+
# not schema
|
61
|
+
update(key, val)
|
62
|
+
when Schema::Ambiguous
|
63
|
+
# TODO: How to treat these classes
|
64
|
+
update(key, val)
|
65
|
+
check(key)
|
66
|
+
when Schema::Explicit
|
67
|
+
@schema.declare!(key, declare)
|
68
|
+
check(key) if exist?(key)
|
69
|
+
when Schema::Implicit
|
70
|
+
@schema.declare!(key, declare)
|
71
|
+
update(key, val)
|
72
|
+
check(key)
|
73
|
+
else
|
74
|
+
raise NotImplementedError, "[BUG] no assignment logic for: #{declare.class}"
|
87
75
|
end
|
88
|
-
update(key, val)
|
89
76
|
end
|
90
77
|
|
91
78
|
######################################################################
|
@@ -100,13 +87,8 @@ module Typed
|
|
100
87
|
end
|
101
88
|
|
102
89
|
def check(key, type = nil)
|
103
|
-
|
104
|
-
|
105
|
-
self[key].must.struct(type) {
|
106
|
-
got = Must::StructInfo.new(self[key]).compact.inspect
|
107
|
-
value = self[key].inspect.truncate(200)
|
108
|
-
raise TypeError, "%s(%s) got %s: %s" % [key, type.inspect, got, value]
|
109
|
-
}
|
90
|
+
type ||= @schema[key]
|
91
|
+
@schema.validate!(key, self[key], type)
|
110
92
|
end
|
111
93
|
|
112
94
|
######################################################################
|
@@ -133,12 +115,11 @@ module Typed
|
|
133
115
|
|
134
116
|
private
|
135
117
|
def load(key)
|
136
|
-
# LazyValue should be evaluated at runtime
|
137
118
|
value = @hash[key]
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
119
|
+
|
120
|
+
# LazyValue should be evaluated at runtime
|
121
|
+
return (self[key] = value.value.call) if value.is_a?(Schema::LazyValue)
|
122
|
+
|
142
123
|
return value
|
143
124
|
end
|
144
125
|
|
data/lib/typed/schema.rb
CHANGED
@@ -1,41 +1,102 @@
|
|
1
1
|
module Typed
|
2
2
|
class Schema
|
3
3
|
NotFound = Class.new(RuntimeError)
|
4
|
+
Declared = Struct.new(:value)
|
5
|
+
class Ambiguous < Declared; end
|
6
|
+
class Implicit < Declared; end
|
7
|
+
class Explicit < Declared; end
|
8
|
+
class Nothing < Declared; end
|
9
|
+
class LazyValue < Declared; end
|
4
10
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
11
|
+
def self.schema?(obj)
|
12
|
+
return true if obj.is_a?(Class) or obj.is_a?(Module)
|
13
|
+
return false if obj == [] or obj == {}
|
14
|
+
return schema?(obj.first) if obj.is_a?(Array)
|
15
|
+
return obj.first.any?{|i| schema?(i)} if obj.is_a?(::Hash)
|
16
|
+
return false
|
8
17
|
end
|
9
18
|
|
10
|
-
def
|
11
|
-
|
19
|
+
def self.struct(obj)
|
20
|
+
struct = Must::StructInfo.new(obj).compact
|
21
|
+
struct = Array if struct == []
|
22
|
+
struct = ::Hash if struct == {}
|
23
|
+
return struct
|
12
24
|
end
|
13
25
|
|
14
|
-
def
|
26
|
+
def self.declare_method(val)
|
27
|
+
case val
|
28
|
+
when LazyValue; val
|
29
|
+
when true,false,nil; Ambiguous.new(val)
|
30
|
+
else; schema?(val) ? Explicit.new(val) : Implicit.new(val)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
delegate :schema?, :declare_method, :to => "self.class"
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@types = ::Hash.new{ Nothing.new }
|
38
|
+
end
|
39
|
+
|
40
|
+
def definition(key)
|
15
41
|
@types[key]
|
16
42
|
end
|
17
43
|
|
18
|
-
def []
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
44
|
+
def [](key)
|
45
|
+
@types[key].value
|
46
|
+
end
|
47
|
+
|
48
|
+
def declare!(key, declare)
|
49
|
+
case declare.must.be.kind_of(Explicit, Implicit)
|
50
|
+
when Explicit
|
51
|
+
case @types[key]
|
52
|
+
when Explicit
|
53
|
+
raise TypeError, "%s has already been declared as `%s'" % [key, @types[key].value.inspect]
|
54
|
+
when Implicit
|
55
|
+
# update schema if sub-class, otherwise raises
|
56
|
+
declare.value.must.struct?(@types[key].value) or
|
57
|
+
raise TypeError, "%s has already been typed as `%s'" % [key, @types[key].value.inspect]
|
58
|
+
end
|
59
|
+
explicit(key, declare)
|
23
60
|
|
24
|
-
|
61
|
+
when Implicit
|
62
|
+
case @types[key]
|
63
|
+
when Explicit
|
64
|
+
# nop
|
65
|
+
when Implicit
|
66
|
+
# update schema if sub-struct
|
67
|
+
struct = self.class.struct(declare.value)
|
68
|
+
if struct.must.struct?(@types[key].value)
|
69
|
+
implicit(key, struct)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
implicit(key, self.class.struct(declare.value))
|
73
|
+
end
|
74
|
+
end
|
25
75
|
end
|
26
76
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
77
|
+
def validate!(key, val, klass)
|
78
|
+
return true unless klass
|
79
|
+
# raise Schema::NotFound, key.to_s unless klass
|
30
80
|
|
31
|
-
if val.must.struct?(
|
81
|
+
if val.must.struct?(klass)
|
32
82
|
return true
|
33
83
|
else
|
34
|
-
expected =
|
84
|
+
expected = klass.inspect
|
35
85
|
got = Must::StructInfo.new(val).compact.inspect
|
36
86
|
value = val.inspect.truncate(200)
|
37
87
|
raise TypeError, "%s(%s) got %s: %s" % [key, expected, got, value]
|
38
88
|
end
|
39
89
|
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def implicit(key, val)
|
93
|
+
val = Implicit.new(val) unless val.is_a?(Implicit)
|
94
|
+
@types[key] = val
|
95
|
+
end
|
96
|
+
|
97
|
+
def explicit(key, val)
|
98
|
+
val = Explicit.new(val) unless val.is_a?(Explicit)
|
99
|
+
@types[key] = val
|
100
|
+
end
|
40
101
|
end
|
41
102
|
end
|
data/lib/typed/version.rb
CHANGED
data/spec/hash_spec.rb
CHANGED
@@ -36,9 +36,12 @@ describe Typed::Hash do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
describe "#[]=" do
|
39
|
-
it "should
|
39
|
+
it "should accept as schema when class is given" do
|
40
40
|
data[:foo] = String
|
41
41
|
data.exist?(:foo).should == false
|
42
|
+
|
43
|
+
data[:bar] = Hash
|
44
|
+
data.exist?(:bar).should == false
|
42
45
|
end
|
43
46
|
|
44
47
|
it "should check schema if exists" do
|
@@ -49,11 +52,29 @@ describe Typed::Hash do
|
|
49
52
|
}.should raise_error(TypeError)
|
50
53
|
end
|
51
54
|
|
52
|
-
it "should
|
55
|
+
it "should implicitly declare schema when assigned" do
|
53
56
|
data[:foo] = 1
|
54
|
-
|
55
|
-
|
56
|
-
}
|
57
|
+
data.schema(:foo).should == Fixnum
|
58
|
+
|
59
|
+
data[:bar] = {:a => 1}
|
60
|
+
data.schema(:bar).should == {Symbol => Fixnum}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should implicitly declare schema if not exists" do
|
64
|
+
data[:foo] = 1
|
65
|
+
data.schema(:foo).should == Fixnum
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: How can we declare Boolean? How to treat nil?
|
69
|
+
it "should not implicitly declare schema when nil,true,false" do
|
70
|
+
data[:foo] = nil
|
71
|
+
data.schema(:foo).should == nil
|
72
|
+
|
73
|
+
data[:bar] = true
|
74
|
+
data.schema(:bar).should == nil
|
75
|
+
|
76
|
+
data[:baz] = false
|
77
|
+
data.schema(:baz).should == nil
|
57
78
|
end
|
58
79
|
|
59
80
|
it "can override existing value if same type" do
|
@@ -76,21 +97,67 @@ describe Typed::Hash do
|
|
76
97
|
data[:foo].should == 0.5
|
77
98
|
end
|
78
99
|
|
79
|
-
it "should
|
80
|
-
data[:
|
100
|
+
it "should create new objects for Array,Hash is given for schema" do
|
101
|
+
data[:a] = []
|
102
|
+
data[:a][0] = 1
|
103
|
+
data.schema(:a).should == Array
|
104
|
+
|
105
|
+
data[:h] = {}
|
106
|
+
data[:h]["x"] = 1
|
107
|
+
data.schema(:h).should == Hash
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should check existing value when explicit declarement is given" do
|
111
|
+
data[:foo] = {}
|
112
|
+
data[:foo][:a] = 1
|
81
113
|
lambda {
|
82
|
-
data[:foo] = Integer
|
114
|
+
data[:foo] = {String => Integer}
|
83
115
|
}.should raise_error(TypeError)
|
84
116
|
end
|
85
117
|
|
86
|
-
it "
|
118
|
+
it "cannot ovreride explicit declarement" do
|
87
119
|
data[:num] = Numeric
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
120
|
+
lambda {
|
121
|
+
data[:num] = Fixnum
|
122
|
+
}.should raise_error(TypeError)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "can override implicitly declared schema by sub-struct schema" do
|
126
|
+
data[:foo] = {}
|
127
|
+
data[:foo].should == {}
|
128
|
+
data.schema(:foo).should == Hash
|
129
|
+
|
130
|
+
data[:foo] = {String => Integer}
|
131
|
+
data[:foo].should == {}
|
132
|
+
data.schema(:foo).should == {String => Integer}
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should implicitly override schema when given schema is sub-struct of existing one" do
|
136
|
+
data[:foo] = {}
|
137
|
+
data[:foo].should == {}
|
138
|
+
data.schema(:foo).should == Hash
|
139
|
+
|
140
|
+
data[:foo] = {:a => 1}
|
141
|
+
data[:foo].should == {:a => 1}
|
142
|
+
data.schema(:foo).should == {Symbol => Fixnum}
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not override schema if explicitly declarement exists" do
|
146
|
+
data[:foo] = Hash
|
147
|
+
data[:foo] = {}
|
148
|
+
data.schema(:foo).should == Hash
|
149
|
+
|
150
|
+
data[:foo] = {:a => 1}
|
151
|
+
data[:foo].should == {:a => 1}
|
152
|
+
data.schema(:foo).should == Hash
|
153
|
+
end
|
154
|
+
|
155
|
+
it "raise TypeError when re-declarement causes type mismatch" do
|
156
|
+
data[:foo] = Numeric
|
157
|
+
data[:foo] = 0.5
|
158
|
+
lambda {
|
159
|
+
data[:foo] = Fixnum
|
160
|
+
}.should raise_error(TypeError)
|
94
161
|
end
|
95
162
|
end
|
96
163
|
|
data/spec/schema_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Typed::Schema do
|
4
|
+
describe ".schema?" do
|
5
|
+
delegate :schema?, :to => "Typed::Schema"
|
6
|
+
|
7
|
+
it "should return true for Class,Module" do
|
8
|
+
schema?(Class ).should == true
|
9
|
+
schema?(Module).should == true
|
10
|
+
schema?(Array ).should == true
|
11
|
+
schema?(Hash ).should == true
|
12
|
+
schema?(Fixnum).should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return false for [],{}" do
|
16
|
+
schema?([]).should == false
|
17
|
+
schema?({}).should == false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return true for [Class,Module,...]" do
|
21
|
+
schema?([Array ]).should == true
|
22
|
+
schema?([Hash ]).should == true
|
23
|
+
schema?([Object]).should == true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return true for {(Class,Module) => (Class,Module)}" do
|
27
|
+
schema?({String => Fixnum}).should == true
|
28
|
+
schema?({Hash => Array }).should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return false for instances" do
|
32
|
+
schema?(1).should == false
|
33
|
+
schema?([1]).should == false
|
34
|
+
schema?({:a => 2}).should == false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/typed.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- maiha
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-02-
|
18
|
+
date: 2012-02-13 00:00:00 +09:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -40,12 +40,12 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
hash:
|
43
|
+
hash: 27
|
44
44
|
segments:
|
45
45
|
- 0
|
46
46
|
- 2
|
47
|
-
-
|
48
|
-
version: 0.2.
|
47
|
+
- 6
|
48
|
+
version: 0.2.6
|
49
49
|
type: :runtime
|
50
50
|
version_requirements: *id002
|
51
51
|
- !ruby/object:Gem::Dependency
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- lib/typed/schema.rb
|
83
83
|
- lib/typed/version.rb
|
84
84
|
- spec/hash_spec.rb
|
85
|
+
- spec/schema_spec.rb
|
85
86
|
- spec/spec_helper.rb
|
86
87
|
- typed.gemspec
|
87
88
|
has_rdoc: true
|