sketchup_json 0.1.0

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,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
+