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 CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gem 'activesupport'
4
- gem 'must', '>= 0.2.5'
4
+ gem 'must', '>= 0.2.6'
data/lib/typed.rb CHANGED
@@ -5,7 +5,6 @@ require "typed/hash"
5
5
 
6
6
  module Typed
7
7
  NotDefined = Class.new(RuntimeError)
8
- LazyValue = Struct.new(:block)
9
8
 
10
9
  autoload :Default, "typed/default"
11
10
  autoload :Schema , "typed/schema"
data/lib/typed/default.rb CHANGED
@@ -12,7 +12,7 @@ module Typed
12
12
  def regsiter_lazy(key, block)
13
13
  return if @kvs.exist?(key)
14
14
  raise ArgumentError, "Lazy default value needs block: #{key}" unless block
15
- @kvs[key] = LazyValue.new(block)
15
+ @kvs[key] = Schema::LazyValue.new(block)
16
16
  end
17
17
  end
18
18
  end
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(self)
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
- if check_schema?(key)
58
- if @schema.exist?(key)
59
- case val
60
- when LazyValue
61
- # not schema
62
- when [], {}
63
- # not schema cause already declared
64
- else
65
- struct = Must::StructInfo.new(val).compact
66
- # when schema format, try update schema
67
- @schema[key] = val if struct == val
68
- end
69
- @schema.check!(key, val)
70
-
71
- else
72
- case val
73
- when LazyValue
74
- # not schema
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
- return @schema.check!(key, self[key]) unless type
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
- if value.is_a?(LazyValue)
139
- value = value.block.call
140
- self[key] = value
141
- end
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 initialize(kvs)
6
- @kvs = kvs
7
- @types = {}
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 exist?(key)
11
- @types.has_key?(key)
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 [](key)
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 []=(key, val)
19
- # update schema if sub-class, otherwise raises
20
- val.must.struct(@types[key]) {
21
- raise TypeError, "%s is already typed as `%s'" % [key, @types[key].inspect]
22
- } if exist?(key)
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
- @types[key] = val
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 check!(key, val)
28
- struct = @types[key] or
29
- raise Schema::NotFound, key.to_s
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?(struct)
81
+ if val.must.struct?(klass)
32
82
  return true
33
83
  else
34
- expected = @types[key].inspect
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
@@ -1,3 +1,3 @@
1
1
  module Typed
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
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 set not data but schema when schema given" do
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 guess and check schema if not exists" do
55
+ it "should implicitly declare schema when assigned" do
53
56
  data[:foo] = 1
54
- lambda {
55
- data[:foo] = "test"
56
- }.should raise_error(TypeError)
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 raise TypeError when schema given twice" do
80
- data[:foo] = String
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 "can ovreride schema when given schema is sub-class of existing one" do
118
+ it "cannot ovreride explicit declarement" do
87
119
  data[:num] = Numeric
88
- data[:num] = 10
89
- data.schema(:num).should == Numeric
90
- data[:num] = Fixnum
91
- data.schema(:num).should == Fixnum
92
- data[:num] = 20
93
- data.schema(:num).should == Fixnum
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
 
@@ -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
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_dependency "activesupport"
22
- s.add_dependency "must", ">= 0.2.5"
22
+ s.add_dependency "must", ">= 0.2.6"
23
23
 
24
24
  s.add_development_dependency "rspec"
25
25
  end
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: 25
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.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-10 00:00:00 +09:00
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: 29
43
+ hash: 27
44
44
  segments:
45
45
  - 0
46
46
  - 2
47
- - 5
48
- version: 0.2.5
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