aws-sdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. data/.yardopts +6 -0
  2. data/LICENSE.txt +171 -0
  3. data/NOTICE.txt +2 -0
  4. data/README.rdoc +189 -0
  5. data/lib/aws-sdk.rb +14 -0
  6. data/lib/aws.rb +63 -0
  7. data/lib/aws/api_config.rb +45 -0
  8. data/lib/aws/api_config/.document +0 -0
  9. data/lib/aws/api_config/EC2-2011-02-28.yml +2314 -0
  10. data/lib/aws/api_config/SNS-2010-03-31.yml +171 -0
  11. data/lib/aws/api_config/SQS-2009-02-01.yml +161 -0
  12. data/lib/aws/api_config/SimpleDB-2009-04-15.yml +278 -0
  13. data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +147 -0
  14. data/lib/aws/api_config_transform.rb +32 -0
  15. data/lib/aws/async_handle.rb +90 -0
  16. data/lib/aws/authorize_v2.rb +37 -0
  17. data/lib/aws/authorize_v3.rb +37 -0
  18. data/lib/aws/base_client.rb +524 -0
  19. data/lib/aws/cacheable.rb +92 -0
  20. data/lib/aws/common.rb +228 -0
  21. data/lib/aws/configurable.rb +36 -0
  22. data/lib/aws/configuration.rb +272 -0
  23. data/lib/aws/configured_client_methods.rb +81 -0
  24. data/lib/aws/configured_grammars.rb +65 -0
  25. data/lib/aws/configured_option_grammars.rb +46 -0
  26. data/lib/aws/configured_xml_grammars.rb +47 -0
  27. data/lib/aws/default_signer.rb +38 -0
  28. data/lib/aws/ec2.rb +321 -0
  29. data/lib/aws/ec2/attachment.rb +149 -0
  30. data/lib/aws/ec2/attachment_collection.rb +57 -0
  31. data/lib/aws/ec2/availability_zone.rb +80 -0
  32. data/lib/aws/ec2/availability_zone_collection.rb +47 -0
  33. data/lib/aws/ec2/block_device_mappings.rb +53 -0
  34. data/lib/aws/ec2/client.rb +54 -0
  35. data/lib/aws/ec2/client/xml.rb +127 -0
  36. data/lib/aws/ec2/collection.rb +39 -0
  37. data/lib/aws/ec2/config_transform.rb +63 -0
  38. data/lib/aws/ec2/elastic_ip.rb +107 -0
  39. data/lib/aws/ec2/elastic_ip_collection.rb +85 -0
  40. data/lib/aws/ec2/errors.rb +29 -0
  41. data/lib/aws/ec2/filtered_collection.rb +65 -0
  42. data/lib/aws/ec2/has_permissions.rb +46 -0
  43. data/lib/aws/ec2/image.rb +245 -0
  44. data/lib/aws/ec2/image_collection.rb +235 -0
  45. data/lib/aws/ec2/instance.rb +515 -0
  46. data/lib/aws/ec2/instance_collection.rb +276 -0
  47. data/lib/aws/ec2/key_pair.rb +86 -0
  48. data/lib/aws/ec2/key_pair_collection.rb +102 -0
  49. data/lib/aws/ec2/permission_collection.rb +177 -0
  50. data/lib/aws/ec2/region.rb +81 -0
  51. data/lib/aws/ec2/region_collection.rb +55 -0
  52. data/lib/aws/ec2/request.rb +27 -0
  53. data/lib/aws/ec2/reserved_instances.rb +50 -0
  54. data/lib/aws/ec2/reserved_instances_collection.rb +44 -0
  55. data/lib/aws/ec2/reserved_instances_offering.rb +55 -0
  56. data/lib/aws/ec2/reserved_instances_offering_collection.rb +43 -0
  57. data/lib/aws/ec2/resource.rb +340 -0
  58. data/lib/aws/ec2/resource_tag_collection.rb +218 -0
  59. data/lib/aws/ec2/security_group.rb +246 -0
  60. data/lib/aws/ec2/security_group/ip_permission.rb +70 -0
  61. data/lib/aws/ec2/security_group/ip_permission_collection.rb +59 -0
  62. data/lib/aws/ec2/security_group_collection.rb +132 -0
  63. data/lib/aws/ec2/snapshot.rb +138 -0
  64. data/lib/aws/ec2/snapshot_collection.rb +90 -0
  65. data/lib/aws/ec2/tag.rb +88 -0
  66. data/lib/aws/ec2/tag_collection.rb +114 -0
  67. data/lib/aws/ec2/tagged_collection.rb +48 -0
  68. data/lib/aws/ec2/tagged_item.rb +87 -0
  69. data/lib/aws/ec2/volume.rb +190 -0
  70. data/lib/aws/ec2/volume_collection.rb +95 -0
  71. data/lib/aws/errors.rb +129 -0
  72. data/lib/aws/http/builtin_handler.rb +69 -0
  73. data/lib/aws/http/curb_handler.rb +123 -0
  74. data/lib/aws/http/handler.rb +77 -0
  75. data/lib/aws/http/httparty_handler.rb +61 -0
  76. data/lib/aws/http/request.rb +136 -0
  77. data/lib/aws/http/request_param.rb +63 -0
  78. data/lib/aws/http/response.rb +75 -0
  79. data/lib/aws/ignore_result_element.rb +38 -0
  80. data/lib/aws/indifferent_hash.rb +86 -0
  81. data/lib/aws/inflection.rb +46 -0
  82. data/lib/aws/lazy_error_classes.rb +64 -0
  83. data/lib/aws/meta_utils.rb +43 -0
  84. data/lib/aws/model.rb +57 -0
  85. data/lib/aws/naming.rb +32 -0
  86. data/lib/aws/option_grammar.rb +544 -0
  87. data/lib/aws/policy.rb +912 -0
  88. data/lib/aws/rails.rb +209 -0
  89. data/lib/aws/record.rb +79 -0
  90. data/lib/aws/record/attribute.rb +94 -0
  91. data/lib/aws/record/attribute_macros.rb +288 -0
  92. data/lib/aws/record/attributes/boolean.rb +49 -0
  93. data/lib/aws/record/attributes/datetime.rb +86 -0
  94. data/lib/aws/record/attributes/float.rb +48 -0
  95. data/lib/aws/record/attributes/integer.rb +68 -0
  96. data/lib/aws/record/attributes/sortable_float.rb +60 -0
  97. data/lib/aws/record/attributes/sortable_integer.rb +95 -0
  98. data/lib/aws/record/attributes/string.rb +69 -0
  99. data/lib/aws/record/base.rb +728 -0
  100. data/lib/aws/record/conversion.rb +38 -0
  101. data/lib/aws/record/dirty_tracking.rb +286 -0
  102. data/lib/aws/record/errors.rb +153 -0
  103. data/lib/aws/record/exceptions.rb +48 -0
  104. data/lib/aws/record/finder_methods.rb +262 -0
  105. data/lib/aws/record/naming.rb +31 -0
  106. data/lib/aws/record/scope.rb +157 -0
  107. data/lib/aws/record/validations.rb +653 -0
  108. data/lib/aws/record/validator.rb +237 -0
  109. data/lib/aws/record/validators/acceptance.rb +51 -0
  110. data/lib/aws/record/validators/block.rb +38 -0
  111. data/lib/aws/record/validators/confirmation.rb +43 -0
  112. data/lib/aws/record/validators/count.rb +108 -0
  113. data/lib/aws/record/validators/exclusion.rb +43 -0
  114. data/lib/aws/record/validators/format.rb +57 -0
  115. data/lib/aws/record/validators/inclusion.rb +56 -0
  116. data/lib/aws/record/validators/length.rb +107 -0
  117. data/lib/aws/record/validators/numericality.rb +138 -0
  118. data/lib/aws/record/validators/presence.rb +45 -0
  119. data/lib/aws/resource_cache.rb +39 -0
  120. data/lib/aws/response.rb +113 -0
  121. data/lib/aws/response_cache.rb +50 -0
  122. data/lib/aws/s3.rb +109 -0
  123. data/lib/aws/s3/access_control_list.rb +252 -0
  124. data/lib/aws/s3/acl_object.rb +266 -0
  125. data/lib/aws/s3/bucket.rb +320 -0
  126. data/lib/aws/s3/bucket_collection.rb +122 -0
  127. data/lib/aws/s3/bucket_version_collection.rb +85 -0
  128. data/lib/aws/s3/client.rb +999 -0
  129. data/lib/aws/s3/client/xml.rb +190 -0
  130. data/lib/aws/s3/data_options.rb +99 -0
  131. data/lib/aws/s3/errors.rb +43 -0
  132. data/lib/aws/s3/multipart_upload.rb +318 -0
  133. data/lib/aws/s3/multipart_upload_collection.rb +78 -0
  134. data/lib/aws/s3/object_collection.rb +159 -0
  135. data/lib/aws/s3/object_metadata.rb +67 -0
  136. data/lib/aws/s3/object_upload_collection.rb +83 -0
  137. data/lib/aws/s3/object_version.rb +141 -0
  138. data/lib/aws/s3/object_version_collection.rb +78 -0
  139. data/lib/aws/s3/paginated_collection.rb +94 -0
  140. data/lib/aws/s3/policy.rb +76 -0
  141. data/lib/aws/s3/prefix_and_delimiter_collection.rb +56 -0
  142. data/lib/aws/s3/prefixed_collection.rb +84 -0
  143. data/lib/aws/s3/presigned_post.rb +504 -0
  144. data/lib/aws/s3/request.rb +198 -0
  145. data/lib/aws/s3/s3_object.rb +794 -0
  146. data/lib/aws/s3/tree.rb +116 -0
  147. data/lib/aws/s3/tree/branch_node.rb +71 -0
  148. data/lib/aws/s3/tree/child_collection.rb +108 -0
  149. data/lib/aws/s3/tree/leaf_node.rb +99 -0
  150. data/lib/aws/s3/tree/node.rb +22 -0
  151. data/lib/aws/s3/tree/parent.rb +90 -0
  152. data/lib/aws/s3/uploaded_part.rb +82 -0
  153. data/lib/aws/s3/uploaded_part_collection.rb +86 -0
  154. data/lib/aws/service_interface.rb +60 -0
  155. data/lib/aws/simple_db.rb +202 -0
  156. data/lib/aws/simple_db/attribute.rb +159 -0
  157. data/lib/aws/simple_db/attribute_collection.rb +227 -0
  158. data/lib/aws/simple_db/client.rb +52 -0
  159. data/lib/aws/simple_db/client/options.rb +34 -0
  160. data/lib/aws/simple_db/client/xml.rb +68 -0
  161. data/lib/aws/simple_db/consistent_read_option.rb +42 -0
  162. data/lib/aws/simple_db/delete_attributes.rb +64 -0
  163. data/lib/aws/simple_db/domain.rb +118 -0
  164. data/lib/aws/simple_db/domain_collection.rb +116 -0
  165. data/lib/aws/simple_db/domain_metadata.rb +112 -0
  166. data/lib/aws/simple_db/errors.rb +46 -0
  167. data/lib/aws/simple_db/expect_condition_option.rb +45 -0
  168. data/lib/aws/simple_db/item.rb +84 -0
  169. data/lib/aws/simple_db/item_collection.rb +594 -0
  170. data/lib/aws/simple_db/item_data.rb +70 -0
  171. data/lib/aws/simple_db/put_attributes.rb +62 -0
  172. data/lib/aws/simple_db/request.rb +27 -0
  173. data/lib/aws/simple_email_service.rb +373 -0
  174. data/lib/aws/simple_email_service/client.rb +39 -0
  175. data/lib/aws/simple_email_service/client/options.rb +24 -0
  176. data/lib/aws/simple_email_service/client/xml.rb +38 -0
  177. data/lib/aws/simple_email_service/email_address_collection.rb +66 -0
  178. data/lib/aws/simple_email_service/errors.rb +29 -0
  179. data/lib/aws/simple_email_service/quotas.rb +64 -0
  180. data/lib/aws/simple_email_service/request.rb +27 -0
  181. data/lib/aws/sns.rb +69 -0
  182. data/lib/aws/sns/client.rb +37 -0
  183. data/lib/aws/sns/client/options.rb +24 -0
  184. data/lib/aws/sns/client/xml.rb +38 -0
  185. data/lib/aws/sns/errors.rb +29 -0
  186. data/lib/aws/sns/policy.rb +49 -0
  187. data/lib/aws/sns/request.rb +27 -0
  188. data/lib/aws/sns/subscription.rb +100 -0
  189. data/lib/aws/sns/subscription_collection.rb +84 -0
  190. data/lib/aws/sns/topic.rb +384 -0
  191. data/lib/aws/sns/topic_collection.rb +70 -0
  192. data/lib/aws/sns/topic_subscription_collection.rb +58 -0
  193. data/lib/aws/sqs.rb +70 -0
  194. data/lib/aws/sqs/client.rb +38 -0
  195. data/lib/aws/sqs/client/xml.rb +36 -0
  196. data/lib/aws/sqs/errors.rb +33 -0
  197. data/lib/aws/sqs/policy.rb +50 -0
  198. data/lib/aws/sqs/queue.rb +507 -0
  199. data/lib/aws/sqs/queue_collection.rb +105 -0
  200. data/lib/aws/sqs/received_message.rb +184 -0
  201. data/lib/aws/sqs/received_sns_message.rb +112 -0
  202. data/lib/aws/sqs/request.rb +44 -0
  203. data/lib/aws/xml_grammar.rb +923 -0
  204. data/rails/init.rb +15 -0
  205. metadata +298 -0
