benofsky-yajl-ruby 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.md +281 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +320 -0
  5. data/Rakefile +40 -0
  6. data/VERSION.yml +5 -0
  7. data/benchmark/encode.rb +58 -0
  8. data/benchmark/encode_json_and_marshal.rb +42 -0
  9. data/benchmark/encode_json_and_yaml.rb +53 -0
  10. data/benchmark/http.rb +32 -0
  11. data/benchmark/parse.rb +59 -0
  12. data/benchmark/parse_json_and_marshal.rb +50 -0
  13. data/benchmark/parse_json_and_yaml.rb +55 -0
  14. data/benchmark/parse_stream.rb +54 -0
  15. data/benchmark/subjects/item.json +1 -0
  16. data/benchmark/subjects/ohai.json +1216 -0
  17. data/benchmark/subjects/ohai.marshal_dump +0 -0
  18. data/benchmark/subjects/ohai.yml +975 -0
  19. data/benchmark/subjects/twitter_search.json +1 -0
  20. data/benchmark/subjects/twitter_stream.json +430 -0
  21. data/benchmark/subjects/unicode.json +1 -0
  22. data/examples/encoding/chunked_encoding.rb +27 -0
  23. data/examples/encoding/one_shot.rb +13 -0
  24. data/examples/encoding/to_an_io.rb +12 -0
  25. data/examples/http/twitter_search_api.rb +12 -0
  26. data/examples/http/twitter_stream_api.rb +26 -0
  27. data/examples/parsing/from_file.rb +14 -0
  28. data/examples/parsing/from_stdin.rb +9 -0
  29. data/examples/parsing/from_string.rb +13 -0
  30. data/ext/api/yajl_common.h +85 -0
  31. data/ext/api/yajl_gen.h +159 -0
  32. data/ext/api/yajl_parse.h +196 -0
  33. data/ext/extconf.rb +9 -0
  34. data/ext/yajl.c +164 -0
  35. data/ext/yajl_alloc.c +65 -0
  36. data/ext/yajl_alloc.h +50 -0
  37. data/ext/yajl_buf.c +119 -0
  38. data/ext/yajl_buf.h +73 -0
  39. data/ext/yajl_bytestack.h +85 -0
  40. data/ext/yajl_encode.c +188 -0
  41. data/ext/yajl_encode.h +50 -0
  42. data/ext/yajl_ext.c +911 -0
  43. data/ext/yajl_ext.h +128 -0
  44. data/ext/yajl_gen.c +317 -0
  45. data/ext/yajl_lex.c +747 -0
  46. data/ext/yajl_lex.h +135 -0
  47. data/ext/yajl_parser.c +450 -0
  48. data/ext/yajl_parser.h +82 -0
  49. data/lib/yajl/bzip2/stream_reader.rb +32 -0
  50. data/lib/yajl/bzip2/stream_writer.rb +15 -0
  51. data/lib/yajl/bzip2.rb +11 -0
  52. data/lib/yajl/deflate/stream_reader.rb +44 -0
  53. data/lib/yajl/deflate/stream_writer.rb +21 -0
  54. data/lib/yajl/deflate.rb +6 -0
  55. data/lib/yajl/gzip/stream_reader.rb +31 -0
  56. data/lib/yajl/gzip/stream_writer.rb +14 -0
  57. data/lib/yajl/gzip.rb +6 -0
  58. data/lib/yajl/http_stream.rb +197 -0
  59. data/lib/yajl/json_gem/encoding.rb +50 -0
  60. data/lib/yajl/json_gem/parsing.rb +27 -0
  61. data/lib/yajl/json_gem.rb +14 -0
  62. data/lib/yajl.rb +93 -0
  63. data/spec/encoding/encoding_spec.rb +234 -0
  64. data/spec/global/global_spec.rb +55 -0
  65. data/spec/http/fixtures/http.bzip2.dump +0 -0
  66. data/spec/http/fixtures/http.chunked.dump +11 -0
  67. data/spec/http/fixtures/http.deflate.dump +0 -0
  68. data/spec/http/fixtures/http.error.dump +12 -0
  69. data/spec/http/fixtures/http.gzip.dump +0 -0
  70. data/spec/http/fixtures/http.html.dump +1220 -0
  71. data/spec/http/fixtures/http.raw.dump +1226 -0
  72. data/spec/http/http_delete_spec.rb +99 -0
  73. data/spec/http/http_error_spec.rb +33 -0
  74. data/spec/http/http_get_spec.rb +110 -0
  75. data/spec/http/http_post_spec.rb +124 -0
  76. data/spec/http/http_put_spec.rb +106 -0
  77. data/spec/json_gem_compatibility/compatibility_spec.rb +203 -0
  78. data/spec/parsing/active_support_spec.rb +64 -0
  79. data/spec/parsing/chunked_spec.rb +98 -0
  80. data/spec/parsing/fixtures/fail.15.json +1 -0
  81. data/spec/parsing/fixtures/fail.16.json +1 -0
  82. data/spec/parsing/fixtures/fail.17.json +1 -0
  83. data/spec/parsing/fixtures/fail.26.json +1 -0
  84. data/spec/parsing/fixtures/fail11.json +1 -0
  85. data/spec/parsing/fixtures/fail12.json +1 -0
  86. data/spec/parsing/fixtures/fail13.json +1 -0
  87. data/spec/parsing/fixtures/fail14.json +1 -0
  88. data/spec/parsing/fixtures/fail19.json +1 -0
  89. data/spec/parsing/fixtures/fail20.json +1 -0
  90. data/spec/parsing/fixtures/fail21.json +1 -0
  91. data/spec/parsing/fixtures/fail22.json +1 -0
  92. data/spec/parsing/fixtures/fail23.json +1 -0
  93. data/spec/parsing/fixtures/fail24.json +1 -0
  94. data/spec/parsing/fixtures/fail25.json +1 -0
  95. data/spec/parsing/fixtures/fail27.json +2 -0
  96. data/spec/parsing/fixtures/fail28.json +2 -0
  97. data/spec/parsing/fixtures/fail3.json +1 -0
  98. data/spec/parsing/fixtures/fail4.json +1 -0
  99. data/spec/parsing/fixtures/fail5.json +1 -0
  100. data/spec/parsing/fixtures/fail6.json +1 -0
  101. data/spec/parsing/fixtures/fail9.json +1 -0
  102. data/spec/parsing/fixtures/pass.array.json +6 -0
  103. data/spec/parsing/fixtures/pass.codepoints_from_unicode_org.json +1 -0
  104. data/spec/parsing/fixtures/pass.contacts.json +1 -0
  105. data/spec/parsing/fixtures/pass.db100.xml.json +1 -0
  106. data/spec/parsing/fixtures/pass.db1000.xml.json +1 -0
  107. data/spec/parsing/fixtures/pass.dc_simple_with_comments.json +11 -0
  108. data/spec/parsing/fixtures/pass.deep_arrays.json +1 -0
  109. data/spec/parsing/fixtures/pass.difficult_json_c_test_case.json +1 -0
  110. data/spec/parsing/fixtures/pass.difficult_json_c_test_case_with_comments.json +1 -0
  111. data/spec/parsing/fixtures/pass.doubles.json +1 -0
  112. data/spec/parsing/fixtures/pass.empty_array.json +1 -0
  113. data/spec/parsing/fixtures/pass.empty_string.json +1 -0
  114. data/spec/parsing/fixtures/pass.escaped_bulgarian.json +4 -0
  115. data/spec/parsing/fixtures/pass.escaped_foobar.json +1 -0
  116. data/spec/parsing/fixtures/pass.item.json +1 -0
  117. data/spec/parsing/fixtures/pass.json-org-sample1.json +23 -0
  118. data/spec/parsing/fixtures/pass.json-org-sample2.json +11 -0
  119. data/spec/parsing/fixtures/pass.json-org-sample3.json +26 -0
  120. data/spec/parsing/fixtures/pass.json-org-sample4-nows.json +88 -0
  121. data/spec/parsing/fixtures/pass.json-org-sample4.json +89 -0
  122. data/spec/parsing/fixtures/pass.json-org-sample5.json +27 -0
  123. data/spec/parsing/fixtures/pass.map-spain.xml.json +1 -0
  124. data/spec/parsing/fixtures/pass.ns-invoice100.xml.json +1 -0
  125. data/spec/parsing/fixtures/pass.ns-soap.xml.json +1 -0
  126. data/spec/parsing/fixtures/pass.numbers-fp-4k.json +6 -0
  127. data/spec/parsing/fixtures/pass.numbers-fp-64k.json +61 -0
  128. data/spec/parsing/fixtures/pass.numbers-int-4k.json +11 -0
  129. data/spec/parsing/fixtures/pass.numbers-int-64k.json +154 -0
  130. data/spec/parsing/fixtures/pass.twitter-search.json +1 -0
  131. data/spec/parsing/fixtures/pass.twitter-search2.json +1 -0
  132. data/spec/parsing/fixtures/pass.unicode.json +3315 -0
  133. data/spec/parsing/fixtures/pass.yelp.json +1 -0
  134. data/spec/parsing/fixtures/pass1.json +56 -0
  135. data/spec/parsing/fixtures/pass2.json +1 -0
  136. data/spec/parsing/fixtures/pass3.json +6 -0
  137. data/spec/parsing/fixtures_spec.rb +41 -0
  138. data/spec/parsing/one_off_spec.rb +81 -0
  139. data/spec/rcov.opts +3 -0
  140. data/spec/spec.opts +2 -0
  141. data/spec/spec_helper.rb +16 -0
  142. data/yajl-ruby.gemspec +203 -0
  143. metadata +232 -0
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Bzip2
4
+ # This is a wrapper around Bzip::Reader to allow it's #read method to adhere
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Bzip2::Reader
7
+ # A helper method to allow use similar to IO#read
8
+ def read(len=nil, buffer=nil)
9
+ if val = super(len)
10
+ unless buffer.nil?
11
+ buffer.replace(val)
12
+ return buffer
13
+ end
14
+ super(len)
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ # Helper method for one-off parsing from a bzip2-compressed stream
21
+ #
22
+ # See Yajl::Parser#parse for parameter documentation
23
+ def self.parse(input, options={}, buffer_size=nil, &block)
24
+ if input.is_a?(String)
25
+ input = StringIO.new(input)
26
+ end
27
+
28
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Bzip2
4
+ # A wrapper around the Bzip2::Writer class for easier JSON stream encoding
5
+ class StreamWriter < ::Bzip2::Writer
6
+
7
+ # A helper method for encoding to a bzip2-compressed stream
8
+ #
9
+ # Look up Yajl::Encoder#encode for parameter documentation
10
+ def self.encode(obj, io)
11
+ Yajl::Encoder.new.encode(obj, new(io))
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/yajl/bzip2.rb ADDED
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+
5
+ begin
6
+ require 'bzip2' unless defined?(Bzip2)
7
+ require 'yajl/bzip2/stream_reader.rb'
8
+ require 'yajl/bzip2/stream_writer.rb'
9
+ rescue LoadError => e
10
+ raise "Unable to load the bzip2 library. Is the bzip2-ruby gem installed?"
11
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Deflate
4
+ # This is a wrapper around Zlib::Inflate, creating a #read method that adheres
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Zlib::Inflate
7
+
8
+ # Wrapper to the initialize method so we can set the initial IO to parse from.
9
+ def initialize(io, options)
10
+ @io = io
11
+ super(options)
12
+ end
13
+
14
+ # A helper method to allow use similar to IO#read
15
+ def read(len=nil, buffer=nil)
16
+ if val = @io.read(len)
17
+ unless buffer.nil?
18
+ buffer.replace(inflate(val))
19
+ return buffer
20
+ end
21
+ inflate(@io.read(len))
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ # Helper method for one-off parsing from a deflate-compressed stream
28
+ #
29
+ # See Yajl::Parser#parse for parameter documentation
30
+ def self.parse(input, options={}, buffer_size=nil, &block)
31
+ if input.is_a?(String)
32
+ input = StringIO.new(input)
33
+ end
34
+
35
+ if options.is_a?(Hash)
36
+ deflate_options = options.delete(:deflate_options)
37
+ Yajl::Parser.new(options).parse(new(input, deflate_options), buffer_size, &block)
38
+ elsif options.is_a?(Fixnum)
39
+ Yajl::Parser.new.parse(new(input, options), buffer_size, &block)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Deflate
4
+ # A wrapper around the Zlib::Deflate class for easier JSON stream parsing
5
+ class StreamWriter < ::Zlib::Deflate
6
+
7
+ # A helper method to allow use similar to IO#write
8
+ def write(str)
9
+ deflate(str)
10
+ str.size unless str.nil?
11
+ end
12
+
13
+ # A helper method for one-off encoding to a deflate-compressed stream
14
+ #
15
+ # Look up Yajl::Encoder#encode for parameter documentation
16
+ def self.encode(obj, io)
17
+ Yajl::Encoder.new.encode(obj, new(io))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+ require 'zlib' unless defined?(Zlib)
5
+ require 'yajl/deflate/stream_reader.rb'
6
+ require 'yajl/deflate/stream_writer.rb'
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Gzip
4
+ # This is a wrapper around Zlib::GzipReader to allow it's #read method to adhere
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Zlib::GzipReader
7
+ # A helper method to allow use similar to IO#read
8
+ def read(len=nil, buffer=nil)
9
+ if val = super(len)
10
+ unless buffer.nil?
11
+ buffer.replace(val)
12
+ return buffer
13
+ end
14
+ super(len)
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ # Helper method for one-off parsing from a gzip-compressed stream
21
+ #
22
+ # See Yajl::Parser#parse for parameter documentation
23
+ def self.parse(input, options={}, buffer_size=nil, &block)
24
+ if input.is_a?(String)
25
+ input = StringIO.new(input)
26
+ end
27
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Gzip
4
+ # Wraper around the Zlib::GzipWriter class
5
+ class StreamWriter < ::Zlib::GzipWriter
6
+ # A helper method for one-off encoding to a gzip-compressed stream
7
+ #
8
+ # Look up Yajl::Encoder#encode for parameter documentation
9
+ def self.encode(obj, io)
10
+ Yajl::Encoder.new.encode(obj, new(io))
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/yajl/gzip.rb ADDED
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+ require 'zlib' unless defined?(Zlib)
5
+ require 'yajl/gzip/stream_reader.rb'
6
+ require 'yajl/gzip/stream_writer.rb'
@@ -0,0 +1,197 @@
1
+ # encoding: UTF-8
2
+ require 'socket' unless defined?(Socket)
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+ require 'uri' unless defined?(URI)
5
+
6
+ module Yajl
7
+ # This module is for making HTTP requests to which the response bodies (and possibly requests in the near future)
8
+ # are streamed directly into Yajl.
9
+ class HttpStream
10
+
11
+ # This Exception is thrown when an HTTP response isn't in ALLOWED_MIME_TYPES
12
+ # and therefore cannot be parsed.
13
+ class InvalidContentType < Exception; end
14
+ class HttpError < StandardError
15
+
16
+ attr_reader :message, :headers
17
+
18
+ def initialize(message, headers)
19
+ @message = message
20
+ @headers = headers
21
+ end
22
+ end
23
+
24
+ # The mime-type we expect the response to be. If it's anything else, we can't parse it
25
+ # and an InvalidContentType is raised.
26
+ ALLOWED_MIME_TYPES = ["application/json", "text/plain"]
27
+
28
+ # Makes a basic HTTP GET request to the URI provided
29
+ def self.get(uri, opts = {}, &block)
30
+ request("GET", uri, opts, &block)
31
+ end
32
+
33
+ # Makes a basic HTTP GET request to the URI provided allowing the user to terminate the connection
34
+ def get(uri, opts = {}, &block)
35
+ initialize_socket(uri, opts)
36
+ HttpStream::get(uri, opts, &block)
37
+ rescue IOError => e
38
+ raise e unless @intentional_termination
39
+ end
40
+
41
+ # Makes a basic HTTP POST request to the URI provided
42
+ def self.post(uri, body, opts = {}, &block)
43
+ request("POST", uri, opts.merge({:body => body}), &block)
44
+ end
45
+
46
+ # Makes a basic HTTP POST request to the URI provided allowing the user to terminate the connection
47
+ def post(uri, body, opts = {}, &block)
48
+ initialize_socket(uri, opts)
49
+ HttpStream::post(uri, body, opts, &block)
50
+ rescue IOError => e
51
+ raise e unless @intentional_termination
52
+ end
53
+
54
+ # Makes a basic HTTP PUT request to the URI provided
55
+ def self.put(uri, body, opts = {}, &block)
56
+ request("PUT", uri, opts.merge({:body => body}), &block)
57
+ end
58
+
59
+ # Makes a basic HTTP PUT request to the URI provided allowing the user to terminate the connection
60
+ def put(uri, body, opts = {}, &block)
61
+ initialize_socket(uri, opts)
62
+ HttpStream::put(uri, body, opts, &block)
63
+ rescue IOError => e
64
+ raise e unless @intentional_termination
65
+ end
66
+
67
+ # Makes a basic HTTP DELETE request to the URI provided
68
+ def self.delete(uri, opts = {}, &block)
69
+ request("DELETE", uri, opts, &block)
70
+ end
71
+
72
+ # Makes a basic HTTP DELETE request to the URI provided allowing the user to terminate the connection
73
+ def delete(uri, opts = {}, &block)
74
+ initialize_socket(uri, opts)
75
+ HttpStream::delete(uri, opts, &block)
76
+ rescue IOError => e
77
+ raise e unless @intentional_termination
78
+ end
79
+
80
+ # Terminate a running HTTPStream instance
81
+ def terminate
82
+ @intentional_termination = true
83
+ @socket.close
84
+ end
85
+
86
+ protected
87
+ def self.request(method, uri, opts = {}, &block)
88
+ if uri.is_a?(String)
89
+ uri = URI.parse(uri)
90
+ end
91
+
92
+ user_agent = opts.has_key?('User-Agent') ? opts.delete(['User-Agent']) : "Yajl::HttpStream #{Yajl::VERSION}"
93
+ if method == "POST" || method == "PUT"
94
+ content_type = opts.has_key?('Content-Type') ? opts.delete(['Content-Type']) : "application/x-www-form-urlencoded"
95
+ body = opts.delete(:body)
96
+ if body.is_a?(Hash)
97
+ body = body.keys.collect {|param| "#{URI.escape(param.to_s)}=#{URI.escape(body[param].to_s)}"}.join('&')
98
+ end
99
+ end
100
+
101
+ socket = opts.has_key?(:socket) ? opts.delete(:socket) : TCPSocket.new(uri.host, uri.port)
102
+ request = "#{method} #{uri.path}#{uri.query ? "?"+uri.query : nil} HTTP/1.1\r\n"
103
+ request << "Host: #{uri.host}\r\n"
104
+ request << "Authorization: Basic #{[uri.userinfo].pack('m').strip!}\r\n" unless uri.userinfo.nil?
105
+ request << "User-Agent: #{user_agent}\r\n"
106
+ request << "Accept: */*\r\n"
107
+ if method == "POST" || method == "PUT"
108
+ request << "Content-Length: #{body.length}\r\n"
109
+ request << "Content-Type: #{content_type}\r\n"
110
+ end
111
+ encodings = []
112
+ encodings << "bzip2" if defined?(Yajl::Bzip2)
113
+ encodings << "gzip" if defined?(Yajl::Gzip)
114
+ encodings << "deflate" if defined?(Yajl::Deflate)
115
+ request << "Accept-Encoding: #{encodings.join(',')}\r\n" if encodings.any?
116
+ request << "Accept-Charset: utf-8\r\n\r\n"
117
+ if method == "POST" || method == "PUT"
118
+ request << body
119
+ end
120
+ socket.write(request)
121
+ response_head = {}
122
+ response_head[:headers] = {}
123
+
124
+ socket.each_line do |line|
125
+ if line == "\r\n" # end of the headers
126
+ break
127
+ else
128
+ header = line.split(": ")
129
+ if header.size == 1
130
+ header = header[0].split(" ")
131
+ response_head[:version] = header[0]
132
+ response_head[:code] = header[1].to_i
133
+ response_head[:msg] = header[2]
134
+ # this is the response code line
135
+ else
136
+ response_head[:headers][header[0]] = header[1].strip
137
+ end
138
+ end
139
+ end
140
+
141
+ if (response_head[:code] != 200)
142
+ raise HttpError.new("Code 200 expected got #{response_head[:code]}", response_head[:headers])
143
+ end
144
+
145
+ parser = Yajl::Parser.new(opts)
146
+ parser.on_parse_complete = block if block_given?
147
+ if response_head[:headers]["Transfer-Encoding"] == 'chunked'
148
+ if block_given?
149
+ chunkLeft = 0
150
+ while !socket.eof? && (line = socket.gets)
151
+ break if line.match /0.*?\r\n/
152
+ next if line == "\r\n"
153
+ size = line.hex
154
+ json = socket.read(size)
155
+ next if json.nil?
156
+ chunkLeft = size-json.size
157
+ if chunkLeft == 0
158
+ parser << json
159
+ else
160
+ # received only part of the chunk, grab the rest
161
+ parser << socket.read(chunkLeft)
162
+ end
163
+ end
164
+ else
165
+ raise Exception, "Chunked responses detected, but no block given to handle the chunks."
166
+ end
167
+ else
168
+ content_type = response_head[:headers]["Content-Type"].split(';')
169
+ content_type = content_type.first
170
+ if ALLOWED_MIME_TYPES.include?(content_type)
171
+ case response_head[:headers]["Content-Encoding"]
172
+ when "gzip"
173
+ return Yajl::Gzip::StreamReader.parse(socket, opts, &block)
174
+ when "deflate"
175
+ return Yajl::Deflate::StreamReader.parse(socket, opts.merge({:deflate_options => -Zlib::MAX_WBITS}), &block)
176
+ when "bzip2"
177
+ return Yajl::Bzip2::StreamReader.parse(socket, opts, &block)
178
+ else
179
+ return parser.parse(socket)
180
+ end
181
+ else
182
+ raise InvalidContentType, "The response MIME type #{content_type}"
183
+ end
184
+ end
185
+ ensure
186
+ socket.close if !socket.nil? and !socket.closed?
187
+ end
188
+
189
+ private
190
+ # Initialize socket and add it to the opts
191
+ def initialize_socket(uri, opts = {})
192
+ @socket = TCPSocket.new(uri.host, uri.port)
193
+ opts.merge!({:socket => @socket})
194
+ @intentional_termination = false
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+ require 'yajl' unless defined?(Yajl::Parser)
3
+
4
+ # NOTE: this is probably temporary until I can split out the JSON compat C code into it's own
5
+ # extension that can be included when this file is.
6
+ Yajl::Encoder.enable_json_gem_compatability
7
+
8
+ # Our fallback to_json definition
9
+ class Object
10
+ def to_json(*args, &block)
11
+ "\"#{to_s}\""
12
+ end
13
+ end
14
+
15
+ module JSON
16
+ class JSONError < StandardError; end unless defined?(JSON::JSONError)
17
+ class GeneratorError < JSONError; end unless defined?(JSON::GeneratorError)
18
+
19
+ def self.generate(obj, opts={})
20
+ begin
21
+ options_map = {}
22
+ if opts.has_key?(:indent)
23
+ options_map[:pretty] = true
24
+ options_map[:indent] = opts[:indent]
25
+ end
26
+ Yajl::Encoder.encode(obj, options_map)
27
+ rescue Yajl::EncodeError => e
28
+ raise JSON::GeneratorError, e.message
29
+ end
30
+ end
31
+
32
+ def self.pretty_generate(obj, opts={})
33
+ begin
34
+ options_map = {}
35
+ options_map[:pretty] = true
36
+ options_map[:indent] = opts[:indent] if opts.has_key?(:indent)
37
+ Yajl::Encoder.encode(obj, options_map)
38
+ rescue Yajl::EncodeError => e
39
+ raise JSON::GeneratorError, e.message
40
+ end
41
+ end
42
+
43
+ def self.dump(obj, io=nil, *args)
44
+ begin
45
+ Yajl::Encoder.encode(obj, io)
46
+ rescue Yajl::EncodeError => e
47
+ raise JSON::GeneratorError, e.message
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+ require 'yajl' unless defined?(Yajl::Parser)
3
+
4
+ module JSON
5
+ class JSONError < StandardError; end unless defined?(JSON::JSONError)
6
+ class ParserError < JSONError; end unless defined?(JSON::ParserError)
7
+
8
+ def self.default_options
9
+ @default_options ||= {:symbolize_keys => false}
10
+ end
11
+
12
+ def self.parse(str, opts=JSON.default_options)
13
+ begin
14
+ Yajl::Parser.parse(str, opts)
15
+ rescue Yajl::ParseError => e
16
+ raise JSON::ParserError, e.message
17
+ end
18
+ end
19
+
20
+ def self.load(input, *args)
21
+ begin
22
+ Yajl::Parser.parse(input, default_options)
23
+ rescue Yajl::ParseError => e
24
+ raise JSON::ParserError, e.message
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ require 'yajl' unless defined?(Yajl::Parser)
3
+ require 'yajl/json_gem/parsing'
4
+ require 'yajl/json_gem/encoding'
5
+
6
+ module ::Kernel
7
+ def JSON(object, opts = {})
8
+ if object.respond_to? :to_s
9
+ JSON.parse(object.to_s, JSON.default_options.merge(opts))
10
+ else
11
+ JSON.generate(object, opts)
12
+ end
13
+ end
14
+ end
data/lib/yajl.rb ADDED
@@ -0,0 +1,93 @@
1
+ # encoding: UTF-8
2
+ require 'yajl_ext'
3
+
4
+ # = Extras
5
+ # We're not going to load these auotmatically, because you might not need them ;)
6
+ #
7
+ # require 'yajl/http_stream.rb' unless defined?(Yajl::HttpStream)
8
+ # require 'yajl/gzip.rb' unless defined?(Yajl::Gzip)
9
+ # require 'yajl/deflate.rb' unless defined?(Yajl::Deflate)
10
+ # require 'yajl/bzip2.rb' unless defined?(Yajl::Bzip2)
11
+
12
+ # = Yajl
13
+ #
14
+ # Ruby bindings to the excellent Yajl (Yet Another JSON Parser) ANSI C library.
15
+ module Yajl
16
+ VERSION = "0.7.6"
17
+
18
+ # For compatibility, has the same signature of Yajl::Parser.parse
19
+ def self.load(str_or_io, options={}, read_bufsize=nil, &block)
20
+ Parser.parse(str_or_io, options, read_bufsize, &block)
21
+ end
22
+
23
+ # For compatibility, has the same signature of Yajl::Encoder.encode
24
+ def self.dump(obj, *args, &block)
25
+ Encoder.encode(obj, args, &block)
26
+ end
27
+
28
+ class Parser
29
+ # A helper method for parse-and-forget use-cases
30
+ #
31
+ # +io+ is the stream to parse JSON from
32
+ #
33
+ # The +options+ hash allows you to set two parsing options - :allow_comments and :check_utf8
34
+ #
35
+ # :allow_comments accepts a boolean will enable/disable checks for in-line comments in the JSON stream
36
+ #
37
+ # :check_utf8 accepts a boolean will enable/disable UTF8 validation for the JSON stream
38
+ def self.parse(str_or_io, options={}, read_bufsize=nil, &block)
39
+ new(options).parse(str_or_io, read_bufsize, &block)
40
+ end
41
+ end
42
+
43
+ class Encoder
44
+ # A helper method for encode-and-forget use-cases
45
+ #
46
+ # Examples:
47
+ # Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t", &block])
48
+ #
49
+ # output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t", &block])
50
+ #
51
+ # +obj+ is a ruby object to encode to JSON format
52
+ #
53
+ # +io+ is the optional IO stream to encode the ruby object to.
54
+ # If +io+ isn't passed, the resulting JSON string is returned. If +io+ is passed, nil is returned.
55
+ #
56
+ # The +options+ hash allows you to set two encoding options - :pretty and :indent
57
+ #
58
+ # :pretty accepts a boolean and will enable/disable "pretty printing" the resulting output
59
+ #
60
+ # :indent accepts a string and will be used as the indent character(s) during the pretty print process
61
+ #
62
+ # If a block is passed, it will be used as (and work the same as) the +on_progress+ callback
63
+ def self.encode(obj, *args, &block)
64
+ # TODO: this code smells, any ideas?
65
+ args.flatten!
66
+ options = {}
67
+ io = nil
68
+ args.each do |arg|
69
+ if arg.is_a?(Hash)
70
+ options = arg
71
+ elsif arg.respond_to?(:read)
72
+ io = arg
73
+ end
74
+ end if args.any?
75
+ new(options).encode(obj, io, &block)
76
+ end
77
+ end
78
+
79
+ # DEPRECATED - See Yajl::Parser and Yajl::Encoder
80
+ module Stream
81
+ # DEPRECATED - See Yajl::Parser
82
+ def self.parse(str_or_io)
83
+ warn "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Parser class instead."
84
+ Parser.new.parse(str_or_io)
85
+ end
86
+
87
+ # DEPRECATED - See Yajl::Encoder
88
+ def self.encode(obj, str_or_io=nil)
89
+ warn "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Encoder class instead."
90
+ Encoder.new.encode(obj, str_or_io)
91
+ end
92
+ end
93
+ end