i18nema 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/ext/i18nema/i18nema.c +109 -33
  2. data/test/i18nema_test.rb +15 -6
  3. metadata +2 -2
@@ -12,13 +12,20 @@ struct i_key_value;
12
12
  static VALUE array_to_rarray(struct i_object *array);
13
13
  static VALUE hash_to_rhash(struct i_object *hash);
14
14
  static void merge_hash(struct i_object *hash, struct i_object *other_hash);
15
- static void delete_hash(struct i_key_value **hash);
16
- static void delete_object(struct i_object *object);
15
+ static void delete_hash(struct i_key_value **hash, int delete_child_objects);
16
+ static void delete_object(struct i_object *object, int delete_child_objects);
17
17
 
18
18
  enum i_object_type {
19
+ i_type_none,
19
20
  i_type_string,
20
21
  i_type_array,
21
- i_type_hash
22
+ i_type_hash,
23
+ i_type_int,
24
+ i_type_float,
25
+ i_type_symbol,
26
+ i_type_true,
27
+ i_type_false,
28
+ i_type_null
22
29
  };
23
30
 
24
31
  union i_object_data {
@@ -42,11 +49,17 @@ typedef struct i_key_value
42
49
  } i_key_value_t;
43
50
 
44
51
  static int current_translation_count = 0;
45
- static ID s_to_s,
46
- s_init_translations;
52
+ static ID s_init_translations,
53
+ s_to_f,
54
+ s_to_s,
55
+ s_to_sym;
56
+ static i_object_t i_object_null,
57
+ i_object_true,
58
+ i_object_false;
47
59
 
48
60
  static VALUE
49
61
  i_object_to_robject(i_object_t *object) {
62
+ VALUE s;
50
63
  if (object == NULL)
51
64
  return Qnil;
52
65
  switch (object->type) {
@@ -56,8 +69,20 @@ i_object_to_robject(i_object_t *object) {
56
69
  return array_to_rarray(object);
57
70
  case i_type_hash:
58
71
  return hash_to_rhash(object);
72
+ case i_type_int:
73
+ return rb_cstr2inum(object->data.string, 10);
74
+ case i_type_float:
75
+ s = rb_str_new(object->data.string, object->size);
76
+ return rb_funcall(s, s_to_f, 0);
77
+ case i_type_symbol:
78
+ return ID2SYM(rb_intern(object->data.string));
79
+ case i_type_true:
80
+ return Qtrue;
81
+ case i_type_false:
82
+ return Qfalse;
83
+ default:
84
+ return Qnil;
59
85
  }
60
- return Qnil;
61
86
  }
62
87
 
63
88
  static VALUE
@@ -118,49 +143,60 @@ direct_lookup(int argc, VALUE *argv, VALUE self)
118
143
  }
119
144
 
120
145
  static void
121
- empty_object(i_object_t *object)
146
+ empty_object(i_object_t *object, int delete_child_objects)
122
147
  {
123
148
  if (object == NULL)
124
149
  return;
125
150
 
126
151
  switch (object->type) {
127
- case i_type_string:
128
- xfree(object->data.string);
129
- break;
130
152
  case i_type_array:
131
- for (unsigned long i = 0; i < object->size; i++)
132
- delete_object(object->data.array[i]);
153
+ if (delete_child_objects) {
154
+ for (unsigned long i = 0; i < object->size; i++)
155
+ delete_object(object->data.array[i], 1);
156
+ }
133
157
  xfree(object->data.array);
134
158
  break;
135
159
  case i_type_hash:
136
- delete_hash(&object->data.hash);
160
+ delete_hash(&object->data.hash, delete_child_objects);
161
+ break;
162
+ case i_type_none:
163
+ break;
164
+ default:
165
+ xfree(object->data.string);
137
166
  break;
138
167
  }
139
168
  }
140
169
 
141
170
  static void
142
- delete_object(i_object_t *object)
171
+ delete_object_r(i_object_t *object)
143
172
  {
144
- empty_object(object);
145
- xfree(object);
173
+ delete_object(object, 1);
174
+ }
175
+
176
+ static void
177
+ delete_object(i_object_t *object, int delete_child_objects)
178
+ {
179
+ empty_object(object, delete_child_objects);
180
+ if (object->type != i_type_null && object->type != i_type_true && object->type != i_type_false)
181
+ xfree(object);
146
182
  }
147
183
 
148
184
  static void
149
185
  delete_key_value(i_key_value_t *kv, int delete_value)
150
186
  {
151
187
  if (delete_value)
152
- delete_object(kv->value);
188
+ delete_object(kv->value, 1);
153
189
  xfree(kv->key);
154
190
  xfree(kv);
155
191
  }
156
192
 
157
193
  static void
