brianmario-yajl-ruby 0.4.8 → 0.5.0

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