sprout-ruby-aws 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History.txt +38 -0
  2. data/LICENSE.txt +202 -0
  3. data/Manifest.txt +69 -0
  4. data/NOTICE.txt +4 -0
  5. data/README.txt +105 -0
  6. data/Rakefile +20 -0
  7. data/bin/ruby-aws +9 -0
  8. data/lib/amazon/util.rb +10 -0
  9. data/lib/amazon/util/binder.rb +44 -0
  10. data/lib/amazon/util/data_reader.rb +157 -0
  11. data/lib/amazon/util/filter_chain.rb +79 -0
  12. data/lib/amazon/util/hash_nesting.rb +93 -0
  13. data/lib/amazon/util/lazy_results.rb +59 -0
  14. data/lib/amazon/util/logging.rb +23 -0
  15. data/lib/amazon/util/paginated_iterator.rb +70 -0
  16. data/lib/amazon/util/proactive_results.rb +116 -0
  17. data/lib/amazon/util/threadpool.rb +129 -0
  18. data/lib/amazon/util/user_data_store.rb +100 -0
  19. data/lib/amazon/webservices/mechanical_turk.rb +117 -0
  20. data/lib/amazon/webservices/mechanical_turk_requester.rb +340 -0
  21. data/lib/amazon/webservices/mturk/mechanical_turk_error_handler.rb +136 -0
  22. data/lib/amazon/webservices/mturk/question_generator.rb +58 -0
  23. data/lib/amazon/webservices/util/amazon_authentication_relay.rb +64 -0
  24. data/lib/amazon/webservices/util/command_line.rb +156 -0
  25. data/lib/amazon/webservices/util/convenience_wrapper.rb +90 -0
  26. data/lib/amazon/webservices/util/filter_proxy.rb +45 -0
  27. data/lib/amazon/webservices/util/mock_transport.rb +70 -0
  28. data/lib/amazon/webservices/util/request_signer.rb +42 -0
  29. data/lib/amazon/webservices/util/rest_transport.rb +108 -0
  30. data/lib/amazon/webservices/util/soap_simplifier.rb +48 -0
  31. data/lib/amazon/webservices/util/soap_transport.rb +38 -0
  32. data/lib/amazon/webservices/util/soap_transport_header_handler.rb +27 -0
  33. data/lib/amazon/webservices/util/unknown_result_exception.rb +27 -0
  34. data/lib/amazon/webservices/util/validation_exception.rb +55 -0
  35. data/lib/amazon/webservices/util/xml_simplifier.rb +61 -0
  36. data/lib/ruby-aws.rb +21 -0
  37. data/lib/ruby-aws/version.rb +8 -0
  38. data/samples/mturk/best_image/BestImage.rb +61 -0
  39. data/samples/mturk/best_image/best_image.properties +39 -0
  40. data/samples/mturk/best_image/best_image.question +82 -0
  41. data/samples/mturk/blank_slate/BlankSlate.rb +63 -0
  42. data/samples/mturk/blank_slate/BlankSlate_multithreaded.rb +67 -0
  43. data/samples/mturk/helloworld/MTurkHelloWorld.rb +56 -0
  44. data/samples/mturk/helloworld/mturk.yml +8 -0
  45. data/samples/mturk/reviewer/Reviewer.rb +103 -0
  46. data/samples/mturk/reviewer/mturk.yml +8 -0
  47. data/samples/mturk/simple_survey/SimpleSurvey.rb +90 -0
  48. data/samples/mturk/simple_survey/simple_survey.question +30 -0
  49. data/samples/mturk/site_category/SiteCategory.rb +87 -0
  50. data/samples/mturk/site_category/externalpage.htm +71 -0
  51. data/samples/mturk/site_category/site_category.input +6 -0
  52. data/samples/mturk/site_category/site_category.properties +45 -0
  53. data/samples/mturk/site_category/site_category.question +9 -0
  54. data/test/mturk/test_changehittypeofhit.rb +130 -0
  55. data/test/mturk/test_error_handler.rb +135 -0
  56. data/test/mturk/test_mechanical_turk_requester.rb +178 -0
  57. data/test/mturk/test_mock_mechanical_turk_requester.rb +205 -0
  58. data/test/test_ruby-aws.rb +22 -0
  59. data/test/unit/test_binder.rb +89 -0
  60. data/test/unit/test_data_reader.rb +135 -0
  61. data/test/unit/test_exceptions.rb +32 -0
  62. data/test/unit/test_hash_nesting.rb +93 -0
  63. data/test/unit/test_lazy_results.rb +89 -0
  64. data/test/unit/test_mock_transport.rb +132 -0
  65. data/test/unit/test_paginated_iterator.rb +58 -0
  66. data/test/unit/test_proactive_results.rb +108 -0
  67. data/test/unit/test_question_generator.rb +54 -0
  68. data/test/unit/test_threadpool.rb +50 -0
  69. data/test/unit/test_user_data_store.rb +80 -0
  70. metadata +177 -0
