type_struct 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf544085d5325212144ddfb4119b2360807e1869
4
- data.tar.gz: 2c7efe99a85c7442ddc2fc5b304bb55ca9e78ac1
3
+ metadata.gz: 155aa214f6353b480dc87c69555db00a31623f7d
4
+ data.tar.gz: 31e28839d1fa9930ba9fddddf288f32dbeb02dee
5
5
  SHA512:
6
- metadata.gz: 6b6872ff8ea9c9bcb43c2743726a8458a7d0018a98f81191c511d03a700df971794de75f6d75ffbef9f8ff94e12230fb1483ede2c8cd66581ac7b94c97b4cfe5
7
- data.tar.gz: 93d5f28458f94944711361846df2ecdb9223ee87d8636444fb8cedba51a0068088679c53873c37a08f7c0b2d00118feda067eb2f38c565964c4cd9ff122a3362
6
+ metadata.gz: 56c32708f5d233891dbfeda2a403fffb04d2d8e7a674749a6f6f1c80eadae3f6ac868815299c608cb156585768d0aaffee01a8bff9590530fe7b4d99991885bc
7
+ data.tar.gz: dffe91d28e705eee66d507a3552d94d2a7ece6bd6d52be9d949643e50eebb3568a4cd7e9ee4c4363b5321b397c332728edb002310d758b810af674ab1a6265c1
data/README.md CHANGED
@@ -109,7 +109,9 @@ sample.str = 1 #=> TypeError
109
109
 
110
110
  ### Recursive Mapping
111
111
 
112
- Generate object from hash by recursive
112
+ Generate object from hash by recursive.
113
+
114
+ Like JSON package of golang and crystal-lang.
113
115
 
114
116
  ```ruby
115
117
  Point = TypeStruct.new(
@@ -0,0 +1,25 @@
1
+ class TypeStruct
2
+ UnionNotFoundError = Class.new(StandardError)
3
+
4
+ class MultiTypeError < StandardError
5
+ THIS_LIB_REGEXP = %r{lib/type_struct[./]}
6
+ PWD = Pathname.new(Dir.pwd)
7
+ attr_reader :errors
8
+ def initialize(errors)
9
+ @errors = errors
10
+ super("\n#{build_message}")
11
+ end
12
+
13
+ private
14
+
15
+ def build_message
16
+ @errors.map { |e|
17
+ b = e.backtrace_locations.find do |b|
18
+ b.absolute_path !~ THIS_LIB_REGEXP
19
+ end
20
+ relative_path = Pathname.new(b.absolute_path).relative_path_from(PWD)
21
+ "#{relative_path}:#{b.lineno}:in #{e.class} #{e}"
22
+ }.join("\n")
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  class TypeStruct
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/type_struct.rb CHANGED
@@ -1,34 +1,44 @@
1
+ require 'pathname'
2
+
1
3
  class TypeStruct
2
4
  require "type_struct/union"
3
5
  require "type_struct/array_of"
4
6
  require "type_struct/hash_of"
5
7
  require "type_struct/interface"
8
+ require "type_struct/exceptions"
6
9
  require "type_struct/version"
7
10
 
8
- UnionNotFoundError = Class.new(StandardError)
9
-
10
11
  def initialize(arg)
11
12
  sym_arg = {}
12
13
  arg.each do |k, v|
13
14
  sym_arg[k.to_sym] = v
14
15
  end
15
- self.class.members.each do |k|
16
- self[k] = sym_arg[k]
16
+ errors = []
17
+ klass = self.class
18
+ klass.members.each do |k|
19
+ unless klass.valid?(k, sym_arg[k])
20
+ begin
21
+ raise TypeError, "#{klass}##{k} expect #{klass.type(k)} got #{sym_arg[k].inspect}"
22
+ rescue TypeError => e
23
+ errors << e
24
+ end
25
+ end
26
+ instance_variable_set("@#{k}", sym_arg[k])
17
27
  end
28
+ raise MultiTypeError, errors unless errors.empty?
18
29
  end
19
30
 
20
31
  def ==(other)
21
32
  return false unless TypeStruct === other
22
- return false unless to_h == other.to_h
23
- true
33
+ to_h == other.to_h
24
34
  end
25
35
 
26
36
  def []=(k, v)
27
- __send__("#{k}=", v)
37
+ __send__ "#{k}=", v
28
38
  end
29
39
 
30
40
  def [](k)
31
- __send__(k)
41
+ __send__ k
32
42
  end
33
43
 
34
44
  def inspect
@@ -47,97 +57,117 @@ class TypeStruct
47
57
  m
48
58
  end
49
59
 
50
- class << self
51
- alias original_new new
52
- def new(**args, &block)
53
- c = Class.new(TypeStruct) do
54
- const_set :DEFINITION, args
60
+ module ClassMethods
61
+ def from_hash(h)
62
+ unless Hash === h
63
+ h = h.to_hash if h.respond_to?(:to_hash)
64
+ unless Hash === h
65
+ raise TypeError, "#{self}.from_hash only accept Hash got `#{h.class}'"
66
+ end
67
+ end
68
+ args = {}
69
+ errors = []
70
+ h.each do |key, value|
71
+ key = key.to_sym
72
+ t = type(key)
73
+ args[key] = try_convert(t, key, value, errors)
74
+ end
75
+ raise MultiTypeError, errors unless errors.empty?
76
+ new(args)
77
+ end
55
78
 
