aapis-gist 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ describe Gist::AuthTokenFile do
2
+ subject { Gist::AuthTokenFile }
3
+
4
+ before(:each) do
5
+ stub_const("Gist::URL_ENV_NAME", "STUBBED_GITHUB_URL")
6
+ end
7
+
8
+ describe "::filename" do
9
+ let(:filename) { double() }
10
+
11
+ context "with default GITHUB_URL" do
12
+ it "is ~/.gist" do
13
+ File.should_receive(:expand_path).with("~/.gist").and_return(filename)
14
+ subject.filename.should be filename
15
+ end
16
+ end
17
+
18
+ context "with custom GITHUB_URL" do
19
+ before do
20
+ ENV[Gist::URL_ENV_NAME] = github_url
21
+ end
22
+ let(:github_url) { "http://gh.custom.org:442/" }
23
+
24
+ it "is ~/.gist.{custom_github_url}" do
25
+ File.should_receive(:expand_path).with("~/.gist.http.gh.custom.org.442").and_return(filename)
26
+ subject.filename.should be filename
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ describe "::read" do
33
+ let(:token) { "auth_token" }
34
+
35
+ it "reads file contents" do
36
+ File.should_receive(:read).and_return(token)
37
+ subject.read.should eq token
38
+ end
39
+
40
+ it "chomps file contents" do
41
+ File.should_receive(:read).and_return(token + "\n")
42
+ subject.read.should eq token
43
+ end
44
+ end
45
+
46
+ describe "::write" do
47
+ let(:token) { double() }
48
+ let(:filename) { double() }
49
+ let(:token_file) { double() }
50
+
51
+ before do
52
+ subject.stub(:filename) { filename }
53
+ end
54
+
55
+ it "writes token to file" do
56
+ File.should_receive(:open).with(filename, 'w', 0600).and_yield(token_file)
57
+ token_file.should_receive(:write).with(token)
58
+ subject.write(token)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,40 @@
1
+ describe '...' do
2
+ before do
3
+ @saved_path = ENV['PATH']
4
+ @bobo_url = 'http://example.com'
5
+ end
6
+
7
+ after do
8
+ ENV['PATH'] = @saved_path
9
+ end
10
+
11
+ def ask_for_copy
12
+ Gist.on_success({'html_url' => @bobo_url}.to_json, :copy => true, :output => :html_url)
13
+ end
14
+ def gist_but_dont_ask_for_copy
15
+ Gist.on_success({'html_url' => 'http://example.com/'}.to_json, :output => :html_url)
16
+ end
17
+
18
+ it 'should try to copy the url when the clipboard option is passed' do
19
+ Gist.should_receive(:copy).with(@bobo_url)
20
+ ask_for_copy
21
+ end
22
+
23
+ it 'should try to copy the embed url when the clipboard-js option is passed' do
24
+ js_link = %Q{<script src="#{@bobo_url}.js"></script>}
25
+ Gist.should_receive(:copy).with(js_link)
26
+ Gist.on_success({'html_url' => @bobo_url}.to_json, :copy => true, :output => :javascript)
27
+ end
28
+
29
+ it "should not copy when not asked to" do
30
+ Gist.should_not_receive(:copy).with(@bobo_url)
31
+ gist_but_dont_ask_for_copy
32
+ end
33
+
34
+ it "should raise an error if no copying mechanisms are available" do
35
+ ENV['PATH'] = ''
36
+ lambda{
37
+ ask_for_copy
38
+ }.should raise_error(/Could not find copy command.*http/m)
39
+ end
40
+ end
data/spec/ghe_spec.rb ADDED
@@ -0,0 +1,91 @@
1
+ describe '...' do
2
+
3
+ MOCK_GHE_HOST = 'ghe.example.com'
4
+ MOCK_GHE_PROTOCOL = 'http'
5
+ MOCK_USER = 'foo'
6
+ MOCK_PASSWORD = 'bar'
7
+
8
+ MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_USER}:#{MOCK_PASSWORD}@#{MOCK_GHE_HOST}/api/v3/"
9
+ MOCK_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/"
10
+ MOCK_AUTHZ_GITHUB_URL = "https://#{MOCK_USER}:#{MOCK_PASSWORD}@api.github.com/"
11
+ MOCK_GITHUB_URL = "https://api.github.com/"
12
+
13
+ before do
14
+ @saved_env = ENV[Gist::URL_ENV_NAME]
15
+
16
+ # stub requests for /gists
17
+ stub_request(:post, /#{MOCK_GHE_URL}gists/).to_return(:body => %[{"html_url": "http://#{MOCK_GHE_HOST}"}])
18
+ stub_request(:post, /#{MOCK_GITHUB_URL}gists/).to_return(:body => '{"html_url": "http://github.com/"}')
19
+
20
+ # stub requests for /authorizations
21
+ stub_request(:post, /#{MOCK_AUTHZ_GHE_URL}authorizations/).
22
+ to_return(:status => 201, :body => '{"token": "asdf"}')
23
+ stub_request(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/).
24
+ to_return(:status => 201, :body => '{"token": "asdf"}')
25
+ end
26
+
27
+ after do
28
+ ENV[Gist::URL_ENV_NAME] = @saved_env
29
+ end
30
+
31
+ describe :login! do
32
+ before do
33
+ @saved_stdin = $stdin
34
+
35
+ # stdin emulation
36
+ $stdin = StringIO.new "#{MOCK_USER}\n#{MOCK_PASSWORD}\n"
37
+
38
+ # intercept for updating ~/.gist
39
+ File.stub(:open)
40
+ end
41
+
42
+ after do
43
+ $stdin = @saved_stdin
44
+ end
45
+
46
+ it "should access to api.github.com when $#{Gist::URL_ENV_NAME} wasn't set" do
47
+ ENV.delete Gist::URL_ENV_NAME
48
+
49
+ Gist.login!
50
+
51
+ assert_requested(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/)
52
+ end
53
+
54
+ it "should access to #{MOCK_GHE_HOST} when $#{Gist::URL_ENV_NAME} was set" do
55
+ ENV[Gist::URL_ENV_NAME] = MOCK_GHE_URL
56
+
57
+ Gist.login!
58
+
59
+ assert_requested(:post, /#{MOCK_AUTHZ_GHE_URL}authorizations/)
60
+ end
61
+
62
+ context "when credentials are passed in" do
63
+
64
+ it "uses them" do
65
+ $stdin = StringIO.new "#{MOCK_USER}_wrong\n#{MOCK_PASSWORD}_wrong\n"
66
+ Gist.login! :username => MOCK_USER, :password => MOCK_PASSWORD
67
+
68
+ assert_requested(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/)
69
+ end
70
+
71
+ end
72
+ end
73
+
74
+ describe :gist do
75
+ it "should access to api.github.com when $#{Gist::URL_ENV_NAME} wasn't set" do
76
+ ENV.delete Gist::URL_ENV_NAME
77
+
78
+ Gist.gist "test gist"
79
+
80
+ assert_requested(:post, /#{MOCK_GITHUB_URL}gists/)
81
+ end
82
+
83
+ it "should access to #{MOCK_GHE_HOST} when $#{Gist::URL_ENV_NAME} was set" do
84
+ ENV[Gist::URL_ENV_NAME] = MOCK_GHE_URL
85
+
86
+ Gist.gist "test gist"
87
+
88
+ assert_requested(:post, /#{MOCK_GHE_URL}gists/)
89
+ end
90
+ end
91
+ end
data/spec/gist_spec.rb ADDED
@@ -0,0 +1,23 @@
1
+ describe Gist do
2
+
3
+ describe "should_be_public?" do
4
+ it "should return false if -p is specified" do
5
+ Gist.should_be_public?(:private => true).should be_falsy
6
+ end
7
+
8
+ it "should return false if legacy_private_gister?" do
9
+ Gist.should_receive(:legacy_private_gister?).and_return(true)
10
+ Gist.should_be_public?.should be_falsy
11
+ end
12
+
13
+ it "should return true if --no-private is specified" do
14
+ Gist.stub(:legacy_private_gister?).and_return(true)
15
+ Gist.should_be_public?(:private => false).should be_truthy
16
+ end
17
+
18
+ it "should return true by default" do
19
+ Gist.should_be_public?.should be_truthy
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,21 @@
1
+ describe '...' do
2
+ before do
3
+ @saved_env = ENV['HTTP_PROXY']
4
+ end
5
+
6
+ after do
7
+ ENV['HTTP_PROXY'] = @saved_env
8
+ end
9
+
10
+ FOO_URL = URI('http://ddg.gg/')
11
+
12
+ it "should be Net::HTTP when $HTTP_PROXY wasn't set" do
13
+ ENV['HTTP_PROXY'] = ''
14
+ Gist.http_connection(FOO_URL).should be_an_instance_of(Net::HTTP)
15
+ end
16
+
17
+ it "should be Net::HTTP::Proxy when $HTTP_PROXY was set" do
18
+ ENV['HTTP_PROXY'] = 'http://proxy.example.com:8080'
19
+ Gist.http_connection(FOO_URL).should_not be_an_instance_of(Net::HTTP)
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ describe '...' do
2
+ before do
3
+ stub_request(:post, /api\.github.com\/gists/).to_return(:body => '{"html_url": "https://gist.github.com/XXXXXX"}')
4
+ stub_request(:get, "https://gist.github.com/XXXXXX").to_return(:status => 304, :headers => { 'Location' => 'https://gist.github.com/anonymous/XXXXXX' })
5
+ stub_request(:get, "https://gist.github.com/anonymous/XXXXXX").to_return(:status => 200)
6
+ end
7
+
8
+ it "should return the raw file url" do
9
+ Gist.gist("Test gist", :output => :raw_url, :anonymous => false).should == "https://gist.github.com/anonymous/XXXXXX/raw"
10
+ end
11
+
12
+ it 'should raise an error when trying to do operations without being logged in' do
13
+ error_msg = 'Anonymous gists are no longer supported. Please log in with `gist --login`. ' \
14
+ '(GitHub now requires credentials to gist https://bit.ly/2GBBxKw)'
15
+
16
+ expect do
17
+ Gist.gist("Test gist", output: :raw_url, anonymous: true)
18
+ end.to raise_error(StandardError, error_msg)
19
+ end
20
+ end
21
+
@@ -0,0 +1,24 @@
1
+ describe '...' do
2
+ before do
3
+ stub_request(:post, /api\.github.com\/gists/).to_return(:body => '{"html_url": "http://github.com/"}')
4
+ end
5
+
6
+ it "should return a shortened version of the URL when response is 200" do
7
+ stub_request(:post, "https://git.io/create").to_return(:status => 200, :body => 'XXXXXX')
8
+ Gist.gist("Test gist", :output => :short_url, anonymous: false).should == "https://git.io/XXXXXX"
9
+ end
10
+
11
+ it "should return a shortened version of the URL when response is 201" do
12
+ stub_request(:post, "https://git.io/create").to_return(:status => 201, :headers => { 'Location' => 'https://git.io/XXXXXX' })
13
+ Gist.gist("Test gist", :output => :short_url, anonymous: false).should == "https://git.io/XXXXXX"
14
+ end
15
+
16
+ it 'should raise an error when trying to get short urls without being logged in' do
17
+ error_msg = 'Anonymous gists are no longer supported. Please log in with `gist --login`. ' \
18
+ '(GitHub now requires credentials to gist https://bit.ly/2GBBxKw)'
19
+
20
+ expect do
21
+ Gist.gist("Test gist", output: :short_url, anonymous: true)
22
+ end.to raise_error(StandardError, error_msg)
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+ config.mock_with :rspec do |mocks|
11
+ mocks.syntax = :should
12
+ end
13
+ config.expect_with :rspec do |expectations|
14
+ expectations.syntax = [:should, :expect]
15
+ end
16
+ end
17
+
18
+ require 'webmock/rspec'
19
+ require 'gist'
data/vendor/json.rb ADDED
@@ -0,0 +1,1304 @@
1
+ require 'strscan'
2
+
3
+ module JSON
4
+ module Pure
5
+ # This class implements the JSON parser that is used to parse a JSON string
6
+ # into a Ruby data structure.
7
+ class Parser < StringScanner
8
+ STRING = /" ((?:[^\x0-\x1f"\\] |
9
+ # escaped special characters:
10
+ \\["\\\/bfnrt] |
11
+ \\u[0-9a-fA-F]{4} |
12
+ # match all but escaped special characters:
13
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
14
+ "/nx
15
+ INTEGER = /(-?0|-?[1-9]\d*)/
16
+ FLOAT = /(-?
17
+ (?:0|[1-9]\d*)
18
+ (?:
19
+ \.\d+(?i:e[+-]?\d+) |
20
+ \.\d+ |
21
+ (?i:e[+-]?\d+)
22
+ )
23
+ )/x
24
+ NAN = /NaN/
25
+ INFINITY = /Infinity/
26
+ MINUS_INFINITY = /-Infinity/
27
+ OBJECT_OPEN = /\{/
28
+ OBJECT_CLOSE = /\}/
29
+ ARRAY_OPEN = /\[/
30
+ ARRAY_CLOSE = /\]/
31
+ PAIR_DELIMITER = /:/
32
+ COLLECTION_DELIMITER = /,/
33
+ TRUE = /true/
34
+ FALSE = /false/
35
+ NULL = /null/
36
+ IGNORE = %r(
37
+ (?:
38
+ //[^\n\r]*[\n\r]| # line comments
39
+ /\* # c-style comments
40
+ (?:
41
+ [^*/]| # normal chars
42
+ /[^*]| # slashes that do not start a nested comment
43
+ \*[^/]| # asterisks that do not end this comment
44
+ /(?=\*/) # single slash before this comment's end
45
+ )*
46
+ \*/ # the End of this comment
47
+ |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
48
+ )+
49
+ )mx
50
+
51
+ UNPARSED = Object.new
52
+
53
+ # Creates a new JSON::Pure::Parser instance for the string _source_.
54
+ #
55
+ # It will be configured by the _opts_ hash. _opts_ can have the following
56
+ # keys:
57
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
58
+ # structures. Disable depth checking with :max_nesting => false|nil|0,
59
+ # it defaults to 19.
60
+ # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
61
+ # defiance of RFC 4627 to be parsed by the Parser. This option defaults
62
+ # to false.
63
+ # * *symbolize_names*: If set to true, returns symbols for the names
64
+ # (keys) in a JSON object. Otherwise strings are returned, which is also
65
+ # the default.
66
+ # * *create_additions*: If set to false, the Parser doesn't create
67
+ # additions even if a matchin class and create_id was found. This option
68
+ # defaults to true.
69
+ # * *object_class*: Defaults to Hash
70
+ # * *array_class*: Defaults to Array
71
+ # * *quirks_mode*: Enables quirks_mode for parser, that is for example
72
+ # parsing single JSON values instead of documents is possible.
73
+ def initialize(source, opts = {})
74
+ opts ||= {}
75
+ unless @quirks_mode = opts[:quirks_mode]
76
+ source = convert_encoding source
77
+ end
78
+ super source
79
+ if !opts.key?(:max_nesting) # defaults to 19
80
+ @max_nesting = 19
81
+ elsif opts[:max_nesting]
82
+ @max_nesting = opts[:max_nesting]
83
+ else
84
+ @max_nesting = 0
85
+ end
86
+ @allow_nan = !!opts[:allow_nan]
87
+ @symbolize_names = !!opts[:symbolize_names]
88
+ if opts.key?(:create_additions)
89
+ @create_additions = !!opts[:create_additions]
90
+ else
91
+ @create_additions = true
92
+ end
93
+ @create_id = @create_additions ? JSON.create_id : nil
94
+ @object_class = opts[:object_class] || Hash
95
+ @array_class = opts[:array_class] || Array
96
+ @match_string = opts[:match_string]
97
+ end
98
+
99
+ alias source string
100
+
101
+ def quirks_mode?
102
+ !!@quirks_mode
103
+ end
104
+
105
+ def reset
106
+ super
107
+ @current_nesting = 0
108
+ end
109
+
110
+ # Parses the current JSON string _source_ and returns the complete data
111
+ # structure as a result.
112
+ def parse
113
+ reset
114
+ obj = nil
115
+ if @quirks_mode
116
+ while !eos? && skip(IGNORE)
117
+ end
118
+ if eos?
119
+ raise ParserError, "source did not contain any JSON!"
120
+ else
121
+ obj = parse_value
122
+ obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
123
+ end
124
+ else
125
+ until eos?
126
+ case
127
+ when scan(OBJECT_OPEN)
128
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
129
+ @current_nesting = 1
130
+ obj = parse_object
131
+ when scan(ARRAY_OPEN)
132
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
133
+ @current_nesting = 1
134
+ obj = parse_array
135
+ when skip(IGNORE)
136
+ ;
137
+ else
138
+ raise ParserError, "source '#{peek(20)}' not in JSON!"
139
+ end
140
+ end
141
+ obj or raise ParserError, "source did not contain any JSON!"
142
+ end
143
+ obj
144
+ end
145
+
146
+ private
147
+
148
+ def convert_encoding(source)
149
+ if source.respond_to?(:to_str)
150
+ source = source.to_str
151
+ else
152
+ raise TypeError, "#{source.inspect} is not like a string"
153
+ end
154
+ if defined?(::Encoding)
155
+ if source.encoding == ::Encoding::ASCII_8BIT
156
+ b = source[0, 4].bytes.to_a
157
+ source =
158
+ case
159
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
160
+ source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
161
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
162
+ source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
163
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
164
+ source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
165
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
166
+ source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
167
+ else
168
+ source.dup
169
+ end
170
+ else
171
+ source = source.encode(::Encoding::UTF_8)
172
+ end
173
+ source.force_encoding(::Encoding::ASCII_8BIT)
174
+ else
175
+ b = source
176
+ source =
177
+ case
178
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
179
+ JSON.iconv('utf-8', 'utf-32be', b)
180
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
181
+ JSON.iconv('utf-8', 'utf-16be', b)
182
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
183
+ JSON.iconv('utf-8', 'utf-32le', b)
184
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
185
+ JSON.iconv('utf-8', 'utf-16le', b)
186
+ else
187
+ b
188
+ end
189
+ end
190
+ source
191
+ end
192
+
193
+ # Unescape characters in strings.
194
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
195
+ UNESCAPE_MAP.update({
196
+ ?" => '"',
197
+ ?\\ => '\\',
198
+ ?/ => '/',
199
+ ?b => "\b",
200
+ ?f => "\f",
201
+ ?n => "\n",
202
+ ?r => "\r",
203
+ ?t => "\t",
204
+ ?u => nil,
205
+ })
206
+
207
+ EMPTY_8BIT_STRING = ''
208
+ if ::String.method_defined?(:encode)
209
+ EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
210
+ end
211
+
212
+ def parse_string
213
+ if scan(STRING)
214
+ return '' if self[1].empty?
215
+ string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
216
+ if u = UNESCAPE_MAP[$&[1]]
217
+ u
218
+ else # \uXXXX
219
+ bytes = EMPTY_8BIT_STRING.dup
220
+ i = 0
221
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
222
+ bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
223
+ i += 1
224
+ end
225
+ JSON.iconv('utf-8', 'utf-16be', bytes)
226
+ end
227
+ end
228
+ if string.respond_to?(:force_encoding)
229
+ string.force_encoding(::Encoding::UTF_8)
230
+ end
231
+ if @create_additions and @match_string
232
+ for (regexp, klass) in @match_string
233
+ klass.json_creatable? or next
234
+ string =~ regexp and return klass.json_create(string)
235
+ end
236
+ end
237
+ string
238
+ else
239
+ UNPARSED
240
+ end
241
+ rescue => e
242
+ raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
243
+ end
244
+
245
+ def parse_value
246
+ case
247
+ when scan(FLOAT)
248
+ Float(self[1])
249
+ when scan(INTEGER)
250
+ Integer(self[1])
251
+ when scan(TRUE)
252
+ true
253
+ when scan(FALSE)
254
+ false
255
+ when scan(NULL)
256
+ nil
257
+ when (string = parse_string) != UNPARSED
258
+ string
259
+ when scan(ARRAY_OPEN)
260
+ @current_nesting += 1
261
+ ary = parse_array
262
+ @current_nesting -= 1
263
+ ary
264
+ when scan(OBJECT_OPEN)
265
+ @current_nesting += 1
266
+ obj = parse_object
267
+ @current_nesting -= 1
268
+ obj
269
+ when @allow_nan && scan(NAN)
270
+ NaN
271
+ when @allow_nan && scan(INFINITY)
272
+ Infinity
273
+ when @allow_nan && scan(MINUS_INFINITY)
274
+ MinusInfinity
275
+ else
276
+ UNPARSED
277
+ end
278
+ end
279
+
280
+ def parse_array
281
+ raise NestingError, "nesting of #@current_nesting is too deep" if
282
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
283
+ result = @array_class.new
284
+ delim = false
285
+ until eos?
286
+ case
287
+ when (value = parse_value) != UNPARSED
288
+ delim = false
289
+ result << value
290
+ skip(IGNORE)
291
+ if scan(COLLECTION_DELIMITER)
292
+ delim = true
293
+ elsif match?(ARRAY_CLOSE)
294
+ ;
295
+ else
296
+ raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
297
+ end
298
+ when scan(ARRAY_CLOSE)
299
+ if delim
300
+ raise ParserError, "expected next element in array at '#{peek(20)}'!"
301
+ end
302
+ break
303
+ when skip(IGNORE)
304
+ ;
305
+ else
306
+ raise ParserError, "unexpected token in array at '#{peek(20)}'!"
307
+ end
308
+ end
309
+ result
310
+ end
311
+
312
+ def parse_object
313
+ raise NestingError, "nesting of #@current_nesting is too deep" if
314
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
315
+ result = @object_class.new
316
+ delim = false
317
+ until eos?
318
+ case
319
+ when (string = parse_string) != UNPARSED
320
+ skip(IGNORE)
321
+ unless scan(PAIR_DELIMITER)
322
+ raise ParserError, "expected ':' in object at '#{peek(20)}'!"
323
+ end
324
+ skip(IGNORE)
325
+ unless (value = parse_value).equal? UNPARSED
326
+ result[@symbolize_names ? string.to_sym : string] = value
327
+ delim = false
328
+ skip(IGNORE)
329
+ if scan(COLLECTION_DELIMITER)
330
+ delim = true
331
+ elsif match?(OBJECT_CLOSE)
332
+ ;
333
+ else
334
+ raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
335
+ end
336
+ else
337
+ raise ParserError, "expected value in object at '#{peek(20)}'!"
338
+ end
339
+ when scan(OBJECT_CLOSE)
340
+ if delim
341
+ raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
342
+ end
343
+ if @create_additions and klassname = result[@create_id]
344
+ klass = JSON.deep_const_get klassname
345
+ break unless klass and klass.json_creatable?
346
+ result = klass.json_create(result)
347
+ end
348
+ break
349
+ when skip(IGNORE)
350
+ ;
351
+ else
352
+ raise ParserError, "unexpected token in object at '#{peek(20)}'!"
353
+ end
354
+ end
355
+ result
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ module JSON
362
+ MAP = {
363
+ "\x0" => '\u0000',
364
+ "\x1" => '\u0001',
365
+ "\x2" => '\u0002',
366
+ "\x3" => '\u0003',
367
+ "\x4" => '\u0004',
368
+ "\x5" => '\u0005',
369
+ "\x6" => '\u0006',
370
+ "\x7" => '\u0007',
371
+ "\b" => '\b',
372
+ "\t" => '\t',
373
+ "\n" => '\n',
374
+ "\xb" => '\u000b',
375
+ "\f" => '\f',
376
+ "\r" => '\r',
377
+ "\xe" => '\u000e',
378
+ "\xf" => '\u000f',
379
+ "\x10" => '\u0010',
380
+ "\x11" => '\u0011',
381
+ "\x12" => '\u0012',
382
+ "\x13" => '\u0013',
383
+ "\x14" => '\u0014',
384
+ "\x15" => '\u0015',
385
+ "\x16" => '\u0016',
386
+ "\x17" => '\u0017',
387
+ "\x18" => '\u0018',
388
+ "\x19" => '\u0019',
389
+ "\x1a" => '\u001a',
390
+ "\x1b" => '\u001b',
391
+ "\x1c" => '\u001c',
392
+ "\x1d" => '\u001d',
393
+ "\x1e" => '\u001e',
394
+ "\x1f" => '\u001f',
395
+ '"' => '\"',
396
+ '\\' => '\\\\',
397
+ } # :nodoc:
398
+
399
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
400
+ # UTF16 big endian characters as \u????, and return it.
401
+ if defined?(::Encoding)
402
+ def utf8_to_json(string) # :nodoc:
403
+ string = string.dup
404
+ string << '' # XXX workaround: avoid buffer sharing
405
+ string.force_encoding(::Encoding::ASCII_8BIT)
406
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
407
+ string.force_encoding(::Encoding::UTF_8)
408
+ string
409
+ end
410
+
411
+ def utf8_to_json_ascii(string) # :nodoc:
412
+ string = string.dup
413
+ string << '' # XXX workaround: avoid buffer sharing
414
+ string.force_encoding(::Encoding::ASCII_8BIT)
415
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
416
+ string.gsub!(/(
417
+ (?:
418
+ [\xc2-\xdf][\x80-\xbf] |
419
+ [\xe0-\xef][\x80-\xbf]{2} |
420
+ [\xf0-\xf4][\x80-\xbf]{3}
421
+ )+ |
422
+ [\x80-\xc1\xf5-\xff] # invalid
423
+ )/nx) { |c|
424
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
425
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
426
+ s.gsub!(/.{4}/n, '\\\\u\&')
427
+ }
428
+ string.force_encoding(::Encoding::UTF_8)
429
+ string
430
+ rescue => e
431
+ raise GeneratorError, "Caught #{e.class}: #{e}"
432
+ end
433
+ else
434
+ def utf8_to_json(string) # :nodoc:
435
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
436
+ end
437
+
438
+ def utf8_to_json_ascii(string) # :nodoc:
439
+ string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
440
+ string.gsub!(/(
441
+ (?:
442
+ [\xc2-\xdf][\x80-\xbf] |
443
+ [\xe0-\xef][\x80-\xbf]{2} |
444
+ [\xf0-\xf4][\x80-\xbf]{3}
445
+ )+ |
446
+ [\x80-\xc1\xf5-\xff] # invalid
447
+ )/nx) { |c|
448
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
449
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
450
+ s.gsub!(/.{4}/n, '\\\\u\&')
451
+ }
452
+ string
453
+ rescue => e
454
+ raise GeneratorError, "Caught #{e.class}: #{e}"
455
+ end
456
+ end
457
+ module_function :utf8_to_json, :utf8_to_json_ascii
458
+
459
+ module Pure
460
+ module Generator
461
+ # This class is used to create State instances, that are use to hold data
462
+ # while generating a JSON text from a Ruby data structure.
463
+ class State
464
+ # Creates a State object from _opts_, which ought to be Hash to create
465
+ # a new State instance configured by _opts_, something else to create
466
+ # an unconfigured instance. If _opts_ is a State object, it is just
467
+ # returned.
468
+ def self.from_state(opts)
469
+ case
470
+ when self === opts
471
+ opts
472
+ when opts.respond_to?(:to_hash)
473
+ new(opts.to_hash)
474
+ when opts.respond_to?(:to_h)
475
+ new(opts.to_h)
476
+ else
477
+ SAFE_STATE_PROTOTYPE.dup
478
+ end
479
+ end
480
+
481
+ # Instantiates a new State object, configured by _opts_.
482
+ #
483
+ # _opts_ can have the following keys:
484
+ #
485
+ # * *indent*: a string used to indent levels (default: ''),
486
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
487
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
488
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
489
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
490
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
491
+ # * *max_nesting*: sets the maximum level of data structure nesting in
492
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
493
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
494
+ # generated, otherwise an exception is thrown, if these values are
495
+ # encountered. This options defaults to false.
496
+ # * *quirks_mode*: Enables quirks_mode for parser, that is for example
497
+ # generating single JSON values instead of documents is possible.
498
+ def initialize(opts = {})
499
+ @indent = ''
500
+ @space = ''
501
+ @space_before = ''
502
+ @object_nl = ''
503
+ @array_nl = ''
504
+ @allow_nan = false
505
+ @ascii_only = false
506
+ @quirks_mode = false
507
+ @buffer_initial_length = 1024
508
+ configure opts
509
+ end
510
+
511
+ # This string is used to indent levels in the JSON text.
512
+ attr_accessor :indent
513
+
514
+ # This string is used to insert a space between the tokens in a JSON
515
+ # string.
516
+ attr_accessor :space
517
+
518
+ # This string is used to insert a space before the ':' in JSON objects.
519
+ attr_accessor :space_before
520
+
521
+ # This string is put at the end of a line that holds a JSON object (or
522
+ # Hash).
523
+ attr_accessor :object_nl
524
+
525
+ # This string is put at the end of a line that holds a JSON array.
526
+ attr_accessor :array_nl
527
+
528
+ # This integer returns the maximum level of data structure nesting in
529
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
530
+ attr_accessor :max_nesting
531
+
532
+ # If this attribute is set to true, quirks mode is enabled, otherwise
533
+ # it's disabled.
534
+ attr_accessor :quirks_mode
535
+
536
+ # :stopdoc:
537
+ attr_reader :buffer_initial_length
538
+
539
+ def buffer_initial_length=(length)
540
+ if length > 0
541
+ @buffer_initial_length = length
542
+ end
543
+ end
544
+ # :startdoc:
545
+
546
+ # This integer returns the current depth data structure nesting in the
547
+ # generated JSON.
548
+ attr_accessor :depth
549
+
550
+ def check_max_nesting # :nodoc:
551
+ return if @max_nesting.zero?
552
+ current_nesting = depth + 1
553
+ current_nesting > @max_nesting and
554
+ raise NestingError, "nesting of #{current_nesting} is too deep"
555
+ end
556
+
557
+ # Returns true, if circular data structures are checked,
558
+ # otherwise returns false.
559
+ def check_circular?
560
+ !@max_nesting.zero?
561
+ end
562
+
563
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
564
+ # valid JSON and output.
565
+ def allow_nan?
566
+ @allow_nan
567
+ end
568
+
569
+ # Returns true, if only ASCII characters should be generated. Otherwise
570
+ # returns false.
571
+ def ascii_only?
572
+ @ascii_only
573
+ end
574
+
575
+ # Returns true, if quirks mode is enabled. Otherwise returns false.
576
+ def quirks_mode?
577
+ @quirks_mode
578
+ end
579
+
580
+ # Configure this State instance with the Hash _opts_, and return
581
+ # itself.
582
+ def configure(opts)
583
+ @indent = opts[:indent] if opts.key?(:indent)
584
+ @space = opts[:space] if opts.key?(:space)
585
+ @space_before = opts[:space_before] if opts.key?(:space_before)
586
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
587
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
588
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
589
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
590
+ @depth = opts[:depth] || 0
591
+ @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
592
+ if !opts.key?(:max_nesting) # defaults to 19
593
+ @max_nesting = 19
594
+ elsif opts[:max_nesting]
595
+ @max_nesting = opts[:max_nesting]
596
+ else
597
+ @max_nesting = 0
598
+ end
599
+ self
600
+ end
601
+ alias merge configure
602
+
603
+ # Returns the configuration instance variables as a hash, that can be
604
+ # passed to the configure method.
605
+ def to_h
606
+ result = {}
607
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode buffer_initial_length depth]
608
+ result[iv.intern] = instance_variable_get("@#{iv}")
609
+ end
610
+ result
611
+ end
612
+
613
+ # Generates a valid JSON document from object +obj+ and returns the
614
+ # result. If no valid JSON document can be created this method raises a
615
+ # GeneratorError exception.
616
+ def generate(obj)
617
+ result = obj.to_json(self)
618
+ unless @quirks_mode
619
+ unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
620
+ result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
621
+ then
622
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
623
+ end
624
+ end
625
+ result
626
+ end
627
+
628
+ # Return the value returned by method +name+.
629
+ def [](name)
630
+ __send__ name
631
+ end
632
+ end
633
+
634
+ module GeneratorMethods
635
+ module Object
636
+ # Converts this object to a string (calling #to_s), converts
637
+ # it to a JSON string, and returns the result. This is a fallback, if no
638
+ # special method #to_json was defined for some object.
639
+ def to_json(*) to_s.to_json end
640
+ end
641
+
642
+ module Hash
643
+ # Returns a JSON string containing a JSON object, that is unparsed from
644
+ # this Hash instance.
645
+ # _state_ is a JSON::State object, that can also be used to configure the
646
+ # produced JSON string output further.
647
+ # _depth_ is used to find out nesting depth, to indent accordingly.
648
+ def to_json(state = nil, *)
649
+ state = State.from_state(state)
650
+ state.check_max_nesting
651
+ json_transform(state)
652
+ end
653
+
654
+ private
655
+
656
+ def json_shift(state)
657
+ state.object_nl.empty? or return ''
658
+ state.indent * state.depth
659
+ end
660
+
661
+ def json_transform(state)
662
+ delim = ','
663
+ delim << state.object_nl
664
+ result = '{'
665
+ result << state.object_nl
666
+ depth = state.depth += 1
667
+ first = true
668
+ indent = !state.object_nl.empty?
669
+ each { |key,value|
670
+ result << delim unless first
671
+ result << state.indent * depth if indent
672
+ result << key.to_s.to_json(state)
673
+ result << state.space_before
674
+ result << ':'
675
+ result << state.space
676
+ result << value.to_json(state)
677
+ first = false
678
+ }
679
+ depth = state.depth -= 1
680
+ result << state.object_nl
681
+ result << state.indent * depth if indent if indent
682
+ result << '}'
683
+ result
684
+ end
685
+ end
686
+
687
+ module Array
688
+ # Returns a JSON string containing a JSON array, that is unparsed from
689
+ # this Array instance.
690
+ # _state_ is a JSON::State object, that can also be used to configure the
691
+ # produced JSON string output further.
692
+ def to_json(state = nil, *)
693
+ state = State.from_state(state)
694
+ state.check_max_nesting
695
+ json_transform(state)
696
+ end
697
+
698
+ private
699
+
700
+ def json_transform(state)
701
+ delim = ','
702
+ delim << state.array_nl
703
+ result = '['
704
+ result << state.array_nl
705
+ depth = state.depth += 1
706
+ first = true
707
+ indent = !state.array_nl.empty?
708
+ each { |value|
709
+ result << delim unless first
710
+ result << state.indent * depth if indent
711
+ result << value.to_json(state)
712
+ first = false
713
+ }
714
+ depth = state.depth -= 1
715
+ result << state.array_nl
716
+ result << state.indent * depth if indent
717
+ result << ']'
718
+ end
719
+ end
720
+
721
+ module Integer
722
+ # Returns a JSON string representation for this Integer number.
723
+ def to_json(*) to_s end
724
+ end
725
+
726
+ module Float
727
+ # Returns a JSON string representation for this Float number.
728
+ def to_json(state = nil, *)
729
+ state = State.from_state(state)
730
+ case
731
+ when infinite?
732
+ if state.allow_nan?
733
+ to_s
734
+ else
735
+ raise GeneratorError, "#{self} not allowed in JSON"
736
+ end
737
+ when nan?
738
+ if state.allow_nan?
739
+ to_s
740
+ else
741
+ raise GeneratorError, "#{self} not allowed in JSON"
742
+ end
743
+ else
744
+ to_s
745
+ end
746
+ end
747
+ end
748
+
749
+ module String
750
+ if defined?(::Encoding)
751
+ # This string should be encoded with UTF-8 A call to this method
752
+ # returns a JSON string encoded with UTF16 big endian characters as
753
+ # \u????.
754
+ def to_json(state = nil, *args)
755
+ state = State.from_state(state)
756
+ if encoding == ::Encoding::UTF_8
757
+ string = self
758
+ else
759
+ string = encode(::Encoding::UTF_8)
760
+ end
761
+ if state.ascii_only?
762
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
763
+ else
764
+ '"' << JSON.utf8_to_json(string) << '"'
765
+ end
766
+ end
767
+ else
768
+ # This string should be encoded with UTF-8 A call to this method
769
+ # returns a JSON string encoded with UTF16 big endian characters as
770
+ # \u????.
771
+ def to_json(state = nil, *args)
772
+ state = State.from_state(state)
773
+ if state.ascii_only?
774
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
775
+ else
776
+ '"' << JSON.utf8_to_json(self) << '"'
777
+ end
778
+ end
779
+ end
780
+
781
+ # Module that holds the extinding methods if, the String module is
782
+ # included.
783
+ module Extend
784
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
785
+ # array for the key "raw"). The Ruby String can be created by this
786
+ # module method.
787
+ def json_create(o)
788
+ o['raw'].pack('C*')
789
+ end
790
+ end
791
+
792
+ # Extends _modul_ with the String::Extend module.
793
+ def self.included(modul)
794
+ modul.extend Extend
795
+ end
796
+
797
+ # This method creates a raw object hash, that can be nested into
798
+ # other data structures and will be unparsed as a raw string. This
799
+ # method should be used, if you want to convert raw strings to JSON
800
+ # instead of UTF-8 strings, e. g. binary data.
801
+ def to_json_raw_object
802
+ {
803
+ JSON.create_id => self.class.name,
804
+ 'raw' => self.unpack('C*'),
805
+ }
806
+ end
807
+
808
+ # This method creates a JSON text from the result of
809
+ # a call to to_json_raw_object of this String.
810
+ def to_json_raw(*args)
811
+ to_json_raw_object.to_json(*args)
812
+ end
813
+ end
814
+
815
+ module TrueClass
816
+ # Returns a JSON string for true: 'true'.
817
+ def to_json(*) 'true' end
818
+ end
819
+
820
+ module FalseClass
821
+ # Returns a JSON string for false: 'false'.
822
+ def to_json(*) 'false' end
823
+ end
824
+
825
+ module NilClass
826
+ # Returns a JSON string for nil: 'null'.
827
+ def to_json(*) 'null' end
828
+ end
829
+ end
830
+ end
831
+ end
832
+ end
833
+
834
+ module JSON
835
+ class << self
836
+ # If _object_ is string-like, parse the string and return the parsed result
837
+ # as a Ruby data structure. Otherwise generate a JSON text from the Ruby
838
+ # data structure object and return it.
839
+ #
840
+ # The _opts_ argument is passed through to generate/parse respectively. See
841
+ # generate and parse for their documentation.
842
+ def [](object, opts = {})
843
+ if object.respond_to? :to_str
844
+ JSON.parse(object.to_str, opts)
845
+ else
846
+ JSON.generate(object, opts)
847
+ end
848
+ end
849
+
850
+ # Returns the JSON parser class that is used by JSON. This is either
851
+ # JSON::Ext::Parser or JSON::Pure::Parser.
852
+ attr_reader :parser
853
+
854
+ # Set the JSON parser class _parser_ to be used by JSON.
855
+ def parser=(parser) # :nodoc:
856
+ @parser = parser
857
+ remove_const :Parser if JSON.const_defined_in?(self, :Parser)
858
+ const_set :Parser, parser
859
+ end
860
+
861
+ # Return the constant located at _path_. The format of _path_ has to be
862
+ # either ::A::B::C or A::B::C. In any case, A has to be located at the top
863
+ # level (absolute namespace path?). If there doesn't exist a constant at
864
+ # the given path, an ArgumentError is raised.
865
+ def deep_const_get(path) # :nodoc:
866
+ path.to_s.split(/::/).inject(Object) do |p, c|
867
+ case
868
+ when c.empty? then p
869
+ when JSON.const_defined_in?(p, c) then p.const_get(c)
870
+ else
871
+ begin
872
+ p.const_missing(c)
873
+ rescue NameError => e
874
+ raise ArgumentError, "can't get const #{path}: #{e}"
875
+ end
876
+ end
877
+ end
878
+ end
879
+
880
+ # Set the module _generator_ to be used by JSON.
881
+ def generator=(generator) # :nodoc:
882
+ old, $VERBOSE = $VERBOSE, nil
883
+ @generator = generator
884
+ generator_methods = generator::GeneratorMethods
885
+ for const in generator_methods.constants
886
+ klass = deep_const_get(const)
887
+ modul = generator_methods.const_get(const)
888
+ klass.class_eval do
889
+ instance_methods(false).each do |m|
890
+ m.to_s == 'to_json' and remove_method m
891
+ end
892
+ include modul
893
+ end
894
+ end
895
+ self.state = generator::State
896
+ const_set :State, self.state
897
+ const_set :SAFE_STATE_PROTOTYPE, State.new
898
+ const_set :FAST_STATE_PROTOTYPE, State.new(
899
+ :indent => '',
900
+ :space => '',
901
+ :object_nl => "",
902
+ :array_nl => "",
903
+ :max_nesting => false
904
+ )
905
+ const_set :PRETTY_STATE_PROTOTYPE, State.new(
906
+ :indent => ' ',
907
+ :space => ' ',
908
+ :object_nl => "\n",
909
+ :array_nl => "\n"
910
+ )
911
+ ensure
912
+ $VERBOSE = old
913
+ end
914
+
915
+ # Returns the JSON generator module that is used by JSON. This is
916
+ # either JSON::Ext::Generator or JSON::Pure::Generator.
917
+ attr_reader :generator
918
+
919
+ # Returns the JSON generator state class that is used by JSON. This is
920
+ # either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
921
+ attr_accessor :state
922
+
923
+ # This is create identifier, which is used to decide if the _json_create_
924
+ # hook of a class should be called. It defaults to 'json_class'.
925
+ attr_accessor :create_id
926
+ end
927
+ self.create_id = 'json_class'
928
+
929
+ NaN = 0.0/0
930
+
931
+ Infinity = 1.0/0
932
+
933
+ MinusInfinity = -Infinity
934
+
935
+ # The base exception for JSON errors.
936
+ class JSONError < StandardError; end
937
+
938
+ # This exception is raised if a parser error occurs.
939
+ class ParserError < JSONError; end
940
+
941
+ # This exception is raised if the nesting of parsed data structures is too
942
+ # deep.
943
+ class NestingError < ParserError; end
944
+
945
+ # :stopdoc:
946
+ class CircularDatastructure < NestingError; end
947
+ # :startdoc:
948
+
949
+ # This exception is raised if a generator or unparser error occurs.
950
+ class GeneratorError < JSONError; end
951
+ # For backwards compatibility
952
+ UnparserError = GeneratorError
953
+
954
+ # This exception is raised if the required unicode support is missing on the
955
+ # system. Usually this means that the iconv library is not installed.
956
+ class MissingUnicodeSupport < JSONError; end
957
+
958
+ module_function
959
+
960
+ # Parse the JSON document _source_ into a Ruby data structure and return it.
961
+ #
962
+ # _opts_ can have the following
963
+ # keys:
964
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
965
+ # structures. Disable depth checking with :max_nesting => false. It defaults
966
+ # to 19.
967
+ # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
968
+ # defiance of RFC 4627 to be parsed by the Parser. This option defaults
969
+ # to false.
970
+ # * *symbolize_names*: If set to true, returns symbols for the names
971
+ # (keys) in a JSON object. Otherwise strings are returned. Strings are
972
+ # the default.
973
+ # * *create_additions*: If set to false, the Parser doesn't create
974
+ # additions even if a matching class and create_id was found. This option
975
+ # defaults to true.
976
+ # * *object_class*: Defaults to Hash
977
+ # * *array_class*: Defaults to Array
978
+ def parse(source, opts = {})
979
+ Parser.new(source, opts).parse
980
+ end
981
+
982
+ # Parse the JSON document _source_ into a Ruby data structure and return it.
983
+ # The bang version of the parse method defaults to the more dangerous values
984
+ # for the _opts_ hash, so be sure only to parse trusted _source_ documents.
985
+ #
986
+ # _opts_ can have the following keys:
987
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
988
+ # structures. Enable depth checking with :max_nesting => anInteger. The parse!
989
+ # methods defaults to not doing max depth checking: This can be dangerous
990
+ # if someone wants to fill up your stack.
991
+ # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
992
+ # defiance of RFC 4627 to be parsed by the Parser. This option defaults
993
+ # to true.
994
+ # * *create_additions*: If set to false, the Parser doesn't create
995
+ # additions even if a matching class and create_id was found. This option
996
+ # defaults to true.
997
+ def parse!(source, opts = {})
998
+ opts = {
999
+ :max_nesting => false,
1000
+ :allow_nan => true
1001
+ }.update(opts)
1002
+ Parser.new(source, opts).parse
1003
+ end
1004
+
1005
+ # Generate a JSON document from the Ruby data structure _obj_ and return
1006
+ # it. _state_ is * a JSON::State object,
1007
+ # * or a Hash like object (responding to to_hash),
1008
+ # * an object convertible into a hash by a to_h method,
1009
+ # that is used as or to configure a State object.
1010
+ #
1011
+ # It defaults to a state object, that creates the shortest possible JSON text
1012
+ # in one line, checks for circular data structures and doesn't allow NaN,
1013
+ # Infinity, and -Infinity.
1014
+ #
1015
+ # A _state_ hash can have the following keys:
1016
+ # * *indent*: a string used to indent levels (default: ''),
1017
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
1018
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
1019
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
1020
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
1021
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
1022
+ # generated, otherwise an exception is thrown if these values are
1023
+ # encountered. This options defaults to false.
1024
+ # * *max_nesting*: The maximum depth of nesting allowed in the data
1025
+ # structures from which JSON is to be generated. Disable depth checking
1026
+ # with :max_nesting => false, it defaults to 19.
1027
+ #
1028
+ # See also the fast_generate for the fastest creation method with the least
1029
+ # amount of sanity checks, and the pretty_generate method for some
1030
+ # defaults for pretty output.
1031
+ def generate(obj, opts = nil)
1032
+ if State === opts
1033
+ state, opts = opts, nil
1034
+ else
1035
+ state = SAFE_STATE_PROTOTYPE.dup
1036
+ end
1037
+ if opts
1038
+ if opts.respond_to? :to_hash
1039
+ opts = opts.to_hash
1040
+ elsif opts.respond_to? :to_h
1041
+ opts = opts.to_h
1042
+ else
1043
+ raise TypeError, "can't convert #{opts.class} into Hash"
1044
+ end
1045
+ state = state.configure(opts)
1046
+ end
1047
+ state.generate(obj)
1048
+ end
1049
+
1050
+ # :stopdoc:
1051
+ # I want to deprecate these later, so I'll first be silent about them, and
1052
+ # later delete them.
1053
+ alias unparse generate
1054
+ module_function :unparse
1055
+ # :startdoc:
1056
+
1057
+ # Generate a JSON document from the Ruby data structure _obj_ and return it.
1058
+ # This method disables the checks for circles in Ruby objects.
1059
+ #
1060
+ # *WARNING*: Be careful not to pass any Ruby data structures with circles as
1061
+ # _obj_ argument because this will cause JSON to go into an infinite loop.
1062
+ def fast_generate(obj, opts = nil)
1063
+ if State === opts
1064
+ state, opts = opts, nil
1065
+ else
1066
+ state = FAST_STATE_PROTOTYPE.dup
1067
+ end
1068
+ if opts
1069
+ if opts.respond_to? :to_hash
1070
+ opts = opts.to_hash
1071
+ elsif opts.respond_to? :to_h
1072
+ opts = opts.to_h
1073
+ else
1074
+ raise TypeError, "can't convert #{opts.class} into Hash"
1075
+ end
1076
+ state.configure(opts)
1077
+ end
1078
+ state.generate(obj)
1079
+ end
1080
+
1081
+ # :stopdoc:
1082
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
1083
+ alias fast_unparse fast_generate
1084
+ module_function :fast_unparse
1085
+ # :startdoc:
1086
+
1087
+ # Generate a JSON document from the Ruby data structure _obj_ and return it.
1088
+ # The returned document is a prettier form of the document returned by
1089
+ # #unparse.
1090
+ #
1091
+ # The _opts_ argument can be used to configure the generator. See the
1092
+ # generate method for a more detailed explanation.
1093
+ def pretty_generate(obj, opts = nil)
1094
+ if State === opts
1095
+ state, opts = opts, nil
1096
+ else
1097
+ state = PRETTY_STATE_PROTOTYPE.dup
1098
+ end
1099
+ if opts
1100
+ if opts.respond_to? :to_hash
1101
+ opts = opts.to_hash
1102
+ elsif opts.respond_to? :to_h
1103
+ opts = opts.to_h
1104
+ else
1105
+ raise TypeError, "can't convert #{opts.class} into Hash"
1106
+ end
1107
+ state.configure(opts)
1108
+ end
1109
+ state.generate(obj)
1110
+ end
1111
+
1112
+ # :stopdoc:
1113
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
1114
+ alias pretty_unparse pretty_generate
1115
+ module_function :pretty_unparse
1116
+ # :startdoc:
1117
+
1118
+ class << self
1119
+ # The global default options for the JSON.load method:
1120
+ # :max_nesting: false
1121
+ # :allow_nan: true
1122
+ # :quirks_mode: true
1123
+ attr_accessor :load_default_options
1124
+ end
1125
+ self.load_default_options = {
1126
+ :max_nesting => false,
1127
+ :allow_nan => true,
1128
+ :quirks_mode => true,
1129
+ }
1130
+
1131
+ # Load a ruby data structure from a JSON _source_ and return it. A source can
1132
+ # either be a string-like object, an IO-like object, or an object responding
1133
+ # to the read method. If _proc_ was given, it will be called with any nested
1134
+ # Ruby object as an argument recursively in depth first order. The default
1135
+ # options for the parser can be changed via the load_default_options method.
1136
+ #
1137
+ # This method is part of the implementation of the load/dump interface of
1138
+ # Marshal and YAML.
1139
+ def load(source, proc = nil)
1140
+ opts = load_default_options
1141
+ if source.respond_to? :to_str
1142
+ source = source.to_str
1143
+ elsif source.respond_to? :to_io
1144
+ source = source.to_io.read
1145
+ elsif source.respond_to?(:read)
1146
+ source = source.read
1147
+ end
1148
+ if opts[:quirks_mode] && (source.nil? || source.empty?)
1149
+ source = 'null'
1150
+ end
1151
+ result = parse(source, opts)
1152
+ recurse_proc(result, &proc) if proc
1153
+ result
1154
+ end
1155
+
1156
+ # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
1157
+ def recurse_proc(result, &proc)
1158
+ case result
1159
+ when Array
1160
+ result.each { |x| recurse_proc x, &proc }
1161
+ proc.call result
1162
+ when Hash
1163
+ result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
1164
+ proc.call result
1165
+ else
1166
+ proc.call result
1167
+ end
1168
+ end
1169
+
1170
+ alias restore load
1171
+ module_function :restore
1172
+
1173
+ class << self
1174
+ # The global default options for the JSON.dump method:
1175
+ # :max_nesting: false
1176
+ # :allow_nan: true
1177
+ # :quirks_mode: true
1178
+ attr_accessor :dump_default_options
1179
+ end
1180
+ self.dump_default_options = {
1181
+ :max_nesting => false,
1182
+ :allow_nan => true,
1183
+ :quirks_mode => true,
1184
+ }
1185
+
1186
+ # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
1187
+ # the result.
1188
+ #
1189
+ # If anIO (an IO-like object or an object that responds to the write method)
1190
+ # was given, the resulting JSON is written to it.
1191
+ #
1192
+ # If the number of nested arrays or objects exceeds _limit_, an ArgumentError
1193
+ # exception is raised. This argument is similar (but not exactly the
1194
+ # same!) to the _limit_ argument in Marshal.dump.
1195
+ #
1196
+ # The default options for the generator can be changed via the
1197
+ # dump_default_options method.
1198
+ #
1199
+ # This method is part of the implementation of the load/dump interface of
1200
+ # Marshal and YAML.
1201
+ def dump(obj, anIO = nil, limit = nil)
1202
+ if anIO and limit.nil?
1203
+ anIO = anIO.to_io if anIO.respond_to?(:to_io)
1204
+ unless anIO.respond_to?(:write)
1205
+ limit = anIO
1206
+ anIO = nil
1207
+ end
1208
+ end
1209
+ opts = JSON.dump_default_options
1210
+ limit and opts.update(:max_nesting => limit)
1211
+ result = generate(obj, opts)
1212
+ if anIO
1213
+ anIO.write result
1214
+ anIO
1215
+ else
1216
+ result
1217
+ end
1218
+ rescue JSON::NestingError
1219
+ raise ArgumentError, "exceed depth limit"
1220
+ end
1221
+
1222
+ # Swap consecutive bytes of _string_ in place.
1223
+ def self.swap!(string) # :nodoc:
1224
+ 0.upto(string.size / 2) do |i|
1225
+ break unless string[2 * i + 1]
1226
+ string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
1227
+ end
1228
+ string
1229
+ end
1230
+
1231
+ # Shortuct for iconv.
1232
+ if ::String.method_defined?(:encode)
1233
+ # Encodes string using Ruby's _String.encode_
1234
+ def self.iconv(to, from, string)
1235
+ string.encode(to, from)
1236
+ end
1237
+ else
1238
+ require 'iconv'
1239
+ # Encodes string using _iconv_ library
1240
+ def self.iconv(to, from, string)
1241
+ Iconv.conv(to, from, string)
1242
+ end
1243
+ end
1244
+
1245
+ if ::Object.method(:const_defined?).arity == 1
1246
+ def self.const_defined_in?(modul, constant)
1247
+ modul.const_defined?(constant)
1248
+ end
1249
+ else
1250
+ def self.const_defined_in?(modul, constant)
1251
+ modul.const_defined?(constant, false)
1252
+ end
1253
+ end
1254
+ end
1255
+
1256
+ module ::Kernel
1257
+ private
1258
+
1259
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
1260
+ # one line.
1261
+ def j(*objs)
1262
+ objs.each do |obj|
1263
+ puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
1264
+ end
1265
+ nil
1266
+ end
1267
+
1268
+ # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
1269
+ # indentation and over many lines.
1270
+ def jj(*objs)
1271
+ objs.each do |obj|
1272
+ puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
1273
+ end
1274
+ nil
1275
+ end
1276
+
1277
+ # If _object_ is string-like, parse the string and return the parsed result as
1278
+ # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
1279
+ # structure object and return it.
1280
+ #
1281
+ # The _opts_ argument is passed through to generate/parse respectively. See
1282
+ # generate and parse for their documentation.
1283
+ def JSON(object, *args)
1284
+ if object.respond_to? :to_str
1285
+ JSON.parse(object.to_str, args.first)
1286
+ else
1287
+ JSON.generate(object, args.first)
1288
+ end
1289
+ end
1290
+ end
1291
+
1292
+ # Extends any Class to include _json_creatable?_ method.
1293
+ class ::Class
1294
+ # Returns true if this class can be used to create an instance
1295
+ # from a serialised JSON string. The class has to implement a class
1296
+ # method _json_create_ that expects a hash as first parameter. The hash
1297
+ # should include the required data.
1298
+ def json_creatable?
1299
+ respond_to?(:json_create)
1300
+ end
1301
+ end
1302
+
1303
+ JSON.generator = JSON::Pure::Generator
1304
+ JSON.parser = JSON::Pure::Parser