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.
- data/.yardopts +6 -0
- data/LICENSE.txt +171 -0
- data/NOTICE.txt +2 -0
- data/README.rdoc +189 -0
- data/lib/aws-sdk.rb +14 -0
- data/lib/aws.rb +63 -0
- data/lib/aws/api_config.rb +45 -0
- data/lib/aws/api_config/.document +0 -0
- data/lib/aws/api_config/EC2-2011-02-28.yml +2314 -0
- data/lib/aws/api_config/SNS-2010-03-31.yml +171 -0
- data/lib/aws/api_config/SQS-2009-02-01.yml +161 -0
- data/lib/aws/api_config/SimpleDB-2009-04-15.yml +278 -0
- data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +147 -0
- data/lib/aws/api_config_transform.rb +32 -0
- data/lib/aws/async_handle.rb +90 -0
- data/lib/aws/authorize_v2.rb +37 -0
- data/lib/aws/authorize_v3.rb +37 -0
- data/lib/aws/base_client.rb +524 -0
- data/lib/aws/cacheable.rb +92 -0
- data/lib/aws/common.rb +228 -0
- data/lib/aws/configurable.rb +36 -0
- data/lib/aws/configuration.rb +272 -0
- data/lib/aws/configured_client_methods.rb +81 -0
- data/lib/aws/configured_grammars.rb +65 -0
- data/lib/aws/configured_option_grammars.rb +46 -0
- data/lib/aws/configured_xml_grammars.rb +47 -0
- data/lib/aws/default_signer.rb +38 -0
- data/lib/aws/ec2.rb +321 -0
- data/lib/aws/ec2/attachment.rb +149 -0
- data/lib/aws/ec2/attachment_collection.rb +57 -0
- data/lib/aws/ec2/availability_zone.rb +80 -0
- data/lib/aws/ec2/availability_zone_collection.rb +47 -0
- data/lib/aws/ec2/block_device_mappings.rb +53 -0
- data/lib/aws/ec2/client.rb +54 -0
- data/lib/aws/ec2/client/xml.rb +127 -0
- data/lib/aws/ec2/collection.rb +39 -0
- data/lib/aws/ec2/config_transform.rb +63 -0
- data/lib/aws/ec2/elastic_ip.rb +107 -0
- data/lib/aws/ec2/elastic_ip_collection.rb +85 -0
- data/lib/aws/ec2/errors.rb +29 -0
- data/lib/aws/ec2/filtered_collection.rb +65 -0
- data/lib/aws/ec2/has_permissions.rb +46 -0
- data/lib/aws/ec2/image.rb +245 -0
- data/lib/aws/ec2/image_collection.rb +235 -0
- data/lib/aws/ec2/instance.rb +515 -0
- data/lib/aws/ec2/instance_collection.rb +276 -0
- data/lib/aws/ec2/key_pair.rb +86 -0
- data/lib/aws/ec2/key_pair_collection.rb +102 -0
- data/lib/aws/ec2/permission_collection.rb +177 -0
- data/lib/aws/ec2/region.rb +81 -0
- data/lib/aws/ec2/region_collection.rb +55 -0
- data/lib/aws/ec2/request.rb +27 -0
- data/lib/aws/ec2/reserved_instances.rb +50 -0
- data/lib/aws/ec2/reserved_instances_collection.rb +44 -0
- data/lib/aws/ec2/reserved_instances_offering.rb +55 -0
- data/lib/aws/ec2/reserved_instances_offering_collection.rb +43 -0
- data/lib/aws/ec2/resource.rb +340 -0
- data/lib/aws/ec2/resource_tag_collection.rb +218 -0
- data/lib/aws/ec2/security_group.rb +246 -0
- data/lib/aws/ec2/security_group/ip_permission.rb +70 -0
- data/lib/aws/ec2/security_group/ip_permission_collection.rb +59 -0
- data/lib/aws/ec2/security_group_collection.rb +132 -0
- data/lib/aws/ec2/snapshot.rb +138 -0
- data/lib/aws/ec2/snapshot_collection.rb +90 -0
- data/lib/aws/ec2/tag.rb +88 -0
- data/lib/aws/ec2/tag_collection.rb +114 -0
- data/lib/aws/ec2/tagged_collection.rb +48 -0
- data/lib/aws/ec2/tagged_item.rb +87 -0
- data/lib/aws/ec2/volume.rb +190 -0
- data/lib/aws/ec2/volume_collection.rb +95 -0
- data/lib/aws/errors.rb +129 -0
- data/lib/aws/http/builtin_handler.rb +69 -0
- data/lib/aws/http/curb_handler.rb +123 -0
- data/lib/aws/http/handler.rb +77 -0
- data/lib/aws/http/httparty_handler.rb +61 -0
- data/lib/aws/http/request.rb +136 -0
- data/lib/aws/http/request_param.rb +63 -0
- data/lib/aws/http/response.rb +75 -0
- data/lib/aws/ignore_result_element.rb +38 -0
- data/lib/aws/indifferent_hash.rb +86 -0
- data/lib/aws/inflection.rb +46 -0
- data/lib/aws/lazy_error_classes.rb +64 -0
- data/lib/aws/meta_utils.rb +43 -0
- data/lib/aws/model.rb +57 -0
- data/lib/aws/naming.rb +32 -0
- data/lib/aws/option_grammar.rb +544 -0
- data/lib/aws/policy.rb +912 -0
- data/lib/aws/rails.rb +209 -0
- data/lib/aws/record.rb +79 -0
- data/lib/aws/record/attribute.rb +94 -0
- data/lib/aws/record/attribute_macros.rb +288 -0
- data/lib/aws/record/attributes/boolean.rb +49 -0
- data/lib/aws/record/attributes/datetime.rb +86 -0
- data/lib/aws/record/attributes/float.rb +48 -0
- data/lib/aws/record/attributes/integer.rb +68 -0
- data/lib/aws/record/attributes/sortable_float.rb +60 -0
- data/lib/aws/record/attributes/sortable_integer.rb +95 -0
- data/lib/aws/record/attributes/string.rb +69 -0
- data/lib/aws/record/base.rb +728 -0
- data/lib/aws/record/conversion.rb +38 -0
- data/lib/aws/record/dirty_tracking.rb +286 -0
- data/lib/aws/record/errors.rb +153 -0
- data/lib/aws/record/exceptions.rb +48 -0
- data/lib/aws/record/finder_methods.rb +262 -0
- data/lib/aws/record/naming.rb +31 -0
- data/lib/aws/record/scope.rb +157 -0
- data/lib/aws/record/validations.rb +653 -0
- data/lib/aws/record/validator.rb +237 -0
- data/lib/aws/record/validators/acceptance.rb +51 -0
- data/lib/aws/record/validators/block.rb +38 -0
- data/lib/aws/record/validators/confirmation.rb +43 -0
- data/lib/aws/record/validators/count.rb +108 -0
- data/lib/aws/record/validators/exclusion.rb +43 -0
- data/lib/aws/record/validators/format.rb +57 -0
- data/lib/aws/record/validators/inclusion.rb +56 -0
- data/lib/aws/record/validators/length.rb +107 -0
- data/lib/aws/record/validators/numericality.rb +138 -0
- data/lib/aws/record/validators/presence.rb +45 -0
- data/lib/aws/resource_cache.rb +39 -0
- data/lib/aws/response.rb +113 -0
- data/lib/aws/response_cache.rb +50 -0
- data/lib/aws/s3.rb +109 -0
- data/lib/aws/s3/access_control_list.rb +252 -0
- data/lib/aws/s3/acl_object.rb +266 -0
- data/lib/aws/s3/bucket.rb +320 -0
- data/lib/aws/s3/bucket_collection.rb +122 -0
- data/lib/aws/s3/bucket_version_collection.rb +85 -0
- data/lib/aws/s3/client.rb +999 -0
- data/lib/aws/s3/client/xml.rb +190 -0
- data/lib/aws/s3/data_options.rb +99 -0
- data/lib/aws/s3/errors.rb +43 -0
- data/lib/aws/s3/multipart_upload.rb +318 -0
- data/lib/aws/s3/multipart_upload_collection.rb +78 -0
- data/lib/aws/s3/object_collection.rb +159 -0
- data/lib/aws/s3/object_metadata.rb +67 -0
- data/lib/aws/s3/object_upload_collection.rb +83 -0
- data/lib/aws/s3/object_version.rb +141 -0
- data/lib/aws/s3/object_version_collection.rb +78 -0
- data/lib/aws/s3/paginated_collection.rb +94 -0
- data/lib/aws/s3/policy.rb +76 -0
- data/lib/aws/s3/prefix_and_delimiter_collection.rb +56 -0
- data/lib/aws/s3/prefixed_collection.rb +84 -0
- data/lib/aws/s3/presigned_post.rb +504 -0
- data/lib/aws/s3/request.rb +198 -0
- data/lib/aws/s3/s3_object.rb +794 -0
- data/lib/aws/s3/tree.rb +116 -0
- data/lib/aws/s3/tree/branch_node.rb +71 -0
- data/lib/aws/s3/tree/child_collection.rb +108 -0
- data/lib/aws/s3/tree/leaf_node.rb +99 -0
- data/lib/aws/s3/tree/node.rb +22 -0
- data/lib/aws/s3/tree/parent.rb +90 -0
- data/lib/aws/s3/uploaded_part.rb +82 -0
- data/lib/aws/s3/uploaded_part_collection.rb +86 -0
- data/lib/aws/service_interface.rb +60 -0
- data/lib/aws/simple_db.rb +202 -0
- data/lib/aws/simple_db/attribute.rb +159 -0
- data/lib/aws/simple_db/attribute_collection.rb +227 -0
- data/lib/aws/simple_db/client.rb +52 -0
- data/lib/aws/simple_db/client/options.rb +34 -0
- data/lib/aws/simple_db/client/xml.rb +68 -0
- data/lib/aws/simple_db/consistent_read_option.rb +42 -0
- data/lib/aws/simple_db/delete_attributes.rb +64 -0
- data/lib/aws/simple_db/domain.rb +118 -0
- data/lib/aws/simple_db/domain_collection.rb +116 -0
- data/lib/aws/simple_db/domain_metadata.rb +112 -0
- data/lib/aws/simple_db/errors.rb +46 -0
- data/lib/aws/simple_db/expect_condition_option.rb +45 -0
- data/lib/aws/simple_db/item.rb +84 -0
- data/lib/aws/simple_db/item_collection.rb +594 -0
- data/lib/aws/simple_db/item_data.rb +70 -0
- data/lib/aws/simple_db/put_attributes.rb +62 -0
- data/lib/aws/simple_db/request.rb +27 -0
- data/lib/aws/simple_email_service.rb +373 -0
- data/lib/aws/simple_email_service/client.rb +39 -0
- data/lib/aws/simple_email_service/client/options.rb +24 -0
- data/lib/aws/simple_email_service/client/xml.rb +38 -0
- data/lib/aws/simple_email_service/email_address_collection.rb +66 -0
- data/lib/aws/simple_email_service/errors.rb +29 -0
- data/lib/aws/simple_email_service/quotas.rb +64 -0
- data/lib/aws/simple_email_service/request.rb +27 -0
- data/lib/aws/sns.rb +69 -0
- data/lib/aws/sns/client.rb +37 -0
- data/lib/aws/sns/client/options.rb +24 -0
- data/lib/aws/sns/client/xml.rb +38 -0
- data/lib/aws/sns/errors.rb +29 -0
- data/lib/aws/sns/policy.rb +49 -0
- data/lib/aws/sns/request.rb +27 -0
- data/lib/aws/sns/subscription.rb +100 -0
- data/lib/aws/sns/subscription_collection.rb +84 -0
- data/lib/aws/sns/topic.rb +384 -0
- data/lib/aws/sns/topic_collection.rb +70 -0
- data/lib/aws/sns/topic_subscription_collection.rb +58 -0
- data/lib/aws/sqs.rb +70 -0
- data/lib/aws/sqs/client.rb +38 -0
- data/lib/aws/sqs/client/xml.rb +36 -0
- data/lib/aws/sqs/errors.rb +33 -0
- data/lib/aws/sqs/policy.rb +50 -0
- data/lib/aws/sqs/queue.rb +507 -0
- data/lib/aws/sqs/queue_collection.rb +105 -0
- data/lib/aws/sqs/received_message.rb +184 -0
- data/lib/aws/sqs/received_sns_message.rb +112 -0
- data/lib/aws/sqs/request.rb +44 -0
- data/lib/aws/xml_grammar.rb +923 -0
- data/rails/init.rb +15 -0
- 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
|