158
- delete_hash(i_key_value_t **hash)
194
+ delete_hash(i_key_value_t **hash, int delete_child_objects)
159
195
  {
160
196
  i_key_value_t *kv, *tmp;
161
197
  HASH_ITER(hh, *hash, kv, tmp) {
162
198
  HASH_DEL(*hash, kv);
163
- delete_key_value(kv, 1);
199
+ delete_key_value(kv, delete_child_objects);
164
200
  }
165
201
  }
166
202
 
@@ -191,12 +227,16 @@ merge_hash(i_object_t *hash, i_object_t *other_hash)
191
227
  HASH_DEL(other_hash->data.hash, kv);
192
228
  add_key_value(&hash->data.hash, kv);
193
229
  }
194
- delete_object(other_hash);
230
+ delete_object(other_hash, 1);
195
231
  }
196
232
 
197
233
  static int
198
234
  delete_syck_st_entry(char *key, char *value, char *arg)
199
235
  {
236
+ i_object_t *object = (i_object_t *)value;
237
+ // key object whose string we have yoinked into a kv
238
+ if (object->type == i_type_none)
239
+ delete_object(object, 1);
200
240
  return ST_DELETE;
201
241
  }
202
242
 
@@ -204,7 +244,7 @@ static int
204
244
  delete_syck_object(char *key, char *value, char *arg)
205
245
  {
206
246
  i_object_t *object = (i_object_t *)value;
207
- delete_object(object);
247
+ delete_object(object, 0); // objects are in the syck symbol table, thus we don't want to double-free
208
248
  return ST_DELETE;
209
249
  }
210
250
 
@@ -230,23 +270,52 @@ handle_syck_badanchor(SyckParser *parser, char *anchor)
230
270
  return NULL;
231
271
  }
232
272
 
273
+ static i_object_t*
274
+ new_string_object(char *str, long len)
275
+ {
276
+ i_object_t *object = ALLOC(i_object_t);
277
+ object->type = i_type_string;
278
+ object->size = len;
279
+ object->data.string = xmalloc(len + 1);
280
+ strncpy(object->data.string, str, len);
281
+ object->data.string[len] = '\0';
282
+ return object;
283
+ }
284
+
233
285
  static SYMID
234
286
  handle_syck_node(SyckParser *parser, SyckNode *node)
