sketchup_json 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ module SketchUpJSON
2
+ JSONEncodeError = Class.new(StandardError)
3
+ end
4
+
5
+ class String
6
+ def to_json
7
+ %Q["#{ self.escape_quotes }"]
8
+ end
9
+
10
+ def escape_quotes
11
+ self.gsub('"', '\"')
12
+ end
13
+ end
14
+
15
+ class Numeric
16
+ def to_json
17
+ to_s
18
+ end
19
+ end
20
+
21
+ class TrueClass
22
+ def to_json
23
+ to_s
24
+ end
25
+ end
26
+
27
+ class FalseClass
28
+ def to_json
29
+ to_s
30
+ end
31
+ end
32
+
33
+ class NilClass
34
+ def to_json
35
+ "null"
36
+ end
37
+ end
38
+
39
+ class Array
40
+ def to_json
41
+ values = collect { |item| item.to_json }
42
+ "[#{ values.join(", ") }]"
43
+ end
44
+ end
45
+
46
+ class Hash
47
+ def to_json
48
+ values = collect do |k, v|
49
+ validate_key! k
50
+ %Q["#{k.to_s}" : #{v.to_json}]
51
+ end
52
+ "{#{ values.join(", ") }}"
53
+ end
54
+
55
+ # In valid json all object keys are strings
56
+ def validate_key!(key)
57
+ unless key.is_a?(String) or key.is_a?(Symbol)
58
+ raise SketchUpJSON::JSONEncodeError, "This hash can not generate valid JSON"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,257 @@
1
+ class String
2
+ def char_at(n)
3
+ self[n] and self[n].chr
4
+ end
5
+ end
6
+
7
+ module SketchUpJSON
8
+ SyntaxError = Class.new(StandardError)
9
+
10
+ class Parser
11
+ ESCAPEE = {
12
+ '"' => '"',
13
+ '\\' => '\\',
14
+ '/' => '/',
15
+ 'b' => 'b',
16
+ 'f' => '\f',
17
+ 'n' => '\n',
18
+ 't' => '\t',
19
+ 'r' => '\r',
20
+ }.freeze
21
+
22
+ def initialize(json_string)
23
+ @at, @ch = 0, nil
24
+ @openned_brackets = 0
25
+ @openned_square_brackets = 0
26
+ @openned_quotes = 0
27
+ @text = json_string
28
+ end
29
+
30
+ def parse
31
+ value
32
+ end
33
+
34
+ private
35
+
36
+ def value
37
+ white
38
+ case @ch
39
+ when '{'
40
+ return object
41
+ when '['
42
+ return array
43
+ when '"'
44
+ return string
45
+ when '-', '+', /\d/
46
+ return number
47
+ else
48
+ return word
49
+ end
50
+ raise SyntaxError.new "Invalid syntax. Index #{@at}"
51
+ end
52
+
53
+ def object
54
+ obj = {}
55
+
56
+ if @ch == '{'
57
+ update_delimiter_counter! @ch
58
+ next_char '{'
59
+ white
60
+
61
+ if @ch == '}'
62
+ update_delimiter_counter! @ch
63
+ next_char '}'
64
+ return obj
65
+ end
66
+
67
+ while @ch
68
+ key = string
69
+ white
70
+ next_char ':'
71
+ obj[key] = value
72
+ white
73
+
74
+ if @ch == '}'
75
+ update_delimiter_counter! @ch
76
+ next_char '}'
77
+ return obj
78
+ end
79
+
80
+ next_char ','
81
+ white
82
+ end
83
+ end
84
+
85
+ raise SyntaxError.new "Bad object"
86
+ end
87
+
88
+ def array
89
+ arr = []
90
+
91
+ if @ch == '['
92
+ next_char '['
93
+ white
94
+
95
+ if @ch == ']'
96
+ next_char ']'
97
+ return arr
98
+ end
99
+
100
+ while @ch
101
+ arr.push(value)
102
+ white
103
+
104
+ if @ch == ']'
105
+ next_char ']'
106
+ return arr
107
+ end
108
+
109
+ next_char ','
110
+ white
111
+ end
112
+ end
113
+
114
+ raise SyntaxError.new "Bad array"
115
+ end
116
+
117
+ def string
118
+ str = ''
119
+
120
+ if @ch == '"'
121
+ while next_char
122
+ if @ch == '"'
123
+ next_char
124
+ return str
125
+
126
+ elsif @ch == '\\'
127
+ next_char
128
+
129
+ if @ch == 'u'
130
+ uffff = 0
131
+ 4.times do
132
+ hex = next_char.to_i(16)
133
+ break unless hex.is_a? Numeric
134
+ uffff = uffff * 16 + hex
135
+ end
136
+
137
+ str += uffff.chr
138
+ elsif ESCAPEE[@ch]
139
+ str += ESCAPEE[@ch]
140
+ else
141
+ break
142
+ end
143
+ else
144
+ str += @ch
145
+ end
146
+ end
147
+ end
148
+
149
+ raise SyntaxError.new "Bad string"
150
+ end
151
+
152
+ def number
153
+ string = ''
154
+
155
+ if @ch == '-'
156
+ string = '-'
157
+ next_char '-'
158
+ end
159
+
160
+ while decimal?(@ch)
161
+ string += @ch
162
+ next_char
163
+ end
164
+
165
+ if @ch == '.'
166
+ string += '.'
167
+
168
+ while next_char && decimal?(@ch)
169
+ string += @ch
170
+ end
171
+ end
172
+
173
+ if @ch == 'e' || @ch == 'E'
174
+ string += @ch
175
+ next_char
176
+
177
+ if @ch == '-' || @ch == '+'
178
+ string += @ch
179
+ next_char
180
+ end
181
+
182
+ while decimal?(@ch)
183
+ string += @ch
184
+ next_char
185
+ end
186
+ end
187
+
188
+ raise SyntaxError.new "Bad number" unless number?(string)
189
+ string.send convertion_method(string)
190
+ end
191
+
192
+ def word
193
+ proc = Proc.new { |l| next_char l }
194
+ case @ch
195
+ when 't'
196
+ %w(t r u e).map &proc
197
+ return true
198
+ when 'f'
199
+ %w(f a l s e).map &proc
200
+ return false
201
+ when 'n'
202
+ %w(n u l l).map &proc
203
+ return nil
204
+ end
205
+ raise SyntaxError.new "Unexpected #{@ch}"
206
+ end
207
+
208
+ def next_char(c = nil)
209
+ ensure_ended_chars!
210
+ if c && c != @ch
211
+ raise SyntaxError.new "Expected #{c} instead of #{@ch}"
212
+ else
213
+ @ch = @text.char_at(@at)
214
+ @at += 1
215
+ @ch
216
+ end
217
+ end
218
+
219
+ def convertion_method(str)
220
+ str.index('.') || str.index('e') || str.index('E') ? :to_f : :to_i
221
+ end
222
+
223
+ def number?(str)
224
+ str.match /[-]?([1-9]|(0\.))[0-9]*[eE]?[+-]?[0-9]*/
225
+ end
226
+
227
+ def decimal?(n)
228
+ n && n.match(/\d/)
229
+ end
230
+
231
+ def white
232
+ next_char while @ch.nil? || @ch.strip.empty?
233
+ end
234
+
235
+ def ensure_ended_chars!
236
+ if @at == @text.size
237
+ counters = [@openned_brackets, @openned_square_brackets, @openned_quotes]
238
+ counters.map do |counter|
239
+ raise SyntaxError unless counter.zero?
240
+ end
241
+ end
242
+ end
243
+
244
+ def update_delimiter_counter!(char)
245
+ case char
246
+ when '{'
247
+ @openned_brackets += 1
248
+ when '}'
249
+ @openned_brackets -= 1
250
+ when '['
251
+ @openned_square_brackets +=1
252
+ when ']'
253
+ @openned_square_brackets -=1
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "sketchup_json"
3
+ gem.version = "0.1.0"
4
+ gem.authors = ["Dalto Curvelano Junior", "Superficie.org"]
5
+ gem.description = "A simple JSON parser/generator suited to be used in Google Sketchup"
6
+ gem.summary = "Google Sketchup's Ruby version doesn't have any standard libraries. This gem provides a simple API for parsing and generating JSON data."
7
+
8
+ gem.files = [
9
+ "sketchup_json.rb",
10
+ "sketchup_json.gemspec",
11
+ "lib/sketchup_json/generator.rb",
12
+ "lib/sketchup_json/parser.rb",
13
+ "spec/fixtures/so-alltags.json",
14
+ "spec/sketchup_json/generator_spec.rb",
15
+ "spec/sketchup_json/parser_spec.rb",
16
+ "spec/spec.opts",
17
+ "spec/spec_helper.rb"
18
+ ]
19
+ gem.homepage = ""
20
+ end
@@ -0,0 +1,5 @@
1
+ APP_BASE_PATH = File.expand_path(File.join(File.dirname(__FILE__), "lib"))
2
+ $:.unshift APP_BASE_PATH
3
+
4
+ require "sketchup_json/parser.rb"
5
+ require "sketchup_json/generator.rb"
@@ -0,0 +1,287 @@
1
+ {
2
+ "total": 29199,
3
+ "page": 1,
4
+ "pagesize": 70,
5
+ "tags": [
6
+ {
7
+ "name": "c#",
8
+ "count": 84875
9
+ },
10
+ {
11
+ "name": "java",
12
+ "count": 50661
13
+ },
14
+ {
15
+ "name": ".net",
16
+ "count": 42982
17
+ },
18
+ {
19
+ "name": "php",
20
+ "count": 42359
21
+ },
22
+ {
23
+ "name": "asp.net",
24
+ "count": 38560
25
+ },
26
+ {
27
+ "name": "javascript",
28
+ "count": 37024
29
+ },
30
+ {
31
+ "name": "c++",
32
+ "count": 31709
33
+ },
34
+ {
35
+ "name": "jquery",
36
+ "count": 29607
37
+ },
38
+ {
39
+ "name": "iphone",
40
+ "count": 26442
41
+ },
42
+ {
43
+ "name": "python",
44
+ "count": 25583
45
+ },
46
+ {
47
+ "name": "sql",
48
+ "count": 20885
49
+ },
50
+ {
51
+ "name": "mysql",
52
+ "count": 18581
53
+ },
54
+ {
55
+ "name": "html",
56
+ "count": 17460
57
+ },
58
+ {
59
+ "name": "sql-server",
60
+ "count": 15911
61
+ },
62
+ {
63
+ "name": "ruby-on-rails",
64
+ "count": 14369
65
+ },
66
+ {
67
+ "name": "c",
68
+ "count": 13986
69
+ },
70
+ {
71
+ "name": "asp.net-mvc",
72
+ "count": 13154
73
+ },
74
+ {
75
+ "name": "css",
76
+ "count": 13126
77
+ },
78
+ {
79
+ "name": "wpf",
80
+ "count": 13074
81
+ },
82
+ {
83
+ "name": "objective-c",
84
+ "count": 12652
85
+ },
86
+ {
87
+ "name": "windows",
88
+ "count": 10966
89
+ },
90
+ {
91
+ "name": "xml",
92
+ "count": 10172
93
+ },
94
+ {
95
+ "name": "ruby",
96
+ "count": 10157
97
+ },
98
+ {
99
+ "name": "database",
100
+ "count": 9314
101
+ },
102
+ {
103
+ "name": "best-practices",
104
+ "count": 9280
105
+ },
106
+ {
107
+ "name": "android",
108
+ "count": 9129
109
+ },
110
+ {
111
+ "name": "vb.net",
112
+ "count": 8826
113
+ },
114
+ {
115
+ "name": "visual-studio",
116
+ "count": 8564
117
+ },
118
+ {
119
+ "name": "ajax",
120
+ "count": 8168
121
+ },
122
+ {
123
+ "name": "winforms",
124
+ "count": 8154
125
+ },
126
+ {
127
+ "name": "regex",
128
+ "count": 8120
129
+ },
130
+ {
131
+ "name": "linux",
132
+ "count": 8031
133
+ },
134
+ {
135
+ "name": "django",
136
+ "count": 7724
137
+ },
138
+ {
139
+ "name": "iphone-sdk",
140
+ "count": 7322
141
+ },
142
+ {
143
+ "name": "visual-studio-2008",
144
+ "count": 6996
145
+ },
146
+ {
147
+ "name": "beginner",
148
+ "count": 6687
149
+ },
150
+ {
151
+ "name": "web-development",
152
+ "count": 6485
153
+ },
154
+ {
155
+ "name": "flex",
156
+ "count": 6404
157
+ },
158
+ {
159
+ "name": "subjective",
160
+ "count": 6229
161
+ },
162
+ {
163
+ "name": "flash",
164
+ "count": 6086
165
+ },
166
+ {
167
+ "name": "linq",
168
+ "count": 5998
169
+ },
170
+ {
171
+ "name": "wcf",
172
+ "count": 5865
173
+ },
174
+ {
175
+ "name": "silverlight",
176
+ "count": 5761
177
+ },
178
+ {
179
+ "name": "cocoa",
180
+ "count": 5659
181
+ },
182
+ {
183
+ "name": "actionscript-3",
184
+ "count": 5359
185
+ },
186
+ {
187
+ "name": "sql-server-2005",
188
+ "count": 5329
189
+ },
190
+ {
191
+ "name": "cocoa-touch",
192
+ "count": 5257
193
+ },
194
+ {
195
+ "name": "performance",
196
+ "count": 5236
197
+ },
198
+ {
199
+ "name": "algorithm",
200
+ "count": 5142
201
+ },
202
+ {
203
+ "name": "perl",
204
+ "count": 5081
205
+ },
206
+ {
207
+ "name": "eclipse",
208
+ "count": 5072
209
+ },
210
+ {
211
+ "name": "oracle",
212
+ "count": 5045
213
+ },
214
+ {
215
+ "name": "web-services",
216
+ "count": 4859
217
+ },
218
+ {
219
+ "name": "security",
220
+ "count": 4848
221
+ },
222
+ {
223
+ "name": "svn",
224
+ "count": 4837
225
+ },
226
+ {
227
+ "name": "delphi",
228
+ "count": 4778
229
+ },
230
+ {
231
+ "name": "multithreading",
232
+ "count": 4762
233
+ },
234
+ {
235
+ "name": "sharepoint",
236
+ "count": 4760
237
+ },
238
+ {
239
+ "name": "arrays",
240
+ "count": 4395
241
+ },
242
+ {
243
+ "name": "unit-testing",
244
+ "count": 4292
245
+ },
246
+ {
247
+ "name": "nhibernate",
248
+ "count": 4216
249
+ },
250
+ {
251
+ "name": "linq-to-sql",
252
+ "count": 4171
253
+ },
254
+ {
255
+ "name": "tsql",
256
+ "count": 4031
257
+ },
258
+ {
259
+ "name": "apache",
260
+ "count": 3882
261
+ },
262
+ {
263
+ "name": "string",
264
+ "count": 3790
265
+ },
266
+ {
267
+ "name": "homework",
268
+ "count": 3664
269
+ },
270
+ {
271
+ "name": "excel",
272
+ "count": 3629
273
+ },
274
+ {
275
+ "name": "design",
276
+ "count": 3506
277
+ },
278
+ {
279
+ "name": "xcode",
280
+ "count": 3472
281
+ },
282
+ {
283
+ "name": "mvc",
284
+ "count": 3427
285
+ }
286
+ ]
287
+ }
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe "JSON Generator" do
4
+
5
+ it "should generate json from strings" do
6
+ "text".to_json.should == '"text"'
7
+ 'nested "quotes"'.to_json.should == '"nested \"quotes\""'
8
+ end
9
+
10
+ it "should generate json from integers" do
11
+ 1.to_json.should == "1"
12
+ 10000.to_json.should == "10000"
13
+ end
14
+
15
+ it "should generate json from numerics" do
16
+ 1.4901.to_json.should == "1.4901"
17
+ end
18
+
19
+ it "should generate json from booleans" do
20
+ true.to_json.should == "true"
21
+ false.to_json.should == "false"
22
+ nil.to_json.should == "null"
23
+ end
24
+
25
+ it "should generate json from arrays" do
26
+ [1, 2, 3].to_json.should == "[1, 2, 3]"
27
+ ["oi", "como", "vai?"].to_json.should == '["oi", "como", "vai?"]'
28
+ end
29
+
30
+ it "should generate json from hashes" do
31
+ { "text" => "text", "array" => [1, 2, 3] }.to_json.should == '{"text" : "text", "array" : [1, 2, 3]}'
32
+ { :ratio => 1.534 }.to_json.should == '{"ratio" : 1.534}'
33
+ lambda { { 6 => [] }.to_json }.should raise_exception SketchUpJSON::JSONEncodeError
34
+ end
35
+ end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+ include SketchUpJSON
3
+
4
+ describe "JSON parser" do
5
+ it "should parse keywords" do
6
+ Parser.new("true").parse.should be_true
7
+ Parser.new("false").parse.should be_false
8
+ Parser.new("null").parse.should be_nil
9
+
10
+ lambda { Parser.new("puts").parse }.should raise_exception SketchUpJSON::SyntaxError
11
+ end
12
+
13
+ it "should parse numbers" do
14
+ Parser.new("1").parse.should == 1
15
+ Parser.new("10").parse.should == 10
16
+ Parser.new("12345").parse.should == 12345
17
+ Parser.new("1.1").parse.should == 1.1
18
+ Parser.new("0.1234").parse.should == 0.1234
19
+ Parser.new("-1").parse.should == -1
20
+ Parser.new("-0.98").parse.should == -0.98
21
+ Parser.new("0.2e1").parse.should == 0.2e1
22
+ Parser.new("0.2e+1").parse.should == 0.2e+1
23
+ Parser.new("0.2e-1").parse.should == 0.2e-1
24
+ Parser.new("0.2e1").parse.should == 0.2E1
25
+ end
26
+
27
+ it "should parse arrays" do
28
+ Parser.new("[]").parse.should == []
29
+ Parser.new("[1]").parse.should == [1]
30
+ Parser.new("[1, 3, 4]").parse.should == [1, 3, 4]
31
+ Parser.new("[null, true, false, []]").parse.should == [nil, true, false, []]
32
+ Parser.new("[[], {}, 1]").parse.should == [[], {}, 1]
33
+
34
+ lambda { Parser.new("[;]").parse }.should raise_exception SketchUpJSON::SyntaxError
35
+ end
36
+
37
+ it "should parse objects" do
38
+ Parser.new("{}").parse.should == {}
39
+ Parser.new(%Q[{ "number" : 1, "text" : "text"}]).parse.should == { 'number' => 1, 'text' => 'text' }
40
+ Parser.new(%Q[{ "array" : [], "object" : {}}]).parse.should == { 'array' => [], 'object' => {} }
41
+ Parser.new(%Q[{ "array" : [1, 2, 3], "null" : null}]).parse.should == { 'array' => [1, 2, 3], 'null' => nil }
42
+
43
+ lambda { Parser.new(%Q[{ "t" : }]).parse }.should raise_exception SketchUpJSON::SyntaxError
44
+ end
45
+
46
+ it "should parse strings" do
47
+ Parser.new('""').parse.should == ""
48
+ Parser.new('"hello world"').parse.should == "hello world"
49
+ Parser.new('" "').parse.should == " "
50
+ Parser.new('"abracadabra666"').parse.should == "abracadabra666"
51
+ Parser.new('" }:8)"').parse.should == " }:8)"
52
+ Parser.new('"nested \"quotes\""').parse.should == "nested \"quotes\""
53
+ Parser.new('"\u005C"').parse.should == "\\"
54
+ Parser.new('"\u0022"').parse.should == '"'
55
+ Parser.new('"\u002F"').parse.should == "/"
56
+ Parser.new('"\u0008"').parse.should == "\b"
57
+ Parser.new('"\u000C"').parse.should == "\f"
58
+ Parser.new('"\u000A"').parse.should == "\n"
59
+ Parser.new('"\u000D"').parse.should == "\r"
60
+ Parser.new('"\u0009"').parse.should == "\t"
61
+ end
62
+
63
+ it "should parse BIG json strings" do
64
+ parsed = Parser.new(read_fixture_contents("so-alltags.json")).parse
65
+ parsed['total'].should == 29199
66
+ parsed['tags'].size.should == 70
67
+ end
68
+
69
+ it "should raise an exception when trying to parse a malformed json string" do
70
+ lambda { Parser.new('{{{{}').parse }.should raise_exception SketchUpJSON::SyntaxError
71
+ lambda { Parser.new('}{}').parse }.should raise_exception SketchUpJSON::SyntaxError
72
+ lambda { Parser.new('}[]}').parse }.should raise_exception SketchUpJSON::SyntaxError
73
+ lambda { Parser.new('{[[[}').parse }.should raise_exception SketchUpJSON::SyntaxError
74
+ end
75
+ end
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format specdoc
@@ -0,0 +1,11 @@
1
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
2
+ $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include? lib_path
3
+
4
+ require "spec"
5
+ require "sketchup_json"
6
+
7
+ FIXTURES_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
8
+
9
+ def read_fixture_contents(file_name)
10
+ File.read(FIXTURES_PATH + "/#{file_name}")
11
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sketchup_json
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Dalto Curvelano Junior
14
+ - Superficie.org
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-07-30 00:00:00 -03:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: A simple JSON parser/generator suited to be used in Google Sketchup
24
+ email:
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - sketchup_json.rb
33
+ - sketchup_json.gemspec
34
+ - lib/sketchup_json/generator.rb
35
+ - lib/sketchup_json/parser.rb
36
+ - spec/fixtures/so-alltags.json
37
+ - spec/sketchup_json/generator_spec.rb
38
+ - spec/sketchup_json/parser_spec.rb
39
+ - spec/spec.opts
40
+ - spec/spec_helper.rb
41
+ has_rdoc: true
42
+ homepage: ""
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.7
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Google Sketchup's Ruby version doesn't have any standard libraries. This gem provides a simple API for parsing and generating JSON data.
75
+ test_files: []
76
+