emery 0.0.2 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/emery/type.rb CHANGED
@@ -1,187 +1,185 @@
1
- module Emery
2
- module T
3
- def T.check_not_nil(type, value)
4
- if value == nil
5
- raise TypeError.new("Type #{type.to_s} does not allow nil value")
6
- end
1
+ module T
2
+ def T.check_not_nil(type, value)
3
+ if value == nil
4
+ raise TypeError.new("Type #{type.to_s} does not allow nil value")
7
5
  end
6
+ end
8
7
 
9
- class UntypedType
10
- def to_s
11
- "Untyped"
12
- end
13
- def check(value)
14
- T.check_not_nil(self, value)
15
- end
8
+ class UntypedType
9
+ def to_s
10
+ "Untyped"
16
11
  end
12
+ def check(value)
13
+ T.check_not_nil(self, value)
14
+ end
15
+ end
17
16
 
18
- class Nilable
19
- attr_reader :type
20
- def initialize(type)
21
- @type = type
22
- end
23
- def to_s
24
- "Nilable[#{type.to_s}]"
25
- end
26
- def check(value)
27
- if value != nil
28
- T.check(type, value)
29
- end
30
- end
31
- def ==(other)
32
- T.instance_of?(Nilable, other) and self.type == other.type
17
+ class Nilable
18
+ attr_reader :type
19
+ def initialize(type)
20
+ @type = type
21
+ end
22
+ def to_s
23
+ "Nilable[#{type.to_s}]"
24
+ end
25
+ def check(value)
26
+ if value != nil
27
+ T.check(type, value)
33
28
  end
34
29
  end
30
+ def ==(other)
31
+ T.instance_of?(Nilable, other) and self.type == other.type
32
+ end
33
+ end
35
34
 
36
- class AnyType
37
- attr_reader :types
38
- def initialize(*types)
39
- @types = types
40
- end
41
- def to_s
42
- "Any[#{types.map { |t| t.to_s}.join(', ')}]"
43
- end
44
- def check(value)
45
- type = types.find {|t| T.instance_of?(t, value) }
46
- if type == nil
47
- raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - any of #{@types.map { |t| t.to_s}.join(', ')} required")
48
- end
49
- end
50
- def ==(other)
51
- T.instance_of?(AnyType, other) and (self.types - other.types).empty?
35
+ class AnyType
36
+ attr_reader :types
37
+ def initialize(*types)
38
+ @types = types
39
+ end
40
+ def to_s
41
+ "Any[#{types.map { |t| t.to_s}.join(', ')}]"
42
+ end
43
+ def check(value)
44
+ type = types.find {|t| T.instance_of?(t, value) }
45
+ if type == nil
46
+ raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - any of #{@types.map { |t| t.to_s}.join(', ')} required")
52
47
  end
53
48
  end
49
+ def ==(other)
50
+ T.instance_of?(AnyType, other) and (self.types - other.types).empty?
51
+ end
52
+ end
54
53
 
55
- class UnionType < AnyType
56
- attr_reader :cases
57
- def initialize(cases)
58
- @cases = cases
59
- super(*cases.values)
60
- end
61
- def to_s
62
- "Union[#{cases.map { |k, t| "#{k}: #{t}"}.join(', ')}]"
63
- end
54
+ class UnionType < AnyType
55
+ attr_reader :cases
56
+ def initialize(cases)
57
+ @cases = cases
58
+ super(*cases.values)
64
59
  end
60
+ def to_s
61
+ "Union[#{cases.map { |k, t| "#{k}: #{t}"}.join(', ')}]"
62
+ end
63
+ end
65
64
 
66
- class ArrayType
67
- attr_reader :item_type
68
- def initialize(item_type)
69
- @item_type = item_type
70
- end
71
- def to_s
72
- "Array[#{item_type.to_s}]"
73
- end
74
- def check(value)
75
- T.check_not_nil(self, value)
76
- if !value.is_a? Array
77
- raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - Array is required")
78
- end
79
- value.each { |item_value| T.check(item_type, item_value) }
80
- end
81
- def ==(other)
82
- T.instance_of?(ArrayType, other) and self.item_type == other.item_type
65
+ class ArrayType
66
+ attr_reader :item_type
67
+ def initialize(item_type)
68
+ @item_type = item_type
69
+ end
70
+ def to_s
71
+ "Array[#{item_type.to_s}]"
72
+ end
73
+ def check(value)
74
+ T.check_not_nil(self, value)
75
+ if !value.is_a? Array
76
+ raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - Array is required")
83
77
  end
78
+ value.each { |item_value| T.check(item_type, item_value) }
84
79
  end
80
+ def ==(other)
81
+ T.instance_of?(ArrayType, other) and self.item_type == other.item_type
82
+ end
83
+ end
85
84
 