56
- class << self
57
- alias_method :new, :original_new
79
+ def definition
80
+ const_get(:DEFINITION)
81
+ end
58
82
 
59
- def from_hash(h)
60
- unless Hash === h
61
- h = h.to_hash if h.respond_to?(:to_hash)
62
- unless Hash === h
63
- raise TypeError, "#{self}.from_hash only accept Hash got `#{h.class}'"
64
- end
65
- end
66
- args = {}
67
- h.each do |key, value|
68
- key = key.to_sym
69
- t = type(key)
70
- args[key] = try_convert(t, key, value)
71
- end
72
- new(args)
73
- end
83
+ def members
84
+ definition.keys
85
+ end
74
86
 
75
- def definition
76
- const_get(:DEFINITION)
77
- end
87
+ def type(k)
88
+ definition[k]
89
+ end
78
90
 
79
- def members
80
- definition.keys
81
- end
91
+ def valid?(k, v)
92
+ definition[k] === v
93
+ end
82
94
 
83
- def type(k)
84
- definition[k]
95
+ private
96
+
97
+ def try_convert(klass, key, value, errors)
98
+ case klass
99
+ when Union
100
+ union_errors = []
101
+ klass.each do |k|
102
+ begin
103
+ return try_convert(k, key, value, nil)
104
+ rescue TypeError, MultiTypeError => e
105
+ union_errors << e
85
106
  end
107
+ end
86
108
 
87
- def valid?(k, v)
88
- definition[k] === v
109
+ raise UnionNotFoundError, "#{klass} is not found with value `#{value}'\nerrors:\n#{union_errors.join("\n")}"
110
+ when ArrayOf
111
+ unless Array === value
112
+ begin
113
+ raise TypeError, "#{self}##{key} expect #{klass.inspect} got #{value.inspect}"
114
+ rescue TypeError => e
115
+ raise unless errors
116
+ errors << e
89
117
  end