@@ -0,0 +1,105 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+ require 'aws/sqs/queue'
16
+
17
+ module AWS
18
+ class SQS
19
+
20
+ # Represents all the {Queue} objects in your account.
21
+ #
22
+ # If you have permission to access a queue created by another
23
+ # account, you can also use this collection to access that queue
24
+ # by URL.
25
+ #
26
+ # @example Printing the URLs of all queues
27
+ # pp sqs.queues.map(&:url)
28
+ #
29
+ # @example Filtering queues by queue name prefix
30
+ # pp sqs.queues.with_prefix("production_").map(&:url)
31
+ #
32
+ # @example Accessing a queue by URL
33
+ # url = "http://sqs.us-east-1.amazonaws.com/123456789012/myqueue"
34
+ # sqs.queues[url].send_message("HELLO")
35
+ class QueueCollection
36
+
37
+ include Model
38
+ include Enumerable
39
+
40
+ # @private
41
+ def initialize(opts = {})
42
+ @prefix = opts[:prefix]
43
+ super
44
+ end
45
+
46
+ # @return [String] The queue name prefix by which this
47
+ # collection is filtered.
48
+ attr_reader :prefix
49
+
50
+ # Creates a new queue.
51
+ #
52
+ # @note If you delete a queue, you must wait at least 60
53
+ # seconds before creating a queue with the same name.
54
+ #
55
+ # @param [String] name The name to use for the queue created.
56
+ # Constraints: Maximum 80 characters; alphanumeric
57
+ # characters, hyphens (-), and underscores (_) are allowed.
58
+ #
59
+ # To successfully create a new queue, you must provide a
60
+ # name that is unique within the scope of your own
61
+ # queues. If you provide the name of an existing queue, a
62
+ # new queue isn't created and an error isn't
63
+ # returned. Instead, the request succeeds and the queue URL
64
+ # for the existing queue is returned. Exception: if you
65
+ # provide a value for +:default_visibility_timeout+ that is
66
+ # different from the value for the existing queue, you
67
+ # receive an error.
68
+ #
69
+ # @param [Hash] opts Additional options for creating the
70
+ # queue.
71
+ #
72
+ # @option opts [Integer] :default_visibility_timeout The
73
+ # visibility timeout (in seconds) to use for this queue.
74
+ #
75
+ # @return [Queue] The newly created queue.
76
+ #
77
+ def create(name, opts = {})
78
+ resp = client.create_queue(opts.merge(:queue_name => name))
79
+ Queue.new(resp.queue_url, :config => config)
80
+ end
81
+
82
+ # @yieldparam [Queue] queue Each {Queue} object in the collection.
83
+ def each(&block)
84
+ client.list_queues.queue_urls.each do |url|
85
+ queue = self[url]
86
+ yield(queue)
87
+ end
88
+ end
89
+
90
+ # @param [String] prefix The queue name prefix.
91
+ # @return [QueueCollection] A new collection representing only
92
+ # the queues whose names start with the given prefix.
93
+ def with_prefix(prefix)
94
+ self.class.new(:prefix => prefix, :config => config)
95
+ end
96
+
97
+ # @return [Queue] The queue with the given URL.
98
+ def [](url)
99
+ Queue.new(url, :config => config)
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,184 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+
16
+ module AWS
17
+ class SQS
18
+
19
+ # Represents a message received from an Amazon SQS Queue.
20
+ class ReceivedMessage
21
+
22
+ include Model
23
+
24
+ # @return [Queue] The queue from which this message was
25
+ # received.
26
+ attr_reader :queue
27
+
28
+ # @return [String] The ID of the message.
29
+ attr_reader :id
30
+
31
+ # @return [String] A string associated with this specific
32
+ # instance of receiving the message.
33
+ attr_reader :handle
34
+
35
+ # @return [String] The message's contents.
36
+ attr_reader :body
37
+
38
+ # @return [String] An MD5 digest of the message body.
39
+ attr_reader :md5
40
+
41
+ # @private
42
+ attr_reader :attributes
43
+
44
+ # @private
45
+ ATTRIBUTE_ALIASES = {
46
+ :sent_at => :sent_timestamp,
47
+ :receive_count => :approximate_receive_count,
48
+ :first_received_at => :approximate_first_receive_timestamp
49
+ }
50
+
51
+ # @private
52
+ def initialize(queue, id, handle, opts = {})
53
+ @queue = queue
54
+ @id = id
55
+ @handle = handle
56
+ @body = opts[:body]
57
+ @md5 = opts[:md5]
58
+ @attributes = opts[:attributes] || {}
59
+ super
60
+ end
61
+
62
+ # When SNS publishes messages to SQS queues the message body is
63
+ # formatted as a json message and then base 64 encoded.
64
+ # An easy way to work with SNS messages is to call this method:
65
+ #
66
+ # sns_msg = message.as_sns_message
67
+ #
68
+ # sns_msg.topic
69
+ # #=> <AWS::SNS::Topic ...>
70
+ #
71
+ # sns_msg.to_h.inspect
72
+ # #=> { :body => '...', :topic_arn => ... }
73
+ #
74
+ # @return [ReceivedSNSMessage]
75
+ def as_sns_message
76
+ ReceivedSNSMessage.new(body, :config => config)
77
+ end
78
+
79
+ # Deletes the message from the queue. Even if the message is
80
+ # locked by another reader due to the visibility timeout
81
+ # setting, it is still deleted from the queue. If you leave a
82
+ # message in the queue for more than 4 days, SQS automatically
83
+ # deletes it.
84
+ #
85
+ # If you use {Queue#poll} or {Queue#receive_message} in block
86
+ # form, the messages you receive will be deleted automatically
87
+ # unless an exception occurs while you are processing them.
88
+ # You can still use this method if you want to delete a
89
+ # message early and then continue processing it.
90
+ #
91
+ # @note It is possible you will receive a message even after
92
+ # you have deleted it. This might happen on rare occasions
93
+ # if one of the servers storing a copy of the message is
94
+ # unavailable when you request to delete the message. The
95
+ # copy remains on the server and might be returned to you
96
+ # again on a subsequent receive request. You should create
97
+ # your system to be idempotent so that receiving a
98
+ # particular message more than once is not a problem.
99
+ #
100
+ # @return [nil]
101
+ def delete
102
+ client.delete_message(
103
+ :queue_url => queue.url,
104
+ :receipt_handle => handle
105
+ )
106
+ nil
107
+ end
108
+
109
+ # Changes the visibility timeout of a specified message in a
110
+ # queue to a new value. The maximum allowed timeout value you
111
+ # can set the value to is 12 hours. This means you can't
112
+ # extend the timeout of a message in an existing queue to more
113
+ # than a total visibility timeout of 12 hours. (For more
114
+ # information visibility timeout, see {Visibility
115
+ # Timeout}[http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/IntroductionArticle.html#AboutVT]
116
+ # in the Amazon SQS Developer Guide.)
117
+ #
118
+ # For example, let's say the timeout for the queue is 30
119
+ # seconds, and you receive a message. Once you're 20 seconds
120
+ # into the timeout for that message (i.e., you have 10 seconds
121
+ # left), you extend it by 60 seconds by calling this method
122
+ # with +timeout+ set to 60 seconds. You have then changed the
123
+ # remaining visibility timeout from 10 seconds to 60 seconds.
124
+ #
125
+ # @note If you attempt to set the timeout to an amount more
126
+ # than the maximum time left, Amazon SQS returns an
127
+ # error. It will not automatically recalculate and increase
128
+ # the timeout to the maximum time remaining.
129
+ #
130
+ # @note Unlike with a queue, when you change the visibility
131
+ # timeout for a specific message, that timeout value is
132
+ # applied immediately but is not saved in memory for that
133
+ # message. If you don't delete a message after it is
134
+ # received, the visibility timeout for the message the next
135
+ # time it is received reverts to the original timeout value,
136
+ # not the value you set with this method.
137
+ #
138
+ # @return Returns the timeout argument as passed.
139
+ #
140
+ def visibility_timeout=(timeout)
141
+ client.change_message_visibility(
142
+ :queue_url => queue.url,
143
+ :receipt_handle => handle,
144
+ :visibility_timeout => timeout
145
+ )
146
+ timeout
147
+ end
148
+
149
+ # @return [String] The AWS account number (or the IP address,
150
+ # if anonymous access is allowed) of the sender.
151
+ def sender_id
152
+ attributes["SenderId"]
153
+ end
154
+
155
+ # @return [Time] The time when the message was sent.
156
+ def sent_timestamp
157
+ @sent_at ||=
158
+ (timestamp = attributes["SentTimestamp"] and
159
+ Time.at(timestamp.to_i / 1000.0)) || nil
160
+ rescue RangeError => e
161
+ p [timestamp, timestamp.to_i]
162
+ end
163
+ alias_method :sent_at, :sent_timestamp
164
+
165
+ # @return [Integer] The number of times a message has been
166
+ # received but not deleted.
167
+ def approximate_receive_count
168
+ (count = attributes["ApproximateReceiveCount"] and
169
+ count.to_i) or nil
170
+ end
171
+ alias_method :receive_count, :approximate_receive_count
172
+
173
+ # @return [Time] The time when the message was first received.
174
+ def approximate_first_receive_timestamp
175
+ @received_at ||=
176
+ (timestamp = attributes["ApproximateFirstReceiveTimestamp"] and
177
+ Time.at(timestamp.to_i / 1000.0)) || nil
178
+ end
179
+ alias_method :first_received_at, :approximate_first_receive_timestamp
180
+
181
+ end
182
+
183
+ end
184
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+ require 'time'
16
+
17
+ module AWS
18
+ class SQS
19
+
20
+ # Represents message published from an {SNS::Topic} to an {SQS::Queue}.
21
+ class ReceivedSNSMessage
22
+
23
+ include Model
24
+
25
+ # @param [String] encoded_body The base64 encoded json string
26
+ # from a message published by SNS.
27
+ # @param [Hash] options
28
+ def initialize encoded_body, options = {}
29
+ @encoded_body = encoded_body
30
+ super
31
+ end
32
+
33
+ # @return [String] Returns the Base64 encoded JSON hash as was
34
+ # published by SNS. See {#body} to get the decoded message
35
+ # or {#to_h} to get the decoded JSON hash as a ruby hash.
36
+ def encoded_body
37
+ @encoded_body
38
+ end
39
+
40
+ # @return[String] Returns the decoded message as was published.
41
+ def body
42
+ to_h[:body]
43
+ end
44
+
45
+ # @return [String] Returns the ARN for the topic that published this
46
+ # message.
47
+ def topic_arn
48
+ to_h[:topic_arn]
49
+ end
50
+
51
+ # @return [SNS::Topic] Returns the topic that published this message.
52
+ def topic
53
+ SNS::Topic.new(topic_arn, :config => config)
54
+ end
55
+
56
+ # @return [String] Returns the message type.
57
+ def message_type
58
+ to_h[:message_type]
59
+ end
60
+
61
+ # @return [String] Returns the calculated signature for the message.
62
+ def signature
63
+ to_h[:signature]
64
+ end
65
+
66
+ # @return [String] Returns the signature version.
67
+ def signature_version
68
+ to_h[:signature_version]
69
+ end
70
+
71
+ # @return [Time] Returns the time the message was published at by SNS.
72
+ # by SNS.
73
+ def published_at
74
+ to_h[:published_at]
75
+ end
76
+
77
+ # @return [String] Returns the unique id of the SNS message.
78
+ def message_id
79
+ to_h[:message_id]
80
+ end
81
+
82
+ # @return [String] Returns the url for the signing cert.
83
+ def signing_cert_url
84
+ to_h[:signing_cert_url]
85
+ end
86
+
87
+ # @return [String] Returns the url you can request to unsubscribe message
88
+ # from this queue.
89
+ def unsubscribe_url
90
+ to_h[:unsubscribe_url]
91
+ end
92
+
93
+ # @return [Hash] Returns the decoded message as a hash.
94
+ def to_h
95
+ data = JSON.parse(Base64.decode64(@encoded_body))
96
+ {
97
+ :body => data['Message'],
98
+ :topic_arn => data['TopicArn'],
99
+ :message_type => data['Type'],
100
+ :signature => data['Signature'],
101
+ :signature_version => data['SignatureVersion'],
102
+ :published_at => Time.parse(data['Timestamp']),
103
+ :message_id => data['MessageId'],
104
+ :signing_cert_url => data['SigningCertURL'],
105
+ :unsubscribe_url => data['UnsubscribeURL'],
106
+ }
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/http/request'
15
+ require 'aws/authorize_v2'
16
+
17
+ module AWS
18
+ class SQS
19
+
20
+ # @private
21
+ class Request < AWS::Http::Request
22
+
23
+ include AuthorizeV2
24
+
25
+ def path
26
+ full_url.path
27
+ end
28
+
29
+ def host
30
+ full_url.host
31
+ end
32
+
33
+ private
34
+ def full_url
35
+ if url_param = params.find { |p| p.name == "QueueUrl" }
36
+ URI.parse(url_param.value)
37
+ else
38
+ URI::HTTP.build(:host => @host, :path => '/')
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,923 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/meta_utils'
15
+ require 'aws/inflection'
16
+ require 'rexml/document'
17
+
18
+ begin
19
+ require 'nokogiri'
20
+ rescue LoadError => e
21
+ end
22
+
23
+ module AWS
24
+
25
+ # @private
26
+ class XmlGrammar
27
+
28
+ # @private
29
+ class Context
30
+
31
+ def initialize
32
+ @data = {}
33
+ end
34
+
35
+ def id
36
+ @data[:id]
37
+ end
38
+
39
+ def method_missing(m, *args)
40
+ key = m.to_sym
41
+
42
+ return super unless @data.key?(key)
43
+ @data[key]
44
+ end
45
+
46
+ def respond_to?(m)
47
+ @data.key?(m.to_sym) or super
48
+ end
49
+
50
+ def inspect
51
+ methods = @data.keys
52
+ "<Object #{methods.reject{|m| m =~ /=$/ }.join(', ')}>"
53
+ end
54
+
55
+ # this gets called a LOT during response parsing, and having
56
+ # it be a public method is the fastest way to call it.
57
+ # Strictly speaking it should be private.
58
+ # @private
59
+ def __set_data__(getter, value)
60
+ @data[getter.to_sym] = value
61
+ end
62
+
63
+ end
64
+
65
+ # @private
66
+ class CustomizationContext < Hash
67
+
68
+ def initialize(element_name = nil)
69
+ self[:children] = {}
70
+
71
+ if element_name
72
+ self[:name] = element_name
73
+ recompute_accessors
74
+ end
75
+ end
76
+
77
+ def []=(name, value)
78
+ super
79
+ if respond_to?("changed_#{name}")
80
+ send("changed_#{name}", value)
81
+ end
82
+ end
83
+
84
+ def changed_boolean(value)
85
+ recompute_accessors
86
+ end
87
+
88
+ def changed_renamed(value)
89
+ recompute_accessors
90
+ end
91
+
92
+ def deep_copy(hash = self)
93
+ hash.inject(hash.class.new) do |copy,(key,value)|
94
+ if value.is_a?(CustomizationContext)
95
+ copy[key] = value.deep_copy
96
+ elsif value.is_a?(Hash)
97
+ copy[key] = deep_copy(value)
98
+ else
99
+ copy[key] = value
100
+ end
101
+ copy
102
+ end
103
+ end
104
+
105
+ private
106
+ def recompute_accessors
107
+ ruby_name = Inflection.ruby_name((self[:renamed] ||
108
+ self[:name]).to_s)
109
+ self[:getter] =
110
+ if self[:boolean] && ruby_name != "value"
111
+ "#{ruby_name}?"
112
+ else
113
+ ruby_name
114
+ end
115
+ self[:setter] = "#{ruby_name}="
116
+ end
117
+
118
+ end
119
+
120
+ # @private
121
+ class << self
122
+
123
+ def parse xml, options = {}
124
+
125
+ context = options[:context] || Context.new
126
+
127
+ if defined? Nokogiri
128
+ parser = Parser.new(context, customizations)
129
+ parser.extend(NokogiriAdapter)
130
+ xml = "<foo/>" if xml.empty?
131
+ Nokogiri::XML::SAX::Parser.new(parser).parse(xml.strip)
132
+ else
133
+ parser = Parser.new(context, customizations)
134
+ parser.extend(REXMLSaxParserAdapter)
135
+ REXML::Parsers::StreamParser.new(REXML::Source.new(xml), parser).parse
136
+ end
137
+
138
+ context
139
+
140
+ end
141
+
142
+ def simulate context = nil
143
+ StubResponse.new(customizations, context)
144
+ end
145
+
146
+ def customize config = nil, &block
147
+ grammar = Class.new(self)
148
+ grammar.customizations = customizations.deep_copy
149
+ grammar.config_eval(config) if config
150
+ grammar.module_eval(&block) if block_given?
151
+ grammar
152
+ end
153
+
154
+ def element element_name, &block
155
+ eval_customization_context(element_name, &block)
156
+ end
157
+
158
+ def add_method method_name, &block
159
+ format_value do |value|
160
+ MetaUtils.extend_method(value = super(value), method_name, &block)
161
+ value
162
+ end
163
+ # different strategey, slightly different behavior
164
+ # element(method_name.to_s) do
165
+ # format_value(&block)
166
+ # force
167
+ # end
168
+ end
169
+
170
+ def ignore
171
+ @current[:ignored] = true
172
+ end
173
+
174
+ def rename new_name
175
+ @current[:renamed] = new_name.to_s
176
+ end
177
+
178
+ def force
179
+ @current[:forced] = true
180
+ end
181
+
182
+ def collect_values
183
+ @current[:collected] = true
184
+ @current[:initial_collection] = lambda { [] }
185
+ @current[:add_to_collection] =
186
+ lambda { |ary, val| ary << val }
187
+ force
188
+ end
189
+
190
+ def index(name, &block)
191
+ (@customizations[:index_names] ||= []) << name
192
+ @current[:indexed] = [name, block]
193
+ end
194
+
195
+ def boolean_value
196
+ @current[:boolean] = true
197
+ format_value {|value| super(value) == 'true' }
198
+ end
199
+ # required by the hash configuration method
200
+ alias_method :boolean, :boolean_value
201
+
202
+ TRANSLATE_DIGITS = ['0123456789'.freeze, ('X'*10).freeze]
203
+ EASY_FORMAT = "XXXX-XX-XXTXX:XX:XX.XXXZ".freeze
204
+ DATE_PUNCTUATION = ['-:.TZ'.freeze, (' '*5).freeze]
205
+
206
+ def datetime_value
207
+ datetime_like_value(DateTime, :civil)
208
+ end
209
+
210
+ def time_value
211
+ datetime_like_value(Time, :utc)
212
+ end
213
+ alias_method :timestamp, :time_value
214
+
215
+ def datetime_like_value(klass, parts_constructor)
216
+ format_value do |value|
217
+ value = super(value)
218
+ if value and value.tr(*TRANSLATE_DIGITS) == EASY_FORMAT
219
+
220
+ # it's way faster to parse this specific format manually
221
+ # vs. DateTime#parse, and this happens to be the format
222
+ # that AWS uses almost (??) everywhere.
223
+
224
+ parts = value.tr(*DATE_PUNCTUATION).
225
+ chop.split.map { |elem| elem.to_i }
226
+ klass.send(parts_constructor, *parts)
227
+ elsif value
228
+ # fallback in case we have to handle another date format
229
+ klass.parse(value)
230
+ else
231
+ nil
232
+ end
233
+ end
234
+ end
235
+
236
+ def integer_value
237
+ format_value do |value|
238
+ value = super(value)
239
+ value.nil? ? nil : value.to_i
240
+ end
241
+ end
242
+ # required by the hash configuration method
243
+ alias_method :integer, :integer_value
244
+ alias_method :long, :integer_value
245
+
246
+ def float_value
247
+ format_value do |value|
248
+ value = super(value)
249
+ value.nil? ? nil : value.to_f
250
+ end
251
+ end
252
+
253
+ alias_method :float, :float_value
254
+
255
+ def symbol_value
256
+ format_value do |value|
257
+ value = super(value)
258
+ ['', nil].include?(value) ? nil : Inflection.ruby_name(value).to_sym
259
+ end
260
+ end
261
+
262
+ def format_value &block
263
+ @current[:value_formatter] ||= ValueFormatter.new
264
+ @current[:value_formatter].extend_format_value(&block)
265
+ end
266
+
267
+ def list child_element_name = nil, &block
268
+ if child_element_name
269
+ ignore
270
+ parent_element_name = @current_name
271
+ element(child_element_name) do
272
+ rename(parent_element_name)
273
+ collect_values
274
+ yield if block_given?
275
+ end
276
+ else
277
+ collect_values
278
+ end
279
+ end
280
+
281
+ def map_entry(key, value)
282
+ collect_values
283
+ element(key) { rename :key }
284
+ element(value) { rename :value }
285
+ @current[:initial_collection] = lambda { {} }
286
+ @current[:add_to_collection] = lambda do |hash, entry|
287
+ hash[entry.key] = entry.value
288
+ end
289
+ end
290
+
291
+ def map entry_name, key, value
292
+ parent_element_name = @current_name
293
+ ignore
294
+ element(entry_name) do
295
+ rename(parent_element_name)
296
+ map_entry(key, value)
297
+ end
298
+ end
299
+
300
+ def wrapper method_name, options = {}, &blk
301
+ if block_given?
302
+ customizations =
303
+ eval_customization_context(method_name,
304
+ CustomizationContext.new(method_name),
305
+ &blk)
306
+ raise NotImplementedError.new("can't customize wrapped " +
307
+ "elements within the wrapper") unless
308
+ customizations[:children].empty?
309
+ @current[:wrapper_frames] ||= {}
310
+ @current[:wrapper_frames][method_name] = customizations
311
+ end
312
+
313
+ (options[:for] || []).each do |element_name|
314
+ element element_name do
315
+ @current[:wrapper] ||= []
316
+ @current[:wrapper] << method_name
317
+ end
318
+ end
319
+ end
320
+
321
+ def construct_value &block
322
+ @current[:construct_value] = block
323
+ end
324
+
325
+ def eql? other
326
+ self.customizations == other.customizations
327
+ end
328
+
329
+ protected
330
+ def initial_customizations(element_name = nil)
331
+ CustomizationContext.new(element_name)
332
+ end
333
+
334
+ protected
335
+ def eval_customization_context name, initial = nil, &block
336
+ current_name = @current_name
337
+ current = @current
338
+ parent = @parent
339
+ begin
340
+ @current_name = name
341
+ @parent = @current
342
+ initial ||= customizations_for(name)
343
+ @current = initial
344
+ yield if block_given?
345
+ ensure
346
+ @current_name = current_name
347
+ @current = current
348
+ @parent = parent
349
+ end
350
+
351
+ # will be modified to include the customizations defined in
352
+ # the block
353
+ initial
354
+ end
355
+
356
+ protected
357
+ def config_eval(config)
358
+ config.each do |item|
359
+ (type, identifier, args) = parse_config_item(item)
360
+ case type
361
+ when :method
362
+ validate_config_method(identifier)
363
+ validate_args(identifier, args)
364
+ send(identifier, *args)
365
+ when :element
366
+ element(identifier) do
367
+ config_eval(args)
368
+ end
369
+ end
370
+ end
371
+ end
372
+
373
+ protected
374
+ def validate_args(identifier, args)
375
+ arity = method(identifier).arity
376
+ if args.length > 0
377
+ raise "#{identifier} does not accept an argument" if
378
+ arity == 0
379
+ else
380
+ raise "#{identifier} requires an argument" unless
381
+ arity == 0 || arity == -1
382
+ end
383
+ end
384
+
385
+ protected
386
+ def parse_config_item(item)
387
+ case item
388
+ when Symbol
389
+ [:method, item, []]
390
+ when Hash
391
+ (method, arg) = item.to_a.first
392
+ if method.kind_of?(Symbol)
393
+ [:method, method, [arg].flatten]
394
+ else
395
+ [:element, method, arg]
396
+ end
397
+ end
398
+ end
399
+
400
+ protected
401
+ def validate_config_method(method)
402
+ allow_methods = %w(
403
+ rename attribute_name boolean integer long float list force
404
+ ignore collect_values symbol_value timestamp map_entry map
405
+ )
406
+ unless allow_methods.include?(method.to_s)
407
+ raise "#{method} cannot be used in configuration"
408
+ end
409
+ end
410
+
411
+ protected
412
+ def customizations
413
+ @customizations ||= CustomizationContext.new
414
+ end
415
+
416
+ protected
417
+ def customizations_for element_name
418
+ if @parent
419
+ @parent[:children][element_name] ||=
420
+ CustomizationContext.new(element_name)
421
+ else
422
+ customizations[:children][element_name] ||=
423
+ CustomizationContext.new(element_name)
424
+ end
425
+ end
426
+
427
+ protected
428
+ def customizations= customizations
429
+ @customizations = customizations
430
+ @current = customizations
431
+ end
432
+
433
+ end
434
+
435
+ # @private
436
+ class ValueFormatter
437
+
438
+ def extend_format_value &block
439
+ MetaUtils.extend_method(self, :format_value, &block)
440
+ end
441
+
442
+ def format_value value
443
+ value
444
+ end
445
+
446
+ end
447
+
448
+ # @private
449
+ class Parser
450
+
451
+ def initialize context, customizations
452
+ @context = context
453
+ @customizations = customizations
454
+ end
455
+
456
+ def start_element element_name, attrs
457
+
458
+ if @frame
459
+ @frame = @frame.build_child_frame(element_name)
460
+ else
461
+ @frame = RootFrame.new(@context, @customizations)
462
+ end
463
+
464
+ # consume attributes the same way we consume nested xml elements
465
+ attrs.each do |(attr_name, attr_value)|
466
+ attr_frame = @frame.build_child_frame(attr_name)
467
+ attr_frame.add_text(attr_value)
468
+ @frame.consume_child_frame(attr_frame)
469
+ end
470
+
471
+ end
472
+
473
+ def end_element name
474
+ @frame.close
475
+ if @frame.parent_frame
476
+ child_frame = @frame
477
+ parent_frame = @frame.parent_frame
478
+ parent_frame.consume_child_frame(child_frame)
479
+ end
480
+ @frame = @frame.parent_frame
481
+ end
482
+
483
+ def characters chars
484
+ @frame.add_text(chars) if @frame
485
+ end
486
+
487
+ end
488
+
489
+ module REXMLSaxParserAdapter
490
+
491
+ require 'rexml/streamlistener'
492
+ include REXML::StreamListener
493
+
494
+ def tag_start(name, attrs)
495
+ start_element(name, attrs)
496
+ end
497
+
498
+ def tag_end(name)
499
+ end_element(name)
500
+ end
501
+
502
+ def text(chars)
503
+ characters(chars)
504
+ end
505
+
506
+ end
507
+
508
+ module NokogiriAdapter
509
+
510
+ def xmldecl(*args); end
511
+ def start_document; end
512
+ def end_document; end
513
+ def start_element_namespace(name, attrs = [], prefix = nil, uri = nil, ns = [])
514
+ start_element(name, attrs.map { |att| [att.localname, att.value] })
515
+ end
516
+ def end_element_namespace(name, prefix = nil, uri = nil)
517
+ end_element(name)
518
+ end
519
+ def error(*args); end
520
+
521
+ end
522
+
523
+ # @private
524
+ class Frame
525
+
526
+ attr_reader :parent_frame
527
+
528
+ attr_reader :root_frame
529
+
530
+ attr_reader :element_name
531
+
532
+ attr_accessor :customizations
533
+
534
+ def initialize element_name, options = {}
535
+
536
+ @element_name = element_name
537
+ @context = options[:context]
538
+ @parent_frame = options[:parent_frame]
539
+ @root_frame = options[:root_frame]
540
+ @wrapper_frames = {}
541
+
542
+ if @parent_frame
543
+ @customizations = @parent_frame.customizations_for_child(element_name)
544
+ else
545
+ @customizations = options[:customizations]
546
+ @root_frame ||= self
547
+ end
548
+
549
+ if @root_frame == self and
550
+ indexes = @customizations[:index_names]
551
+ indexes.each do |name|
552
+ if context.kind_of?(Context)
553
+ context.__set_data__(name, {})
554
+ else
555
+ add_mutators(name)
556
+ context.send("#{name}=", {})
557
+ end
558
+ end
559
+ end
560
+
561
+ # we build and discard child frames here so we can know
562
+ # which children should always add a method to this
563
+ # frame's context (forced elements, like collected arrays)
564
+ @customizations[:children].keys.each do |child_element_name|
565
+ consume_initial_frame(build_child_frame(child_element_name))
566
+ end
567
+
568
+ if @customizations[:wrapper_frames]
569
+ @customizations[:wrapper_frames].keys.each do |method_name|
570
+ consume_initial_frame(wrapper_frame_for(method_name))
571
+ end
572
+ end
573
+
574
+ end
575
+
576
+ def build_child_frame(child_element_name)
577
+ Frame.new(child_element_name,
578
+ :parent_frame => self,
579
+ :root_frame => root_frame)
580
+ end
581
+
582
+ def consume_child_frame child_frame
583
+
584
+ return if child_frame.ignored?
585
+
586
+ if child_frame.wrapped?
587
+ child_frame.wrapper_methods.each do |method_name|
588
+ consume_in_wrapper(method_name, child_frame)
589
+ end
590
+ else
591
+ # forced child frames have already added mutators to this context
592
+ add_mutators_for(child_frame) unless child_frame.forced?
593
+
594
+ if child_frame.collected?
595
+ child_frame.add_to_collection(context.send(child_frame.getter),
596
+ child_frame.value)
597
+ else
598
+ invoke_setter(child_frame, child_frame.value)
599
+ end
600
+ end
601
+
602
+ end
603
+
604
+ def close
605
+ if indexed = @customizations[:indexed]
606
+ (name, block) = indexed
607
+ key = block.call(context)
608
+ [key].flatten.each do |k|
609
+ index(name)[k] = context
610
+ end
611
+ end
612
+ end
613
+
614
+ def add_text text
615
+ @text ||= ''
616
+ @text << text
617
+ end
618
+
619
+ def value
620
+ @customizations[:value_formatter] ?
621
+ @customizations[:value_formatter].format_value(default_value) :
622
+ default_value
623
+ end
624
+
625
+ def context
626
+ @context ||= (self.ignored? ? parent_frame.context : construct_context)
627
+ end
628
+
629
+ def setter
630
+ @customizations[:setter]
631
+ end
632
+
633
+ def getter
634
+ @customizations[:getter]
635
+ end
636
+
637
+ def initial_collection
638
+ @customizations[:initial_collection].call
639
+ end
640
+
641
+ def add_to_collection(collection, value)
642
+ @customizations[:add_to_collection].call(collection, value)
643
+ end
644
+
645
+ def index(name)
646
+ return root_frame.index(name) unless root_frame == self
647
+ context.send(name)
648
+ end
649
+
650
+ protected
651
+ def consume_initial_frame(child_frame)
652
+ if child_frame.forced?
653
+ add_mutators_for(child_frame)
654
+ if child_frame.collected?
655
+ invoke_setter(child_frame, child_frame.initial_collection)
656
+ else
657
+ # this allows nested forced elements to appear
658
+ invoke_setter(child_frame, child_frame.value)
659
+ end
660
+ end
661
+ end
662
+
663
+ protected
664
+ def construct_context
665
+ if @customizations[:construct_value]
666
+ instance_eval(&@customizations[:construct_value])
667
+ else
668
+ Context.new
669
+ end
670
+ end
671
+
672
+ protected
673
+ def consume_in_wrapper method_name, child_frame
674
+ wrapper_frame = wrapper_frame_for(method_name)
675
+ add_mutators(method_name)
676
+
677
+ # the wrapper consumes the unwrapped child
678
+ customizations = child_frame.customizations.merge(:wrapper => nil)
679
+ child_frame = child_frame.dup
680
+ child_frame.customizations = customizations
681
+
682
+ wrapper_frame.consume_child_frame(child_frame)
683
+ consume_child_frame(wrapper_frame)
684
+ end
685
+
686
+ protected
687
+ def wrapper_frame_for(method_name)
688
+ @wrapper_frames[method_name] ||=
689
+ Frame.new(method_name.to_s,
690
+ :customizations => wrapper_customizations(method_name))
691
+ end
692
+
693
+ protected
694
+ def wrapper_customizations(method_name)
695
+ customizations = CustomizationContext.new(method_name)
696
+ customizations[:children] = @customizations[:children]
697
+ if wrapper_frames = @customizations[:wrapper_frames] and
698
+ additional = wrapper_frames[method_name]
699
+ additional[:children] = @customizations[:children].merge(additional[:children]) if
700
+ additional[:children]
701
+ customizations.merge!(additional)
702
+ end
703
+ customizations
704
+ end
705
+
706
+ protected
707
+ def invoke_setter(child_frame, value)
708
+ if context.kind_of?(Context)
709
+ context.__set_data__(child_frame.getter, value)
710
+ else
711
+ context.send(child_frame.setter, value)
712
+ end
713
+ end
714
+
715
+ protected
716
+ def add_mutators_for child_frame
717
+ return if context.kind_of?(Context)
718
+ add_mutators(child_frame.ruby_name,
719
+ child_frame.setter,
720
+ child_frame.getter)
721
+ end
722
+
723
+ protected
724
+ def add_mutators(variable_name,
725
+ setter = nil,
726
+ getter = nil)
727
+ return if context.kind_of?(Context)
728
+ variable_name = variable_name.to_s.gsub(/\?$/, '')
729
+ setter ||= "#{variable_name}="
730
+ getter ||= variable_name
731
+ return if context.respond_to?(getter) && context.respond_to?(setter)
732
+ MetaUtils.extend_method(context, setter) do |val|
733
+ instance_variable_set("@#{variable_name}", val)
734
+ end
735
+ MetaUtils.extend_method(context, getter) do
736
+ instance_variable_get("@#{variable_name}")
737
+ end
738
+ end
739
+
740
+ protected
741
+ def forced?
742
+ @customizations[:forced]
743
+ end
744
+
745
+ protected
746
+ def ignored?
747
+ @customizations[:ignored]
748
+ end
749
+
750
+ protected
751
+ def collected?
752
+ @customizations[:collected]
753
+ end
754
+
755
+ protected
756
+ def wrapped?
757
+ @customizations[:wrapper]
758
+ end
759
+
760
+ protected
761
+ def wrapper_methods
762
+ @customizations[:wrapper]
763
+ end
764
+
765
+ protected
766
+ def default_value
767
+ if
768
+ # TODO : move this out of the default value method
769
+ @context and
770
+ @context.respond_to?(:encoding) and
771
+ @context.encoding == 'base64'
772
+ then
773
+ Base64.decode64(@text.strip)
774
+ else
775
+ @context || @text
776
+ end
777
+ end
778
+
779
+ protected
780
+ def ruby_name
781
+ Inflection.ruby_name(@customizations[:renamed] || element_name)
782
+ end
783
+
784
+ protected
785
+ def customizations_for_child child_element_name
786
+ @customizations[:children][child_element_name] ||
787
+ CustomizationContext.new(child_element_name)
788
+ end
789
+
790
+ protected
791
+ def initial_customizations(element_name = nil)
792
+ end
793
+
794
+ end
795
+
796
+ # @private
797
+ class RootFrame < Frame
798
+
799
+ def initialize context, customizations
800
+ super('ROOT', :context => context, :customizations => customizations)
801
+ end
802
+
803
+ end
804
+
805
+ # @private
806
+ class StubResponse
807
+
808
+ def initialize customizations, context = nil
809
+ @customizations = customizations
810
+ stub_methods(customizations, context || self)
811
+ end
812
+
813
+ def inspect
814
+ methods = public_methods - Object.public_methods
815
+ "<Stub #{methods.collect{|m| ":#{m}" }.sort.join(', ')}>"
816
+ end
817
+
818
+ # @private
819
+ private
820
+ def stub_methods customizations, context
821
+ add_wrappers_to_context(customizations, context)
822
+ add_child_elements_to_context(customizations, context)
823
+ add_indexes_to_context(customizations, context)
824
+ end
825
+
826
+ # @private
827
+ private
828
+ def add_wrappers_to_context customizations, context
829
+ wrappers(customizations) do |wrapper_name,wrapper_customizations|
830
+ MetaUtils.extend_method(context, wrapper_name) do
831
+ StubResponse.new(wrapper_customizations)
832
+ end
833
+ end
834
+ end
835
+
836
+ # @private
837
+ private
838
+ def add_child_elements_to_context customizations, context
839
+ without_wrapper(customizations) do |child_name,child_rules|
840
+
841
+ ruby_name = Inflection.ruby_name(child_rules[:renamed] || child_name)
842
+
843
+ # we stop at any collected elements
844
+ if child_rules[:collected]
845
+ MetaUtils.extend_method(context, ruby_name) { [] }
846
+ next
847
+ end
848
+
849
+ if child_rules[:construct_value]
850
+
851
+ MetaUtils.extend_method(context, ruby_name) do
852
+ child_rules[:construct_value].call
853
+ end
854
+
855
+ elsif child_rules[:children].empty? # has no child elements
856
+
857
+ unless child_rules[:ignored]
858
+
859
+ method_name = child_rules[:boolean] ? "#{ruby_name}?" : ruby_name
860
+
861
+ MetaUtils.extend_method(context, method_name) do
862
+ if child_rules[:value_formatter]
863
+ child_rules[:value_formatter].format_value('')
864
+ else
865
+ nil
866
+ end
867
+ end
868
+ end
869
+
870
+ else # it has one or more child elements
871
+
872
+ if child_rules[:ignored]
873
+ stub_methods(child_rules, context)
874
+ else
875
+ MetaUtils.extend_method(context, ruby_name) do
876
+ StubResponse.new(child_rules)
877
+ end
878
+ end
879
+
880
+ end
881
+
882
+ end
883
+ end
884
+
885
+ # @private
886
+ def add_indexes_to_context(customizations, context)
887
+ if indexes = customizations[:index_names]
888
+ indexes.each do |index|
889
+ MetaUtils.extend_method(context, index) { {} }
890
+ end
891
+ end
892
+ end
893
+
894
+ # @private
895
+ def wrappers customizations, &block
896
+ wrappers = {}
897
+ customizations[:children].each_pair do |child_name,child_rules|
898
+ if child_rules[:wrapper]
899
+ wrapper_name = child_rules[:wrapper].first
900
+ wrappers[wrapper_name] ||= { :children => {} }
901
+ wrappers[wrapper_name][:children][child_name] = child_rules.merge(:wrapper => nil)
902
+ end
903
+ end
904
+
905
+ wrappers.each_pair do |wrapper_name, wrapper_customizations|
906
+ yield(wrapper_name, wrapper_customizations)
907
+ end
908
+ end
909
+
910
+ # @private
911
+ private
912
+ def without_wrapper customizations, &block
913
+ customizations[:children].each_pair do |child_name,child_rules|
914
+ unless child_rules[:wrapper]
915
+ yield(child_name, child_rules)
916
+ end
917
+ end
918
+ end
919
+
920
+ end
921
+
922
+ end
923
+ end