yaji 0.2.3-x86-mingw32 → 0.3.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,8 @@
1
+ === 0.3.0 / 2012-04-18
2
+
3
+ * Allow to specify filter at parser initialization
4
+ * Implement feeding of the input on the fly
5
+
1
6
  === 0.2.3 / 2012-04-10
2
7
 
3
8
  * Add build for Windows
@@ -156,6 +156,8 @@ static int yaji_end_array(void *ctx)
156
156
  return STATUS_CONTINUE;
157
157
  }
158
158
 
159
+ static VALUE rb_yaji_each_iter(VALUE chunk, VALUE* params_p);
160
+
159
161
  static VALUE rb_yaji_parser_parse_chunk(VALUE chunk, VALUE self)
160
162
  {
161
163
  yajl_status rc;
@@ -175,7 +177,16 @@ static VALUE rb_yaji_parser_parse_chunk(VALUE chunk, VALUE self)
175
177
  RERAISE_PARSER_ERROR(p);
176
178
  }
177
179
  for (i=0; i<RARRAY_LEN(p->events); i++) {
178
- rb_funcall(p->parser_cb, id_call, 1, RARRAY_PTR(p->events)[i]);
180
+ if (NIL_P(p->input)) {
181
+ VALUE params[4];
182
+ params[0] = p->on_object_cb;
183
+ params[1] = p->object_stack;
184
+ params[2] = p->filter;
185
+ params[3] = p->with_path ? Qtrue : Qfalse;
186
+ rb_yaji_each_iter(RARRAY_PTR(p->events)[i], params);
187
+ } else {
188
+ rb_funcall(p->parser_cb, id_call, 1, RARRAY_PTR(p->events)[i]);
189
+ }
179
190
  }
180
191
  return rb_funcall(chunk, id_bytesize, 0, NULL);
181
192
  }
@@ -190,18 +201,27 @@ static VALUE rb_yaji_parser_new(int argc, VALUE *argv, VALUE klass)
190
201
  p->config.allowComments = 1;
191
202
  p->config.checkUTF8 = 1;
192
203
  p->symbolize_keys = 0;
204
+ p->with_path = 0;
205
+ p->filter = Qnil;
193
206
  p->rbufsize = Qnil;
194
207
  p->input = Qnil;
195
208
  p->parser_cb = Qnil;
209
+ p->on_object_cb = Qnil;
196
210
 
197
- rb_scan_args(argc, argv, "11", &p->input, &opts);
198
- if (TYPE(p->input) == T_STRING) {
199
- p->input = rb_class_new_instance(1, &p->input, c_stringio);
200
- } else if (rb_respond_to(p->input, id_perform) && rb_respond_to(p->input, id_on_body)) {
201
- rb_block_call(p->input, id_on_body, 0, NULL, rb_yaji_parser_parse_chunk, obj);
202
- } else if (!rb_respond_to(p->input, id_read)) {
203
- rb_raise(c_parse_error, "input must be a String or IO or "
204
- "something responding to #perform and #on_body e.g. Curl::Easy");
211
+ rb_scan_args(argc, argv, "02", &p->input, &opts);
212
+ if (NIL_P(opts) && TYPE(p->input) == T_HASH) {
213
+ opts = p->input;
214
+ p->input = Qnil;
215
+ }
216
+ if (!NIL_P(p->input)) {
217
+ if (TYPE(p->input) == T_STRING) {
218
+ p->input = rb_class_new_instance(1, &p->input, c_stringio);
219
+ } else if (rb_respond_to(p->input, id_perform) && rb_respond_to(p->input, id_on_body)) {
220
+ rb_block_call(p->input, id_on_body, 0, NULL, rb_yaji_parser_parse_chunk, obj);
221
+ } else if (!rb_respond_to(p->input, id_read)) {
222
+ rb_raise(c_parse_error, "input must be a String or IO or "
223
+ "something responding to #perform and #on_body e.g. Curl::Easy");
224
+ }
205
225
  }
206
226
  if (!NIL_P(opts)) {
207
227
  Check_Type(opts, T_HASH);
@@ -215,12 +235,20 @@ static VALUE rb_yaji_parser_new(int argc, VALUE *argv, VALUE klass)
215
235
  p->symbolize_keys = 1;
216
236
  }
217
237
  p->rbufsize = rb_hash_aref(opts, sym_read_buffer_size);
238
+ if (rb_hash_aref(opts, sym_with_path) == Qtrue) {
239
+ p->with_path = 1;
240
+ }
241
+ p->filter = rb_hash_aref(opts, sym_filter);
218
242
  }