90
-
91
- private
92
-
93
- def try_convert(klass, key, value)
94
- return nil unless !klass.nil? && !value.nil?
95
-
96
- case klass
97
- when Union
98
- errors = []
99
- klass.each do |k|
100
- t = begin
101
- try_convert(k, key, value)
102
- rescue TypeError => e
103
- errors << e
104
- nil
105
- end
106
- return t if !t.nil?
107
- end
108
- raise UnionNotFoundError, "#{klass} is not found with value `#{value}'\nerrors:\n#{errors.join("\n")}"
109
- when ArrayOf
110
- unless Array === value
111
- raise TypeError, "#{self}##{key} expect #{klass.inspect} got #{value.inspect}"
112
- end
113
- value.map { |v| try_convert(klass.type, key, v) }
114
- when HashOf
115
- unless Hash === value
116
- raise TypeError, "#{self}##{key} expect #{klass.inspect} got #{value.inspect}"
117
- end
118
- new_hash = {}
119
- value.each do |hk, hv|
120
- new_hash[hk] = try_convert(klass.value_type, key, hv)
121
- end
122
- new_hash
123
- else
124
- if klass.respond_to?(:ancestors)
125
- if klass.ancestors.include?(TypeStruct)
126
- klass.from_hash(value)
127
- elsif klass.ancestors.include?(Struct)
128
- struct = klass.new
129
- value.each { |k, v| struct[k] = v }
130
- struct
131
- elsif klass === value
132
- value
133
- else
134
- raise TypeError, "#{self}##{key} expect #{klass.inspect} got #{value.inspect}"
135
- end
136
- else
137
- value
138
- end
118
+ return value
119
+ end
120
+ value.map { |v| try_convert(klass.type, key, v, errors) }
121
+ when HashOf
122
+ unless Hash === value
123
+ begin
124
+ raise TypeError, "#{self}##{key} expect #{klass.inspect} got #{value.inspect}"
125
+ rescue TypeError => e
126
+ raise unless errors
127
+ errors << e
128
+ end
129
+ return value
130
+ end
131
+ new_hash = {}
132
+ value.each do |hk, hv|
133
+ new_hash[hk] = try_convert(klass.value_type, key, hv, errors)
134
+ end
135
+ new_hash
136
+ else
137
+ if klass.respond_to?(:ancestors)
138
+ if klass.ancestors.include?(TypeStruct)
139
+ klass.from_hash(value)
140
+ elsif klass.ancestors.include?(Struct)
141
+ struct = klass.new
142
+ value.each { |k, v| struct[k] = v }
143
+ struct
144
+ elsif klass === value
145
+ value
146
+ else
147
+ begin
148
+ raise TypeError, "#{self}##{key} expect #{klass} got #{value.inspect}"
149
+ rescue => e
150
+ raise unless errors
151
+ errors << e
139
152
  end
153
+ value
140
154
  end
155
+ else
156
+ value
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ class << self
163
+ alias original_new new
164
+ def new(**args, &block)
165
+ c = Class.new(TypeStruct) do
166
+ extend ClassMethods
167
+ const_set :DEFINITION, args
168
+
169
+ class << self
170
+ alias_method :new, :original_new
141
171
  end
142
172
 
143
173
  args.each_key do |k|
@@ -153,6 +183,7 @@ class TypeStruct
153
183
  end
154
184
  end
155
185
  end
186
+
156
187
  if block_given?
157
188
  c.module_eval(&block)
158
189
  end
@@ -16,6 +16,40 @@ module TypeStructBenchmarkTest
16
16
  end
17
17
  end
18
18
 
19
+ def benchmark_struct_new(b)
20
+ i = 0
21
+ while i < b.n
22
+ Struct.new(:a, :b, :c)
23
+ i += 1
24
+ end
25
+ end
26
+
27
+ def benchmark_new_instance(b)
28
+ t = TypeStruct.new(
29
+ a: String,
30
+ b: Integer,
31
+ c: Regexp,
32
+ )
33
+ hash = { a: "aaa".freeze, b: 1, c: /abc/ }
34
+ i = 0
35
+ while i < b.n
36
+ t.new(hash)
37
+ i += 1
38
+ end
39
+ end
40
+
41
+ def benchmark_struct_new_instance(b)
42
+ t = Struct.new(:a, :b, :c)
43
+ a1 = "aaa".freeze
44
+ a2 = 1
45
+ a3 = /abc/
46
+ i = 0
47
+ while i < b.n
48
+ t.new(a1, a2, a3)
49
+ i += 1
50
+ end
51
+ end
52
+
19
53
  A = TypeStruct.new(
20
54
  a: Integer,
21
55
  )
