brianmario-yajl-ruby 0.4.8 → 0.5.0

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 (44) hide show
  1. data/CHANGELOG.rdoc +21 -0
  2. data/README.rdoc +13 -0
  3. data/VERSION.yml +2 -2
  4. data/benchmark/encode.rb +4 -3
  5. data/benchmark/encode_json_and_marshal.rb +4 -3
  6. data/benchmark/encode_json_and_yaml.rb +4 -3
  7. data/benchmark/parse.rb +5 -4
  8. data/benchmark/parse_json_and_marshal.rb +4 -3
  9. data/benchmark/parse_json_and_yaml.rb +4 -3
  10. data/benchmark/parse_stream.rb +48 -0
  11. data/benchmark/subjects/twitter_stream.json +430 -0
  12. data/examples/http/twitter_search_api.rb +15 -0
  13. data/examples/http/twitter_stream_api.rb +24 -0
  14. data/examples/parsing/from_file.rb +14 -0
  15. data/examples/parsing/from_stdin.rb +9 -0
  16. data/examples/parsing/from_string.rb +15 -0
  17. data/ext/api/yajl_parse.h +3 -0
  18. data/ext/extconf.rb +2 -1
  19. data/ext/yajl.c +5 -0
  20. data/ext/yajl_ext.c +235 -122
  21. data/ext/yajl_ext.h +49 -36
  22. data/ext/yajl_lex.c +7 -0
  23. data/ext/yajl_lex.h +2 -0
  24. data/ext/yajl_parser.c +3 -1
  25. data/lib/yajl.rb +18 -17
  26. data/lib/yajl/bzip2.rb +1 -1
  27. data/lib/yajl/bzip2/stream_reader.rb +1 -1
  28. data/lib/yajl/bzip2/stream_writer.rb +1 -1
  29. data/lib/yajl/deflate.rb +1 -1
  30. data/lib/yajl/deflate/stream_reader.rb +1 -1
  31. data/lib/yajl/deflate/stream_writer.rb +1 -1
  32. data/lib/yajl/gzip.rb +1 -1
  33. data/lib/yajl/gzip/stream_reader.rb +1 -1
  34. data/lib/yajl/gzip/stream_writer.rb +1 -1
  35. data/lib/yajl/http_stream.rb +21 -5
  36. data/spec/encoding/encoding_spec.rb +14 -9
  37. data/spec/http/http_spec.rb +1 -5
  38. data/spec/parsing/active_support_spec.rb +5 -3
  39. data/spec/parsing/chunked_spec.rb +72 -0
  40. data/spec/parsing/fixtures_spec.rb +4 -2
  41. data/spec/parsing/one_off_spec.rb +2 -1
  42. data/spec/spec_helper.rb +8 -1
  43. data/yajl-ruby.gemspec +17 -3
  44. metadata +16 -2
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../../lib')
3
+
4
+ require 'rubygems'
5
+ require 'yajl/http_stream'
6
+ require 'uri'
7
+
8
+ unless keywords = ARGV[0]
9
+ puts "\nUsage: ruby examples/http/twitter_search_api.rb keyword\n\n"
10
+ exit(0)
11
+ end
12
+ captured = 0
13
+ uri = URI.parse("http://search.twitter.com/search.json?q=#{keywords}")
14
+
15
+ puts Yajl::HttpStream.get(uri).inspect
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../../lib')
3
+
4
+ require 'rubygems'
5
+ require 'yajl/http_stream'
6
+ require 'uri'
7
+
8
+ unless (username = ARGV[0]) && (password = ARGV[1])
9
+ puts "\nUsage: ruby examples/http/twitter_stream_api.rb username password\n\n"
10
+ exit(0)
11
+ end
12
+ captured = 0
13
+ uri = URI.parse("http://#{username}:#{password}@stream.twitter.com/spritzer.json")
14
+
15
+ trap('INT') {
16
+ puts "\n\nCaptured #{captured} objects from the stream"
17
+ puts "CTRL+C caught, later!"
18
+ exit(0)
19
+ }
20
+
21
+ Yajl::HttpStream.get(uri) do |hash|
22
+ puts hash.inspect
23
+ captured += 1
24
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'yajl'
5
+
6
+ unless file = ARGV[0]
7
+ puts "\nUsage: ruby examples/from_file.rb benchmark/subjects/item.json\n\n"
8
+ exit(0)
9
+ end
10
+
11
+ json = File.new(file, 'r')
12
+
13
+ hash = Yajl::Parser.new.parse(json)
14
+ puts hash.inspect
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'yajl'
5
+
6
+ # Usage: cat benchmark/subjects/item.json | ruby examples/from_stdin.rb
7
+
8
+ hash = Yajl::Parser.new.parse(STDIN)
9
+ puts hash.inspect
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'yajl'
5
+ require 'stringio'
6
+
7
+ unless string = ARGV[0]
8
+ puts "\nUsage: ruby examples/from_string.rb '{\"foo\": 1145}'\n\n"
9
+ exit(0)
10
+ end
11
+
12
+ json = StringIO.new(string)
13
+
14
+ hash = Yajl::Parser.new.parse(json)
15
+ puts hash.inspect
data/ext/api/yajl_parse.h CHANGED
@@ -131,6 +131,9 @@ extern "C" {
131
131
  const yajl_parser_config * config,
132
132
  const yajl_alloc_funcs * allocFuncs,
133
133
  void * ctx);