@@ -0,0 +1,340 @@
1
+ # Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'erb'
5
+ require 'monitor'
6
+ require 'amazon/util'
7
+ require 'amazon/webservices/util/xml_simplifier'
8
+ require 'amazon/webservices/util/convenience_wrapper'
9
+ require 'amazon/webservices/mechanical_turk'
10
+
11
+ module Amazon
12
+ module WebServices
13
+
14
+ class MechanicalTurkRequester < Amazon::WebServices::Util::ConvenienceWrapper
15
+
16
+ WSDL_VERSION = "2007-06-21"
17
+
18
+ ABANDONMENT_RATE_QUALIFICATION_TYPE_ID = "00000000000000000070";
19
+ APPROVAL_RATE_QUALIFICATION_TYPE_ID = "000000000000000000L0";
20
+ REJECTION_RATE_QUALIFICATION_TYPE_ID = "000000000000000000S0";
21
+ RETURN_RATE_QUALIFICATION_TYPE_ID = "000000000000000000E0";
22
+ SUBMISSION_RATE_QUALIFICATION_TYPE_ID = "00000000000000000000";
23
+ LOCALE_QUALIFICATION_TYPE_ID = "00000000000000000071";
24
+
25
+ DEFAULT_THREADCOUNT = 10
26
+
27
+ serviceCall :RegisterHITType, :RegisterHITTypeResult, {
28
+ :AssignmentDurationInSeconds => 60*60,
29
+ :AutoApprovalDelayInSeconds => 60*60*24*7
30
+ }
31
+
32
+ serviceCall :CreateHIT, :HIT, { :MaxAssignments => 1,
33
+ :AssignmentDurationInSeconds => 60*60,
34
+ :AutoApprovalDelayInSeconds => 60*60*24*7,
35
+ :LifetimeInSeconds => 60*60*24,
36
+ :ResponseGroup => %w( HITQuestion Minimal)
37
+ }
38
+
39
+ serviceCall :DisableHIT, :DisableHITResult
40
+ serviceCall :DisposeHIT, :DisposeHITResult
41
+ serviceCall :ExtendHIT, :ExtendHITResult
42
+ serviceCall :ForceExpireHIT, :ForceExpireHITResult
43
+ serviceCall :GetHIT, :HIT, { :ResponseGroup => %w( Minimal HITDetail HITQuestion HITAssignmentSummary ) }
44
+ serviceCall :ChangeHITTypeOfHIT, :ChangeHITTypeOfHITResult
45
+
46
+ serviceCall :SearchHITs, :SearchHITsResult
47
+ serviceCall :GetReviewableHITs, :GetReviewableHITsResult
48
+ serviceCall :SetHITAsReviewing, :SetHITAsReviewingResult
49
+ serviceCall :GetAssignmentsForHIT, :GetAssignmentsForHITResult
50
+ serviceCall :ApproveAssignment, :ApproveAssignmentResult
51
+ serviceCall :RejectAssignment, :RejectAssignmentResult
52
+
53
+ paginate :SearchHITs, :HIT
54
+ paginate :GetReviewableHITs, :HIT
55
+ paginate :GetAssignmentsForHIT, :Assignment
56
+
57
+ serviceCall :GrantBonus, :GrantBonusResult
58
+ serviceCall :GetBonusPayments, :GetBonusPaymentsResult
59
+
60
+ serviceCall :CreateQualificationType, :QualificationType, { :QualificationTypeStatus => 'Active' }
61
+ serviceCall :GetQualificationType, :QualificationType
62
+ serviceCall :SearchQualificationTypes, :SearchQualificationTypesResult, { :MustBeRequestable => true }
63
+ serviceCall :UpdateQualificationType, :QualificationType
64
+ serviceCall :GetQualificationsForQualificationType, :GetQualificationsForQualificationTypeResult, { :Status => 'Granted' }
65
+ serviceCall :GetHITsForQualificationType, :GetHITsForQualificationTypeResult
66
+
67
+ paginate :SearchQualificationTypes, :QualificationType
68
+ paginate :GetQualificationsForQualificationType, :Qualification
69
+
70
+ serviceCall :AssignQualification, :AssignQualificationResult
71
+ serviceCall :GetQualificationRequests, :GetQualificationRequestsResult
72
+ serviceCall :GrantQualification, :GrantQualificationResult
73
+ serviceCall :RejectQualificationRequest, :RejectQualificationRequestResult
74
+ serviceCall :GetQualificationScore, :Qualification
75
+ serviceCall :UpdateQualificationScore, :UpdateQualificationScoreResult
76
+ serviceCall :RevokeQualification, :RevokeQualificationResult
77
+
78
+ paginate :GetQualificationRequests, :QualificationRequest
79
+
80
+ serviceCall :SetHITTypeNotification, :SetHITTypeNotificationResult
81
+ serviceCall :SetWorkerAcceptLimit, :SetWorkerAcceptLimitResult
82
+ serviceCall :GetWorkerAcceptLimit, :GetWorkerAcceptLimitResult
83
+ serviceCall :BlockWorker, :BlockWorkerResult
84
+ serviceCall :UnblockWorker, :BlockWorkerResult
85
+
86
+ serviceCall :GetFileUploadURL, :GetFileUploadURLResult
87
+ serviceCall :GetAccountBalance, :GetAccountBalanceResult
88
+ serviceCall :GetRequesterStatistic, :GetStatisticResult, { :Count => 1 }
89
+
90
+ serviceCall :NotifyWorkers, :NotifyWorkersResult
91
+
92
+ def initialize(args={})
93
+ newargs = args.dup
94
+ unless args[:Config].nil?
95
+ loaded = Amazon::Util::DataReader.load( args[:Config], :YAML )
96
+ newargs = args.merge loaded.inject({}) {|a,b| a[b[0].to_sym] = b[1] ; a }
97
+ end
98
+ @threadcount = args[:ThreadCount].to_i
99
+ @threadcount = DEFAULT_THREADCOUNT unless @threadcount >= 1
100
+ raise "Cannot override WSDL version ( #{WSDL_VERSION} )" unless args[:Version].nil? or args[:Version].equals? WSDL_VERSION
101
+ super newargs.merge( :Name => :AWSMechanicalTurkRequester,
102
+ :ServiceClass => Amazon::WebServices::MechanicalTurk,
103
+ :Version => WSDL_VERSION )
104
+ end
105
+
106
+ # Create a series of similar HITs, sharing common parameters. Utilizes HITType
107
+ # * hit_template is the array of parameters to pass to createHIT.
108
+ # * question_template will be passed as a template into ERB to generate the :Question parameter
109
+ # * the RequesterAnnotation parameter of hit_template will also be passed through ERB
110
+ # * hit_data_set should consist of an array of hashes defining unique instance variables utilized by question_template
111
+ def createHITs( hit_template, question_template, hit_data_set )
112
+ hit_template = hit_template.dup
113
+ lifetime = hit_template[:LifetimeInSeconds]
114
+ numassignments_template = hit_template[:MaxAssignments]
115
+ annotation_template = hit_template[:RequesterAnnotation]
116
+ hit_template.delete :LifetimeInSeconds
117
+ hit_template.delete :MaxAssignments
118
+ hit_template.delete :RequesterAnnotation
119
+
120
+ ht = hit_template[:HITTypeId] || registerHITType( hit_template )[:HITTypeId]
121
+
122
+ tp = Amazon::Util::ThreadPool.new @threadcount
123
+
124
+ created = [].extend(MonitorMixin)
125
+ failed = [].extend(MonitorMixin)
126
+ hit_data_set.each do |hd|
127
+ tp.addWork(hd) do |hit_data|
128
+ begin
129
+ b = Amazon::Util::Binder.new( hit_data )
130
+ annotation = b.erb_eval( annotation_template )
131
+ numassignments = b.erb_eval( numassignments_template.to_s ).to_i
132
+ question = b.erb_eval( question_template )
133
+ result = self.createHIT( :HITTypeId => ht,
134
+ :LifetimeInSeconds => lifetime,
135
+ :MaxAssignments => ( hit_data[:MaxAssignments] || numassignments || 1 ),
136
+ :Question => question,
137
+ :RequesterAnnotation => ( hit_data[:RequesterAnnotation] || annotation || "")
138
+ )
139
+ created.synchronize do
140
+ created << result
141
+ end
142
+ rescue => e
143
+ failed.synchronize do
144
+ failed << hit_data.merge( :Error => e.message, :Description => e.description )
145
+ end
146
+ end
147
+ end # tp.addWork
148
+ end # hit_data_set.each
149
+ tp.finish
150
+
151
+ return :Created => created, :Failed => failed
152
+ end
153
+
154
+ # Update a series of HITs to belong to a new HITType
155
+ # * hit_template is the array of parameters to pass to registerHITType
156
+ # * hit_ids is a list of HITIds (strings)
157
+ def updateHITs( hit_template, hit_ids )
158
+ hit_template = hit_template.dup
159
+ hit_template.delete :LifetimeInSeconds
160
+ hit_template.delete :RequesterAnnotation
161
+
162
+ hit_type_id = registerHITType( hit_template )[:HITTypeId]
163
+
164
+ tp = Amazon::Util::ThreadPool.new @threadcount
165
+
166
+ updated = [].extend(MonitorMixin)
167
+ failed = [].extend(MonitorMixin)
168
+ hit_ids.each do |hid|
169
+ tp.addWork(hid) do |hit_id|
170
+ begin
171
+ changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
172
+ updated.synchronize do
173
+ updated << hit_id
174
+ end
175
+ rescue => e
176
+ failed.synchronize do
177
+ failed << { :HITId => hit_id, :Error => e.message }
178
+ end
179
+ end
180
+ end # tp.addWork
181
+ end # hit_ids.each
182
+ tp.finish
183
+
184
+ return :Updated => updated, :Failed => failed
185
+ end
186
+
187
+ # Forces expiration of hits provided. Useful for pausing HITs
188
+ # * hit_ids is an array of HITIds
189
+ def forceExpireHITs(hit_ids)
190
+ tp = Amazon::Util::ThreadPool.new @threadcount
191
+
192
+ expired = [].extend(MonitorMixin)
193
+ failed = [].extend(MonitorMixin)
194
+ hit_ids.each do |hid|
195
+ tp.addWork(hid) do |hit_id|
196
+ begin
197
+ forceExpireHIT( :HITId => hit_id )
198
+ expired.synchronize do
199
+ expired << hit_id
200
+ end
201
+ rescue => e
202
+ failed.synchronize do
203
+ failed << { :HITId => hit_id, :Error => e.message }
204
+ end
205
+ end
206
+ end # tp.addWork
207
+ end # hit_ids.each
208
+ tp.finish
209
+
210
+ return :Expired => expired, :Failed => failed
211
+ end
212
+
213
+ # Adds time for given hits. Useful for resuming paused/expired HITs
214
+ # * hit_ids is an array of HITIds
215
+ # * time is time in second we want to add
216
+ def extendHITs(hit_ids, time)
217
+ tp = Amazon::Util::ThreadPool.new @threadcount
218
+
219
+ extended = [].extend(MonitorMixin)
220
+ failed = [].extend(MonitorMixin)
221
+ hit_ids.each do |hid|
222
+ tp.addWork(hid) do |hit_id|
223
+ begin
224
+ extendHIT( :HITId => hit_id, :ExpirationIncrementInSeconds => time )
225
+ extended.synchronize do
226
+ extended << hit_id
227
+ end
228
+ rescue => e
229
+ failed.synchronize do
230
+ failed << { :HITId => hit_id, :Error => e.message }
231
+ end
232
+ end
233
+ end # tp.addWork
234
+ end # hit_ids.each
235
+ tp.finish
236
+
237
+ return :Extended => extended, :Failed => failed
238
+ end
239
+
240
+ # Deletes given hits from mturk
241
+ # * hit_ids is an array of HITIds
242
+ def deleteHITs(hit_ids)
243
+ tp = Amazon::Util::ThreadPool.new @threadcount
244
+
245
+ deleted = [].extend(MonitorMixin)
246
+ failed = [].extend(MonitorMixin)
247
+ hit_ids.each do |hid|
248
+ tp.addWork(hid) do |hit_id|
249
+ begin
250
+ disableHIT( :HITId => hit_id )
251
+ deleted.synchronize do
252
+ deleted << hit_id
253
+ end
254
+ rescue => e
255
+ failed.synchronize do
256
+ failed << { :HITId => hit_id, :Error => e.message }
257
+ end
258
+ end
259
+ end # tp.addWork
260
+ end # hit_ids.each
261
+ tp.finish
262
+
263
+ return :Deleted => deleted, :Failed => failed
264
+ end
265
+
266
+ # Update a HIT with new properties.
267
+ # hit_id:: Id of the HIT to update
268
+ # hit_template:: hash ( parameter => value ) of parameters to update
269
+ #
270
+ # Acceptable attributes:
271
+ # * Title
272
+ # * Description
273
+ # * Keywords
274
+ # * Reward
275
+ # * QualificationRequirement
276
+ # * AutoApprovalDelayInSeconds
277
+ # * AssignmentDurationInSeconds
278
+ #
279
+ # Behind the scenes, this function retrieves the HIT, merges the HITs
280
+ # current attributes with any you specify, and registers a new HIT
281
+ # Template. It then uses the new ChangeHITTypeOfHIT function to move
282
+ # your HIT to the newly-created HIT Template.
283
+ def updateHIT( hit_id, hit_template )
284
+ hit_template = hit_template.dup
285
+
286
+ hit = getHIT( :HITId => hit_id )
287
+
288
+ props = %w( Title Description Keywords Reward QualificationRequirement
289
+ AutoApprovalDelayInSeconds AssignmentDurationInSeconds
290
+ ).collect {|str| str.to_sym }
291
+
292
+ props.each do |p|
293
+ hit_template[p] = hit[p] if hit_template[p].nil?
294
+ end
295
+
296
+ hit_type_id = registerHITType( hit_template )[:HITTypeId]
297
+
298
+ changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
299
+ end
300
+
301
+
302
+ def getHITResults( list )
303
+ results = [].extend(MonitorMixin)
304
+ tp = Amazon::Util::ThreadPool.new @threadcount
305
+ list.each do |line|
306
+ tp.addWork(line) do |h|
307
+ hit = getHIT( :HITId => h[:HITId] )
308
+ getAssignmentsForHITAll( :HITId => h[:HITId] ).each {|assignment|
309
+ results.synchronize do
310
+ results << ( hit.merge( assignment ) )
311
+ end
312
+ }
313
+ end
314
+ end
315
+ tp.finish
316
+ results.flatten
317
+ end
318
+
319
+ # Returns available funds in USD
320
+ # Calls getAccountBalance and parses out the correct amount
321
+ def availableFunds
322
+ return getAccountBalance[:AvailableBalance][:Amount]
323
+ end
324
+
325
+ # helper function to simplify answer XML
326
+ def simplifyAnswer( answerXML )
327
+ answerHash = Amazon::WebServices::Util::XMLSimplifier.simplify REXML::Document.new(answerXML)
328
+ list = [answerHash[:Answer]].flatten
329
+ list.inject({}) { |list, answer|
330
+ id = answer[:QuestionIdentifier]
331
+ result = answer[:FreeText] || answer[:SelectionIdentifier] || answer[:UploadedFileKey]
332
+ list[id] = result
333
+ list
334
+ }
335
+ end
336
+
337
+ end # MechanicalTurkRequester
338
+
339
+ end # Amazon::WebServices
340
+ end # Amazon
@@ -0,0 +1,136 @@
1
+ # Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'amazon/util/logging'
5
+ require 'amazon/webservices/util/validation_exception'
6
+ require 'amazon/webservices/util/unknown_result_exception'
7
+
8
+ module Amazon
9
+ module WebServices
10
+ module MTurk
11
+
12
+ class MechanicalTurkErrorHandler
13
+ include Amazon::Util::Logging
14
+
15
+ REQUIRED_PARAMETERS = [:Relay]
16
+
17
+ # Commands with these prefixes can be retried if we are unsure of success
18
+ RETRY_PRE = %w( search get register update disable assign set dispose )
19
+
20
+ # Max number of times to retry a call
21
+ MAX_RETRY = 6
22
+
23
+ # Base used in Exponential Backoff retry delay
24
+ BACKOFF_BASE = 2
25
+ # Scale factor for Exponential Backoff retry delay
26
+ BACKOFF_INITIAL = 0.1
27
+
28
+ # Matching pattern to find a 'Results' element in the Response
29
+ RESULT_PATTERN = /Result/
30
+ # Additional elements to be considered a 'Result' despite not matching RESULT_PATTERN
31
+ ACCEPTABLE_RESULTS = %w( HIT Qualification QualificationType QualificationRequest Information )
32
+
33
+ def initialize( args )
34
+ missing_parameters = REQUIRED_PARAMETERS - args.keys
35
+ raise "Missing paramters: #{missing_parameters.join(',')}" unless missing_parameters.empty?
36
+ @relay = args[:Relay]
37
+ end
38
+
39
+ def dispatch(method, *args)
40
+ try = 0
41
+ begin
42
+ try += 1
43
+ log "Dispatching call to #{method} (try #{try})"
44
+ response = @relay.send(method,*args)
45
+ validateResponse( response )
46
+ return response
47
+ rescue Exception => error
48
+ case handleError( error,method )
49
+ when :RetryWithBackoff
50
+ retry if doBackoff( try )
51
+ when :RetryImmediate
52
+ retry if canRetry( try )
53
+ when :Ignore
54
+ return :IgnoredError => error
55
+ when :Unknown
56
+ raise Util::UnknownResultException.new( error, method, args )
57
+ when :Fail
58
+ raise error
59
+ else
60
+ raise "Unknown error handling method: #{handleError( error,method )}"
61
+ end
62
+ raise error
63
+ end
64
+ end
65
+
66
+ def methodRetryable( method )
67
+ RETRY_PRE.each do |pre|
68
+ return true if method.to_s =~ /^#{pre}/i
69
+ end
70
+ return false
71
+ end
72
+
73
+ def handleError( error, method )
74
+ log "Handling error: #{error.inspect}"
75
+ case error.class.to_s
76
+ when 'Timeout::Error','SOAP::HTTPStreamError'
77
+ if methodRetryable( method )
78
+ return :RetryImmediate
79
+ else
80
+ return :Unknown
81
+ end
82
+ when 'SOAP::FaultError'
83
+ case error.faultcode.data
84
+ when "aws:Server.ServiceUnavailable"
85
+ return :RetryWithBackoff
86
+ else
87
+ return :Unknown
88
+ end
89
+ when 'Amazon::WebServices::Util::ValidationException'
90
+ return :Fail
91
+ when 'RuntimeError'
92
+ case error.message
93
+ when 'Throttled'
94
+ return :RetryWithBackoff
95
+ else
96
+ return :RetryImmediate
97
+ end
98
+ else
99
+ return :Unknown
100
+ end
101
+ end
102
+
103
+ def canRetry( try )
104
+ try <= MAX_RETRY
105
+ end
106
+
107
+ def doBackoff( try )
108
+ return false unless canRetry(try)
109
+ delay = BACKOFF_INITIAL * ( BACKOFF_BASE ** try )
110
+ sleep delay
111
+ return true
112
+ end
113
+
114
+ def isResultTag( tag )
115
+ tag.to_s =~ RESULT_PATTERN or ACCEPTABLE_RESULTS.include?( tag.to_s )
116
+ end
117
+
118
+ def validateResponse(response)
119
+ log "Validating response: #{response.inspect}"
120
+ raise 'Throttled' if response[:Errors] and response[:Errors][:Error] and response[:Errors][:Error][:Code] == "ServiceUnavailable"
121
+ raise Util::ValidationException.new(response) unless response[:OperationRequest][:Errors].nil?
122
+ resultTags = response.keys.find_all {|r| isResultTag( r ) }
123
+ raise Util::ValidationException.new(response, "Didn't get back an acceptable result tag (got back #{response.keys.join(',')})") if resultTags.empty?
124
+ resultTags.each do |resultTag|
125
+ log "using result tag <#{resultTag}>"
126
+ result = response[resultTag]
127
+ raise Util::ValidationException.new(response) unless result[:Request][:Errors].nil?
128
+ end
129
+ response
130
+ end
131
+
132
+ end # MechanicalTurkErrorHandler
133
+
134
+ end # Amazon::WebServices::MTurk
135
+ end # Amazon::WebServices
136
+ end # Amazon