@@ -34,8 +68,9 @@ module TypeStructBenchmarkTest
34
68
 
35
69
  def benchmark_from_hash(b)
36
70
  i = 0
71
+ hash = { e: { d: { c: { b: { a: 1 } } } } }
37
72
  while i < b.n
38
- E.from_hash(e: { d: { c: { b: { a: 1 } } } })
73
+ E.from_hash(hash)
39
74
  i += 1
40
75
  end
41
76
  end
@@ -89,7 +89,7 @@ module TypeStructTest
89
89
  hc.new(
90
90
  a: [],
91
91
  )
92
- rescue TypeError
92
+ rescue TypeStruct::MultiTypeError
93
93
  else
94
94
  t.error("TypeError was not railsed")
95
95
  end
@@ -103,7 +103,7 @@ module TypeStructTest
103
103
 
104
104
  begin
105
105
  hh = hc.from_hash(a: 1)
106
- rescue TypeError
106
+ rescue TypeStruct::MultiTypeError
107
107
  else
108
108
  t.error("TypeError dose not raise error")
109
109
  end
@@ -114,35 +114,27 @@ module TypeStructTest
114
114
  begin
115
115
  hsbn.from_hash(a: [])
116
116
  rescue TypeStruct::UnionNotFoundError
117
- rescue => e
118
- t.error("Unexpected error #{e.class}: #{e.message}")
119
117
  else
120
118
  t.error("Unexpected behavior")
121
119
  end
122
120
 
123
121
  begin
124
- hsbn.from_hash(a: {a: {b: 1.1}})
122
+ hsbn.from_hash(a: { a: { b: 1.1 } })
125
123
  rescue TypeStruct::UnionNotFoundError
126
- rescue => e
127
- t.error("Unexpected error #{e.class}: #{e.message}")
128
124
  else
129
125
  t.error("Unexpected behavior")
130
126
  end
131
127
 
132
128
  begin
133
- hsbn.from_hash(a: {"a" => {b: 1}})
134
- rescue TypeError
135
- rescue => e
136
- t.error("Unexpected error #{e.class}: #{e.message}")
129
+ hsbn.from_hash(a: { "a" => { b: 1 } })
130
+ rescue TypeStruct::MultiTypeError
137
131
  else
138
132
  t.error("Unexpected behavior")
139
133
  end
140
134
 
141
135
  begin
142
- hsbn.from_hash(a: {"a" => {b: 1.1}})
136
+ hsbn.from_hash(a: { "a" => { b: 1.1 } })
143
137
  rescue TypeStruct::UnionNotFoundError
144
- rescue => e
145
- t.error("Unexpected error #{e.class}: #{e.message}")
146
138
  else
147
139
  t.error("Unexpected behavior")
148
140
  end
@@ -165,9 +157,7 @@ module TypeStructTest
165
157
  a = TypeStruct.new(a: ArrayOf(Integer))
166
158
  begin
167
159
  a.from_hash(a: [1.1])
168
- rescue TypeError
169
- rescue => e
170
- t.error("Unexpected error #{e.class}")
160
+ rescue TypeStruct::MultiTypeError
171
161
  else
172
162
  t.error("Nothing raised TypeError")
173
163
  end
@@ -176,8 +166,6 @@ module TypeStructTest
176
166
  begin
177
167
  b.from_hash(a: [1.1])
178
168
  rescue TypeStruct::UnionNotFoundError
179
- rescue => e
180
- t.error("Unexpected error #{e.class}")
181
169
  else
182
170
  t.error("Nothing raised TypeStruct::UnionNotFoundError")
183
171
  end
@@ -185,10 +173,8 @@ module TypeStructTest
185
173
  c = TypeStruct.new(c: Integer)
