i18nema 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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