yaji 0.2.3-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ /*
2
+ * Copyright 2010, Lloyd Hilaiel.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions are
6
+ * met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in
13
+ * the documentation and/or other materials provided with the
14
+ * distribution.
15
+ *
16
+ * 3. Neither the name of Lloyd Hilaiel nor the names of its
17
+ * contributors may be used to endorse or promote products derived
18
+ * from this software without specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ #ifndef __YAJL_PARSER_H__
34
+ #define __YAJL_PARSER_H__
35
+
36
+ #include "api/yajl_parse.h"
37
+ #include "yajl_bytestack.h"
38
+ #include "yajl_buf.h"
39
+
40
+
41
+ typedef enum {
42
+ yajl_state_start = 0,
43
+ yajl_state_parse_complete,
44
+ yajl_state_parse_error,
45
+ yajl_state_lexical_error,
46
+ yajl_state_map_start,
47
+ yajl_state_map_sep,
48
+ yajl_state_map_need_val,
49
+ yajl_state_map_got_val,
50
+ yajl_state_map_need_key,
51
+ yajl_state_array_start,
52
+ yajl_state_array_got_val,
53
+ yajl_state_array_need_val
54
+ } yajl_state;
55
+
56
+ struct yajl_handle_t {
57
+ const yajl_callbacks * callbacks;
58
+ void * ctx;
59
+ yajl_lexer lexer;
60
+ const char * parseError;
61
+ /* the number of bytes consumed from the last client buffer,
62
+ * in the case of an error this will be an error offset, in the
63
+ * case of an error this can be used as the error offset */
64
+ unsigned int bytesConsumed;
65
+ /* temporary storage for decoded strings */
66
+ yajl_buf decodeBuf;
67
+ /* a stack of states. access with yajl_state_XXX routines */
68
+ yajl_bytestack stateStack;
69
+ /* memory allocation routines */
70
+ yajl_alloc_funcs alloc;
71
+ };
72
+
73
+ YAJL_API
74
+ yajl_status
75
+ yajl_do_parse(yajl_handle handle, const unsigned char * jsonText,
76
+ unsigned int jsonTextLen);
77
+
78
+ YAJL_API
79
+ unsigned char *
80
+ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
81
+ unsigned int jsonTextLen, int verbose);
82
+
83
+
84
+ #endif
@@ -0,0 +1,7 @@
1
+ #include "api/yajl_version.h"
2
+
3
+ int yajl_version(void)
4
+ {
5
+ return YAJL_VERSION;
6
+ }
7
+
data/lib/yaji.rb ADDED
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Couchbase <info@couchbase.com>
4
+ # Copyright:: 2011 Couchbase, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require "yaji/version"
21
+ require "parser_ext"
22
+
23
+ module YAJI
24
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Couchbase <info@couchbase.com>
4
+ # Copyright:: 2011 Couchbase, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ module YAJI
21
+ VERSION = "0.2.3"
22
+ end
@@ -0,0 +1,45 @@
1
+ gem 'rake-compiler', '>= 0.7.5'
2
+ require "rake/extensiontask"
3
+
4
+ def gemspec
5
+ @clean_gemspec ||= eval(File.read(File.expand_path('../../yaji.gemspec', __FILE__)))
6
+ end
7
+
8
+ Rake::ExtensionTask.new("parser_ext", gemspec) do |ext|
9
+ ext.ext_dir = File.join('ext', 'yaji')
10
+
11
+ ext.cross_compile = true
12
+ ext.cross_platform = [ENV['HOST'] || "i386-mingw32"]
13
+ if ENV['RUBY_CC_VERSION']
14
+ ext.lib_dir = "lib/couchbase"
15
+ end
16
+ ext.cross_compiling do |spec|
17
+ spec.files.delete("lib/yaji/parser_ext.so")
18
+ spec.files.push("lib/parser_ext.rb", Dir["lib/yaji/1.{8,9}/parser_ext.so"])
19
+ end
20
+
21
+ # clean compiled extension
22
+ CLEAN.include "#{ext.lib_dir}/*.#{RbConfig::CONFIG['DLEXT']}"
23
+ end
24
+
25
+ require 'rubygems/package_task'
26
+ Gem::PackageTask.new(gemspec) do |pkg|
27
+ pkg.need_tar = true
28
+ end
29
+
30
+ file "lib/parser_ext.rb" do
31
+ File.open("lib/parser_ext.rb", 'wb') do |f|
32
+ f.write <<-RUBY
33
+ require "yaji/\#{RUBY_VERSION.sub(/\\.\\d+$/, '')}/parser_ext"
34
+ RUBY
35
+ end
36
+ end
37
+
38
+ task :cross => "lib/parser_ext.rb"
39
+
40
+ desc "Package gem for windows"
41
+ task "package:windows" => :package do
42
+ sh("env RUBY_CC_VERSION=1.8.7 rvm 1.8.7 do bundle exec rake cross compile")
43
+ sh("env RUBY_CC_VERSION=1.9.2 rvm 1.9.2 do bundle exec rake cross compile")
44
+ sh("env RUBY_CC_VERSION=1.8.7:1.9.2 rvm 1.9.2 do bundle exec rake cross native gem")
45
+ end
data/tasks/test.rake ADDED
@@ -0,0 +1,7 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new do |test|
3
+ test.libs << "test" << "."
4
+ test.ruby_opts << "-rruby-debug" if ENV['DEBUG']
5
+ test.pattern = 'test/test_*.rb'
6
+ test.verbose = true
7
+ end
data/tasks/util.rake ADDED
@@ -0,0 +1,4 @@
1
+ desc 'Start an irb session and load the library.'
2
+ task :console do
3
+ exec "irb -I lib -rruby-debug -ryaji"
4
+ end
data/test/fixture.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "total_rows": 4,
3
+ "offset": 0,
4
+ "rows": [
5
+ {
6
+ "id": "3e8ceb5873cf91fa418c2ab5a0000386",
7
+ "key": "3e8ceb5873cf91fa418c2ab5a0000386",
8
+ "value": {
9
+ "_id": "3e8ceb5873cf91fa418c2ab5a0000386",
10
+ "_rev": "1-967a00dff5e02add41819138abb3284d"
11
+ }
12
+ },
13
+ {
14
+ "id": "biking",
15
+ "key": "biking",
16
+ "value": {
17
+ "_id": "biking",
18
+ "_rev": "2-d4b43fd051e693a5191db2eaba89903c",
19
+ "title": "Biking2",
20
+ "body": "My biggest hobby is mountainbiking. The other day...",
21
+ "date": "2009/01/30 18:04:11"
22
+ }
23
+ },
24
+ {
25
+ "id": "bought-a-cat",
26
+ "key": "bought-a-cat",
27
+ "value": {
28
+ "_id": "bought-a-cat",
29
+ "_rev": "1-ccd0a7e95ade7eb2d29cc02ffa582b30",
30
+ "title": "Bought a Cat",
31
+ "body": "I went to the the pet store earlier and brought home a little kitty...",
32
+ "date": "2009/02/17 21:13:39"
33
+ }
34
+ },
35
+ {
36
+ "id": "hello-world",
37
+ "key": "hello-world",
38
+ "value": {
39
+ "_id": "hello-world",
40
+ "_rev": "1-97dd85b06c25328a300f3f4041def370",
41
+ "title": "Hello World",
42
+ "body": "Well hello and welcome to my new blog...",
43
+ "date": "2009/01/15 15:52:20"
44
+ }
45
+ }
46
+ ]
47
+ }
@@ -0,0 +1,275 @@
1
+ require 'minitest/autorun'
2
+ require 'yaji'
3
+ require 'curb'
4
+
5
+ class TestParser < MiniTest::Unit::TestCase
6
+
7
+ class Generator
8
+ def initialize(data, options = {})
9
+ @callback = nil
10
+ @options = {:chunk_size => 10}.merge(options)
11
+ @chunks = case data
12
+ when Array
13
+ data
14
+ when String
15
+ count = (data.bytesize / @options[:chunk_size].to_f).ceil
16
+ data.unpack("a#{@options[:chunk_size]}" * count)
17
+ end
18
+ end
19
+
20
+ def on_body
21
+ old = @callback
22
+ if block_given?
23
+ @callback = Proc.new
24
+ end
25
+ old
26
+ end
27
+
28
+ def perform
29
+ size = @options[:chunk_size]
30
+ if @callback
31
+ @chunks.each do |chunk|
32
+ @callback.call(chunk)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def test_it_generates_events
39
+ events = []
40
+ parser = YAJI::Parser.new(toys_json_str)
41
+ parser.parse do |p, e, v|
42
+ events << [p, e, v]
43
+ end
44
+ expected = [
45
+ ["", :start_hash, nil],
46
+ ["", :hash_key, "total_rows"],
47
+ ["/total_rows", :number, 2],
48
+ ["", :hash_key, "rows"],
49
+ ["/rows", :start_array, nil],
50
+ ["/rows/", :start_hash, nil],
51
+ ["/rows/", :hash_key, "id"],
52
+ ["/rows//id", :string, "buzz"],
53
+ ["/rows/", :hash_key, "props"],
54
+ ["/rows//props", :start_hash, nil],
55
+ ["/rows//props", :hash_key, "humanoid"],
56
+ ["/rows//props/humanoid", :boolean, true],
57
+ ["/rows//props", :hash_key, "armed"],
58
+ ["/rows//props/armed", :boolean, true],
59
+ ["/rows//props", :end_hash, nil],
60
+ ["/rows/", :hash_key, "movies"],
61
+ ["/rows//movies", :start_array, nil],
62
+ ["/rows//movies/", :number, 1],
63
+ ["/rows//movies/", :number, 2],
64
+ ["/rows//movies/", :number, 3],
65
+ ["/rows//movies", :end_array, nil],
66
+ ["/rows/", :end_hash, nil],
67
+ ["/rows/", :start_hash, nil],
68
+ ["/rows/", :hash_key, "id"],
69
+ ["/rows//id", :string, "barbie"],
70
+ ["/rows/", :hash_key, "props"],
71
+ ["/rows//props", :start_hash, nil],
72
+ ["/rows//props", :hash_key, "humanoid"],
73
+ ["/rows//props/humanoid", :boolean, true],
74
+ ["/rows//props", :hash_key, "armed"],
75
+ ["/rows//props/armed", :boolean, false],
76
+ ["/rows//props", :end_hash, nil],
77
+ ["/rows/", :hash_key, "movies"],
78
+ ["/rows//movies", :start_array, nil],
79
+ ["/rows//movies/", :number, 2],
80
+ ["/rows//movies/", :number, 3],
81
+ ["/rows//movies", :end_array, nil],
82
+ ["/rows/", :end_hash, nil],
83
+ ["/rows", :end_array, nil],
84
+ ["", :end_hash, nil]
85
+ ]
86
+ assert_equal expected, events
87
+ end
88
+
89
+ def test_it_yields_enumerator
90
+ parser = YAJI::Parser.new('{"hello":"world"}')
91
+ e = parser.parse
92
+ assert_equal ["", :start_hash, nil], e.next
93
+ assert_equal ["", :hash_key, "hello"], e.next
94
+ assert_equal ["/hello", :string, "world"], e.next
95
+ assert_equal ["", :end_hash, nil], e.next
96
+ assert_raises(StopIteration) { e.next }
97
+ end
98
+
99
+ def test_it_symbolizes_keys
100
+ parser = YAJI::Parser.new('{"hello":"world"}', :symbolize_keys => true)
101
+ e = parser.parse
102
+ expected = [
103
+ ["", :start_hash, nil],
104
+ ["", :hash_key, :hello],
105
+ ["/hello", :string, "world"],
106
+ ["", :end_hash, nil]
107
+ ]
108
+ assert_equal expected, e.to_a
109
+ end
110
+
111
+ def test_it_build_ruby_objects
112
+ parser = YAJI::Parser.new(toys_json_str)
113
+ objects = []
114
+ parser.each do |o|
115
+ objects << o
116
+ end
117
+ expected = [{"total_rows" => 2,
118
+ "rows" => [
119
+ {
120
+ "id" => "buzz",
121
+ "props" => { "humanoid"=> true, "armed"=> true },
122
+ "movies" => [1,2,3]
123
+ },
124
+ {
125
+ "id" => "barbie",
126
+ "props" => { "humanoid"=> true, "armed"=> false },
127
+ "movies" => [2,3]
128
+ }
129
+ ]}]
130
+ assert_equal expected, objects
131
+ end
132
+
133
+ def test_it_yields_whole_array
134
+ parser = YAJI::Parser.new(toys_json_str)
135
+ objects = []
136
+ parser.each("/rows") do |o|
137
+ objects << o
138
+ end
139
+ expected = [[{
140
+ "id" => "buzz",
141
+ "props" => { "humanoid"=> true, "armed"=> true },
142
+ "movies" => [1,2,3]
143
+ },
144
+ {
145
+ "id" => "barbie",
146
+ "props" => { "humanoid"=> true, "armed"=> false },
147
+ "movies" => [2,3]
148
+ }]]
149
+ assert_equal expected, objects
150
+ end
151
+
152
+ def test_it_yeilds_array_contents_row_by_row
153
+ parser = YAJI::Parser.new(toys_json_str)
154
+ objects = []
155
+ parser.each("/rows/") do |o|
156
+ objects << o
157
+ end
158
+ expected = [{
159
+ "id" => "buzz",
160
+ "props" => { "humanoid"=> true, "armed"=> true },
161
+ "movies" => [1,2,3]
162
+ },
163
+ {
164
+ "id" => "barbie",
165
+ "props" => { "humanoid"=> true, "armed"=> false },
166
+ "movies" => [2,3]
167
+ }]
168
+ assert_equal expected, objects
169
+ end
170
+
171
+ def test_it_could_curb_async_approach
172
+ curl = Curl::Easy.new('http://avsej.net/test.json')
173
+ parser = YAJI::Parser.new(curl)
174
+ object = parser.each.to_a.first
175
+ expected = {"foo"=>"bar", "baz"=>{"nums"=>[42, 3.1415]}}
176
+ assert_equal expected, object
177
+ end
178
+
179
+ def test_it_allow_several_selectors
180
+ parser = YAJI::Parser.new(toys_json_str)
181
+ objects = []
182
+ parser.each(["/total_rows", "/rows/"]) do |o|
183
+ objects << o
184
+ end
185
+ expected = [2,
186
+ {
187
+ "id" => "buzz",
188
+ "props" => { "humanoid"=> true, "armed"=> true },
189
+ "movies" => [1,2,3]
190
+ },
191
+ {
192
+ "id" => "barbie",
193
+ "props" => { "humanoid"=> true, "armed"=> false },
194
+ "movies" => [2,3]
195
+ }]
196
+ assert_equal expected, objects
197
+ end
198
+
199
+ def test_it_optionally_yeilds_object_path
200
+ parser = YAJI::Parser.new(toys_json_str)
201
+ objects = []
202
+ parser.each(["/total_rows", "/rows/"], :with_path => true) do |o|
203
+ objects << o
204
+ end
205
+ expected = [["/total_rows", 2],
206
+ ["/rows/", {
207
+ "id" => "buzz",
208
+ "props" => { "humanoid"=> true, "armed"=> true },
209
+ "movies" => [1,2,3]
210
+ }],
211
+ ["/rows/", {
212
+ "id" => "barbie",
213
+ "props" => { "humanoid"=> true, "armed"=> false },
214
+ "movies" => [2,3]
215
+ }]]
216
+ assert_equal expected, objects
217
+ end
218
+
219
+ def test_it_doesnt_raise_exception_on_empty_input
220
+ YAJI::Parser.new("").parse
221
+ YAJI::Parser.new(" ").parse
222
+ YAJI::Parser.new("\n").parse
223
+ YAJI::Parser.new(" \n\n ").parse
224
+ end
225
+
226
+ def test_it_parses_chunked_data
227
+ generator = Generator.new(['{"total_rows":', '0,"offset":0,"rows":[]', '}'])
228
+ iter = YAJI::Parser.new(generator).each(["total_rows", "rows/", "errors/"], :with_path => true)
229
+ begin
230
+ loop do
231
+ iter.next
232
+ end
233
+ rescue StopIteration
234
+ end
235
+ end
236
+
237
+ def test_it_skips_empty_chunks
238
+ generator = Generator.new(['{"total_rows":', '0,"offset":0,"rows":[]', '}', '', nil])
239
+ iter = YAJI::Parser.new(generator).each(["total_rows", "rows/", "errors/"], :with_path => true)
240
+ begin
241
+ loop do
242
+ iter.next
243
+ end
244
+ rescue StopIteration
245
+ end
246
+ end
247
+
248
+ protected
249
+
250
+ def toys_json_str
251
+ <<-JSON
252
+ {
253
+ "total_rows": 2,
254
+ "rows": [
255
+ {
256
+ "id": "buzz",
257
+ "props": {
258
+ "humanoid": true,
259
+ "armed": true
260
+ },
261
+ "movies": [1,2,3]
262
+ },
263
+ {
264
+ "id": "barbie",
265
+ "props": {
266
+ "humanoid": true,
267
+ "armed": false
268
+ },
269
+ "movies": [2,3]
270
+ }
271
+ ]
272
+ }
273
+ JSON
274
+ end
275
+ end