186
174
  d = TypeStruct.new(d: ArrayOf(c) | NilClass)
187
175
  begin
188
- d.from_hash(d: [{c: 1.1}])
176
+ d.from_hash(d: [{ c: 1.1 }])
189
177
  rescue TypeStruct::UnionNotFoundError
190
- rescue => e
191
- t.error("Unexpected error #{e.class}")
192
178
  else
193
179
  t.error("Nothing raised TypeStruct::UnionNotFoundError")
194
180
  end
@@ -236,7 +222,7 @@ module TypeStructTest
236
222
 
237
223
  begin
238
224
  foo.from_hash(bar: { baz: nil })
239
- rescue TypeError
225
+ rescue TypeStruct::MultiTypeError
240
226
  else
241
227
  t.error("Bar.baz is not able to nil")
242
228
  end
@@ -255,7 +241,7 @@ module TypeStructTest
255
241
  end
256
242
 
257
243
  def o.to_hash
258
- {a: 1}
244
+ { a: 1 }
259
245
  end
260
246
  unless a === a.from_hash(o)
261
247
  t.error("Unexpected behavior")
@@ -263,30 +249,36 @@ module TypeStructTest
263
249
  end
264
250
 
265
251
  def test_s_from_hash_with_array_of(t)
266
- a = TypeStruct.new(a: ArrayOf(Integer))
252
+ a = TypeStruct.new(a: ArrayOf(Integer), b: Integer)
267
253
  begin
268
- a.from_hash(a: 1)
269
- rescue TypeError => e
270
- unless /#a expect ArrayOf\(Integer\) got 1/ =~ e.message
271
- t.error("message was changed: #{e.message}")
254
+ a.from_hash(a: 1, b: 'a')
255
+ rescue TypeStruct::MultiTypeError => e
256
+ [
257
+ /#a expect ArrayOf\(Integer\) got 1/,
258
+ /#b expect Integer got "a"/,
259
+ ].each do |expect|
260
+ unless expect =~ e.message
261
+ t.error("message was changed: #{e.message}")
262
+ end
272
263
  end
273
- rescue => e
274
- t.error("Unexpected error #{e}")
275
264
  else
276
265
  t.error("Unexpected behavior")
277
266
  end
278
267
  end
279
268
 
280
269
  def test_s_from_hash_with_hash_of(t)
281
- a = TypeStruct.new(a: HashOf(String, Integer))
270
+ a = TypeStruct.new(a: HashOf(String, Integer), b: Integer)
282
271
  begin
283
- a.from_hash(a: 1)
284
- rescue TypeError => e
285
- unless /#a expect HashOf\(String, Integer\) got 1/ =~ e.message
286
- t.error("message was changed: #{e.message}")
272
+ a.from_hash(a: 1, b: 'a')
273
+ rescue TypeStruct::MultiTypeError => e
274
+ [
275
+ /#a expect HashOf\(String, Integer\) got 1/,
276
+ /#b expect Integer got "a"/,
277
+ ].each do |expect|
278
+ unless expect =~ e.message
279
+ t.error("message was changed: #{e.message}")
280
+ end
287
281
  end
288
- rescue => e
289
- t.error("Unexpected error #{e}")
290
282
  else
291
283
  t.error("Unexpected behavior")
292
284
  end
@@ -296,7 +288,7 @@ module TypeStructTest
296
288
  a = TypeStruct.new(a: "a")
297
289
  begin
298
290
  a.from_hash(a: "b")
299
- rescue TypeError
291
+ rescue TypeStruct::MultiTypeError
300
292
  else
301
293
  t.error("Unexpected behavior")
302
294
  end
@@ -309,11 +301,7 @@ module TypeStructTest
309
301
  u = TypeStruct::Union.new(a, b, c)
310
302
  d = TypeStruct.new(d: u)
311
303
 
