typed 0.1.1 → 0.1.2
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/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
|