elastic-mapreduce 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.
Files changed (60) hide show
  1. data/CHANGELOG +51 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +16 -0
  4. data/LICENSE.txt +393 -0
  5. data/NOTICE.txt +26 -0
  6. data/README +1007 -0
  7. data/Rakefile +35 -0
  8. data/VERSION +1 -0
  9. data/bin/elastic-mapreduce +27 -0
  10. data/cacert.pem +280 -0
  11. data/elastic-mapreduce.gemspec +104 -0
  12. data/lib/amazon/aws/exceptions.rb +211 -0
  13. data/lib/amazon/coral/awsquery.rb +128 -0
  14. data/lib/amazon/coral/awsquerychainhelper.rb +92 -0
  15. data/lib/amazon/coral/awsqueryhandler.rb +170 -0
  16. data/lib/amazon/coral/awsqueryurihandler.rb +34 -0
  17. data/lib/amazon/coral/call.rb +68 -0
  18. data/lib/amazon/coral/dispatcher.rb +33 -0
  19. data/lib/amazon/coral/ec2client.rb +91 -0
  20. data/lib/amazon/coral/elasticmapreduceclient.rb +198 -0
  21. data/lib/amazon/coral/handler.rb +20 -0
  22. data/lib/amazon/coral/httpdelegationhelper.rb +27 -0
  23. data/lib/amazon/coral/httpdestinationhandler.rb +36 -0
  24. data/lib/amazon/coral/httphandler.rb +124 -0
  25. data/lib/amazon/coral/identityhandler.rb +32 -0
  26. data/lib/amazon/coral/job.rb +25 -0
  27. data/lib/amazon/coral/logfactory.rb +35 -0
  28. data/lib/amazon/coral/option.rb +70 -0
  29. data/lib/amazon/coral/orchestrator.rb +49 -0
  30. data/lib/amazon/coral/querystringmap.rb +93 -0
  31. data/lib/amazon/coral/service.rb +130 -0
  32. data/lib/amazon/coral/simplelog.rb +98 -0
  33. data/lib/amazon/coral/urlencoding.rb +19 -0
  34. data/lib/amazon/coral/v0signaturehandler.rb +33 -0
  35. data/lib/amazon/coral/v0signaturehelper.rb +83 -0
  36. data/lib/amazon/coral/v1signaturehandler.rb +32 -0
  37. data/lib/amazon/coral/v1signaturehelper.rb +58 -0
  38. data/lib/amazon/coral/v2signaturehandler.rb +46 -0
  39. data/lib/amazon/coral/v2signaturehelper.rb +76 -0
  40. data/lib/amazon/retry_delegator.rb +66 -0
  41. data/lib/amazon/stderr_logger.rb +23 -0
  42. data/lib/client.rb +117 -0
  43. data/lib/commands.rb +1690 -0
  44. data/lib/credentials.rb +86 -0
  45. data/lib/ec2_client_wrapper.rb +73 -0
  46. data/lib/json/lexer.rb +294 -0
  47. data/lib/json/objects.rb +200 -0
  48. data/lib/json.rb +58 -0
  49. data/lib/simple_executor.rb +11 -0
  50. data/lib/simple_logger.rb +38 -0
  51. data/lib/uuidtools/version.rb +32 -0
  52. data/lib/uuidtools.rb +655 -0
  53. data/run_tests.rb +8 -0
  54. data/samples/freebase/code/freebase_jobflow.json +44 -0
  55. data/samples/similarity/lastfm_jobflow.json +78 -0
  56. data/samples/wordSplitter.py +18 -0
  57. data/tests/commands_test.rb +587 -0
  58. data/tests/credentials.json +7 -0
  59. data/tests/example.json +14 -0
  60. metadata +154 -0