134
+
135
+ /** allow resetting of the lexer without the need to realloc a new parser */
136
+ void yajl_reset_parser(yajl_handle hand);
134
137
 
135
138
  /** free a parser handle */
136
139
  void YAJL_API yajl_free(yajl_handle handle);
data/ext/extconf.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  require 'mkmf'
3
3
  require 'rbconfig'
4
4
 
5
- # $CFLAGS << ' -Wall'
5
+ $CFLAGS << ' -Wall'
6
+ # $CFLAGS << ' -O0 -ggdb'
6
7
 
7
8
  create_makefile("yajl_ext")
data/ext/yajl.c CHANGED
@@ -104,6 +104,11 @@ yajl_alloc(const yajl_callbacks * callbacks,
104
104
  return hand;
105
105
  }
106
106
 
107
+ void
108
+ yajl_reset_parser(yajl_handle hand) {
109
+ hand->lexer = yajl_lex_realloc(hand->lexer);
110
+ }
111
+
107
112
  void
108
113
  yajl_free(yajl_handle handle)
109
114
  {
data/ext/yajl_ext.c CHANGED
@@ -1,55 +1,63 @@
1
1
  #include "yajl_ext.h"
2
2
 
3
- void check_and_fire_callback(void * ctx) {
4
- yajl_status stat;
3
+ // Helpers for building objects
4
+ void yajl_check_and_fire_callback(void * ctx) {
5
+ struct yajl_parser_wrapper * wrapper;
6
+ GetParser((VALUE)ctx, wrapper);
5
7
 
6
- if (RARRAY_LEN((VALUE)ctx) == 1 && parse_complete_callback != Qnil) {
7
- // parse any remaining buffered data
8
- stat = yajl_parse_complete(chunkedParser);
9
-
10
- rb_funcall(parse_complete_callback, intern_call, 1, rb_ary_pop((VALUE)ctx));
8
+ // No need to do any of this if the callback isn't even setup
9
+ if (wrapper->parse_complete_callback != Qnil) {
10
+ int len = RARRAY_LEN(wrapper->builderStack);
11
+ if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
12
+ rb_funcall(wrapper->parse_complete_callback, intern_call, 1, rb_ary_pop(wrapper->builderStack));
13
+ }
11
14
  }
12
15
  }
13
16
 
14
- void set_static_value(void * ctx, VALUE val) {
15
- VALUE len = RARRAY_LEN((VALUE)ctx);
17
+ void yajl_set_static_value(void * ctx, VALUE val) {
18
+ struct yajl_parser_wrapper * wrapper;
19
+ VALUE lastEntry, hash;
20
+ int len;
16
21
 
22
+ GetParser((VALUE)ctx, wrapper);
23
+
24
+ len = RARRAY_LEN(wrapper->builderStack);
17
25
  if (len > 0) {
18
- VALUE lastEntry = rb_ary_entry((VALUE)ctx, len-1);
19
- VALUE hash;
26
+ lastEntry = rb_ary_entry(wrapper->builderStack, len-1);
20
27
  switch (TYPE(lastEntry)) {
21
28
  case T_ARRAY:
22
29
  rb_ary_push(lastEntry, val);
23
30
  if (TYPE(val) == T_HASH || TYPE(val) == T_ARRAY) {
24
- rb_ary_push((VALUE)ctx, val);
31
+ rb_ary_push(wrapper->builderStack, val);
25
32
  }
26
33
  break;
27
34
  case T_HASH:
28
35
  rb_hash_aset(lastEntry, val, Qnil);
29
- rb_ary_push((VALUE)ctx, val);
36
+ rb_ary_push(wrapper->builderStack, val);
30
37
  break;
31
38
  case T_STRING:
32
- hash = rb_ary_entry((VALUE)ctx, len-2);
39
+ hash = rb_ary_entry(wrapper->builderStack, len-2);
33
40
  if (TYPE(hash) == T_HASH) {
34
41
  rb_hash_aset(hash, lastEntry, val);
35
- rb_ary_pop((VALUE)ctx);
42
+ rb_ary_pop(wrapper->builderStack);
36
43
  if (TYPE(val) == T_HASH || TYPE(val) == T_ARRAY) {
37
- rb_ary_push((VALUE)ctx, val);
44
+ rb_ary_push(wrapper->builderStack, val);
38
45
  }
39
46
  }
40
47
  break;
41
48
  }
42
49
  } else {
43
- rb_ary_push((VALUE)ctx, val);
50
+ rb_ary_push(wrapper->builderStack, val);
44
51
  }
45
52
  }
46
53
 
47
- void encode_part(yajl_gen hand, VALUE obj, VALUE io) {
54
+ void yajl_encode_part(yajl_gen hand, VALUE obj, VALUE io) {
48
55
  VALUE str, outBuff, otherObj;
49
56
  int objLen;
50
57
  int idx = 0;
51
58
  const unsigned char * buffer;
52
59
  unsigned int len;
60
+
53
61
  yajl_gen_get_buf(hand, &buffer, &len);
54
62
  outBuff = rb_str_new((const char *)buffer, len);
55
63
  rb_io_write(io, outBuff);
@@ -65,9 +73,9 @@ void encode_part(yajl_gen hand, VALUE obj, VALUE io) {
65
73
  for(idx=0; idx<RARRAY_LEN(keys); idx++) {
66
74
  entry = rb_ary_entry(keys, idx);
67
75
  // the key
68
- encode_part(hand, entry, io);
76
+ yajl_encode_part(hand, entry, io);
69
77
  // the value
70
- encode_part(hand, rb_hash_aref(obj, entry), io);
78
+ yajl_encode_part(hand, rb_hash_aref(obj, entry), io);
71
79
  }
72
80
 
73
81
  yajl_gen_map_close(hand);
@@ -76,7 +84,7 @@ void encode_part(yajl_gen hand, VALUE obj, VALUE io) {
76
84
  yajl_gen_array_open(hand);
77
85
  for(idx=0; idx<RARRAY_LEN(obj); idx++) {
78
86
  otherObj = rb_ary_entry(obj, idx);
79
- encode_part(hand, otherObj, io);
87
+ yajl_encode_part(hand, otherObj, io);
80
88
  }
81
89
  yajl_gen_array_close(hand);
82
90
  break;
@@ -104,180 +112,285 @@ void encode_part(yajl_gen hand, VALUE obj, VALUE io) {
104
112
  }
105
113
  }
106
114
 
107
- static int found_null(void * ctx) {
108
- set_static_value(ctx, Qnil);
109
- check_and_fire_callback(ctx);
115
+ void yajl_parser_wrapper_free(void * wrapper) {
116
+ struct yajl_parser_wrapper * w = wrapper;
117
+ yajl_free(w->parser);
118
+ free(w);
119
+ }
120
+
121
+ void yajl_parser_wrapper_mark(void * wrapper) {
122
+ struct yajl_parser_wrapper * w = wrapper;
123
+ rb_gc_mark(w->builderStack);
124
+ rb_gc_mark(w->parse_complete_callback);
125
+ }
126
+
127
+ // YAJL Callbacks
128
+ static int yajl_found_null(void * ctx) {
129
+ yajl_set_static_value(ctx, Qnil);
130
+ yajl_check_and_fire_callback(ctx);
110
131
  return 1;
111
132
  }
112
133
 
113
- static int found_boolean(void * ctx, int boolean) {
114
- set_static_value(ctx, boolean ? Qtrue : Qfalse);
115
- check_and_fire_callback(ctx);
134
+ static int yajl_found_boolean(void * ctx, int boolean) {
135
+ yajl_set_static_value(ctx, boolean ? Qtrue : Qfalse);
136
+ yajl_check_and_fire_callback(ctx);
116
137
  return 1;
117
138
  }
118
139
 
119
- static int found_number(void * ctx, const char * numberVal, unsigned int numberLen) {
140
+ static int yajl_found_number(void * ctx, const char * numberVal, unsigned int numberLen) {
120
141
  VALUE subString = rb_str_new(numberVal, numberLen);
121
- if (strstr(RSTRING_PTR(subString), ".") != NULL || strstr(RSTRING_PTR(subString), "e") != NULL || strstr(RSTRING_PTR(subString), "E") != NULL) {
122
- set_static_value(ctx, rb_Float(subString));
142
+ char * cSubString = RSTRING_PTR(subString);
143
+
144
+ if (strstr(cSubString, ".") != NULL ||
145
+ strstr(cSubString, "e") != NULL ||
146
+ strstr(cSubString, "E") != NULL) {
147
+ yajl_set_static_value(ctx, rb_Float(subString));
123
148
  } else {
124
- set_static_value(ctx, rb_Integer(subString));
149
+ yajl_set_static_value(ctx, rb_Integer(subString));
125
150
  }
126
- check_and_fire_callback(ctx);
151
+ yajl_check_and_fire_callback(ctx);
127
152
  return 1;
128
153
  }
129
154
 
130
- static int found_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
131
- set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
132
- check_and_fire_callback(ctx);
155
+ static int yajl_found_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
156
+ yajl_set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
157
+ yajl_check_and_fire_callback(ctx);
133
158
  return 1;
134
159
  }
135
160
 
136
- static int found_hash_key(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
137
- set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
161
+ static int yajl_found_hash_key(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
162
+ yajl_set_static_value(ctx, rb_str_new((const char *)stringVal, stringLen));
163
+ yajl_check_and_fire_callback(ctx);
138
164
  return 1;
139
165
  }
140
166
 
141
- static int found_start_hash(void * ctx) {
142
- set_static_value(ctx, rb_hash_new());
167
+ static int yajl_found_start_hash(void * ctx) {
168
+ struct yajl_parser_wrapper * wrapper;
169
+ GetParser((VALUE)ctx, wrapper);
170
+ wrapper->nestedHashLevel++;
171
+ yajl_set_static_value(ctx, rb_hash_new());
143
172
  return 1;
144
173
  }
145
174
 
146
- static int found_end_hash(void * ctx) {
147
- if (RARRAY_LEN((VALUE)ctx) > 1) {
148
- rb_ary_pop((VALUE)ctx);
175
+ static int yajl_found_end_hash(void * ctx) {
176
+ struct yajl_parser_wrapper * wrapper;
177
+ GetParser((VALUE)ctx, wrapper);
178
+ wrapper->nestedHashLevel--;
179
+ if (RARRAY_LEN(wrapper->builderStack) > 1) {
180
+ rb_ary_pop(wrapper->builderStack);
149
181
  }
150
- check_and_fire_callback(ctx);
182
+ yajl_check_and_fire_callback(ctx);
151
183
  return 1;
152
184
  }
153
185
 
154
- static int found_start_array(void * ctx) {
155
- set_static_value(ctx, rb_ary_new());
186
+ static int yajl_found_start_array(void * ctx) {
187
+ struct yajl_parser_wrapper * wrapper;
188
+ GetParser((VALUE)ctx, wrapper);
189
+ wrapper->nestedArrayLevel++;
190
+ yajl_set_static_value(ctx, rb_ary_new());
156
191
  return 1;
157
192
  }
158
193
 
159
- static int found_end_array(void * ctx) {
160
- if (RARRAY_LEN((VALUE)ctx) > 1) {
161
- rb_ary_pop((VALUE)ctx);
194
+ static int yajl_found_end_array(void * ctx) {
195
+ struct yajl_parser_wrapper * wrapper;
196
+ GetParser((VALUE)ctx, wrapper);
197
+ wrapper->nestedArrayLevel--;
198
+ if (RARRAY_LEN(wrapper->builderStack) > 1) {
199
+ rb_ary_pop(wrapper->builderStack);
162
200
  }
163
- check_and_fire_callback(ctx);
201
+ yajl_check_and_fire_callback(ctx);
164
202
  return 1;
165
203
  }
166
204
 
167
- static VALUE t_setParseComplete(VALUE self, VALUE callback) {
168
- parse_complete_callback = callback;
169
- return Qnil;
170
- }
171
205
 
172
- static VALUE t_parseSome(VALUE self, VALUE string) {
173
- yajl_status stat;
174
-
175
- if (string == Qnil) {
176
- rb_raise(cParseError, "%s", "Can't parse a nil string.");
177
- return Qnil;
178
- }
206
+ /** Ruby Interface */
207
+
208
+ // Yajl::Parser
209
+ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
210
+ struct yajl_parser_wrapper * wrapper;
211
+ yajl_parser_config cfg;
212
+ VALUE opts, obj;
213
+ int allowComments = 1, checkUTF8 = 1;
179
214
 
180
- if (parse_complete_callback != Qnil) {
181
- if (context == Qnil) {
182
- context = rb_ary_new();
183
- }
184
- if (chunkedParser == NULL) {
185
- // allocate our parser
186
- chunkedParser = yajl_alloc(&callbacks, &cfg, NULL, (void *)context);
187
- }
215
+ // Scan off config vars
216
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
217
+ Check_Type(opts, T_HASH);
188
218
 
189
- stat = yajl_parse(chunkedParser, (const unsigned char *)RSTRING_PTR(string), RSTRING_LEN(string));
190
- if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
191
- unsigned char * str = yajl_get_error(chunkedParser, 1, (const unsigned char *)RSTRING_PTR(string), RSTRING_LEN(string));
192
- rb_raise(cParseError, "%s", (const char *) str);
193
- yajl_free_error(chunkedParser, str);
219
+ if (rb_hash_aref(opts, ID2SYM(sym_allow_comments)) == Qfalse) {
220
+ allowComments = 0;
221
+ }
222
+ if (rb_hash_aref(opts, ID2SYM(sym_check_utf8)) == Qfalse) {
223
+ checkUTF8 = 0;
194
224
  }
195
- } else {
196
- rb_raise(cParseError, "%s", "The on_parse_complete callback isn't setup, parsing useless.");
197
- }
198
-
199
- if (RARRAY_LEN(context) == 0) {
200
- yajl_free(chunkedParser);
201
225
  }
226
+ cfg = (yajl_parser_config){allowComments, checkUTF8};
202
227
 
203
- return Qnil;
228
+ obj = Data_Make_Struct(klass, struct yajl_parser_wrapper, yajl_parser_wrapper_mark, yajl_parser_wrapper_free, wrapper);
229
+ wrapper->parser = yajl_alloc(&callbacks, &cfg, NULL, (void *)obj);
230
+ wrapper->nestedArrayLevel = 0;
231
+ wrapper->nestedHashLevel = 0;
232
+ wrapper->builderStack = rb_ary_new();
233
+ wrapper->parse_complete_callback = Qnil;
234
+ rb_obj_call_init(obj, 0, 0);
235
+ return obj;
204
236
  }
205
237
 
206
- static VALUE t_parse(VALUE self, VALUE io) {
238
+ static VALUE rb_yajl_parser_init(int argc, VALUE * argv, VALUE self) {
239
+ return self;
240
+ }
241
+
242
+ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) {
243
+ struct yajl_parser_wrapper * wrapper;
207
244
  yajl_status stat;
208
- context = rb_ary_new();
245
+ VALUE parsed, rbufsize, io;
209
246
 
210
- // allocate our parser
211
- streamParser = yajl_alloc(&callbacks, &cfg, NULL, (void *)context);
247
+ GetParser(self, wrapper);
248
+ parsed = rb_str_new2("");
212
249
 
213
- VALUE parsed = rb_str_new2("");
214
- VALUE rbufsize = INT2FIX(readBufferSize);
250
+ // setup our parameters
251
+ rb_scan_args(argc, argv, "11", &io, &rbufsize);
252
+ if (NIL_P(rbufsize)) {
253
+ rbufsize = INT2FIX(READ_BUFSIZE);
254
+ } else {
255
+ Check_Type(rbufsize, T_FIXNUM);
256
+ }
215
257
 
216
258
  // now parse from the IO
217
259
  while (rb_funcall(io, intern_eof, 0) != Qtrue) {
218
260
  rb_funcall(io, intern_io_read, 2, rbufsize, parsed);
219
261
 
220
- stat = yajl_parse(streamParser, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
262
+ stat = yajl_parse(wrapper->parser, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
221
263
 
222
264
  if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
223
- unsigned char * str = yajl_get_error(streamParser, 1, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
265
+ unsigned char * str = yajl_get_error(wrapper->parser, 1, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
224
266
  rb_raise(cParseError, "%s", (const char *) str);
225
- yajl_free_error(streamParser, str);
267
+ yajl_free_error(wrapper->parser, str);
226
268
  break;
227
269
  }
228
270
  }
229
271
 
230
272
  // parse any remaining buffered data
231
- stat = yajl_parse_complete(streamParser);
232
- yajl_free(streamParser);
273
+ stat = yajl_parse_complete(wrapper->parser);
274
+
275
+ if (wrapper->parse_complete_callback != Qnil) {
276
+ yajl_check_and_fire_callback((void *)self);
277
+ return Qnil;
278
+ }
279
+
280
+ return rb_ary_pop(wrapper->builderStack);
281
+ }
282
+
283
+ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
284
+ struct yajl_parser_wrapper * wrapper;
285
+ yajl_status stat;
233
286
 
234
- if (parse_complete_callback != Qnil) {
235
- check_and_fire_callback((void *)context);
287
+ GetParser(self, wrapper);
288
+ if (NIL_P(chunk)) {
289
+ rb_raise(cParseError, "Can't parse a nil string.");
236
290
  return Qnil;
237
291
  }
292
+
293
+ if (wrapper->parse_complete_callback != Qnil) {
294
+ stat = yajl_parse(wrapper->parser, (const unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk));
295
+ if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
296
+ unsigned char * str = yajl_get_error(wrapper->parser, 1, (const unsigned char *)RSTRING_PTR(chunk), RSTRING_LEN(chunk));
297
+ rb_raise(cParseError, "%s", (const char *) str);
298
+ yajl_free_error(wrapper->parser, str);
299
+ }
300
+ } else {
301
+ rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless.");
302
+ }
238
303
 
239
- return rb_ary_pop(context);
304
+ return Qnil;
240
305
  }
241
306
 
242
- static VALUE t_encode(VALUE self, VALUE obj, VALUE io) {
243
- yajl_gen_config conf = {0, " "};
244
- yajl_gen hand;
245
- const unsigned char * buffer;
246
- unsigned int len;
247
- VALUE outBuff;
248
-
249
- hand = yajl_gen_alloc(&conf, NULL);
250
- encode_part(hand, obj, io);
251
-
252
- // just make sure we output the remaining buffer
253
- yajl_gen_get_buf(hand, &buffer, &len);
254
- outBuff = rb_str_new((const char *)buffer, len);
255
- rb_io_write(io, outBuff);
256
-
257
- yajl_gen_clear(hand);
258
- yajl_gen_free(hand);
259
- return Qnil;
307
+ static VALUE rb_yajl_set_complete_cb(VALUE self, VALUE callback) {
308
+ struct yajl_parser_wrapper * wrapper;
309
+ GetParser(self, wrapper);
310
+ wrapper->parse_complete_callback = callback;
311
+ return Qnil;
260
312
  }
261
313
 
262
- void Init_yajl_ext() {
263
- mYajl = rb_define_module("Yajl");
314
+ // Yajl::Encoder
315
+ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
316
+ yajl_gen_config cfg;
317
+ yajl_gen encoder;
318
+ VALUE opts, obj, indent;
319
+ const char * indentString = " ";
320
+ int beautify = 0;
264
321
 
265
- mStream = rb_define_module_under(mYajl, "Stream");
266
- rb_define_module_function(mStream, "parse", t_parse, 1);
267
- rb_define_module_function(mStream, "encode", t_encode, 2);
322
+ // Scan off config vars
323
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
324
+ Check_Type(opts, T_HASH);
325
+
326
+ if (rb_hash_aref(opts, ID2SYM(sym_pretty)) == Qtrue) {
327
+ beautify = 1;
328
+ indent = rb_hash_aref(opts, ID2SYM(sym_indent));
329
+ if (indent != Qnil) {
330
+ Check_Type(indent, T_STRING);
331
+ indentString = RSTRING_PTR(indent);
332
+ }
333
+ }
334
+ }
335
+ cfg = (yajl_gen_config){beautify, indentString};
268
336
 
269
- mChunked = rb_define_module_under(mYajl, "Chunked");
270
- rb_define_module_function(mChunked, "parse_some", t_parseSome, 1);
271
- rb_define_module_function(mChunked, "<<", t_parseSome, 1);
272
- rb_define_module_function(mChunked, "on_parse_complete=", t_setParseComplete, 1);
337
+ encoder = yajl_gen_alloc(&cfg, NULL);
338
+ obj = Data_Wrap_Struct(klass, 0, yajl_gen_free, encoder);
339
+ rb_obj_call_init(obj, 0, 0);
340
+ return obj;
341
+ }
342
+
343
+ static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self) {
344
+ return self;
345
+ }
346
+
347
+ static VALUE rb_yajl_encoder_encode(VALUE self, VALUE obj, VALUE io) {
348
+ yajl_gen encoder;
349
+ const unsigned char * buffer;
350
+ unsigned int len;
351
+ VALUE outBuff;
352
+
353
+ GetEncoder(self, encoder);
354
+
355
+ // begin encode process
356
+ yajl_encode_part(encoder, obj, io);
357
+
358
+ // just make sure we output the remaining buffer
359
+ yajl_gen_get_buf(encoder, &buffer, &len);
360
+ outBuff = rb_str_new((const char *)buffer, len);
361
+ rb_io_write(io, outBuff);
362
+ yajl_gen_clear(encoder);
363
+
364
+ return Qnil;
365
+ }
366
+
367
+ // Ruby Extension initializer
368
+ void Init_yajl_ext() {
369
+ mYajl = rb_define_module("Yajl");
273
370
 
274
371
  VALUE rb_cStandardError = rb_const_get(rb_cObject, rb_intern("StandardError"));
275
372
  cParseError = rb_define_class_under(mYajl, "ParseError", rb_cStandardError);
276
373
 
374
+ cParser = rb_define_class_under(mYajl, "Parser", rb_cObject);
375
+ rb_define_singleton_method(cParser, "new", rb_yajl_parser_new, -1);
376
+ rb_define_method(cParser, "initialize", rb_yajl_parser_init, -1);
377
+ rb_define_method(cParser, "parse", rb_yajl_parser_parse, -1);
378
+ rb_define_method(cParser, "parse_chunk", rb_yajl_parser_parse_chunk, -1);
379
+ rb_define_method(cParser, "<<", rb_yajl_parser_parse_chunk, 1);
380
+ rb_define_method(cParser, "on_parse_complete=", rb_yajl_set_complete_cb, 1);
381
+
382
+ cEncoder = rb_define_class_under(mYajl, "Encoder", rb_cObject);
383
+ rb_define_singleton_method(cEncoder, "new", rb_yajl_encoder_new, -1);
384
+ rb_define_method(cEncoder, "initialize", rb_yajl_encoder_init, -1);
385
+ rb_define_method(cEncoder, "encode", rb_yajl_encoder_encode, 2);
386
+
277
387
  intern_io_read = rb_intern("read");
278
388
  intern_eof = rb_intern("eof?");
279
- intern_respond_to = rb_intern("respond_to?");
280
389
  intern_call = rb_intern("call");
281
390
  intern_keys = rb_intern("keys");
282
391
  intern_to_s = rb_intern("to_s");
392
+ sym_allow_comments = rb_intern("allow_comments");
393
+ sym_check_utf8 = rb_intern("check_utf8");
394
+ sym_pretty = rb_intern("pretty");
395
+ sym_indent = rb_intern("indent");
283
396
  }