86
- class HashType
87
- attr_reader :key_type
88
- attr_reader :value_type
89
- def initialize(key_type, value_type)
90
- @key_type = key_type
91
- @value_type = value_type
92
- end
93
- def to_s
94
- "Hash[#{@key_type.to_s}, #{@value_type.to_s}]"
95
- end
96
- def check(value)
97
- T.check_not_nil(self, value)
98
- if !value.is_a? Hash
99
- raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - Hash is required")
100
- end
101
- value.each do |item_key, item_value|
102
- T.check(@key_type, item_key)
103
- T.check(@value_type, item_value)
104
- end
85
+ class HashType
86
+ attr_reader :key_type
87
+ attr_reader :value_type
88
+ def initialize(key_type, value_type)
89
+ @key_type = key_type
90
+ @value_type = value_type
91
+ end
92
+ def to_s
93
+ "Hash[#{@key_type.to_s}, #{@value_type.to_s}]"
94
+ end
95
+ def check(value)
96
+ T.check_not_nil(self, value)
97
+ if !value.is_a? Hash
98
+ raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - Hash is required")
105
99
  end
106
- def ==(other)
107
- T.instance_of?(HashType, other) and self.key_type == other.key_type and self.value_type == other.value_type
100
+ value.each do |item_key, item_value|
101
+ T.check(@key_type, item_key)
102
+ T.check(@value_type, item_value)
108
103
  end
109
104
  end
105
+ def ==(other)
106
+ T.instance_of?(HashType, other) and self.key_type == other.key_type and self.value_type == other.value_type
107
+ end
108
+ end
110
109
 
111
- class StringFormatted
112
- attr_reader :regex
113
- def initialize(regex)
114
- @regex = regex
115
- end
116
- def to_s
117
- "String<#@regex>"
110
+ class StringFormatted
111
+ attr_reader :regex
112
+ def initialize(regex)
113
+ @regex = regex
114
+ end
115
+ def to_s
116
+ "String<#@regex>"
117
+ end
118
+ def check(value)
119
+ T.check_not_nil(self, value)
120
+ if !value.is_a? String
121
+ raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - String is required for StringFormatted")
118
122
  end
119
- def check(value)
120
- T.check_not_nil(self, value)
121
- if !value.is_a? String
122
- raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - String is required for StringFormatted")
123
- end
124
- if !@regex.match?(value)
125
- raise TypeError.new("Value '#{value.inspect.to_s}' is not in required format '#{@regex}'")
126
- end
123
+ if !@regex.match?(value)
124
+ raise TypeError.new("Value '#{value.inspect.to_s}' is not in required format '#{@regex}'")
127
125
  end
128
126
  end
127
+ end
129
128
 
130
- def T.check(type, value)
131
- if type.methods.include? :check
132
- type.check(value)
133
- else
134
- if type != NilClass
135
- T.check_not_nil(type, value)
136
- end
137
- if !value.is_a? type
138
- raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - #{type} is required")
139
- end
129
+ def T.check(type, value)
130
+ if type.methods.include? :check
131
+ type.check(value)
132
+ else
133
+ if type != NilClass
134
+ T.check_not_nil(type, value)
140
135
  end
141
- return value
142
- end
143
-
144
- def T.instance_of?(type, value)
145
- begin
146
- T.check(type, value)
147
- true
148
- rescue TypeError
149
- false
136
+ if !value.is_a? type
137
+ raise TypeError.new("Value '#{value.inspect.to_s}' type is #{value.class} - #{type} is required")
150
138
  end
151
139
  end
140
+ return value
141
+ end
152
142
 
153
- def T.nilable(value_type)
154
- Nilable.new(value_type)
143
+ def T.instance_of?(type, value)
144
+ begin
145
+ T.check(type, value)
146
+ true
147
+ rescue TypeError
148
+ false
155
149
  end
150
+ end
156
151
 
157
- def T.array(item_type)
158
- ArrayType.new(item_type)
159
- end
152
+ def T.nilable(value_type)
153
+ Nilable.new(value_type)
154
+ end
160
155
 
161
- def T.hash(key_type, value_type)
162
- HashType.new(key_type, value_type)
163
- end
156
+ def T.array(item_type)
157
+ ArrayType.new(item_type)
158
+ end
164
159
 
165
- def T.any(*typdefs)
166
- AnyType.new(*typdefs)
167
- end
160
+ def T.hash(key_type, value_type)
161
+ HashType.new(key_type, value_type)
162
+ end
168
163
 
169
- def T.union(*cases)
170
- UnionType.new(*cases)
171
- end
164
+ def T.any(*typdefs)
165
+ AnyType.new(*typdefs)
166
+ end
172
167
 
