fjson 0.0.1
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.
- data/CHANGES +5 -0
- data/GPL +340 -0
- data/README +28 -0
- data/Rakefile +83 -0
- data/TODO +0 -0
- data/VERSION +1 -0
- data/lib/extensions/array_ext.so +0 -0
- data/lib/extensions/class.rb +9 -0
- data/lib/extensions/false_class_ext.so +0 -0
- data/lib/extensions/float_ext.so +0 -0
- data/lib/extensions/hash_ext.so +0 -0
- data/lib/extensions/integer_ext.so +0 -0
- data/lib/extensions/kernel.rb +19 -0
- data/lib/extensions/nil_class_ext.so +0 -0
- data/lib/extensions/object_ext.so +0 -0
- data/lib/extensions/string.rb +23 -0
- data/lib/extensions/string_ext.so +0 -0
- data/lib/extensions/true_class_ext.so +0 -0
- data/lib/fjson.rb +190 -0
- data/lib/json/editor.rb +1195 -0
- data/lib/json_ext.so +0 -0
- data/lib/parser.rb +165 -0
- data/lib/state_ext.so +0 -0
- data/spec/array_spec.rb +36 -0
- data/spec/false_class_spec.rb +15 -0
- data/spec/float_spec.rb +15 -0
- data/spec/hash_spec.rb +37 -0
- data/spec/integer_spec.rb +15 -0
- data/spec/nil_class_spec.rb +15 -0
- data/spec/object_spec.rb +17 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/spec_suite.rb +5 -0
- data/spec/state_spec.rb +67 -0
- data/spec/string_spec.rb +15 -0
- data/spec/string_when_not_supporting_unicode_and_kcode_is_not_utf8_json_spec.rb +18 -0
- data/spec/string_when_supporting_unicode_and_kcode_is_not_utf8_json_spec.rb +18 -0
- data/spec/string_with_latin1_character_set_json_spec.rb +54 -0
- data/spec/string_with_utf8_values_when_supporting_unicode_json_spec.rb +36 -0
- data/spec/true_class_spec.rb +15 -0
- metadata +97 -0
data/lib/json_ext.so
ADDED
Binary file
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
module JSON
|
2
|
+
# This class implements the JSON parser that is used to parse a JSON string
|
3
|
+
# into a Ruby data structure.
|
4
|
+
class Parser < StringScanner
|
5
|
+
STRING = /"((?:[^"\\]|\\.)*)"/
|
6
|
+
INTEGER = /-?\d+/
|
7
|
+
FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
|
8
|
+
OBJECT_OPEN = /\{/
|
9
|
+
OBJECT_CLOSE = /\}/
|
10
|
+
ARRAY_OPEN = /\[/
|
11
|
+
ARRAY_CLOSE = /\]/
|
12
|
+
PAIR_DELIMITER = /:/
|
13
|
+
COLLECTION_DELIMITER = /,/
|
14
|
+
TRUE = /true/
|
15
|
+
FALSE = /false/
|
16
|
+
NULL = /null/
|
17
|
+
IGNORE = %r(
|
18
|
+
(?:
|
19
|
+
//[^\n\r]*[\n\r]| # line comments
|
20
|
+
/\* # c-style comments
|
21
|
+
(?:
|
22
|
+
[^*/]| # normal chars
|
23
|
+
/[^*]| # slashes that do not start a nested comment
|
24
|
+
\*[^/]| # asterisks that do not end this comment
|
25
|
+
/(?=\*/) # single slash before this comment's end
|
26
|
+
)*
|
27
|
+
\*/ # the end of this comment
|
28
|
+
|\s+ # whitespaces
|
29
|
+
)+
|
30
|
+
)mx
|
31
|
+
|
32
|
+
UNPARSED = Object.new
|
33
|
+
|
34
|
+
# Parses the current JSON string and returns the complete data structure
|
35
|
+
# as a result.
|
36
|
+
def parse
|
37
|
+
reset
|
38
|
+
until eos?
|
39
|
+
case
|
40
|
+
when scan(ARRAY_OPEN)
|
41
|
+
return parse_array
|
42
|
+
when scan(OBJECT_OPEN)
|
43
|
+
return parse_object
|
44
|
+
when skip(IGNORE)
|
45
|
+
;
|
46
|
+
when !((value = parse_value).equal? UNPARSED)
|
47
|
+
return value
|
48
|
+
else
|
49
|
+
raise ParserError, "source '#{peek(20)}' not in JSON!"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def parse_string
|
57
|
+
if scan(STRING)
|
58
|
+
return '' if self[1].empty?
|
59
|
+
self[1].gsub(/\\(?:[\\bfnrt"]|u([A-Fa-f\d]{4}))/) do
|
60
|
+
case $~[0]
|
61
|
+
when '\\\\' then '\\'
|
62
|
+
when '\\b' then "\b"
|
63
|
+
when '\\f' then "\f"
|
64
|
+
when '\\n' then "\n"
|
65
|
+
when '\\r' then "\r"
|
66
|
+
when '\\t' then "\t"
|
67
|
+
when '\\"' then '"'
|
68
|
+
else
|
69
|
+
if JSON.support_unicode? and $KCODE == 'UTF8'
|
70
|
+
JSON.utf16_to_utf8($~[1])
|
71
|
+
else
|
72
|
+
# if utf8 mode is switched off or unicode not supported, try to
|
73
|
+
# transform unicode \u-notation to bytes directly:
|
74
|
+
$~[1].to_i(16).chr
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
UNPARSED
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_value
|
84
|
+
case
|
85
|
+
when scan(FLOAT)
|
86
|
+
Float(self[0])
|
87
|
+
when scan(INTEGER)
|
88
|
+
Integer(self[0])
|
89
|
+
when scan(TRUE)
|
90
|
+
true
|
91
|
+
when scan(FALSE)
|
92
|
+
false
|
93
|
+
when scan(NULL)
|
94
|
+
nil
|
95
|
+
when (string = parse_string) != UNPARSED
|
96
|
+
string
|
97
|
+
when scan(ARRAY_OPEN)
|
98
|
+
parse_array
|
99
|
+
when scan(OBJECT_OPEN)
|
100
|
+
parse_object
|
101
|
+
else
|
102
|
+
UNPARSED
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_array
|
107
|
+
result = []
|
108
|
+
until eos?
|
109
|
+
case
|
110
|
+
when (value = parse_value) != UNPARSED
|
111
|
+
result << value
|
112
|
+
skip(IGNORE)
|
113
|
+
unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
|
114
|
+
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
|
115
|
+
end
|
116
|
+
when scan(ARRAY_CLOSE)
|
117
|
+
break
|
118
|
+
when skip(IGNORE)
|
119
|
+
;
|
120
|
+
else
|
121
|
+
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_object
|
128
|
+
result = {}
|
129
|
+
until eos?
|
130
|
+
case
|
131
|
+
when (string = parse_string) != UNPARSED
|
132
|
+
skip(IGNORE)
|
133
|
+
unless scan(PAIR_DELIMITER)
|
134
|
+
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
|
135
|
+
end
|
136
|
+
skip(IGNORE)
|
137
|
+
unless (value = parse_value).equal? UNPARSED
|
138
|
+
result[string] = value
|
139
|
+
skip(IGNORE)
|
140
|
+
unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
|
141
|
+
raise ParserError,
|
142
|
+
"expected ',' or '}' in object at '#{peek(20)}'!"
|
143
|
+
end
|
144
|
+
else
|
145
|
+
raise ParserError, "expected value in object at '#{peek(20)}'!"
|
146
|
+
end
|
147
|
+
when scan(OBJECT_CLOSE)
|
148
|
+
if klassname = result['json_class']
|
149
|
+
klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
|
150
|
+
p.const_get(k) rescue nil
|
151
|
+
end
|
152
|
+
break unless klass and klass.json_creatable?
|
153
|
+
result = klass.json_create(result)
|
154
|
+
end
|
155
|
+
break
|
156
|
+
when skip(IGNORE)
|
157
|
+
;
|
158
|
+
else
|
159
|
+
raise ParserError, "unexpected token in object at '#{peek(20)}'!"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
result
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/state_ext.so
ADDED
Binary file
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A one dimensional Array" do
|
8
|
+
specify "should create json" do
|
9
|
+
array = [1, 'hello', :hello]
|
10
|
+
array.to_json.should_equal '[1,"hello","hello"]'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "A multi-dimensional Array" do
|
15
|
+
specify "should create json" do
|
16
|
+
array = [1, ['hello', [:hello]]]
|
17
|
+
array.to_json.should_equal '[1,["hello",["hello"]]]'
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should not support circular dependencies" do
|
21
|
+
array = []
|
22
|
+
array << 1
|
23
|
+
array << ['hello', array]
|
24
|
+
proc {array.to_json}.should_raise JSON::CircularDatastructure
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#context "Array performance" do
|
29
|
+
# specify "" do
|
30
|
+
# begin_time = Time.now
|
31
|
+
# 100000.times do
|
32
|
+
# [1, ['hello', [:hello]]].to_json
|
33
|
+
# end
|
34
|
+
# puts "#{Time.now - begin_time} seconds"
|
35
|
+
# end
|
36
|
+
#end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A false object" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
false.to_json.should_equal "false"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
false.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/float_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A float number" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
(3.14).to_json.should_equal "3.14"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
(3.14).to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A one dimensional Hash" do
|
8
|
+
specify "should create json" do
|
9
|
+
hash = {
|
10
|
+
:one => 1,
|
11
|
+
"hello" => :hello
|
12
|
+
}
|
13
|
+
json = hash.to_json
|
14
|
+
json.should_include "\"one\":1"
|
15
|
+
json.should_include "\"hello\":\"hello\""
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "A multi-dimensional Hash" do
|
20
|
+
specify "should create json" do
|
21
|
+
hash = {
|
22
|
+
:one => 1,
|
23
|
+
"hello" => {
|
24
|
+
:hello2 => 2
|
25
|
+
},
|
26
|
+
}
|
27
|
+
json = hash.to_json
|
28
|
+
json.should_include "\"one\":1"
|
29
|
+
json.should_include "\"hello\":{\"hello2\":2}"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "should not support circular dependencies" do
|
33
|
+
hash = { :one => 1 }
|
34
|
+
hash["hello"] = hash
|
35
|
+
proc {hash.to_json}.should_raise JSON::CircularDatastructure
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "An integer" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
5.to_json.should_equal "5"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
5.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A nil object" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
nil.to_json.should_equal "null"
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
nil.to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|
data/spec/object_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "An object" do
|
8
|
+
specify "should convert its string representation to json" do
|
9
|
+
obj = Object.new
|
10
|
+
obj_str = obj.to_s
|
11
|
+
obj.to_json.should_equal '"' + obj_str + '"'
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "to_json should accept any number of arguments" do
|
15
|
+
Object.new.to_json(1, 2)
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec'
|
2
|
+
dir = File.dirname(__FILE__)
|
3
|
+
args = ARGV.dup
|
4
|
+
|
5
|
+
class Object
|
6
|
+
def metaclass
|
7
|
+
class << self; self; end
|
8
|
+
end
|
9
|
+
|
10
|
+
def define_instance_method(sym, &block)
|
11
|
+
metaclass.__send__(:define_method, sym, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
unless args.include?("-f") || args.include?("--format")
|
16
|
+
args << "--format"
|
17
|
+
args << "specdoc"
|
18
|
+
end
|
19
|
+
args << $0
|
20
|
+
|
21
|
+
$context_runner = ::Spec::Runner::OptionParser.create_context_runner(args, false, STDERR, STDOUT)
|
22
|
+
|
23
|
+
at_exit do
|
24
|
+
unless context_runner.instance_eval {@reporter}.instance_eval {@start_time}
|
25
|
+
context_runner.run(false)
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_suite.rb
ADDED
data/spec/state_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A JSON state" do
|
8
|
+
specify "method from_state should return the passed in object when a state is passed in" do
|
9
|
+
state = JSON::State.new
|
10
|
+
JSON::State.from_state(state).should_equal state
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "method from_state should return a new state with hash values when a hash is passed in" do
|
14
|
+
shim_state_class = Object.new
|
15
|
+
shim_state_class.extend JSON::State::ClassMethods
|
16
|
+
passed_params = nil
|
17
|
+
shim_state_class.define_instance_method(:new) {|params| passed_params = params}
|
18
|
+
|
19
|
+
args = {}
|
20
|
+
shim_state_class.from_state(args)
|
21
|
+
passed_params.should_equal args
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "method from_state should return a new state with default parameters when an object that is not " +
|
25
|
+
"a state or a hash is passed in" do
|
26
|
+
state = JSON::State.from_state(Object.new)
|
27
|
+
state.should_be_instance_of JSON::State
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should initialize its attributes to their defaults" do
|
31
|
+
state = JSON::State.new
|
32
|
+
state.indent.should_equal ""
|
33
|
+
state.space.should_equal ""
|
34
|
+
state.object_nl.should_equal ""
|
35
|
+
state.array_nl.should_equal ""
|
36
|
+
state.instance_eval {@seen}.should_equal({})
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should initialize its attributes with passed in values" do
|
40
|
+
params = {
|
41
|
+
:indent => Object.new,
|
42
|
+
:space => Object.new,
|
43
|
+
:object_nl => Object.new,
|
44
|
+
:array_nl => Object.new
|
45
|
+
}
|
46
|
+
state = JSON::State.new(params)
|
47
|
+
state.indent.should_equal params[:indent]
|
48
|
+
state.space.should_equal params[:space]
|
49
|
+
state.object_nl.should_equal params[:object_nl]
|
50
|
+
state.array_nl.should_equal params[:array_nl]
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should remember objects" do
|
54
|
+
state = JSON::State.new
|
55
|
+
obj = Object.new
|
56
|
+
state.remember(obj)
|
57
|
+
state.should_be_seen(obj)
|
58
|
+
end
|
59
|
+
|
60
|
+
specify "should forget objects" do
|
61
|
+
state = JSON::State.new
|
62
|
+
obj = Object.new
|
63
|
+
state.remember(obj)
|
64
|
+
state.forget(obj)
|
65
|
+
state.should_not_be_seen(obj)
|
66
|
+
end
|
67
|
+
end
|
data/spec/string_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require File.expand_path("#{dir}/spec_helper")
|
5
|
+
require File.expand_path("#{dir}/../lib/fjson")
|
6
|
+
|
7
|
+
context "A string" do
|
8
|
+
specify "should convert to json" do
|
9
|
+
"test".to_json.should_equal '"test"'
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "to_json should accept any number of arguments" do
|
13
|
+
"test".to_json(1, 2)
|
14
|
+
end
|
15
|
+
end
|