219
243
  if (NIL_P(p->rbufsize)) {
220
244
  p->rbufsize = INT2FIX(READ_BUFSIZE);
221
245
  } else {
222
246
  Check_Type(p->rbufsize, T_FIXNUM);
223
247
  }
248
+ p->object_stack = rb_ary_new();
249
+ p->path = rb_ary_new();
250
+ rb_ary_push(p->path, rb_str_new("", 0));
251
+ p->path_str = rb_str_new("", 0);
224
252
  p->handle = yajl_alloc(&yaji_callbacks, &p->config, NULL, (void *)obj);
225
253
  rb_obj_call_init(obj, 0, 0);
226
254
  return obj;
@@ -229,8 +257,22 @@ static VALUE rb_yaji_parser_new(int argc, VALUE *argv, VALUE klass)
229
257
  static VALUE rb_yaji_parser_init(int argc, VALUE *argv, VALUE self)
230
258
  {
231
259
  return self;
260
+ (void)argc;
261
+ (void)argv;
232
262
  }
233
263
 
264
+ static VALUE rb_yaji_parser_write(VALUE self, VALUE val)
265
+ {
266
+ yaji_parser* p = (yaji_parser*) DATA_PTR(self);
267
+
268
+ if (NIL_P(p->on_object_cb)) {
269
+ rb_raise(rb_eArgError, "#on_object callback required");
270
+ }
271
+ if (!NIL_P(val)) {
272
+ Check_Type(val, T_STRING);
273
+ }
274
+ return rb_yaji_parser_parse_chunk(val, self);
275
+ }
234
276
 
235
277
  static VALUE rb_yaji_parser_parse(int argc, VALUE* argv, VALUE self)
