sparsam 0.1.4 → 0.1.5

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: 3ab4bfb28dc0761f1ef66ea93e239408e4bdb308
4
- data.tar.gz: 78cd96b406e6260832478236d53e7c20a166d628
3
+ metadata.gz: 82b851a7cec00fa3805571f1cd89d43077b86ed5
4
+ data.tar.gz: c4617c449ccd26bb73896e49ceb430b15f6f973c
5
5
  SHA512:
6
- metadata.gz: 9c5cfdaed8ebc55ded0e937139d21d3133459be09b3787c6fc0625fdf09be2452d615969dbcd9aa7f7eb9510ec2bd1dd65c2314b9d979cabb043349f113cbc94
7
- data.tar.gz: 4a72dbf9b165aff8c419beaa7f870ed3cdb00e1d858201325707cdd5248d7e6ce6126896e9e4ae1e381c5e055003f4c8d348f95297e8143c270e86fe2bd198eb
6
+ metadata.gz: 73aff3bcae386d83adb457c63ef35ec0d0281e2bab33436b4f05c7fa8b79f80e5110d7600cfa3427cfbd5eeb222f01cf997ce8f312cd3be72dbb6f7d6fbebdc1
7
+ data.tar.gz: 6f060447c4d06c8abd9f014dcdbafe5e3e1e8ffc8ce0a23c999c7c445138ac019a0257eccf48bdfc5af4cf722ccd64e44155574190c65f67dadc026b1f223317
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Sparsam [![Build Status](https://travis-ci.org/airbnb/sparsam.svg?branch=master)](https://travis-ci.org/airbnb/sparsam) [![Coverage Status](https://coveralls.io/repos/github/airbnb/sparsam/badge.svg?branch=master)](https://coveralls.io/github/airbnb/sparsam?branch=master)
1
+ # Sparsam [![Build Status](https://travis-ci.org/airbnb/sparsam.svg?branch=master)](https://travis-ci.org/airbnb/sparsam) [![Coverage Status](https://coveralls.io/repos/github/airbnb/sparsam/badge.svg?branch=master)](https://coveralls.io/github/airbnb/sparsam?branch=master) [![Gem Version](https://badge.fury.io/rb/sparsam.svg)](https://badge.fury.io/rb/sparsam)
2
2
  New Thrift bindings and generator for Ruby!
3
3
 
4
4
  ## Super basic Example
data/ext/serializer.cpp CHANGED
@@ -136,6 +136,18 @@ static inline VALUE make_ruby_binary(const string &val) {
136
136
  return rb_str_new(val.c_str(), val.size());
137
137
  }
138
138
 
139
+ static void raise_exc_with_struct_and_field_names(
140
+ VALUE exc_class,
141
+ VALUE msg,
142
+ VALUE outer_struct_class,
143
+ VALUE field_sym) {
144
+ VALUE struct_name = rb_class_name(outer_struct_class);
145
+ VALUE field_name = rb_sym_to_s(field_sym);
146
+ VALUE args[3] = {msg, struct_name, field_name};
147
+ VALUE e = rb_class_new_instance(3, args, exc_class);
148
+ rb_exc_raise(e);
149
+ }
150
+
139
151
  static inline VALUE make_ruby_bool(bool val) { return val ? Qtrue : Qfalse; }
140
152
 
141
153
  void ThriftSerializer::skip_n_type(uint32_t n, TType ttype) {
@@ -301,8 +313,11 @@ VALUE ThriftSerializer::readStruct(VALUE klass) {
301
313
  typeId = fieldInfo->ftype;
302
314
 
303
315
  if (typeId != fieldBegin.ftype) {
304
- rb_raise(SparsamTypeMismatchError, "Type Mismatch. Defenition: %d, Actual: %d",
305
- fieldBegin.ftype, typeId);
316
+ raise_exc_with_struct_and_field_names(
317
+ SparsamTypeMismatchError,
318
+ rb_sprintf("Type Mismatch. Defenition: %d, Actual: %d", fieldBegin.ftype, typeId),
319
+ klass,
320
+ fieldInfo->symName);
306
321
  }
307
322
 
308
323
  VALUE rb_value = this->readAny(fieldBegin.ftype, iter->second);
@@ -387,6 +402,8 @@ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
387
402
  HANDLE_TYPE(BYTE, Byte, NUM2SHORT)
388
403
 
389
404
  case protocol::T_STRING: {
405
+ Check_Type(actual, T_STRING);
406
+
390
407
  string data = string(StringValuePtr(actual), RSTRING_LEN(actual));
391
408
  if (field_info->isBinaryString) {
392
409
  this->tprot->writeBinary(data);
@@ -397,6 +414,8 @@ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
397
414
  }
398
415
 
399
416
  case protocol::T_LIST: {
417
+ Check_Type(actual, T_ARRAY);
418
+
400
419
  long length = RARRAY_LEN(actual);
401
420
  TType elem = field_info->elementType->ftype;
402
421
  this->tprot->writeListBegin(elem, static_cast<size_t>(length));
@@ -420,6 +439,8 @@ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
420
439
  }
421
440
 
422
441
  case protocol::T_MAP: {
442
+ Check_Type(actual, T_HASH);
443
+
423
444
  TType keyTType = field_info->keyType->ftype,
424
445
  valueTType = field_info->elementType->ftype;
425
446
  this->tprot->writeMapBegin(keyTType, valueTType,
@@ -454,6 +475,11 @@ void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
454
475
  FieldBegin fieldBegin;
455
476
  FieldInfo *fieldInfo;
456
477
  auto fields = FindOrCreateFieldInfoMap(klass);
478
+
479
+ if (!validateStruct(klass, data, false, false)) {
480
+ return;
481
+ }
482
+
457
483
  for (auto const & entry : *fields) {
458
484
  fieldBegin.fid = entry.first;
459
485
  fieldInfo = entry.second;
@@ -490,15 +516,23 @@ VALUE serializer_readStruct(VALUE self, VALUE klass) {
490
516
  catch_thrift_and_reraise();
491
517
  }
492
518
 
493
- static inline void raise_type_mismatch() {
494
- rb_raise(SparsamTypeMismatchError, "Type mismatch in field data");
519
+ static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym) {
520
+ raise_exc_with_struct_and_field_names(
521
+ SparsamTypeMismatchError,
522
+ rb_str_new2("Type mismatch in field data"),
523
+ CLASS_OF(outer_struct),
524
+ field_sym);
495
525
  }
496
526
 
497
- bool validateArray(FieldInfo *type, VALUE arr, bool recursive) {
527
+ bool validateArray(
528
+ FieldInfo *type,
529
+ VALUE arr,
530
+ bool recursive,
531
+ VALUE outer_struct,
532
+ VALUE field_sym) {
498
533
  long length = RARRAY_LEN(arr);
499
534
  for (long i = 0; i < length; i++) {
500
- if (!validateAny(type, rb_ary_entry(arr, i), recursive)) {
501
- rb_raise(SparsamTypeMismatchError, "Type mismatch in container element");
535
+ if (!validateAny(type, rb_ary_entry(arr, i), recursive, outer_struct, field_sym)) {
502
536
  return false;
503
537
  }
504
538
  }
@@ -507,7 +541,7 @@ bool validateArray(FieldInfo *type, VALUE arr, bool recursive) {
507
541
 
508
542
  #define TEST_RB_VAL_FOR_CLASS(VAL, KLASS) \
509
543
  if (!RTEST(rb_obj_is_kind_of(VAL, KLASS))) { \
510
- raise_type_mismatch(); \
544
+ raise_type_mismatch(outer_struct, field_sym); \
511
545
  ret = false; \
512
546
  }
513
547
 
@@ -517,7 +551,7 @@ bool validateArray(FieldInfo *type, VALUE arr, bool recursive) {
517
551
  break; \
518
552
  }
519
553
 
520
- bool validateAny(FieldInfo *type, VALUE val, bool recursive) {
554
+ bool validateAny(FieldInfo *type, VALUE val, bool recursive, VALUE outer_struct, VALUE field_sym) {
521
555
  bool ret = true;
522
556
  switch (type->ftype) {
523
557
 
@@ -528,6 +562,14 @@ bool validateAny(FieldInfo *type, VALUE val, bool recursive) {
528
562
  HANDLE_TYPE(DOUBLE, klass_for_float)
529
563
  HANDLE_TYPE(STRING, klass_for_string)
530
564
 
565
+ case protocol::T_BOOL: {
566
+ if ( !(val == Qtrue || val == Qfalse) ) {
567
+ raise_type_mismatch(outer_struct, field_sym);
568
+ ret = false;
569
+ }
570
+ break;
571
+ }
572
+
531
573
  case protocol::T_STRUCT: {
532
574
  TEST_RB_VAL_FOR_CLASS(val, type->klass)
533
575
  if (ret && recursive) {
@@ -540,14 +582,14 @@ bool validateAny(FieldInfo *type, VALUE val, bool recursive) {
540
582
  TEST_RB_VAL_FOR_CLASS(val, klass_for_set)
541
583
  if (ret) {
542
584
  VALUE ary = rb_funcall(val, intern_for_to_a, 0);
543
- ret = validateArray(type->elementType, ary, recursive);
585
+ ret = validateArray(type->elementType, ary, recursive, outer_struct, field_sym);
544
586
  }
545
587
  break;
546
588
  }
547
589
  case protocol::T_LIST: {
548
590
  TEST_RB_VAL_FOR_CLASS(val, klass_for_array)
549
591
  if (ret) {
550
- ret = validateArray(type->elementType, val, recursive);
592
+ ret = validateArray(type->elementType, val, recursive, outer_struct, field_sym);
551
593
  }
552
594
  break;
553
595
  }
@@ -557,12 +599,13 @@ bool validateAny(FieldInfo *type, VALUE val, bool recursive) {
557
599
  if (ret) {
558
600
  bool flag = true;
559
601
  HASH_FOREACH_BEGIN(val, &flag, type->keyType, type->elementType,
560
- &recursive)
602
+ &recursive, &outer_struct, &field_sym)
561
603
  bool *flag = (bool *)argv[0], *recursive = (bool *)argv[3];
604
+ VALUE *outer_struct = (VALUE *)argv[4], *field_sym = (VALUE *)argv[5];
562
605
  FieldInfo *field_info_key = (FieldInfo *)argv[1];
563
606
  FieldInfo *field_info_value = (FieldInfo *)argv[2];
564
- if (!validateAny(field_info_key, k, *recursive) ||
565
- !validateAny(field_info_value, v, *recursive)) {
607
+ if (!validateAny(field_info_key, k, *recursive, *outer_struct, *field_sym) ||
608
+ !validateAny(field_info_value, v, *recursive, *outer_struct, *field_sym)) {
566
609
  *flag = false;
567
610
  HASH_FOREACH_ABORT()
568
611
  }
@@ -594,12 +637,17 @@ bool validateStruct(VALUE klass, VALUE data, bool validateContainerTypes,
594
637
  VALUE val = rb_ivar_get(data, entry.second->ivarName);
595
638
  if (NIL_P(val)) {
596
639
  if (!entry.second->isOptional) {
597
- rb_raise(SparsamMissingMandatory, "Missing: fieldID %d", entry.first);
640
+ raise_exc_with_struct_and_field_names(
641
+ SparsamMissingMandatory,
642
+ rb_sprintf("Missing: fieldID %d", entry.first),
643
+ klass,
644
+ entry.second->symName);
598
645
  return false;
599
646
  }
600
647
  continue;
601
648
  }
602
- if (validateContainerTypes && !validateAny(entry.second, val, recursive)) {
649
+ if (validateContainerTypes &&
650
+ !validateAny(entry.second, val, recursive, data, entry.second->symName)) {
603
651
  return false;
604
652
  }
605
653
  }
data/ext/serializer.h CHANGED
@@ -92,7 +92,7 @@ private:
92
92
 
93
93
  bool validateStruct(VALUE klass, VALUE data, bool validateContainerTypes,
94
94
  bool recursive);
95
- bool validateAny(FieldInfo *type, VALUE val, bool recursive);
95
+ bool validateAny(FieldInfo *type, VALUE val, bool recursive, VALUE outer_struct, VALUE field_sym);
96
96
  FieldInfoMap *FindOrCreateFieldInfoMap(VALUE klass);
97
97
  FieldInfo *CreateFieldInfo(VALUE field_map_entry);
98
98
  FieldInfoMap *CreateFieldInfoMap(VALUE klass);
@@ -73,7 +73,6 @@ module Sparsam
73
73
  end
74
74
 
75
75
  def serialize(prot = Sparsam::CompactProtocol)
76
- validate
77
76
  s = Sparsam::Serializer.new(prot, "")
78
77
  s.serialize(self.class, self)
79
78
  end
@@ -8,14 +8,22 @@ module Sparsam
8
8
  end
9
9
 
10
10
  class MissingMandatory < Exception
11
- def initialize(msg)
12
- super
11
+ attr_reader :struct_name, :field_name
12
+
13
+ def initialize(msg, struct_name = nil, field_name = nil)
14
+ @struct_name = struct_name
15
+ @field_name = field_name
16
+ super(msg)
13
17
  end
14
18
  end
15
19
 
16
20
  class TypeMismatch < Exception
17
- def initialize(msg)
18
- super
21
+ attr_reader :struct_name, :field_name
22
+
23
+ def initialize(msg, struct_name = nil, field_name = nil)
24
+ @struct_name = struct_name
25
+ @field_name = field_name
26
+ super(msg)
19
27
  end
20
28
  end
21
29
 
@@ -104,3 +104,25 @@ class Nothing < ::Sparsam::Struct
104
104
  init_thrift_struct(self)
105
105
  end
106
106
 
107
+ class EveryType < ::Sparsam::Struct
108
+ FIELDS = {
109
+ 1 => {:type => ::Sparsam::Types::BOOL, :name => 'a_bool', :optional => true},
110
+ 2 => {:type => ::Sparsam::Types::BYTE, :name => 'a_byte', :optional => true},
111
+ 3 => {:type => ::Sparsam::Types::I16, :name => 'an_i16', :optional => true},
112
+ 4 => {:type => ::Sparsam::Types::I32, :name => 'an_i32', :optional => true},
113
+ 5 => {:type => ::Sparsam::Types::I64, :name => 'an_i64', :optional => true},
114
+ 6 => {:type => ::Sparsam::Types::DOUBLE, :name => 'a_double', :optional => true},
115
+ 7 => {:type => ::Sparsam::Types::STRING, :name => 'a_binary', :binary => true, :optional => true},
116
+ 8 => {:type => ::Sparsam::Types::STRING, :name => 'a_string', :optional => true},
117
+ 9 => {:type => ::Sparsam::Types::LIST, :name => 'an_i64_list', :element => {:type => ::Sparsam::Types::I64}, :optional => true},
118
+ 10 => {:type => ::Sparsam::Types::SET, :name => 'an_i64_set', :element => {:type => ::Sparsam::Types::I64}, :optional => true},
119
+ 11 => {:type => ::Sparsam::Types::MAP, :name => 'an_i64_map', :key => {:type => ::Sparsam::Types::I64}, :value => {:type => ::Sparsam::Types::I64}, :optional => true},
120
+ 12 => {:type => ::Sparsam::Types::LIST, :name => 'a_list_of_i64_maps', :element => {:type => ::Sparsam::Types::MAP, :key => {:type => ::Sparsam::Types::I64}, :value => {:type => ::Sparsam::Types::I64}}, :optional => true},
121
+ 13 => {:type => ::Sparsam::Types::MAP, :name => 'a_map_of_i64_maps', :key => {:type => ::Sparsam::Types::MAP, :key => {:type => ::Sparsam::Types::I64}, :value => {:type => ::Sparsam::Types::I64}}, :value => {:type => ::Sparsam::Types::MAP, :key => {:type => ::Sparsam::Types::I64}, :value => {:type => ::Sparsam::Types::I64}}, :optional => true},
122
+ 14 => {:type => ::Sparsam::Types::STRUCT, :name => 'a_struct', :class => ::US, :optional => true},
123
+ 15 => {:type => ::Sparsam::Types::STRUCT, :name => 'a_union', :class => ::UN, :optional => true}
124
+ }
125
+
126
+ init_thrift_struct(self)
127
+ end
128
+
data/spec/sparsam_spec.rb CHANGED
@@ -92,6 +92,7 @@ describe 'Sparsam' do
92
92
  expect {
93
93
  Sparsam.validate(NotSS, data, Sparsam::STRICT)
94
94
  }.to raise_error(Sparsam::TypeMismatch)
95
+
95
96
  expect {
96
97
  Sparsam.validate(EasilyInvalid, data, Sparsam::STRICT)
97
98
  }.to raise_error(Sparsam::TypeMismatch)
@@ -151,6 +152,21 @@ describe 'Sparsam' do
151
152
  }.to raise_error(Sparsam::TypeMismatch)
152
153
  end
153
154
 
155
+ it "includes additional data in TypeMismatch errors" do
156
+ data = EasilyInvalid.new
157
+ data.id_i32 = "definitely a string"
158
+
159
+ e = nil
160
+ begin
161
+ Sparsam.validate(EasilyInvalid, data, Sparsam::STRICT)
162
+ rescue Sparsam::TypeMismatch => exception
163
+ e = exception
164
+ end
165
+
166
+ e.struct_name.should == EasilyInvalid.name
167
+ e.field_name.should == "id_i32"
168
+ end
169
+
154
170
  it "works with crazy thriftness" do
155
171
  data = EasilyInvalid.new
156
172
  data.sure = [{ Set.new([1]) => { 1 => Set.new([[{ EasilyInvalid.new => "sure" }]]) } }]
@@ -189,6 +205,20 @@ describe 'Sparsam' do
189
205
  expect { data.validate }.to raise_error(Sparsam::MissingMandatory)
190
206
  end
191
207
 
208
+ it "includes additional information on missing required fields in exception" do
209
+ data = MiniRequired.new
210
+
211
+ e = nil
212
+ begin
213
+ data.validate
214
+ rescue Sparsam::MissingMandatory => exception
215
+ e = exception
216
+ end
217
+
218
+ e.struct_name.should == MiniRequired.name
219
+ e.field_name.should == "id_i32"
220
+ end
221
+
192
222
  it "will throw errors when given junk data" do
193
223
  expect {
194
224
  Sparsam::Deserializer.deserialize(SS, "wolololololol")
@@ -300,5 +330,80 @@ describe 'Sparsam' do
300
330
  Sparsam::Deserializer.deserialize(SS, really_bad, Sparsam::BinaryProtocol)
301
331
  }.to raise_error(Sparsam::Exception)
302
332
  end
333
+
334
+ it "handles all sorts of type issues without crashing" do
335
+ field_map = {
336
+ a_bool: :boolean,
337
+ a_byte: :int,
338
+ an_i16: :int,
339
+ an_i32: :int,
340
+ an_i64: :int,
341
+ a_double: :float,
342
+ a_binary: :string,
343
+ a_string: :string,
344
+
345
+ an_i64_list: :int_list,
346
+ an_i64_set: :int_set,
347
+ an_i64_map: :int_map,
348
+
349
+ a_list_of_i64_maps: :int_map_list,
350
+ a_map_of_i64_maps: :int_map_map,
351
+
352
+ a_struct: :struct,
353
+ a_union: :union,
354
+ }
355
+
356
+ scalar_values = {
357
+ boolean: true,
358
+ int: 42,
359
+ float: 3.14,
360
+ string: "Hello",
361
+ struct: US.new(id_i32: 10),
362
+ union: UN.new(id_s: "woo"),
363
+ complex: Complex(1),
364
+ bigint: 2**128,
365
+ rational: Rational(2, 3),
366
+ }
367
+
368
+ simple_collection_values = scalar_values.each_with_object({}) do |(type, val), obj|
369
+ obj[:"#{type}_list"] = [val]
370
+ obj[:"#{type}_set"] = Set.new([val])
371
+ obj[:"#{type}_map"] = { val => val }
372
+ end
373
+
374
+ nested_collection_values =
375
+ simple_collection_values.each_with_object({}) do |(type, val), obj|
376
+ obj[:"#{type}_list"] = [val]
377
+ obj[:"#{type}_set"] = Set.new([val])
378
+ obj[:"#{type}_map"] = { val => val }
379
+ end
380
+
381
+ all_values = scalar_values.merge(simple_collection_values).merge(nested_collection_values)
382
+
383
+ field_map.each do |field, type|
384
+ all_values.each do |val_type, val|
385
+ next if val_type == type
386
+
387
+ s = EveryType.new
388
+ s.send(:"#{field}=", val)
389
+
390
+ # Validation doesn't do range checking, though serialization does
391
+ unless val_type.to_s =~ /bigint/
392
+ expect {
393
+ Sparsam.validate(s.class, s, Sparsam::STRICT)
394
+ }.to(
395
+ raise_error(Sparsam::TypeMismatch),
396
+ "assigning #{field} : #{type} a value of " \
397
+ "#{val.inspect} : #{val_type} did not raise TypeMismatch"
398
+ )
399
+ end
400
+
401
+ begin
402
+ s.serialize
403
+ rescue TypeError, RangeError, NoMethodError, Sparsam::TypeMismatch
404
+ end
405
+ end
406
+ end
407
+ end
303
408
  end
304
409
  end
data/spec/user.thrift CHANGED
@@ -60,3 +60,24 @@ struct NotSS_plus {
60
60
 
61
61
  struct nothing {
62
62
  }
63
+
64
+ struct EveryType {
65
+ 1: optional bool a_bool
66
+ 2: optional byte a_byte
67
+ 3: optional i16 an_i16
68
+ 4: optional i32 an_i32
69
+ 5: optional i64 an_i64
70
+ 6: optional double a_double
71
+ 7: optional binary a_binary
72
+ 8: optional string a_string
73
+
74
+ 9: optional list<i64> an_i64_list
75
+ 10: optional set<i64> an_i64_set
76
+ 11: optional map<i64, i64> an_i64_map
77
+
78
+ 12: optional list<map<i64, i64>> a_list_of_i64_maps
79
+ 13: optional map<map<i64, i64>, map<i64, i64>> a_map_of_i64_maps
80
+
81
+ 14: optional US a_struct
82
+ 15: optional UN a_union
83
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparsam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbnb Thrift Developers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-01 00:00:00.000000000 Z
11
+ date: 2017-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -161,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
161
  version: '0'
162
162
  requirements: []
163
163
  rubyforge_project: thrift
164
- rubygems_version: 2.6.12
164
+ rubygems_version: 2.6.11
165
165
  signing_key:
166
166
  specification_version: 4
167
167
  summary: Ruby bindings for Apache Thrift