Skip to content

Commit e5fcad2

Browse files
committed
ref params, impl verbose_error flag
1 parent 4859fd0 commit e5fcad2

File tree

3 files changed

+42
-19
lines changed

3 files changed

+42
-19
lines changed

Rakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ GEMSPEC = Gem::Specification.load("json_scanner.gemspec")
1919

2020
Rake::ExtensionTask.new("json_scanner", GEMSPEC) do |ext|
2121
ext.lib_dir = "lib/json_scanner"
22+
# https://karottenreibe.github.io/2009/10/30/ruby-c-extension-7/
2223
end
2324

2425
task default: %i[clobber compile spec rubocop]

ext/json_scanner/json_scanner.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
VALUE rb_mJsonScanner;
44
VALUE rb_mJsonScannerOptions;
55
VALUE rb_eJsonScannerParseError;
6+
ID scan_kwargs_table[2];
67

78
VALUE null_sym;
89
VALUE boolean_sym;
@@ -84,7 +85,7 @@ typedef struct
8485
} scan_ctx;
8586

8687
// FIXME: This will cause memory leak if ruby_xmalloc raises
87-
scan_ctx *scan_ctx_init(VALUE path_ary, VALUE with_path)
88+
scan_ctx *scan_ctx_init(VALUE path_ary, int with_path)
8889
{
8990
int path_ary_len;
9091
scan_ctx *ctx;
@@ -134,7 +135,7 @@ scan_ctx *scan_ctx_init(VALUE path_ary, VALUE with_path)
134135

135136
ctx = ruby_xmalloc(sizeof(scan_ctx));
136137

137-
ctx->with_path = RTEST(with_path);
138+
ctx->with_path = with_path;
138139
ctx->max_path_len = 0;
139140

140141
paths = ruby_xmalloc(sizeof(paths_t) * path_ary_len);
@@ -459,9 +460,12 @@ static yajl_callbacks scan_callbacks = {
459460
scan_on_start_array,
460461
scan_on_end_array};
461462

462-
// TODO: make with_path optional kw: `with_path: false`
463-
VALUE scan(VALUE self, VALUE json_str, VALUE path_ary, VALUE with_path)
463+
// def scan(json_str, path_arr, with_path: false, verbose_error: false)
464+
VALUE scan(int argc, VALUE * argv, VALUE self)
464465
{
466+
VALUE json_str, path_ary, kwargs;
467+
VALUE kwargs_values[2];
468+
int with_path = false, verbose_error = false;
465469
char *json_text;
466470
size_t json_text_len;
467471
yajl_handle handle;
@@ -470,8 +474,12 @@ VALUE scan(VALUE self, VALUE json_str, VALUE path_ary, VALUE with_path)
470474
VALUE err = Qnil, result;
471475
// Turned out callbacks can't raise exceptions
472476
// VALUE callback_err;
473-
// TODO
474-
int opt_verbose_error = 0;
477+
rb_scan_args(argc, argv, "2:", &json_str, &path_ary, &kwargs);
478+
if (kwargs != Qnil) {
479+
rb_get_kwargs(kwargs, scan_kwargs_table, 0, 2, kwargs_values);
480+
if (kwargs_values[0] != Qundef) with_path = RTEST(kwargs_values[0]);
481+
if (kwargs_values[1] != Qundef) verbose_error = RTEST(kwargs_values[1]);
482+
}
475483
rb_check_type(json_str, T_STRING);
476484
json_text = RSTRING_PTR(json_str);
477485
#if LONG_MAX > SIZE_MAX
@@ -492,7 +500,7 @@ VALUE scan(VALUE self, VALUE json_str, VALUE path_ary, VALUE with_path)
492500

493501
if (stat != yajl_status_ok)
494502
{
495-
char *str = (char *)yajl_get_error(handle, opt_verbose_error, (unsigned char *)json_text, json_text_len);
503+
char *str = (char *)yajl_get_error(handle, verbose_error, (unsigned char *)json_text, json_text_len);
496504
err = rb_str_new_cstr(str);
497505
yajl_free_error(handle, (unsigned char *)str);
498506
}
@@ -520,11 +528,13 @@ Init_json_scanner(void)
520528
rb_define_const(rb_mJsonScannerOptions, "ALLOW_TRAILING_GARBAGE", INT2FIX(yajl_allow_trailing_garbage));
521529
rb_define_const(rb_mJsonScannerOptions, "ALLOW_MULTIPLE_VALUES", INT2FIX(yajl_allow_multiple_values));
522530
rb_define_const(rb_mJsonScannerOptions, "ALLOW_PARTIAL_VALUES", INT2FIX(yajl_allow_partial_values));
523-
rb_define_module_function(rb_mJsonScanner, "scan", scan, 3);
531+
rb_define_module_function(rb_mJsonScanner, "scan", scan, -1);
524532
null_sym = rb_id2sym(rb_intern("null"));
525533
boolean_sym = rb_id2sym(rb_intern("boolean"));
526534
number_sym = rb_id2sym(rb_intern("number"));
527535
string_sym = rb_id2sym(rb_intern("string"));
528536
object_sym = rb_id2sym(rb_intern("object"));
529537
array_sym = rb_id2sym(rb_intern("array"));
538+
scan_kwargs_table[0] = rb_intern("with_path");
539+
scan_kwargs_table[1] = rb_intern("verbose_error");
530540
}

spec/json_scanner_spec.rb

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
end
99

1010
it "scans json" do
11-
result = described_class.scan('["1", {"a": 2}]', [[0], [1, "a"], []], false)
11+
result = described_class.scan('["1", {"a": 2}]', [[0], [1, "a"], []])
1212
expect(result).to eq([[[1, 4, :string]], [[12, 13, :number]], [[0, 15, :array]]])
13-
expect(described_class.scan('"2"', [[]], false)).to eq([[[0, 3, :string]]])
13+
expect(described_class.scan('"2"', [[]])).to eq([[[0, 3, :string]]])
1414
expect(
15-
described_class.scan("[0,1,2,3,4,5,6,7]", [[(0..2)], [(4...6)]], false)
15+
described_class.scan("[0,1,2,3,4,5,6,7]", [[(0..2)], [(4...6)]])
1616
).to eq(
1717
[[[1, 2, :number], [3, 4, :number], [5, 6, :number]], [[9, 10, :number], [11, 12, :number]]]
1818
)
19-
expect(described_class.scan('{"a": 1}', [["a"], []], false)).to eq(
19+
expect(described_class.scan('{"a": 1}', [["a"], []])).to eq(
2020
[[[6, 7, :number]], [[0, 8, :object]]]
2121
)
2222
end
@@ -28,10 +28,10 @@
2828
# TODO: investigate
2929
# got "munmap_chunk(): invalid pointer" in in console once after
3030
# JsonScanner.scan '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]', [[0,0,0,0,0,0,0]], true + Ctrl+D
31-
# (last arg wasn't handled at the time)
31+
# (last arg wasn't handled at the time and was intended for with_path kwarg)
3232
# but I don't think it's a problem of tht extension or libyajl, it happened at exit and I free everything before
3333
# `JsonScanner.scan` returns
34-
described_class.scan "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]", [[0, 0, 0, 0, 0, 0, 0]], false
34+
described_class.scan "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]", [[0, 0, 0, 0, 0, 0, 0]]
3535
ensure
3636
GC.stress = false
3737
end
@@ -40,26 +40,38 @@
4040

4141
it "allows to select ranges" do
4242
expect(
43-
described_class.scan("[[1,2],[3,4]]", [[described_class::ANY_INDEX, described_class::ANY_INDEX]], false)
43+
described_class.scan("[[1,2],[3,4]]", [[described_class::ANY_INDEX, described_class::ANY_INDEX]])
4444
).to eq(
4545
[[[2, 3, :number], [4, 5, :number], [8, 9, :number], [10, 11, :number]]]
4646
)
4747
expect(
48-
described_class.scan("[[1,2],[3,4]]", [[described_class::ANY_INDEX, (0...1)]], false)
48+
described_class.scan("[[1,2],[3,4]]", [[described_class::ANY_INDEX, (0...1)]])
4949
).to eq(
5050
[[[2, 3, :number], [8, 9, :number]]]
5151
)
5252
end
5353

5454
it "allows only positive or -1 values" do
5555
expect do
56-
described_class.scan("[[1,2],[3,4]]", [[(0...-1)]], false)
56+
described_class.scan("[[1,2],[3,4]]", [[(0...-1)]])
5757
end.to raise_error ArgumentError
5858
expect do
59-
described_class.scan("[[1,2],[3,4]]", [[(0..-2)]], false)
59+
described_class.scan("[[1,2],[3,4]]", [[(0..-2)]])
6060
end.to raise_error ArgumentError
6161
expect do
62-
described_class.scan("[[1,2],[3,4]]", [[(-42..1)]], false)
62+
described_class.scan("[[1,2],[3,4]]", [[(-42..1)]])
6363
end.to raise_error ArgumentError
6464
end
65+
66+
it "allows to configure error messages" do
67+
expect do
68+
described_class.scan "{1}", []
69+
end.to raise_error described_class::ParseError, /invalid object key(?!.*\(right here\))/m
70+
expect do
71+
described_class.scan "{1}", [], verbose_error: false
72+
end.to raise_error described_class::ParseError, /invalid object key(?!.*\(right here\))/m
73+
expect do
74+
described_class.scan "{1}", [], verbose_error: true
75+
end.to raise_error described_class::ParseError, /invalid object key(?=.*\(right here\))/m
76+
end
6577
end

0 commit comments

Comments
 (0)