yaji 0.2.3-x86-mingw32

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.
@@ -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