236
278
  {
@@ -238,12 +280,12 @@ static VALUE rb_yaji_parser_parse(int argc, VALUE* argv, VALUE self)
238
280
  yaji_parser* p = (yaji_parser*) DATA_PTR(self);
239
281
  int i;
240
282
 
283
+ if (NIL_P(p->input)) {
284
+ rb_raise(rb_eArgError, "input object required to use #parse method");
285
+ }
241
286
  rb_scan_args(argc, argv, "00&", &p->parser_cb);
242
287
  RETURN_ENUMERATOR(self, argc, argv);
243
288
 
244
- p->path = rb_ary_new();
245
- rb_ary_push(p->path, rb_str_new("", 0));
246
- p->path_str = rb_str_new("", 0);
247
289
  p->chunk = Qnil;
248
290
 
249
291
  if (rb_respond_to(p->input, id_perform)) {
@@ -270,20 +312,20 @@ static VALUE rb_yaji_parser_parse(int argc, VALUE* argv, VALUE self)
270
312
  return Qnil;
271
313
  }
272
314
 
273
- static int rb_yaji_str_start_with(VALUE str, VALUE query)
315
+ static int rb_yaji_str_start_with(VALUE str, VALUE filter)
274
316
  {
275
317
  int i;
276
318
  const char *ptr = RSTRING_PTR(str);
277
- int len = RSTRING_LEN(str);
319
+ long len = RSTRING_LEN(str);
278
320
  VALUE entry;
279
321
 
280
- switch(TYPE(query)) {
322
+ switch(TYPE(filter)) {
281
323
  case T_STRING:
282
- return RSTRING_LEN(query) <= len && memcmp(RSTRING_PTR(query), ptr, RSTRING_LEN(query)) == 0;
324
+ return RSTRING_LEN(filter) <= len && memcmp(RSTRING_PTR(filter), ptr, RSTRING_LEN(filter)) == 0;
283
325
  break;
284
326
  case T_ARRAY:
285
- for (i=0; i<RARRAY_LEN(query); i++) {
286
- entry = RARRAY_PTR(query)[i];
327
+ for (i=0; i<RARRAY_LEN(filter); i++) {
328
+ entry = RARRAY_PTR(filter)[i];
287
329
  if (RSTRING_LEN(entry) <= len && memcmp(RSTRING_PTR(entry), ptr, RSTRING_LEN(entry)) == 0) {
288
330
  return 1;
289
331
  }
@@ -301,11 +343,11 @@ static VALUE rb_yaji_each_iter(VALUE chunk, VALUE* params_p)
301
343
  VALUE value = rb_ary_shift(chunk);
302
344
  VALUE proc = params[0];
303
345
  VALUE stack = params[1];
304
- VALUE query = params[2];
346
+ VALUE filter = params[2];
305
347
  VALUE with_path = params[3];
306
348
  VALUE last_entry, object, container, key, hash;
307
349
 
308
- if (NIL_P(query) || rb_yaji_str_start_with(path, query)) {
350
+ if (NIL_P(filter) || rb_yaji_str_start_with(path, filter)) {
309
351
  if (event == sym_hash_key) {
310
352
  rb_ary_push(stack, value);
311
353
  } else if (event == sym_start_hash || event == sym_start_array) {
@@ -356,17 +398,29 @@ static VALUE rb_yaji_each_iter(VALUE chunk, VALUE* params_p)
356
398
 
357
399
  static VALUE rb_yaji_parser_each(int argc, VALUE* argv, VALUE self)
358
400
  {
359
- VALUE query, proc, options, params[4];
401
+ VALUE filter, proc, options, params[4];
402
+ yaji_parser* p = (yaji_parser*) DATA_PTR(self);
403
+
404
+ if (NIL_P(p->input)) {
405
+ rb_raise(rb_eArgError, "input object required to use #each method");
406
+ }
360
407
  RETURN_ENUMERATOR(self, argc, argv);
361
- rb_scan_args(argc, argv, "02&", &query, &options, &proc);
408
+ rb_scan_args(argc, argv, "02&", &filter, &options, &proc);
362
409
  params[0] = proc; // callback
363
410
  params[1] = rb_ary_new(); // stack
364
- params[2] = query;
411
+ if (NIL_P(filter)) {
412
+ params[2] = p->filter;
413
+ } else {
414
+ params[2] = filter;
415
+ }
416
+ params[3] = p->with_path ? Qtrue : Qfalse;
365
417
  if (options != Qnil) {
418
+ VALUE arg;
366
419
  Check_Type(options, T_HASH);
367
- params[3] = rb_hash_aref(options, sym_with_path);
368
- } else {
369
- params[3] = Qnil;
420
+ arg = rb_hash_aref(options, sym_with_path);
421
+ if (!NIL_P(arg)) {
422
+ params[3] = arg;
423
+ }
370
424
  }
371
425
  rb_block_call(self, id_parse, 0, NULL, rb_yaji_each_iter, (VALUE)params);
372
426
  return Qnil;
@@ -383,6 +437,16 @@ static void rb_yaji_parser_free(void *parser)
383
437
  }
384
438
  }
385
439
 
440
+ static VALUE rb_yaji_parser_on_object(VALUE self)
441
+ {
442
+ yaji_parser *p = DATA_PTR(self);
443
+
444
+ if (rb_block_given_p()) {
445
+ p->on_object_cb = rb_block_proc();
446
+ }
447
+ return p->on_object_cb;
448
+ }
449
+
386
450
  static void rb_yaji_parser_mark(void *parser)
387
451
  {
388
452
  yaji_parser* p = parser;
@@ -409,6 +473,9 @@ void Init_parser_ext() {
409
473
  rb_define_method(c_yaji_parser, "initialize", rb_yaji_parser_init, -1);
410
474
  rb_define_method(c_yaji_parser, "parse", rb_yaji_parser_parse, -1);
411
475
  rb_define_method(c_yaji_parser, "each", rb_yaji_parser_each, -1);
476
+ rb_define_method(c_yaji_parser, "on_object", rb_yaji_parser_on_object, 0);
477
+ rb_define_method(c_yaji_parser, "write", rb_yaji_parser_write, 1);
478
+ rb_define_alias(c_yaji_parser, "<<", "write");
412
479
 
413
480
  id_call = rb_intern("call");
414
481
  id_read = rb_intern("read");
@@ -423,6 +490,7 @@ void Init_parser_ext() {
423
490
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
424
491
  sym_read_buffer_size = ID2SYM(rb_intern("read_buffer_size"));
425
492
  sym_with_path = ID2SYM(rb_intern("with_path"));
493
+ sym_filter = ID2SYM(rb_intern("filter"));
426
494
  sym_null = ID2SYM(rb_intern("null"));
427
495
  sym_boolean = ID2SYM(rb_intern("boolean"));
428
496
  sym_number = ID2SYM(rb_intern("number"));
@@ -45,7 +45,7 @@ static ID id_call, id_read, id_parse, id_perform, id_on_body, id_bytesize, id_st
45
45
  static ID sym_allow_comments, sym_check_utf8, sym_symbolize_keys, sym_with_path,
46
46
  sym_read_buffer_size, sym_null, sym_boolean, sym_number, sym_string,
47
47
  sym_hash_key, sym_start_hash, sym_end_hash, sym_start_array,
48
- sym_end_array;
48
+ sym_end_array, sym_filter;
49
49
 
50
50
  static int yaji_null(void *ctx);
51
51
  static int yaji_boolean(void *ctx, int val);
@@ -74,6 +74,7 @@ static yajl_callbacks yaji_callbacks = {
74
74
  typedef struct {
75
75
  int symbolize_keys;
76
76
  int key_in_use;
77
+ int with_path;
77
78
  VALUE input;
78
79
  VALUE rbufsize;
79
80
  VALUE events;
@@ -81,6 +82,9 @@ typedef struct {
81
82
  VALUE path_str;
82
83
  VALUE parser_cb;
83
84
  VALUE chunk;
85
+ VALUE filter;
86
+ VALUE on_object_cb;
87
+ VALUE object_stack;
84
88
  yajl_handle handle;
85
89
  yajl_parser_config config;
86
90
  } yaji_parser;
@@ -18,5 +18,5 @@
18
18
  #
19
19
 
20
20
  module YAJI
21
- VERSION = "0.2.3"
21
+ VERSION = "0.3.0"
22
22
  end
@@ -196,7 +196,7 @@ class TestParser < MiniTest::Unit::TestCase
196
196
  assert_equal expected, objects
197
197
  end
198
198
 
199
- def test_it_optionally_yeilds_object_path
199
+ def test_it_optionally_yields_object_path
200
200
  parser = YAJI::Parser.new(toys_json_str)
201
201
  objects = []
202
202
  parser.each(["/total_rows", "/rows/"], :with_path => true) do |o|
@@ -216,6 +216,28 @@ class TestParser < MiniTest::Unit::TestCase
216
216
  assert_equal expected, objects
217
217
  end
218
218
 
219
+ def test_it_allows_to_specify_filter_and_options_at_initialization
220
+ parser = YAJI::Parser.new(toys_json_str,
221
+ :filter => ["/total_rows", "/rows/"],
222
+ :with_path => true)
223
+ objects = []
224
+ parser.each do |o|
225
+ objects << o
226
+ end
227
+ expected = [["/total_rows", 2],
228
+ ["/rows/", {
229
+ "id" => "buzz",
230
+ "props" => { "humanoid"=> true, "armed"=> true },
231
+ "movies" => [1,2,3]
232
+ }],
233
+ ["/rows/", {
234
+ "id" => "barbie",
235
+ "props" => { "humanoid"=> true, "armed"=> false },
236
+ "movies" => [2,3]
237
+ }]]
238
+ assert_equal expected, objects
239
+ end
240
+
219
241
  def test_it_doesnt_raise_exception_on_empty_input
220
242
  YAJI::Parser.new("").parse
221
243
  YAJI::Parser.new(" ").parse
@@ -223,9 +245,82 @@ class TestParser < MiniTest::Unit::TestCase
223
245
  YAJI::Parser.new(" \n\n ").parse
224
246
  end
225
247
 
248
+ def test_it_allows_to_create_parser_without_input
249
+ YAJI::Parser.new
250
+ YAJI::Parser.new(:filter => 'test')
251
+ YAJI::Parser.new(:with_path => true)
252
+ end
253
+
254
+ def test_it_raises_argument_error_for_parser_without_input
255
+ parser = YAJI::Parser.new
256
+ assert_raises(ArgumentError) do
257
+ parser.parse
258
+ end
259
+ assert_raises(ArgumentError) do
260
+ parser.each{|x| }
261
+ end
262
+ end
263
+
264
+ def test_it_raises_argument_error_on_write_without_callback_set_up
265
+ parser = YAJI::Parser.new
266
+ assert_raises(ArgumentError) do
267
+ parser.write('{"hello":"world"}')
268
+ end
269
+ end
270
+
271
+ def test_it_allows_to_feed_the_data_on_the_fly
272
+ parser = YAJI::Parser.new(:filter => '/rows/')
273
+
274
+ objects = []
275
+ parser.on_object do |obj|
276
+ objects << obj
277
+ end
278
+
279
+ parser.write(<<-JSON)
280
+ {
281
+ "total_rows": 2,
282
+ "rows": [
283
+ {
284
+ JSON
285
+ parser.write(<<-JSON)
286
+ "id": "buzz",
287
+ "props": {
288
+ "humanoid": true,
289
+ "armed": true
290
+ },
291
+ "movies": [1,2,3]
292
+ },
293
+ JSON
294
+ data = <<-JSON
295
+ {
296
+ "id": "barbie",
297
+ "props": {
298
+ "humanoid": true,
299
+ "armed": false
300
+ },
301
+ "movies": [2,3]
302
+ }
303
+ ]
304
+ }
305
+ JSON
306
+ parser << data
307
+
308
+ expected = [{
309
+ "id" => "buzz",
310
+ "props" => { "humanoid"=> true, "armed"=> true },
311
+ "movies" => [1,2,3]
312
+ },
313
+ {
314
+ "id" => "barbie",
315
+ "props" => { "humanoid"=> true, "armed"=> false },
316
+ "movies" => [2,3]
317
+ }]
318
+ assert_equal expected, objects
319
+ end
320
+
226
321
  def test_it_parses_chunked_data
227
322
  generator = Generator.new(['{"total_rows":', '0,"offset":0,"rows":[]', '}'])
228
- iter = YAJI::Parser.new(generator).each(["total_rows", "rows/", "errors/"], :with_path => true)
323
+ iter = YAJI::Parser.new(generator).each(["total_rows", "/rows/", "/errors/"], :with_path => true)
229
324
  begin
230
325
  loop do
231
326
  iter.next
@@ -236,7 +331,7 @@ class TestParser < MiniTest::Unit::TestCase
236
331
 
237
332
  def test_it_skips_empty_chunks
238
333
  generator = Generator.new(['{"total_rows":', '0,"offset":0,"rows":[]', '}', '', nil])
239
- iter = YAJI::Parser.new(generator).each(["total_rows", "rows/", "errors/"], :with_path => true)
334
+ iter = YAJI::Parser.new(generator).each(["total_rows", "/rows/", "/errors/"], :with_path => true)
240
335
  begin
241
336
  loop do
242
337
  iter.next
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaji
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: x86-mingw32
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-10 00:00:00.000000000Z
12
+ date: 2012-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -152,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
152
  version: '0'
153
153
  segments:
154
154
  - 0
155
- hash: -1814389335541304246
155
+ hash: -2453557434810599764
156
156
  required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  none: false
158
158
  requirements:
@@ -161,10 +161,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
161
  version: '0'
162
162
  segments:
163
163
  - 0
164
- hash: -1814389335541304246
164
+ hash: -2453557434810599764
165
165
  requirements: []
166
166
  rubyforge_project: yaji
167
- rubygems_version: 1.8.18
167
+ rubygems_version: 1.8.21
168
168
  signing_key:
169
169
  specification_version: 3
170
170
  summary: Yet another JSON iterator