235
287
  {
236
288
  i_object_t *result;
237
- result = ALLOC(i_object_t);
238
289
  SYMID oid;
239
290
 
240
291
  switch (node->kind) {
241
292
  case syck_str_kind:
242
- // TODO: why does syck sometimes give us empty string nodes? (small) memory leak, since they never end up in a seq/map
243
- result->type = i_type_string;
244
- result->size = node->data.str->len;
245
- result->data.string = xmalloc(node->data.str->len + 1);
246
- strncpy(result->data.string, node->data.str->ptr, node->data.str->len);
247
- result->data.string[node->data.str->len] = '\0';
293
+ if (node->type_id == NULL) {
294
+ result = new_string_object(node->data.str->ptr, node->data.str->len);
295
+ } else if (strcmp(node->type_id, "null") == 0) {
296
+ result = &i_object_null;
297
+ } else if (strcmp(node->type_id, "bool#yes") == 0) {
298
+ result = &i_object_true;
299
+ } else if (strcmp(node->type_id, "bool#no") == 0) {
300
+ result = &i_object_false;
301
+ } else if (strcmp(node->type_id, "int") == 0) {
302
+ syck_str_blow_away_commas(node);
303
+ result = new_string_object(node->data.str->ptr, node->data.str->len);
304
+ result->type = i_type_int;
305
+ } else if (strcmp(node->type_id, "float#fix") == 0 || strcmp(node->type_id, "float#exp") == 0) {
306
+ syck_str_blow_away_commas(node);
307
+ result = new_string_object(node->data.str->ptr, node->data.str->len);
308
+ result->type = i_type_float;
309
+ } else if (node->data.str->style == scalar_plain && node->data.str->len > 1 && strncmp(node->data.str->ptr, ":", 1) == 0) {
310
+ result = new_string_object(node->data.str->ptr + 1, node->data.str->len - 1);
311
+ result->type = i_type_symbol;
312
+ } else {
313
+ // legit strings, and everything else get the string treatment (binary, int#hex, timestamp, etc.)
314
+ result = new_string_object(node->data.str->ptr, node->data.str->len);
315
+ }
248
316
  break;
249
317
  case syck_seq_kind:
318
+ result = ALLOC(i_object_t);
250
319
  result->type = i_type_array;
251
320
  result->size = node->data.list->idx;
252
321
  result->data.array = ALLOC_N(i_object_t*, node->data.list->idx);
@@ -261,6 +330,7 @@ handle_syck_node(SyckParser *parser, SyckNode *node)
261
330
  }
262
331
  break;
263
332
  case syck_map_kind:
333
+ result = ALLOC(i_object_t);
264
334
  result->type = i_type_hash;
265
335
  result->data.hash = NULL;
266
336
  for (long i = 0; i < node->data.pairs->idx; i++) {
@@ -274,7 +344,7 @@ handle_syck_node(SyckParser *parser, SyckNode *node)
274
344
  i_key_value_t *kv;
275
345
  kv = ALLOC(i_key_value_t);
276
346
  kv->key = key->data.string;
277
- xfree(key);
347
+ key->type = i_type_none; // so we know to free this node in delete_syck_st_entry
278
348
  kv->value = value;
279
349
  if (value->type == i_type_string)
280
350
  current_translation_count++;
@@ -317,7 +387,7 @@ load_yml_string(VALUE self, VALUE yml)
317
387
  st_foreach(parser->syms, delete_syck_st_entry, 0);
318
388
  syck_free_parser(parser);
319
389
  if (new_root_object == NULL || new_root_object->type != i_type_hash) {
320
- delete_object(new_root_object);
390
+ delete_object(new_root_object, 1);
321
391
  rb_raise(I18nemaBackendLoadError, "root yml node is not a hash");
322
392
  }
323
393
  merge_hash(root_object, new_root_object);
@@ -362,7 +432,7 @@ static VALUE
362
432
  reload(VALUE self)
363
433
  {
364
434
  i_object_t *root_object = root_object_get(self);
365
- empty_object(root_object);
435
+ empty_object(root_object, 1);
366
436
  rb_iv_set(self, "@initialized", Qfalse);
367
437
  root_object = NULL;
368
438
  return Qtrue;
@@ -375,7 +445,7 @@ initialize(VALUE self)
375
445
  i_object_t *root_object = ALLOC(i_object_t);
376
446
  root_object->type = i_type_hash;
377
447
  root_object->data.hash = NULL;
378
- translations = Data_Wrap_Struct(I18nemaBackend, 0, delete_object, root_object);
448
+ translations = Data_Wrap_Struct(I18nemaBackend, 0, delete_object_r, root_object);
379
449
  rb_iv_set(self, "@translations", translations);
380
450
  return self;
381
451
  }
@@ -387,8 +457,14 @@ Init_i18nema()
387
457
  I18nemaBackend = rb_define_class_under(I18nema, "Backend", rb_cObject);
388
458
  I18nemaBackendLoadError = rb_define_class_under(I18nemaBackend, "LoadError", rb_eStandardError);
389
459
 
390
- s_to_s = rb_intern("to_s");
391
460
  s_init_translations = rb_intern("init_translations");
461
+ s_to_f = rb_intern("to_f");
462
+ s_to_s = rb_intern("to_s");
463
+ s_to_sym = rb_intern("to_sym");
464
+
465
+ i_object_null.type = i_type_null;
466
+ i_object_true.type = i_type_true;
467
+ i_object_false.type = i_type_false;
392
468
 
393
469
  rb_define_method(I18nemaBackend, "initialize", initialize, 0);
394
470
  rb_define_method(I18nemaBackend, "load_yml_string", load_yml_string, 1);
data/test/i18nema_test.rb CHANGED
@@ -10,7 +10,16 @@ class I18nemaTest < Test::Unit::TestCase
10
10
  baz: %w{
11
11
  asdf
12
12
  qwerty
13
- }
13
+ },
14
+ stuff: [
15
+ true,
16
+ true,
17
+ false,
18
+ nil,
19
+ 1,
20
+ 1.1,
21
+ :foo
22
+ ]
14
23
  }
15
24
  @backend = I18nema::Backend.new
16
25
  @backend.store_translations :en, @data
@@ -63,11 +72,11 @@ class I18nemaTest < Test::Unit::TestCase
63
72
  # i_object_t as the root node, causing delete_object to asplode when
64
73
  # it tries to free a garbage pointer
65
74
  #
66
- #exception = assert_raise(I18nema::Backend::LoadError) {
67
- # backend.load_yml_string("en:\n foo: \"lol\"\n\tbar: notabs!")
68
- #}
69
- #assert_match(/TAB found in your indentation/, exception.message)
70
- #assert_equal({}, backend.direct_lookup)
75
+ exception = assert_raise(I18nema::Backend::LoadError) {
76
+ backend.load_yml_string("en:\n foo: \"lol\"\n\tbar: notabs!")
77
+ }
78
+ assert_match(/syntax error/, exception.message)
79
+ assert_equal({}, backend.direct_lookup)
71
80
 
72
81
  exception = assert_raise(I18nema::Backend::LoadError) {
73
82
  backend.load_yml_string("en:\n &a [*a]")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18nema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-29 00:00:00.000000000 Z
12
+ date: 2013-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: i18n