312
- begin
313
- d.from_hash(d: { b: 1 })
314
- rescue => e
315
- t.error("Unexpected error was raised #{e.class}: #{e.message}")
316
- end
304
+ d.from_hash(d: { b: 1 })
317
305
 
318
306
  begin
319
307
  d.from_hash(d: [b: 1])
@@ -516,6 +504,87 @@ module TypeStructTest
516
504
  end
517
505
  end
518
506
 
507
+ def test_multi_type_error(t)
508
+ a = TypeStruct.new(
509
+ a: Integer,
510
+ b: Integer,
511
+ c: Integer,
512
+ )
513
+ begin
514
+ a.new(
515
+ a: 'a',
516
+ b: 1,
517
+ c: '1',
518
+ )
519
+ rescue TypeStruct::MultiTypeError => err
520
+ unless err.errors.all? { |e| TypeError === e }
521
+ t.error("Empty errors")
522
+ end
523
+
524
+ [
525
+ /a expect Integer got "a"/,
526
+ /c expect Integer got "1"/,
527
+ ].each do |reg|
528
+ unless reg =~ err.message
529
+ t.error("should match error message #{reg} got #{err.message}")
530
+ end
531
+ end
532
+ rescue => err
533
+ raise err
534
+ else
535
+ t.error("Nothing raised an error")
536
+ end
537
+ end
538
+
539
+ def test_s_from_hash_multi_type_error(t)
540
+ a = TypeStruct.new(
541
+ a: Integer,
542
+ b: String,
543
+ c: ArrayOf(String),
544
+ )
545
+ b = TypeStruct.new(
546
+ b: a,
547
+ )
548
+ begin
549
+ b.from_hash(b: { a: '1', b: 1, c: /a/ })
550
+ rescue TypeStruct::MultiTypeError => err
551
+ unless err.errors.all? { |e| TypeError === e }
552
+ t.error("Empty errors")
553
+ end
554
+
555
+ [
556
+ /a expect Integer got "1"/,
557
+ /b expect String got 1/,
558
+ %r{c expect ArrayOf\(String\) got /a/},
559
+ ].each do |reg|
560
+ unless reg =~ err.message
561
+ t.error("should match error message #{reg} got #{err.message}")
562
+ end
563
+ end
564
+ end
565
+ end
566
+
567
+ def test_s_from_hash_multi_type_error_with_union(t)
568
+ a = TypeStruct.new(
569
+ a: ArrayOf(String) | NilClass,
570
+ )
571
+ b = TypeStruct.new(
572
+ b: a,
573
+ )
574
+ begin
575
+ b.from_hash(b: { a: '1' })
576
+ rescue TypeStruct::UnionNotFoundError => err
577
+ [
578
+ /a expect ArrayOf\(String\) got "1"/,
579
+ /a expect NilClass got "1"/,
580
+ ].each do |reg|
581
+ unless reg =~ err.message
582
+ t.error("should match error message #{reg} got #{err.message}")
583
+ end
584
+ end
585
+ end
586
+ end
587
+
519
588
  def test_getter(t)
520
589
  dummy = Dummy.new(str: "aaa", num: 123, reg: "abc", any: [1, "bbb"])
521
590
  _, err = go { dummy[:str] }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: type_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-29 00:00:00.000000000 Z
11
+ date: 2016-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -68,6 +68,7 @@ files:
68
68
  - lib/type_struct.rb
69
69
  - lib/type_struct/array_of.rb
70
70
  - lib/type_struct/array_of_test.rb
71
+ - lib/type_struct/exceptions.rb
71
72
  - lib/type_struct/ext.rb
72
73
  - lib/type_struct/hash_of.rb
73
74
  - lib/type_struct/hash_of_test.rb
@@ -99,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
100
  version: '0'
100
101
  requirements: []
101
102
  rubyforge_project:
102
- rubygems_version: 2.6.1
103
+ rubygems_version: 2.6.2
103
104
  signing_key:
104
105
  specification_version: 4
105
106
  summary: Imitating static typed struct.