173
- def T.check_var(var_name, type, value)
174
- begin
175
- check(type, value)
176
- return value
177
- rescue TypeError => e
178
- raise TypeError.new("Variable #{var_name} type check failed, expected type: #{type.to_s}, value: #{value}")
179
- end
168
+ def T.union(*cases)
169
+ UnionType.new(*cases)
170
+ end
171
+
172
+ def T.check_var(var_name, type, value)
173
+ begin
174
+ check(type, value)
175
+ return value
176
+ rescue TypeError => e
177
+ raise TypeError.new("Variable #{var_name} type check failed, expected type: #{type.to_s}, value: #{value}")
180
178
  end
181
179
  end
180
+ end
182
181
 
183
- Boolean = T.any(TrueClass, FalseClass)
184
- Untyped = T::UntypedType.new
185
- NilableUntyped = T.nilable(Untyped)
186
- UUID = T::StringFormatted.new(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
187
- end
182
+ Boolean = T.any(TrueClass, FalseClass)
183
+ Untyped = T::UntypedType.new
184
+ NilableUntyped = T.nilable(Untyped)
185
+ UUID = T::StringFormatted.new(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
@@ -1,115 +1,112 @@
1
1
  require "test/unit/runner/junitxml"
2
2
 
3
- require 'emery/dataclass'
4
- require 'emery/jsoner'
3
+ require 'emery'
5
4
 
6
- module Emery
7
- class DataClassTypeEquality < Test::Unit::TestCase
8
- def test_equals
9
- assert_true TheClass == TheClass
10
- end
5
+ class DataClassTypeEquality < Test::Unit::TestCase
6
+ def test_equals
7
+ assert_true TheClass == TheClass
8
+ end
11
9
 
12
- def test_not_equals
13
- assert_false TheClass == TheClassWithNested
14
- end
10
+ def test_not_equals
11
+ assert_false TheClass == TheClassWithNested
15
12
  end
13
+ end
16
14
 
17
- class DataClassFields < Test::Unit::TestCase
18
- def test_fields_meta
19
- assert_equal ({:string => String, :int => Integer}), TheClass.json_attributes, "Attributes with types should be available on data class"
20
- end
15
+ class DataClassFields < Test::Unit::TestCase
16
+ def test_fields_meta
17
+ assert_equal ({:string => String, :int => Integer}), TheClass.json_attributes, "Attributes with types should be available on data class"
18
+ end
21
19
 
22
- def test_read
23
- obj = TheClass.new(string: "the string", int: 123)
24
- assert_equal "the string", obj.string, "Immutable field should be readable"
25
- assert_equal 123, obj.int, "Mutable field should be readable"
26
- end
20
+ def test_read
21
+ obj = TheClass.new(string: "the string", int: 123)
22
+ assert_equal "the string", obj.string, "Immutable field should be readable"
23
+ assert_equal 123, obj.int, "Mutable field should be readable"
24
+ end
27
25
 
28
- def test_write_mutable
29
- obj = TheClass.new(string: "the string", int: 123)
30
- obj.int = 124
31
- assert_equal 124, obj.int, "Mutable field should be writable"
32
- end
26
+ def test_write_mutable
27
+ obj = TheClass.new(string: "the string", int: 123)
28
+ obj.int = 124
29
+ assert_equal 124, obj.int, "Mutable field should be writable"
30
+ end
33
31
 
34
- def test_write_immutable
35
- obj = TheClass.new(string: "the string", int: 123)
36
- assert_raise NoMethodError do
37
- obj.string = "the other string"
38
- end
32
+ def test_write_immutable
33
+ obj = TheClass.new(string: "the string", int: 123)
34
+ assert_raise NoMethodError do
35
+ obj.string = "the other string"
39
36
  end
40
37
  end
38
+ end
41
39
 
42
- class DataClassEquality < Test::Unit::TestCase
43
- def test_nil
44
- assert_not_equal nil, TheClass.new(string: "the string", int: 123), "Object should not be equal to nil"
45
- end
40
+ class DataClassEquality < Test::Unit::TestCase
41
+ def test_nil
42
+ assert_not_equal nil, TheClass.new(string: "the string", int: 123), "Object should not be equal to nil"
43
+ end
46
44
 
47
- def test_same_fields_values
48
- assert_equal TheClass.new(string: "the string", int: 123), TheClass.new(string: "the string", int: 123), "Objects with same fields should be equal"
49
- end
45
+ def test_same_fields_values
46
+ assert_equal TheClass.new(string: "the string", int: 123), TheClass.new(string: "the string", int: 123), "Objects with same fields should be equal"
47
+ end
50
48
 
51
- def test_different_fields_values
52
- assert_not_equal TheClass.new(string: "the string 2", int: 123), TheClass.new(string: "the string", int: 123), "Objects with different fields should not be equal"
53
- end
49
+ def test_different_fields_values
50
+ assert_not_equal TheClass.new(string: "the string 2", int: 123), TheClass.new(string: "the string", int: 123), "Objects with different fields should not be equal"
54
51
  end
52
+ end
55
53
 
56
- class DataClassDeserialization < Test::Unit::TestCase
57
- def test_deserialize_object
58
- data = Jsoner.from_json(TheClass, '{"string": "the string", "int": 123}')
59
- T.check(TheClass, data)
60
- assert_equal TheClass.new(string: "the string", int: 123), data, "Should parse data class object"
61
- end
54
+ class DataClassDeserialization < Test::Unit::TestCase
55
+ def test_deserialize_object
56
+ data = Jsoner.from_json(TheClass, '{"string": "the string", "int": 123}')
57
+ T.check(TheClass, data)
58
+ assert_equal TheClass.new(string: "the string", int: 123), data, "Should parse data class object"
59
+ end
62
60
 
63
- def test_deserialize_nested_object
64
- data = Jsoner.from_json(TheClassWithNested, '{"nested": {"string": "the string", "int": 123}}')
65
- T.check(TheClassWithNested, data)
66
- assert_equal TheClassWithNested.new(nested: TheClass.new(string: "the string", int: 123)), data, "Should parse nested data class object"
67
- end
61
+ def test_deserialize_nested_object
62
+ data = Jsoner.from_json(TheClassWithNested, '{"nested": {"string": "the string", "int": 123}}')
63
+ T.check(TheClassWithNested, data)
64
+ assert_equal TheClassWithNested.new(nested: TheClass.new(string: "the string", int: 123)), data, "Should parse nested data class object"
65
+ end
68
66
 
69
- def test_deserialize_object_fail
70
- assert_raise JsonerError do
71
- Jsoner.from_json(TheClass, '"string"')
72
- end
67
+ def test_deserialize_object_fail
68
+ assert_raise JsonerError do
69
+ Jsoner.from_json(TheClass, '"string"')
73
70
  end
74
-
75
71
  end
76
72
 
77
- class DataClassSerialization < Test::Unit::TestCase
78
- def test_serialize_object
79
- assert_equal '{"string":"the string","int":123}', Jsoner.to_json(TheClass, TheClass.new(string: "the string", int: 123)), "nil should be serializable to JSON"
80
- end
73
+ end
81
74
 
82
- def test_serialize_array_of_objects
83
- assert_equal '[{"string":"the string","int":123},{"string":"the string 2","int":456}]', Jsoner.to_json(T.array(TheClass), [TheClass.new(string: "the string", int: 123), TheClass.new(string: "the string 2", int: 456)]), "Array of objects should be serializable to JSON"
84
- end
75
+ class DataClassSerialization < Test::Unit::TestCase
76
+ def test_serialize_object
77
+ assert_equal '{"string":"the string","int":123}', Jsoner.to_json(TheClass, TheClass.new(string: "the string", int: 123)), "nil should be serializable to JSON"
85
78
  end
86
79
 
87
- class DataClassCopy < Test::Unit::TestCase
88
- def test_copy
89
- a = TheClass.new(string: "the string", int: 123)
90
- b = a.copy(string: "the other string")
91
- assert_equal "the string", a.string
92
- assert_equal "the other string", b.string
93
- end
80
+ def test_serialize_array_of_objects
81
+ assert_equal '[{"string":"the string","int":123},{"string":"the string 2","int":456}]', Jsoner.to_json(T.array(TheClass), [TheClass.new(string: "the string", int: 123), TheClass.new(string: "the string 2", int: 456)]), "Array of objects should be serializable to JSON"
82
+ end
83
+ end
84
+
85
+ class DataClassCopy < Test::Unit::TestCase
86
+ def test_copy
87
+ a = TheClass.new(string: "the string", int: 123)
88
+ b = a.copy(string: "the other string")
89
+ assert_equal "the string", a.string
90
+ assert_equal "the other string", b.string
91
+ end
94
92
 
95
- def test_copy_non_existing_field
96
- assert_raise TypeError do
97
- a = TheClass.new(string: "the string", int: 123)
98
- a.copy(non_existing: "the other string")
99
- end
93
+ def test_copy_non_existing_field
94
+ assert_raise TypeError do
95
+ a = TheClass.new(string: "the string", int: 123)
96
+ a.copy(non_existing: "the other string")
100
97
  end
101
98
  end
99
+ end
102
100
 
103
- class TheClass
104
- include DataClass
101
+ class TheClass
102
+ include DataClass
105
103
 
106
- val :string, String
107
- var :int, Integer
108
- end
104
+ val :string, String
105
+ var :int, Integer
106
+ end
109
107
 
110
- class TheClassWithNested
111
- include DataClass
108
+ class TheClassWithNested
109
+ include DataClass
112
110
 
113
- val :nested, TheClass
114
- end
111
+ val :nested, TheClass
115
112
  end