oj 3.13.0 → 3.13.4

Sign up to get free protection for your applications and to get access to all the features.
data/ext/oj/rails.c CHANGED
@@ -157,7 +157,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
157
157
  assure_size(out, 2);
158
158
  *out->cur++ = '{';
159
159
  for (i = 0; i < cnt; i++) {
160
- volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
160
+ volatile VALUE s = rb_sym2str(rb_ary_entry(ma, i));
161
161
 
162
162
  name = RSTRING_PTR(s);
163
163
  len = (int)RSTRING_LEN(s);
data/ext/oj/rxclass.c CHANGED
@@ -110,7 +110,7 @@ oj_rxclass_match(RxClass rc, const char *str, int len) {
110
110
  }
111
111
  } else if (len < (int)sizeof(buf)) {
112
112
  #if !IS_WINDOWS
113
- // string is not \0 terminated so copy and atempt a match
113
+ // string is not \0 terminated so copy and attempt a match
114
114
  memcpy(buf, str, len);
115
115
  buf[len] = '\0';
116
116
  if (0 == regexec(&rxc->rx, buf, 0, NULL, 0)) { // match
data/ext/oj/saj.c CHANGED
@@ -628,7 +628,7 @@ static void saj_parse(VALUE handler, char *json) {
628
628
  * @param [IO|String] io IO Object to read from
629
629
  * @deprecated The sc_parse() method along with the ScHandler is the preferred
630
630
  * callback parser. It is slightly faster and handles streams while the
631
- * saj_parse() methos requires a complete read before parsing.
631
+ * saj_parse() method requires a complete read before parsing.
632
632
  * @see sc_parse
633
633
  */
634
634
  VALUE
data/ext/oj/saj2.c CHANGED
@@ -158,7 +158,7 @@ static void add_str(ojParser p) {
158
158
  const char * str = buf_str(&p->buf);
159
159
  size_t len = buf_len(&p->buf);
160
160
 
161
- if (d->cache_str <= len) {
161
+ if (d->cache_str < len) {
162
162
  rstr = cache_intern(d->str_cache, str, len);
163
163
  } else {
164
164
  rstr = rb_utf8_str_new(str, len);
@@ -172,7 +172,7 @@ static void add_str_key(ojParser p) {
172
172
  const char * str = buf_str(&p->buf);
173
173
  size_t len = buf_len(&p->buf);
174
174
 
175
- if (d->cache_str <= len) {
175
+ if (d->cache_str < len) {
176
176
  rstr = cache_intern(d->str_cache, str, len);
177
177
  } else {
178
178
  rstr = rb_utf8_str_new(str, len);
@@ -182,8 +182,9 @@ static void add_str_key(ojParser p) {
182
182
 
183
183
  static void reset(ojParser p) {
184
184
  Funcs end = p->funcs + 3;
185
+ Funcs f;
185
186
 
186
- for (Funcs f = p->funcs; f < end; f++) {
187
+ for (f = p->funcs; f < end; f++) {
187
188
  f->add_null = noop;
188
189
  f->add_true = noop;
189
190
  f->add_false = noop;
@@ -312,13 +313,14 @@ static void mark(ojParser p) {
312
313
  return;
313
314
  }
314
315
  Delegate d = (Delegate)p->ctx;
316
+ VALUE *kp;
315
317
 
316
318
  cache_mark(d->str_cache);
317
319
  if (Qnil != d->handler) {
318
320
  rb_gc_mark(d->handler);
319
321
  }
320
322
  if (!d->cache_keys) {
321
- for (VALUE *kp = d->keys; kp < d->tail; kp++) {
323
+ for (kp = d->keys; kp < d->tail; kp++) {
322
324
  rb_gc_mark(*kp);
323
325
  }
324
326
  }
@@ -334,7 +336,7 @@ void oj_set_parser_saj(ojParser p) {
334
336
  d->klen = 256;
335
337
  d->keys = ALLOC_N(VALUE, d->klen);
336
338
  d->tail = d->keys;
337
- d->str_cache = cache_create(0, form_str, true);
339
+ d->str_cache = cache_create(0, form_str, true, false);
338
340
 
339
341
  p->ctx = (void *)d;
340
342
  reset(p);
data/ext/oj/sparse.c CHANGED
@@ -494,7 +494,7 @@ static void read_num(ParseInfo pi) {
494
494
  if ('.' == c) {
495
495
  c = reader_get(&pi->rd);
496
496
  // A trailing . is not a valid decimal but if encountered allow it
497
- // except when mimicing the JSON gem.
497
+ // except when mimicking the JSON gem.
498
498
  if (CompatMode == pi->options.mode) {
499
499
  if (c < '0' || '9' < c) {
500
500
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
@@ -67,7 +67,7 @@ static VALUE buffer_size_sym = Qundef;
67
67
  * should be and also a hint on when to flush.
68
68
  *
69
69
  * - *io* [_IO_] stream to write to
70
- * - *options* [_Hash_] formating options
70
+ * - *options* [_Hash_] formatting options
71
71
  */
72
72
  static VALUE stream_writer_new(int argc, VALUE *argv, VALUE self) {
73
73
  StreamWriterType type = STREAM_IO;
data/ext/oj/strict.c CHANGED
@@ -37,8 +37,8 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
37
37
  } else {
38
38
  rkey = rb_str_new(parent->key, parent->klen);
39
39
  rkey = oj_encode(rkey);
40
+ OBJ_FREEZE(rkey);
40
41
  }
41
- OBJ_FREEZE(rkey);
42
42
  return rkey;
43
43
  }
44
44
  if (Yes == pi->options.sym_key) {
@@ -46,7 +46,6 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
46
46
  } else {
47
47
  rkey = oj_str_intern(parent->key, parent->klen);
48
48
  }
49
- OBJ_FREEZE(rkey);
50
49
  return rkey;
51
50
  }
52
51
 
@@ -248,7 +248,7 @@ static void str_writer_free(void *ptr) {
248
248
  * should be.
249
249
  *
250
250
  * - *io* [_IO_] stream to write to
251
- * - *options* [_Hash_] formating options
251
+ * - *options* [_Hash_] formatting options
252
252
  */
253
253
  static VALUE str_writer_new(int argc, VALUE *argv, VALUE self) {
254
254
  StrWriter sw = ALLOC(struct _strWriter);
@@ -466,7 +466,7 @@ static VALUE str_writer_as_json(VALUE self) {
466
466
  * by pushing values into the document. Pushing an array or an object will
467
467
  * create that element in the JSON document and subsequent pushes will add the
468
468
  * elements to that array or object until a pop() is called. When complete
469
- * calling to_s() will return the JSON document. Note tha calling to_s() before
469
+ * calling to_s() will return the JSON document. Note that calling to_s() before
470
470
  * construction is complete will return the document in it's current state.
471
471
  */
472
472
  void oj_string_writer_init() {
data/ext/oj/usual.c CHANGED
@@ -77,6 +77,7 @@ typedef struct _delegate {
77
77
  char * create_id;
78
78
  uint8_t create_id_len;
79
79
  uint8_t cache_str;
80
+ uint8_t cache_xrate;
80
81
  uint8_t miss_class;
81
82
  bool cache_keys;
82
83
  bool ignore_json_create;
@@ -100,7 +101,6 @@ static VALUE form_str(const char *str, size_t len) {
100
101
  }
101
102
 
102
103
  static VALUE form_sym(const char *str, size_t len) {
103
- // return ID2SYM(rb_intern3(str, len, oj_utf8_encoding));
104
104
  return rb_str_intern(rb_utf8_str_new(str, len));
105
105
  }
106
106
 
@@ -323,6 +323,7 @@ static void open_array_key(ojParser p) {
323
323
  }
324
324
 
325
325
  static void close_object(ojParser p) {
326
+ VALUE * vp;
326
327
  Delegate d = (Delegate)p->ctx;
327
328
 
328
329
  d->ctail--;
@@ -333,7 +334,7 @@ static void close_object(ojParser p) {
333
334
  volatile VALUE obj = rb_hash_new();
334
335
 
335
336
  #if HAVE_RB_HASH_BULK_INSERT
336
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
337
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
337
338
  *vp = d->get_key(p, kp);
338
339
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
339
340
  xfree(kp->key);
@@ -341,7 +342,7 @@ static void close_object(ojParser p) {
341
342
  }
342
343
  rb_hash_bulk_insert(d->vtail - head, head, obj);
343
344
  #else
344
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
345
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
345
346
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
346
347
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
347
348
  xfree(kp->key);
@@ -355,6 +356,7 @@ static void close_object(ojParser p) {
355
356
  }
356
357
 
357
358
  static void close_object_class(ojParser p) {
359
+ VALUE * vp;
358
360
  Delegate d = (Delegate)p->ctx;
359
361
 
360
362
  d->ctail--;
@@ -364,7 +366,7 @@ static void close_object_class(ojParser p) {
364
366
  VALUE * head = d->vhead + c->vi + 1;
365
367
  volatile VALUE obj = rb_class_new_instance(0, NULL, d->hash_class);
366
368
 
367
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
369
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
368
370
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
369
371
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
370
372
  xfree(kp->key);
@@ -377,6 +379,7 @@ static void close_object_class(ojParser p) {
377
379
  }
378
380
 
379
381
  static void close_object_create(ojParser p) {
382
+ VALUE * vp;
380
383
  Delegate d = (Delegate)p->ctx;
381
384
 
382
385
  d->ctail--;
@@ -391,7 +394,7 @@ static void close_object_create(ojParser p) {
391
394
  if (Qnil == d->hash_class) {
392
395
  obj = rb_hash_new();
393
396
  #if HAVE_RB_HASH_BULK_INSERT
394
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
397
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
395
398
  *vp = d->get_key(p, kp);
396
399
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
397
400
  xfree(kp->key);
@@ -399,7 +402,7 @@ static void close_object_create(ojParser p) {
399
402
  }
400
403
  rb_hash_bulk_insert(d->vtail - head, head, obj);
401
404
  #else
402
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
405
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
403
406
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
404
407
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
405
408
  xfree(kp->key);
@@ -408,7 +411,7 @@ static void close_object_create(ojParser p) {
408
411
  #endif
409
412
  } else {
410
413
  obj = rb_class_new_instance(0, NULL, d->hash_class);
411
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
414
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
412
415
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
413
416
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
414
417
  xfree(kp->key);
@@ -423,7 +426,7 @@ static void close_object_create(ojParser p) {
423
426
  volatile VALUE arg = rb_hash_new();
424
427
 
425
428
  #if HAVE_RB_HASH_BULK_INSERT
426
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
429
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
427
430
  *vp = d->get_key(p, kp);
428
431
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
429
432
  xfree(kp->key);
@@ -431,7 +434,7 @@ static void close_object_create(ojParser p) {
431
434
  }
432
435
  rb_hash_bulk_insert(d->vtail - head, head, arg);
433
436
  #else
434
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
437
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
435
438
  rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
436
439
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
437
440
  xfree(kp->key);
@@ -441,7 +444,7 @@ static void close_object_create(ojParser p) {
441
444
  obj = rb_funcall(clas, oj_json_create_id, 1, arg);
442
445
  } else {
443
446
  obj = rb_class_new_instance(0, NULL, clas);
444
- for (VALUE *vp = head; kp < d->ktail; kp++, vp += 2) {
447
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
445
448
  rb_ivar_set(obj, get_attr_id(p, kp), *(vp + 1));
446
449
  if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
447
450
  xfree(kp->key);
@@ -468,13 +471,14 @@ static void close_array(ojParser p) {
468
471
  }
469
472
 
470
473
  static void close_array_class(ojParser p) {
474
+ VALUE * vp;
471
475
  Delegate d = (Delegate)p->ctx;
472
476
 
473
477
  d->ctail--;
474
478
  VALUE * head = d->vhead + d->ctail->vi + 1;
475
479
  volatile VALUE a = rb_class_new_instance(0, NULL, d->array_class);
476
480
 
477
- for (VALUE *vp = head; vp < d->vtail; vp++) {
481
+ for (vp = head; vp < d->vtail; vp++) {
478
482
  rb_funcall(a, ltlt_id, 1, *vp);
479
483
  }
480
484
  d->vtail = head;
@@ -682,9 +686,10 @@ static void mark(ojParser p) {
682
686
  return;
683
687
  }
684
688
  Delegate d = (Delegate)p->ctx;
689
+ VALUE * vp;
685
690
 
686
691
  if (NULL == d) {
687
- return;
692
+ return;
688
693
  }
689
694
  cache_mark(d->str_cache);
690
695
  if (NULL != d->sym_cache) {
@@ -693,7 +698,7 @@ static void mark(ojParser p) {
693
698
  if (NULL != d->class_cache) {
694
699
  cache_mark(d->class_cache);
695
700
  }
696
- for (VALUE *vp = d->vhead; vp < d->vtail; vp++) {
701
+ for (vp = d->vhead; vp < d->vtail; vp++) {
697
702
  if (Qundef != *vp) {
698
703
  rb_gc_mark(*vp);
699
704
  }
@@ -787,6 +792,30 @@ static VALUE opt_cache_strings_set(ojParser p, VALUE value) {
787
792
  return INT2NUM((int)d->cache_str);
788
793
  }
789
794
 
795
+ static VALUE opt_cache_expunge(ojParser p, VALUE value) {
796
+ Delegate d = (Delegate)p->ctx;
797
+
798
+ return INT2NUM((int)d->cache_xrate);
799
+ }
800
+
801
+ static VALUE opt_cache_expunge_set(ojParser p, VALUE value) {
802
+ Delegate d = (Delegate)p->ctx;
803
+ int rate = NUM2INT(value);
804
+
805
+ if (rate < 0) {
806
+ rate = 0;
807
+ } else if (3 < rate) {
808
+ rate = 3;
809
+ }
810
+ d->cache_xrate = (uint8_t)rate;
811
+ cache_set_expunge_rate(d->str_cache, rate);
812
+ cache_set_expunge_rate(d->attr_cache, rate);
813
+ if (NULL != d->sym_cache) {
814
+ cache_set_expunge_rate(d->sym_cache, rate);
815
+ }
816
+ return INT2NUM((int)rate);
817
+ }
818
+
790
819
  static VALUE opt_capacity(ojParser p, VALUE value) {
791
820
  Delegate d = (Delegate)p->ctx;
792
821
 
@@ -825,11 +854,7 @@ static VALUE opt_class_cache_set(ojParser p, VALUE value) {
825
854
 
826
855
  if (Qtrue == value) {
827
856
  if (NULL == d->class_cache) {
828
- if (MISS_AUTO == d->miss_class) {
829
- d->class_cache = cache_create(0, form_class_auto, true);
830
- } else {
831
- d->class_cache = cache_create(0, form_class, false);
832
- }
857
+ d->class_cache = cache_create(0, form_class_auto, MISS_AUTO == d->miss_class, false);
833
858
  }
834
859
  } else if (NULL != d->class_cache) {
835
860
  cache_free(d->class_cache);
@@ -903,7 +928,7 @@ static VALUE opt_decimal_set(ojParser p, VALUE value) {
903
928
  switch (rb_type(value)) {
904
929
  case T_STRING: mode = RSTRING_PTR(value); break;
905
930
  case T_SYMBOL:
906
- s = rb_sym_to_s(value);
931
+ s = rb_sym2str(value);
907
932
  mode = RSTRING_PTR(s);
908
933
  break;
909
934
  default:
@@ -1020,7 +1045,7 @@ static VALUE opt_missing_class_set(ojParser p, VALUE value) {
1020
1045
  switch (rb_type(value)) {
1021
1046
  case T_STRING: mode = RSTRING_PTR(value); break;
1022
1047
  case T_SYMBOL:
1023
- s = rb_sym_to_s(value);
1048
+ s = rb_sym2str(value);
1024
1049
  mode = RSTRING_PTR(s);
1025
1050
  break;
1026
1051
  default:
@@ -1073,7 +1098,8 @@ static VALUE opt_symbol_keys_set(ojParser p, VALUE value) {
1073
1098
  Delegate d = (Delegate)p->ctx;
1074
1099
 
1075
1100
  if (Qtrue == value) {
1076
- d->sym_cache = cache_create(0, form_sym, true);
1101
+ d->sym_cache = cache_create(0, form_sym, true, false);
1102
+ cache_set_expunge_rate(d->sym_cache, d->cache_xrate);
1077
1103
  d->key_cache = d->sym_cache;
1078
1104
  if (!d->cache_keys) {
1079
1105
  d->get_key = sym_key;
@@ -1091,13 +1117,16 @@ static VALUE opt_symbol_keys_set(ojParser p, VALUE value) {
1091
1117
  }
1092
1118
 
1093
1119
  static VALUE option(ojParser p, const char *key, VALUE value) {
1094
- struct opt opts[] = {
1120
+ struct opt *op;
1121
+ struct opt opts[] = {
1095
1122
  {.name = "array_class", .func = opt_array_class},
1096
1123
  {.name = "array_class=", .func = opt_array_class_set},
1097
1124
  {.name = "cache_keys", .func = opt_cache_keys},
1098
1125
  {.name = "cache_keys=", .func = opt_cache_keys_set},
1099
1126
  {.name = "cache_strings", .func = opt_cache_strings},
1100
1127
  {.name = "cache_strings=", .func = opt_cache_strings_set},
1128
+ {.name = "cache_expunge", .func = opt_cache_expunge},
1129
+ {.name = "cache_expunge=", .func = opt_cache_expunge_set},
1101
1130
  {.name = "capacity", .func = opt_capacity},
1102
1131
  {.name = "capacity=", .func = opt_capacity_set},
1103
1132
  {.name = "class_cache", .func = opt_class_cache},
@@ -1119,7 +1148,7 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
1119
1148
  {.name = NULL},
1120
1149
  };
1121
1150
 
1122
- for (struct opt *op = opts; NULL != op->name; op++) {
1151
+ for (op = opts; NULL != op->name; op++) {
1123
1152
  if (0 == strcmp(key, op->name)) {
1124
1153
  return op->func(p, value);
1125
1154
  }
@@ -1157,6 +1186,7 @@ void oj_set_parser_usual(ojParser p) {
1157
1186
  d->create_id = NULL;
1158
1187
  d->create_id_len = 0;
1159
1188
  d->miss_class = MISS_IGNORE;
1189
+ d->cache_xrate = 1;
1160
1190
 
1161
1191
  Funcs f = &p->funcs[TOP_FUN];
1162
1192
  f->add_null = add_null;
@@ -1197,8 +1227,8 @@ void oj_set_parser_usual(ojParser p) {
1197
1227
  f->open_object = open_object_key;
1198
1228
  f->close_object = close_object;
1199
1229
 
1200
- d->str_cache = cache_create(0, form_str, true);
1201
- d->attr_cache = cache_create(0, form_attr, false);
1230
+ d->str_cache = cache_create(0, form_str, true, false);
1231
+ d->attr_cache = cache_create(0, form_attr, false, false);
1202
1232
  d->sym_cache = NULL;
1203
1233
  d->class_cache = NULL;
1204
1234
  d->key_cache = d->str_cache;
data/ext/oj/validate.c CHANGED
@@ -28,8 +28,9 @@ mark(ojParser p) {
28
28
  void oj_set_parser_validator(ojParser p) {
29
29
  p->ctx = NULL;
30
30
  Funcs end = p->funcs + 3;
31
+ Funcs f;
31
32
 
32
- for (Funcs f = p->funcs; f < end; f++) {
33
+ for (f = p->funcs; f < end; f++) {
33
34
  f->add_null = noop;
34
35
  f->add_true = noop;
35
36
  f->add_false = noop;
data/ext/oj/wab.c CHANGED
@@ -305,11 +305,14 @@ static VALUE calc_hash_key(ParseInfo pi, Val parent) {
305
305
  if (Yes == pi->options.cache_keys) {
306
306
  rkey = oj_sym_intern(parent->key, parent->klen);
307
307
  } else {
308
- rkey = rb_str_new(parent->key, parent->klen);
309
- rkey = oj_encode(rkey);
308
+ #if HAVE_RB_ENC_INTERNED_STR
309
+ rkey = rb_enc_interned_str(parent->key, parent->klen, oj_utf8_encoding);
310
+ #else
311
+ rkey = rb_utf8_str_new(parent->key, parent->klen);
310
312
  rkey = rb_str_intern(rkey);
313
+ OBJ_FREEZE(rkey);
314
+ #endif
311
315
  }
312
- OBJ_FREEZE(rkey);
313
316
  return rkey;
314
317
  }
315
318
 
data/lib/oj/error.rb CHANGED
@@ -16,7 +16,7 @@ module Oj
16
16
  # An Exception that is raised if a file fails to load.
17
17
  LoadError = Class.new(Error)
18
18
 
19
- # An Exception that is raised if there is a conflict with mimicing JSON
19
+ # An Exception that is raised if there is a conflict with mimicking JSON
20
20
  MimicError = Class.new(Error)
21
21
 
22
22
  end # Oj
data/lib/oj/state.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  module JSON
3
3
  module Ext
4
- module Generator
4
+ module Generator
5
5
  unless defined?(::JSON::Ext::Generator::State)
6
6
  # This class exists for json gem compatibility only. While it can be
7
7
  # used as the options for other than compatibility a simple Hash is
@@ -44,11 +44,11 @@ module JSON
44
44
  def to_h()
45
45
  return @attrs.dup
46
46
  end
47
-
47
+
48
48
  def to_hash()
49
49
  return @attrs.dup
50
50
  end
51
-
51
+
52
52
  def allow_nan?()
53
53
  @attrs[:allow_nan]
54
54
  end
@@ -104,7 +104,7 @@ module JSON
104
104
  def has_key?(k)
105
105
  @attrs.has_key?(key.to_sym)
106
106
  end
107
-
107
+
108
108
  # Handles requests for Hash values. Others cause an Exception to be raised.
109
109
  # @param [Symbol|String] m method symbol
110
110
  # @return [Boolean] the value of the specified instance variable.
@@ -116,11 +116,12 @@ module JSON
116
116
  m = m.to_s[0..-2]
117
117
  m = m.to_sym
118
118
  return @attrs.store(m, args[0])
119
- else
119
+ end
120
+ if @attrs.has_key?(m.to_sym)
120
121
  raise ArgumentError.new("wrong number of arguments (#{args.size} for 0 with #{m}) to method #{m}") unless args.nil? or args.empty?
121
122
  return @attrs[m.to_sym]
122
- end
123
- raise NoMethodError.new("undefined method #{m}", m)
123
+ end
124
+ return @attrs.send(m, *args, &block)
124
125
  end
125
126
 
126
127
  end # State