@@ -0,0 +1,86 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'json'
5
+
6
+ class Credentials
7
+ def initialize(commands)
8
+ @options = commands.global_options
9
+ end
10
+
11
+ def parse_credentials(credentials, options)
12
+ conversions = [
13
+ # These first ones use an incorrect naming convenion,
14
+ # but we keep them around for backwards compatibility
15
+ [:aws_access_id, "access_id"],
16
+ [:aws_secret_key, "private_key"],
17
+ [:key_pair, "keypair"],
18
+ [:key_pair_file, "keypair-file"],
19
+ [:log_uri, "log_uri"],
20
+
21
+ # Now the current ones
22
+ [:aws_access_id, "access-id"],
23
+ [:aws_secret_key, "private-key"],
24
+ [:key_pair, "key-pair"],
25
+ [:key_pair_file, "key-pair-file"],
26
+ [:log_uri, "log-uri"],
27
+ [:endpoint, "endpoint"],
28
+ [:region, "region"],
29
+ [:enable_debugging, "enable-debugging"],
30
+ [:hadoop_version, "hadoop-version"],
31
+ ]
32
+
33
+ env_options = [
34
+ ['ELASTIC_MAPREDUCE_ACCESS_ID', :aws_access_id],
35
+ ['ELASTIC_MAPREDUCE_PRIVATE_KEY', :aws_secret_key],
36
+ ['ELASTIC_MAPREDUCE_KEY_PAIR', :key_pair],
37
+ ['ELASTIC_MAPREDUCE_KEY_PAIR_FILE', :key_pair_file],
38
+ ['ELASTIC_MAPREDUCE_LOG_URI', :log_uri],
39
+ ['ELASTIC_MAPREDUCE_APPS_PATH', :apps_path],
40
+ ['ELASTIC_MAPREDUCE_BETA_PATH', :beta_path],
41
+ ['ELASTIC_MAPREDUCE_ENDPOINT', :endpoint],
42
+ ['ELASTIC_MAPREDUCE_REGION', :region],
43
+ ['ELASTIC_MAPREDUCE_HADOOP_VERSION', :hadoop_version],
44
+ ['ELASTIC_MAPREDUCE_ENABLE_DEBUGGING', :enable_debugging]
45
+ ]
46
+
47
+ for env_key, option_key in env_options do
48
+ if ! options[option_key] && ENV[env_key] then
49
+ options[option_key] = ENV[env_key]
50
+ end
51
+ end
52
+
53
+ candidates = [
54
+ credentials,
55
+ ENV['ELASTIC_MAPREDUCE_CREDENTIALS'],
56
+ File.join(File.dirname(__FILE__), credentials),
57
+ File.join(ENV['HOME'], "." + credentials),
58
+ File.join(ENV['HOME'], credentials)
59
+ ]
60
+
61
+ filename = candidates.find { |fname| File.exist?(fname) if fname }
62
+ if filename != nil then
63
+ begin
64
+ credentials_hash = JSON.parse(File.read(filename))
65
+ for option_key, credentials_key in conversions do
66
+ if credentials_hash[credentials_key] && !options[option_key] then
67
+ options[option_key] = credentials_hash[credentials_key]
68
+ end
69
+ end
70
+ rescue Exception => e
71
+ raise RuntimeError, "Unable to parse #{filename}: #{e.message}"
72
+ end
73
+ end
74
+
75
+ error_if_nil(options[:aws_access_id], "Missing key access-id")
76
+ error_if_nil(options[:aws_secret_key], "Missing key private-key")
77
+ end
78
+
79
+ def error_if_nil(value, message)
80
+ if value == nil then
81
+ raise RuntimeError, message
82
+ end
83
+ end
84
+
85
+ end
86
+
@@ -0,0 +1,73 @@
1
+ #
2
+ # Copyright 2008-2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'credentials'
5
+ require 'amazon/retry_delegator'
6
+ require 'amazon/coral/ec2client'
7
+
8
+ class Ec2ClientWrapper
9
+ attr_accessor :commands, :logger, :options
10
+
11
+ def initialize(commands, logger)
12
+ @commands = commands
13
+ @logger = logger
14
+ @options = commands.global_options
15
+
16
+ @config = {
17
+ :endpoint => @options[:ec2_endpoint] || "https://ec2.amazonaws.com",
18
+ :api_version => '2010-11-15',
19
+ :ca_file => File.join(File.dirname(__FILE__), "cacert.pem"),
20
+ :aws_access_key => @options[:aws_access_id],
21
+ :aws_secret_key => @options[:aws_secret_key],
22
+ :signature_algorithm => :V2,
23
+ :verbose => (@options[:verbose] != nil)
24
+ }
25
+
26
+ @client = Amazon::RetryDelegator.new(
27
+ Amazon::Coral::Ec2Client.new_aws_query(@config),
28
+ :retry_if => Proc.new { |*opts| self.is_retryable_error_response(*opts) }
29
+ )
30
+ end
31
+
32
+ def is_retryable_error_response(response)
33
+ if response == nil then
34
+ false
35
+ else
36
+ ret = false
37
+ if response['Error'] then
38
+ # note: 'Timeout' is not retryable because the operation might have completed just the connection timed out
39
+ ret ||= ['Throttling', 'ServiceUnavailable'].include?(response['Error']['Code'])
40
+ end
41
+ ret
42
+ end
43
+ end
44
+
45
+ def is_error_response(response)
46
+ response != nil && response.key?('Error')
47
+ end
48
+
49
+ def raise_on_error(response)
50
+ if is_error_response(response) then
51
+ raise RuntimeError, response["Error"].inspect
52
+ end
53
+ return response
54
+ end
55
+
56
+ def allocate_address()
57
+ logger.trace "AllocateAddress()"
58
+ result = @client.AllocateAddress()
59
+ logger.trace result.inspect
60
+ return raise_on_error(result)
61
+ end
62
+
63
+ def associate_address(instance_id, public_ip)
64
+ logger.trace "AssociateAddress('InstanceId' => #{instance_id.inspect}, 'PublicIp' => #{public_ip.inspect})"
65
+ result = @client.AssociateAddress('InstanceId' => instance_id, 'PublicIp' => public_ip)
66
+ logger.trace result.inspect
67
+ return raise_on_error(result)
68
+ end
69
+
70
+ #TODO: Add stubs for all other Ec2 WS operations here, see http://s3.amazonaws.com/ec2-downloads/2010-11-15.ec2.wsdl
71
+
72
+ end
73
+
data/lib/json/lexer.rb ADDED
@@ -0,0 +1,294 @@
1
+ #
2
+ # Lexical analyzer for JSON
3
+ # Copyright (C) 2003,2005 Rafael R. Sevilla <dido@imperium.ph>
4
+ # This file is part of JSON for Ruby
5
+ #
6
+ # JSON for Ruby is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public License
8
+ # as published by the Free Software Foundation; either version 2.1 of
9
+ # the License, or (at your option) any later version.
10
+ #
11
+ # JSON for Ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with JSON for Ruby; if not, write to the Free
18
+ # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19
+ # 02111-1307 USA.
20
+ #
21
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
22
+ # Some bugs fixed by Adam Kramer (mailto:adam@the-kramers.net)
23
+ # Copyright:: Copyright (c) 2003,2005 Rafael R. Sevilla
24
+ # License:: GNU Lesser General Public License
25
+ #
26
+ require 'json/objects'
27
+ require 'cgi'
28
+
29
+ module JSON
30
+
31
+ VERSION ||= '1.1.2'
32
+
33
+ class Lexer
34
+ # This method will initialize the lexer to contain a string.
35
+ # =====Parameters
36
+ # +s+:: the string to initialize the lexer object with
37
+ def initialize(s)
38
+ @index = 0
39
+ @source = s
40
+ end
41
+
42
+ # Backs up the lexer status one character.
43
+ def back
44
+ @index -= 1 if @index > 0
45
+ end
46
+
47
+ def more?
48
+ return(@index < @source.length)
49
+ end
50
+
51
+ # Consumes the next character.
52
+ def nextchar
53
+ c = self.more?() ? @source[@index,1] : "\0"
54
+ @index += 1
55
+ return(c)
56
+ end
57
+
58
+ # Consumes the next character and check that it matches a specified
59
+ # character.
60
+ def nextmatch(char)
61
+ n = self.nextchar
62
+ raise "Expected '#{char}' and instead saw '#{n}'." if (n != char)
63
+ return(n)
64
+ end
65
+
66
+ # Read the next n characters from the string in the lexer.
67
+ # =====Parameters
68
+ # +n+:: the number of characters to read from the lexer
69
+ def nextchars(n)
70
+ raise "substring bounds error" if (@index + n > @source.length)
71
+ i = @index
72
+ @index += n
73
+ return(@source[i,n])
74
+ end
75
+
76
+ # Read the next n characters from the string with escape sequence
77
+ # processing.
78
+ def nextclean
79
+ while true
80
+ c = self.nextchar()
81
+ if (c == '/')
82
+ case self.nextchar()
83
+ when '/'
84
+ c = self.nextchar()
85
+ while c != "\n" && c != "\r" && c != "\0"
86
+ c = self.nextchar()
87
+ end
88
+ when '*'
89
+ while true
90
+ c = self.nextchar()
91
+ raise "unclosed comment" if (c == "\0")
92
+ if (c == '*')
93
+ break if (self.nextchar() == '/')
94
+ self.back()
95
+ end
96
+ end
97
+ else
98
+ self.back()
99
+ return '/';
100
+ end
101
+ elsif c == "\0" || c[0] > " "[0]
102
+ return(c)
103
+ end
104
+ end
105
+ end
106
+
107
+ # Given a Unicode code point, return a string giving its UTF-8
108
+ # representation based on RFC 2279.
109
+ def utf8str(code)
110
+ if (code & ~(0x7f)) == 0
111
+ # UCS-4 range 0x00000000 - 0x0000007F
112
+ return(code.chr)
113
+ end
114
+
115
+ buf = ""
116
+ if (code & ~(0x7ff)) == 0
117
+ # UCS-4 range 0x00000080 - 0x000007FF
118
+ buf << (0b11000000 | (code >> 6)).chr
119
+ buf << (0b10000000 | (code & 0b00111111)).chr
120
+ return(buf)
121
+ end
122
+
123
+ if (code & ~(0x000ffff)) == 0
124
+ # UCS-4 range 0x00000800 - 0x0000FFFF
125
+ buf << (0b11100000 | (code >> 12)).chr
126
+ buf << (0b10000000 | ((code >> 6) & 0b00111111)).chr
127
+ buf << (0b10000000 | (code & 0b0011111)).chr
128
+ return(buf)
129
+ end
130
+
131
+ # Not used -- JSON only has UCS-2, but for the sake
132
+ # of completeness
133
+ if (code & ~(0x1FFFFF)) == 0
134
+ # UCS-4 range 0x00010000 - 0x001FFFFF
135
+ buf << (0b11110000 | (code >> 18)).chr
136
+ buf << (0b10000000 | ((code >> 12) & 0b00111111)).chr
137
+ buf << (0b10000000 | ((code >> 6) & 0b00111111)).chr
138
+ buf << (0b10000000 | (code & 0b0011111)).chr
139
+ return(buf)
140
+ end
141
+
142
+ if (code & ~(0x03FFFFFF)) == 0
143
+ # UCS-4 range 0x00200000 - 0x03FFFFFF
144
+ buf << (0b11110000 | (code >> 24)).chr
145
+ buf << (0b10000000 | ((code >> 18) & 0b00111111)).chr
146
+ buf << (0b10000000 | ((code >> 12) & 0b00111111)).chr
147
+ buf << (0b10000000 | ((code >> 6) & 0b00111111)).chr
148
+ buf << (0b10000000 | (code & 0b0011111)).chr
149
+ return(buf)
150
+ end
151
+
152
+ # UCS-4 range 0x04000000 - 0x7FFFFFFF
153
+ buf << (0b11111000 | (code >> 30)).chr
154
+ buf << (0b10000000 | ((code >> 24) & 0b00111111)).chr
155
+ buf << (0b10000000 | ((code >> 18) & 0b00111111)).chr
156
+ buf << (0b10000000 | ((code >> 12) & 0b00111111)).chr
157
+ buf << (0b10000000 | ((code >> 6) & 0b00111111)).chr
158
+ buf << (0b10000000 | (code & 0b0011111)).chr
159
+ return(buf)
160
+ end
161
+
162
+ # Reads the next string, given a quote character (usually ' or ")
163
+ # =====Parameters
164
+ # +quot+: the next matching quote character to use
165
+ def nextstring(quot)
166
+ c = buf = ""
167
+ while true
168
+ c = self.nextchar()
169
+ case c
170
+ when /\0|\n\r/
171
+ raise "Unterminated string"
172
+ when "\\"
173
+ chr = self.nextchar()
174
+ case chr
175
+ when 'b'
176
+ buf << "\b"
177
+ when 't'
178
+ buf << "\t"
179
+ when 'n'
180
+ buf << "\n"
181
+ when 'f'
182
+ buf << "\f"
183
+ when 'r'
184
+ buf << "\r"
185
+ when 'u'
186
+ buf << utf8str(Integer("0x" + self.nextchars(4)))
187
+ else
188
+ buf << chr
189
+ end
190
+ else
191
+ return(buf) if (c == quot)
192
+ buf << c
193
+ end
194
+ end
195
+ end
196
+
197
+ # Reads the next group of characters that match a regular
198
+ # expresion.
199
+ #
200
+ def nextto(regex)
201
+ buf = ""
202
+ while (true)
203
+ c = self.nextchar()
204
+ if !(regex =~ c).nil? || c == '\0' || c == '\n' || c == '\r'
205
+ self.back() if (c != '\0')
206
+ return(buf.chomp())
207
+ end
208
+ buf += c
209
+ end
210
+ end
211
+
212
+ # Reads the next value from the string. This can return either a
213
+ # string, a FixNum, a floating point value, a JSON array, or a
214
+ # JSON object.
215
+ def nextvalue
216
+ c = self.nextclean
217
+ s = ""
218
+
219
+ case c
220
+ when /\"|\'/
221
+ return(self.nextstring(c))
222
+ when '{'
223
+ self.back()
224
+ return(Hash.new.from_json(self))
225
+ when '['
226
+ self.back()
227
+ return(Array.new.from_json(self))
228
+ else
229
+ buf = ""
230
+ while ((c =~ /"| |:|,|\]|\}|\/|\0/).nil?)
231
+ buf += c
232
+ c = self.nextchar()
233
+ end
234
+ self.back()
235
+ s = buf.chomp
236
+ case s
237
+ when "true"
238
+ return(true)
239
+ when "false"
240
+ return(false)
241
+ when "null"
242
+ return(nil)
243
+ when /^[0-9]|\.|-|\+/
244
+ if s =~ /[.]/ then
245
+ return Float(s)
246
+ else
247
+ return Integer(s)
248
+ end
249
+ end
250
+ if (s == "")
251
+ s = nil
252
+ end
253
+ return(s)
254
+ end
255
+ end
256
+
257
+ # Skip to the next instance of the character specified
258
+ # =====Parameters
259
+ # +to+:: Character to skip to
260
+ def skipto(to)
261
+ index = @index
262
+ loop {
263
+ c = self.nextchar()
264
+ if (c == '\0')
265
+ @index = index
266
+ return(c)
267
+ end
268
+ if (c == to)
269
+ self.back
270
+ return(c)
271
+ end
272
+ }
273
+ end
274
+
275
+ def unescape
276
+ @source = CGI::unescape(@source)
277
+ end
278
+
279
+ # Skip past the next instance of the character specified
280
+ # =====Parameters
281
+ # +to+:: the character to skip past
282
+ def skippast(to)
283
+ @index = @source.index(to, @index)
284
+ @index = (@index.nil?) ? @source.length : @index + to.length
285
+ end
286
+
287
+ def each
288
+ while (n = nextvalue)
289
+ yield(n)
290
+ end
291
+ end
292
+ end
293
+ end
294
+
@@ -0,0 +1,200 @@
1
+ #
2
+ # JSON Objects
3
+ # Copyright (C) 2003,2005 Rafael R. Sevilla <dido@imperium.ph>
4
+ # This file is part of JSON for Ruby
5
+ #
6
+ # JSON for Ruby is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public License
8
+ # as published by the Free Software Foundation; either version 2.1 of
9
+ # the License, or (at your option) any later version.
10
+ #
11
+ # JSON for Ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with JSON for Ruby; if not, write to the Free
18
+ # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19
+ # 02111-1307 USA.
20
+ #
21
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
22
+ # Heavily modified by Adam Kramer (mailto:adam@the-kramers.net)
23
+ # Copyright:: Copyright (c) 2003,2005 Rafael R. Sevilla
24
+ # License:: GNU Lesser General Public License
25
+ #
26
+
27
+
28
+ class Object
29
+ def to_json #all strings in json need to have double quotes around them, treat random objects as strings
30
+ String.to_json(to_s)
31
+ end
32
+ end
33
+
34
+
35
+ class NilClass
36
+ def to_json
37
+ "null"
38
+ end
39
+ end
40
+ class TrueClass
41
+ def to_json
42
+ "true"
43
+ end
44
+ end
45
+
46
+ class FalseClass
47
+ def to_json
48
+ "false"
49
+ end
50
+ end
51
+
52
+ class Numeric
53
+ def to_json
54
+ to_s
55
+ end
56
+ end
57
+
58
+ class String
59
+ # produce a string in double quotes with all the necessary quoting
60
+ # done
61
+ def to_json
62
+ return String.to_json(self)
63
+ end
64
+
65
+ def self.to_json(str)
66
+ return "\"\"" if (str.length == 0)
67
+ newstr = "\""
68
+ str.each_byte {
69
+ |b|
70
+ c = b.chr
71
+ case c
72
+ when /\\|\"|\//
73
+ newstr << "\\" + c
74
+ when "\b"
75
+ newstr << "\\b"
76
+ when "\t"
77
+ newstr << "\\t"
78
+ when "\n"
79
+ newstr << "\\n"
80
+ when "\f"
81
+ newstr << "\\f"
82
+ when "\r"
83
+ newstr << "\\r"
84
+ else
85
+ if (c < ' ')
86
+ t = "000" + sprintf("%0x", b)
87
+ newstr << ("\\u" + t[0,t.length - 4])
88
+ else
89
+ newstr << c
90
+ end
91
+ end
92
+ }
93
+ newstr += '"'
94
+ return(newstr)
95
+ end
96
+ end
97
+
98
+ class Array
99
+
100
+ # This method will return a string giving the contents of the JSON
101
+ # array in standard JSON format.
102
+ def to_json
103
+ retval = '['
104
+
105
+ first=true
106
+ self.each { |obj|
107
+ retval << ',' unless first
108
+ retval << obj.to_json
109
+ first=false
110
+ }
111
+ retval << "]"
112
+ return(retval)
113
+ end
114
+
115
+ # This method will parse a JSON array from the passed lexer
116
+ # object. It takes a lexer object which is about to read a JSON
117
+ # array. It raises a runtime error otherwise. It returns the
118
+ # original JSON array. This method is not intended to be used
119
+ # directly.
120
+ # =====Parameters
121
+ # +lexer+:: Lexer object to use
122
+ def from_json(lexer)
123
+ raise "A JSON Array must begin with '['" if (lexer.nextclean != "[")
124
+ return (self) if lexer.nextclean == ']'
125
+ lexer.back
126
+ loop {
127
+ self << lexer.nextvalue
128
+ case lexer.nextclean
129
+ when ','
130
+ return(self) if (lexer.nextclean == ']')
131
+ lexer.back
132
+ when ']'
133
+ return(self)
134
+ else
135
+ raise "Expected a ',' or ']'"
136
+ end
137
+ }
138
+ end
139
+ end
140
+
141
+
142
+
143
+
144
+ class Hash
145
+
146
+ # This method will serialize the hash into regular JSON format.
147
+ def to_json
148
+ retval = "{"
149
+
150
+ first = true
151
+ self.each {|key, val|
152
+ retval << "," unless first
153
+ key = key.to_s #keys in json hashes need to be strings, nothing else.
154
+ retval << key.to_json + ":"
155
+ retval << val.to_json
156
+ first = false
157
+ }
158
+ retval << "}"
159
+ return(retval)
160
+ end
161
+
162
+ # This method will parse a JSON object from the passed lexer
163
+ # object. It takes a lexer object which is about to read a JSON
164
+ # object. It raises a runtime error otherwise. It returns the
165
+ # original JSON object. This method probably shouldn't be used
166
+ # directly.
167
+ # =====Parameters
168
+ def from_json(lexer)
169
+ lexer.unescape if (lexer.nextclean == '%')
170
+ lexer.back
171
+ raise "A JSON Object must begin with '{'" if (lexer.nextclean != "{")
172
+ loop {
173
+ c = lexer.nextclean
174
+ key = nil
175
+ case c
176
+ when '\0'
177
+ raise "A JSON Object must end with '}'"
178
+ when '}'
179
+ return (self);
180
+ else
181
+ lexer.back
182
+ key = lexer.nextvalue().to_s()
183
+ end
184
+ raise "Expected a ':' after a key" if (lexer.nextclean() != ':')
185
+ self[key] = lexer.nextvalue()
186
+ case lexer.nextclean()
187
+ when ','
188
+ return(self) if (lexer.nextclean() == '}')
189
+ lexer.back
190
+ when '}'
191
+ return(self)
192
+ else
193
+ raise "Expected a ',' or '}'"
194
+ end
195
+ }
196
+ return(self)
197
+ end
198
+
199
+ end
200
+
data/lib/json.rb ADDED
@@ -0,0 +1,58 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'json/objects'
5
+ require 'json/lexer'
6
+ require 'stringio'
7
+
8
+ module JSON
9
+ def self.parse(str)
10
+ return Lexer.new(str).nextvalue
11
+ end
12
+
13
+ def self.pretty_generate(obj)
14
+ s = []
15
+ self.pretty_generate_recursive(obj, s, "")
16
+ return s.join("")
17
+ end
18
+
19
+ def self.pretty_generate_recursive(obj, stream, indent)
20
+ if obj.is_a?(Hash) then
21
+ if obj.size == 0 then
22
+ stream << "{}"
23
+ else
24
+ stream << "{\n"
25
+ first = true
26
+ for key, value in obj do
27
+ if first then
28
+ first = false
29
+ else
30
+ stream << "," << "\n"
31
+ end
32
+ stream << indent << " " << key.to_json.chomp << ": "
33
+ self.pretty_generate_recursive(value, stream, indent + " ")
34
+ end
35
+ stream << "\n" << indent << "}"
36
+ end
37
+ elsif obj.is_a?(Array)
38
+ if obj.size == 0 then
39
+ stream << "[]"
40
+ else
41
+ stream << "[\n"
42
+ first = true
43
+ for value in obj do
44
+ if first then
45
+ first = false
46
+ else
47
+ stream << "," << "\n"
48
+ end
49
+ stream << indent + " "
50
+ self.pretty_generate_recursive(value, stream, indent + " ")
51
+ end
52
+ stream << "\n" << indent << "]"
53
+ end
54
+ else
55
+ stream << obj.to_json.chomp
56
+ end
57
+ end
58
+ end