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.
- data/HISTORY.markdown +5 -0
- data/ext/yaji/parser_ext.c +94 -26
- data/ext/yaji/parser_ext.h +5 -1
- data/lib/yaji/version.rb +1 -1
- data/test/test_parser.rb +98 -3
- metadata +5 -5
data/HISTORY.markdown
CHANGED
data/ext/yaji/parser_ext.c
CHANGED
@@ -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
|
-
|
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, "
|
198
|
-
if (TYPE(p->input) ==
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
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
|
-
|
319
|
+
long len = RSTRING_LEN(str);
|
278
320
|
VALUE entry;
|
279
321
|
|
280
|
-
switch(TYPE(
|
322
|
+
switch(TYPE(filter)) {
|
281
323
|
case T_STRING:
|
282
|
-
return RSTRING_LEN(
|
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(
|
286
|
-
entry = RARRAY_PTR(
|
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
|
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(
|
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
|
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&", &
|
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
|
-
|
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
|
-
|
368
|
-
|
369
|
-
|
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"));
|
data/ext/yaji/parser_ext.h
CHANGED
@@ -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;
|
data/lib/yaji/version.rb
CHANGED
data/test/test_parser.rb
CHANGED
@@ -196,7 +196,7 @@ class TestParser < MiniTest::Unit::TestCase
|
|
196
196
|
assert_equal expected, objects
|
197
197
|
end
|
198
198
|
|
199
|
-
def
|
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.
|
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-
|
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: -
|
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: -
|
164
|
+
hash: -2453557434810599764
|
165
165
|
requirements: []
|
166
166
|
rubyforge_project: yaji
|
167
|
-
rubygems_version: 1.8.
|
167
|
+
rubygems_version: 1.8.21
|
168
168
|
signing_key:
|
169
169
|
specification_version: 3
|
170
170
|
summary: Yet another JSON iterator
|