aws-sdk 1.0.0

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 (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