aws-sdk 1.1.3 → 1.1.4
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/ca-bundle.crt +2 -348
- data/lib/aws.rb +24 -49
- data/lib/aws/{common.rb → core.rb} +120 -9
- data/lib/aws/{api_config.rb → core/api_config.rb} +19 -20
- data/lib/aws/core/api_config_transform.rb +36 -0
- data/lib/aws/core/async_handle.rb +91 -0
- data/lib/aws/core/authorize_v2.rb +39 -0
- data/lib/aws/{authorize_v3.rb → core/authorize_v3.rb} +16 -15
- data/lib/aws/{api_config_transform.rb → core/authorize_with_session_token.rb} +9 -12
- data/lib/aws/core/autoloader.rb +64 -0
- data/lib/aws/core/cacheable.rb +78 -0
- data/lib/aws/core/client.rb +471 -0
- data/lib/aws/core/client_logging.rb +125 -0
- data/lib/aws/core/collections.rb +229 -0
- data/lib/aws/core/configuration.rb +358 -0
- data/lib/aws/core/configured_client_methods.rb +76 -0
- data/lib/aws/core/configured_grammars.rb +63 -0
- data/lib/aws/{configured_option_grammars.rb → core/configured_option_grammars.rb} +18 -20
- data/lib/aws/{configured_xml_grammars.rb → core/configured_xml_grammars.rb} +19 -21
- data/lib/aws/core/default_signer.rb +67 -0
- data/lib/aws/core/http/curb_handler.rb +129 -0
- data/lib/aws/core/http/handler.rb +77 -0
- data/lib/aws/core/http/httparty_handler.rb +113 -0
- data/lib/aws/core/http/net_http_handler.rb +124 -0
- data/lib/aws/core/http/request.rb +207 -0
- data/lib/aws/core/http/response.rb +73 -0
- data/lib/aws/{ignore_result_element.rb → core/ignore_result_element.rb} +14 -18
- data/lib/aws/core/indifferent_hash.rb +88 -0
- data/lib/aws/core/inflection.rb +47 -0
- data/lib/aws/core/lazy_error_classes.rb +62 -0
- data/lib/aws/{uri_escape.rb → core/meta_utils.rb} +24 -23
- data/lib/aws/core/model.rb +57 -0
- data/lib/aws/{authorize_with_session_token.rb → core/naming.rb} +9 -8
- data/lib/aws/core/option_grammar.rb +562 -0
- data/lib/aws/core/policy.rb +914 -0
- data/lib/aws/core/resource.rb +380 -0
- data/lib/aws/core/resource_cache.rb +40 -0
- data/lib/aws/core/response.rb +125 -0
- data/lib/aws/core/response_cache.rb +50 -0
- data/lib/aws/core/service_interface.rb +60 -0
- data/lib/aws/core/uri_escape.rb +46 -0
- data/lib/aws/core/xml_grammar.rb +926 -0
- data/lib/aws/ec2.rb +47 -16
- data/lib/aws/ec2/attachment.rb +0 -2
- data/lib/aws/ec2/attachment_collection.rb +1 -6
- data/lib/aws/ec2/availability_zone.rb +0 -2
- data/lib/aws/ec2/availability_zone_collection.rb +0 -3
- data/lib/aws/ec2/client.rb +6 -9
- data/lib/aws/ec2/client/xml.rb +3 -6
- data/lib/aws/ec2/collection.rb +1 -4
- data/lib/aws/ec2/config.rb +18 -0
- data/lib/aws/ec2/elastic_ip.rb +0 -2
- data/lib/aws/ec2/elastic_ip_collection.rb +0 -4
- data/lib/aws/ec2/errors.rb +1 -4
- data/lib/aws/ec2/has_permissions.rb +0 -2
- data/lib/aws/ec2/image.rb +0 -5
- data/lib/aws/ec2/image_collection.rb +0 -6
- data/lib/aws/ec2/instance.rb +23 -9
- data/lib/aws/ec2/instance_collection.rb +85 -22
- data/lib/aws/ec2/key_pair.rb +0 -2
- data/lib/aws/ec2/key_pair_collection.rb +1 -4
- data/lib/aws/ec2/permission_collection.rb +2 -5
- data/lib/aws/ec2/region.rb +0 -2
- data/lib/aws/ec2/region_collection.rb +0 -4
- data/lib/aws/ec2/request.rb +4 -9
- data/lib/aws/ec2/reserved_instances.rb +0 -3
- data/lib/aws/ec2/reserved_instances_collection.rb +0 -4
- data/lib/aws/ec2/reserved_instances_offering.rb +0 -3
- data/lib/aws/ec2/reserved_instances_offering_collection.rb +0 -4
- data/lib/aws/ec2/resource.rb +3 -5
- data/lib/aws/ec2/resource_tag_collection.rb +1 -6
- data/lib/aws/ec2/security_group.rb +204 -72
- data/lib/aws/ec2/security_group/egress_ip_permission_collection.rb +53 -0
- data/lib/aws/ec2/security_group/ip_permission.rb +92 -10
- data/lib/aws/ec2/security_group/ip_permission_collection.rb +14 -15
- data/lib/aws/ec2/security_group_collection.rb +11 -8
- data/lib/aws/ec2/snapshot.rb +0 -4
- data/lib/aws/ec2/snapshot_collection.rb +0 -4
- data/lib/aws/ec2/tag.rb +0 -3
- data/lib/aws/ec2/tag_collection.rb +1 -8
- data/lib/aws/ec2/tagged_item.rb +1 -3
- data/lib/aws/ec2/volume.rb +0 -6
- data/lib/aws/ec2/volume_collection.rb +0 -4
- data/lib/aws/errors.rb +0 -3
- data/lib/aws/iam.rb +33 -12
- data/lib/aws/iam/access_key.rb +0 -3
- data/lib/aws/iam/access_key_collection.rb +0 -3
- data/lib/aws/iam/account_alias_collection.rb +0 -2
- data/lib/aws/iam/client.rb +6 -8
- data/lib/aws/iam/client/xml.rb +4 -8
- data/lib/aws/iam/collection.rb +2 -5
- data/lib/aws/iam/config.rb +18 -0
- data/lib/aws/iam/errors.rb +2 -4
- data/lib/aws/iam/group.rb +0 -5
- data/lib/aws/iam/group_collection.rb +0 -3
- data/lib/aws/iam/group_policy_collection.rb +0 -2
- data/lib/aws/iam/group_user_collection.rb +1 -4
- data/lib/aws/iam/login_profile.rb +0 -2
- data/lib/aws/iam/mfa_device.rb +1 -3
- data/lib/aws/iam/mfa_device_collection.rb +0 -3
- data/lib/aws/iam/policy.rb +2 -4
- data/lib/aws/iam/policy_collection.rb +0 -3
- data/lib/aws/iam/request.rb +3 -7
- data/lib/aws/iam/resource.rb +1 -4
- data/lib/aws/iam/server_certificate.rb +0 -2
- data/lib/aws/iam/server_certificate_collection.rb +0 -3
- data/lib/aws/iam/signing_certificate.rb +0 -2
- data/lib/aws/iam/signing_certificate_collection.rb +0 -3
- data/lib/aws/iam/user.rb +0 -7
- data/lib/aws/iam/user_collection.rb +0 -3
- data/lib/aws/iam/user_group_collection.rb +0 -3
- data/lib/aws/iam/user_policy.rb +0 -2
- data/lib/aws/iam/user_policy_collection.rb +0 -3
- data/lib/aws/rails.rb +5 -4
- data/lib/aws/record.rb +4 -1
- data/lib/aws/record/base.rb +3 -2
- data/lib/aws/record/errors.rb +1 -3
- data/lib/aws/s3.rb +31 -6
- data/lib/aws/s3/access_control_list.rb +0 -2
- data/lib/aws/s3/acl_object.rb +10 -12
- data/lib/aws/s3/bucket.rb +1 -9
- data/lib/aws/s3/bucket_collection.rb +2 -5
- data/lib/aws/s3/bucket_version_collection.rb +1 -5
- data/lib/aws/s3/client.rb +34 -37
- data/lib/aws/s3/client/xml.rb +11 -14
- data/lib/aws/{resource_cache.rb → s3/config.rb} +6 -20
- data/lib/aws/s3/errors.rb +6 -7
- data/lib/aws/s3/multipart_upload.rb +1 -4
- data/lib/aws/s3/multipart_upload_collection.rb +1 -6
- data/lib/aws/s3/object_collection.rb +1 -5
- data/lib/aws/s3/object_metadata.rb +1 -3
- data/lib/aws/s3/object_upload_collection.rb +1 -7
- data/lib/aws/s3/object_version.rb +1 -4
- data/lib/aws/s3/object_version_collection.rb +1 -4
- data/lib/aws/s3/policy.rb +3 -5
- data/lib/aws/s3/prefix_and_delimiter_collection.rb +0 -2
- data/lib/aws/s3/prefixed_collection.rb +0 -2
- data/lib/aws/s3/presigned_post.rb +1 -3
- data/lib/aws/s3/request.rb +2 -5
- data/lib/aws/s3/s3_object.rb +1 -8
- data/lib/aws/s3/tree.rb +8 -6
- data/lib/aws/s3/tree/branch_node.rb +0 -3
- data/lib/aws/s3/tree/child_collection.rb +1 -5
- data/lib/aws/s3/tree/leaf_node.rb +0 -5
- data/lib/aws/s3/tree/parent.rb +1 -4
- data/lib/aws/s3/uploaded_part.rb +1 -3
- data/lib/aws/s3/uploaded_part_collection.rb +1 -5
- data/lib/aws/simple_db.rb +21 -6
- data/lib/aws/simple_db/attribute.rb +1 -6
- data/lib/aws/simple_db/attribute_collection.rb +1 -7
- data/lib/aws/simple_db/client.rb +7 -9
- data/lib/aws/simple_db/client/options.rb +2 -4
- data/lib/aws/simple_db/client/xml.rb +5 -10
- data/lib/aws/{naming.rb → simple_db/config.rb} +3 -14
- data/lib/aws/simple_db/delete_attributes.rb +0 -2
- data/lib/aws/simple_db/domain.rb +1 -6
- data/lib/aws/simple_db/domain_collection.rb +1 -4
- data/lib/aws/simple_db/domain_metadata.rb +1 -3
- data/lib/aws/simple_db/errors.rb +1 -4
- data/lib/aws/simple_db/item.rb +1 -7
- data/lib/aws/simple_db/item_collection.rb +22 -23
- data/lib/aws/simple_db/put_attributes.rb +0 -2
- data/lib/aws/simple_db/request.rb +3 -7
- data/lib/aws/simple_email_service.rb +11 -6
- data/lib/aws/simple_email_service/client.rb +7 -9
- data/lib/aws/simple_email_service/client/options.rb +3 -6
- data/lib/aws/simple_email_service/client/xml.rb +4 -8
- data/lib/aws/simple_email_service/config.rb +19 -0
- data/lib/aws/simple_email_service/email_address_collection.rb +1 -3
- data/lib/aws/simple_email_service/errors.rb +1 -4
- data/lib/aws/simple_email_service/quotas.rb +1 -3
- data/lib/aws/simple_email_service/request.rb +3 -7
- data/lib/aws/sns.rb +15 -8
- data/lib/aws/sns/client.rb +7 -8
- data/lib/aws/sns/client/options.rb +3 -6
- data/lib/aws/sns/client/xml.rb +4 -8
- data/lib/aws/sns/config.rb +18 -0
- data/lib/aws/sns/errors.rb +1 -4
- data/lib/aws/sns/policy.rb +3 -5
- data/lib/aws/sns/request.rb +4 -9
- data/lib/aws/sns/subscription.rb +1 -5
- data/lib/aws/sns/subscription_collection.rb +1 -5
- data/lib/aws/sns/topic.rb +1 -6
- data/lib/aws/sns/topic_collection.rb +1 -4
- data/lib/aws/sns/topic_subscription_collection.rb +1 -4
- data/lib/aws/sqs.rb +14 -5
- data/lib/aws/sqs/client.rb +6 -9
- data/lib/aws/sqs/client/xml.rb +4 -7
- data/lib/aws/sqs/config.rb +18 -0
- data/lib/aws/sqs/errors.rb +1 -4
- data/lib/aws/sqs/policy.rb +3 -5
- data/lib/aws/sqs/queue.rb +2 -7
- data/lib/aws/sqs/queue_collection.rb +4 -5
- data/lib/aws/sqs/received_message.rb +1 -3
- data/lib/aws/sqs/received_sns_message.rb +2 -2
- data/lib/aws/sqs/request.rb +4 -7
- data/lib/aws/sts.rb +12 -6
- data/lib/aws/sts/client.rb +6 -8
- data/lib/aws/sts/client/xml.rb +4 -8
- data/lib/aws/sts/config.rb +18 -0
- data/lib/aws/sts/errors.rb +1 -4
- data/lib/aws/sts/federated_session.rb +0 -2
- data/lib/aws/sts/policy.rb +3 -5
- data/lib/aws/sts/request.rb +3 -7
- metadata +60 -50
- data/lib/aws/async_handle.rb +0 -90
- data/lib/aws/authorize_v2.rb +0 -37
- data/lib/aws/base_client.rb +0 -488
- data/lib/aws/cacheable.rb +0 -79
- data/lib/aws/client_logging.rb +0 -122
- data/lib/aws/collections.rb +0 -230
- data/lib/aws/configuration.rb +0 -357
- data/lib/aws/configured_client_methods.rb +0 -81
- data/lib/aws/configured_grammars.rb +0 -65
- data/lib/aws/default_signer.rb +0 -65
- data/lib/aws/http/curb_handler.rb +0 -127
- data/lib/aws/http/handler.rb +0 -77
- data/lib/aws/http/httparty_handler.rb +0 -103
- data/lib/aws/http/request.rb +0 -165
- data/lib/aws/http/request_param.rb +0 -64
- data/lib/aws/http/response.rb +0 -72
- data/lib/aws/indifferent_hash.rb +0 -86
- data/lib/aws/inflection.rb +0 -46
- data/lib/aws/lazy_error_classes.rb +0 -64
- data/lib/aws/meta_utils.rb +0 -43
- data/lib/aws/model.rb +0 -57
- data/lib/aws/option_grammar.rb +0 -565
- data/lib/aws/policy.rb +0 -914
- data/lib/aws/resource.rb +0 -381
- data/lib/aws/response.rb +0 -125
- data/lib/aws/response_cache.rb +0 -50
- data/lib/aws/service_interface.rb +0 -60
- data/lib/aws/xml_grammar.rb +0 -925
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
module AWS
|
|
15
|
+
module Core
|
|
16
|
+
|
|
17
|
+
# @private
|
|
18
|
+
class ResponseCache
|
|
19
|
+
|
|
20
|
+
attr_reader :cached_responses
|
|
21
|
+
|
|
22
|
+
attr_reader :resource_cache
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@cached_responses = []
|
|
26
|
+
@indexed_responses = {}
|
|
27
|
+
@resource_cache = ResourceCache.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add(resp)
|
|
31
|
+
cached_responses.unshift(resp)
|
|
32
|
+
@indexed_responses[resp.cache_key] = resp if
|
|
33
|
+
resp.respond_to?(:cache_key)
|
|
34
|
+
@resource_cache = ResourceCache.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def select(*types, &block)
|
|
38
|
+
cached_responses.select do |resp|
|
|
39
|
+
types.map{|t| t.to_s }.include?(resp.request_type.to_s) and
|
|
40
|
+
(block.nil? || block.call(resp))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cached(resp)
|
|
45
|
+
@indexed_responses[resp.cache_key]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
module AWS
|
|
15
|
+
module Core
|
|
16
|
+
|
|
17
|
+
# @private
|
|
18
|
+
module ServiceInterface
|
|
19
|
+
|
|
20
|
+
def self.included base
|
|
21
|
+
|
|
22
|
+
base.send(:attr_reader, :config)
|
|
23
|
+
base.send(:attr_reader, :client)
|
|
24
|
+
|
|
25
|
+
base.module_eval('module Errors; end')
|
|
26
|
+
|
|
27
|
+
unless base::Errors.include?(Errors)
|
|
28
|
+
base::Errors.module_eval { include Errors }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns a new interface object for this service. You can override
|
|
34
|
+
# any of the global configuration parameters by passing them in as
|
|
35
|
+
# hash options. They are merged with AWS.config or merged
|
|
36
|
+
# with the provided +:config+ object.
|
|
37
|
+
#
|
|
38
|
+
# @ec2 = AWS::EC2.new(:max_retries => 2)
|
|
39
|
+
#
|
|
40
|
+
# @see AWS::Cofiguration
|
|
41
|
+
#
|
|
42
|
+
# @param [Hash] options
|
|
43
|
+
# @option options [Configuration] :config An AWS::Configuration
|
|
44
|
+
# object to initialize this service interface object with. Defaults
|
|
45
|
+
# to AWS.config when not provided.
|
|
46
|
+
def initialize options = {}
|
|
47
|
+
@config = options[:config]
|
|
48
|
+
@config ||= AWS.config
|
|
49
|
+
@config = @config.with(options)
|
|
50
|
+
@client = config.send(Inflection.ruby_name(self.class.to_s) + '_client')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @private
|
|
54
|
+
def inspect
|
|
55
|
+
"<#{self.class}>"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
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 'cgi'
|
|
15
|
+
|
|
16
|
+
module AWS
|
|
17
|
+
module Core
|
|
18
|
+
|
|
19
|
+
# @private
|
|
20
|
+
module UriEscape
|
|
21
|
+
|
|
22
|
+
# Does URI escaping the way AWS expects it to be done.
|
|
23
|
+
#
|
|
24
|
+
# @private
|
|
25
|
+
protected
|
|
26
|
+
def escape value
|
|
27
|
+
value = value.encode("UTF-8") if
|
|
28
|
+
value.respond_to?(:encode)
|
|
29
|
+
CGI::escape(value.to_s).gsub('+', '%20')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# URI-escapes a path without escaping the separators
|
|
33
|
+
#
|
|
34
|
+
# @private
|
|
35
|
+
protected
|
|
36
|
+
def escape_path value
|
|
37
|
+
escaped = ""
|
|
38
|
+
value.scan(%r{(/?)([^/]+)(/?)}) do |(leading, part, trailing)|
|
|
39
|
+
escaped << leading + escape(part) + trailing
|
|
40
|
+
end
|
|
41
|
+
escaped
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,926 @@
|
|
|
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 'rexml/document'
|
|
15
|
+
require 'rexml/streamlistener'
|
|
16
|
+
require 'base64'
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
require 'nokogiri'
|
|
20
|
+
rescue LoadError => e
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module AWS
|
|
24
|
+
module Core
|
|
25
|
+
|
|
26
|
+
# @private
|
|
27
|
+
class XmlGrammar
|
|
28
|
+
|
|
29
|
+
# @private
|
|
30
|
+
class Context
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@data = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def id
|
|
37
|
+
@data[:id]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def method_missing(m, *args)
|
|
41
|
+
key = m.to_sym
|
|
42
|
+
|
|
43
|
+
return super unless @data.key?(key)
|
|
44
|
+
@data[key]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def respond_to?(m)
|
|
48
|
+
@data.key?(m.to_sym) or super
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inspect
|
|
52
|
+
methods = @data.keys
|
|
53
|
+
"<Object #{methods.reject{|m| m =~ /=$/ }.join(', ')}>"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# this gets called a LOT during response parsing, and having
|
|
57
|
+
# it be a public method is the fastest way to call it.
|
|
58
|
+
# Strictly speaking it should be private.
|
|
59
|
+
# @private
|
|
60
|
+
def __set_data__(getter, value)
|
|
61
|
+
@data[getter.to_sym] = value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @private
|
|
67
|
+
class CustomizationContext < Hash
|
|
68
|
+
|
|
69
|
+
def initialize(element_name = nil)
|
|
70
|
+
original_store(:children, {})
|
|
71
|
+
|
|
72
|
+
if element_name
|
|
73
|
+
original_store(:name, element_name)
|
|
74
|
+
recompute_accessors
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
alias_method :original_store, :[]=
|
|
79
|
+
|
|
80
|
+
def []=(name, value)
|
|
81
|
+
super
|
|
82
|
+
if respond_to?("changed_#{name}")
|
|
83
|
+
send("changed_#{name}", value)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def changed_boolean(value)
|
|
88
|
+
recompute_accessors
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def changed_renamed(value)
|
|
92
|
+
recompute_accessors
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def deep_copy(hash = self)
|
|
96
|
+
fields = hash.inject({}) do |copy,(key,value)|
|
|
97
|
+
if value.is_a?(CustomizationContext)
|
|
98
|
+
value = value.deep_copy
|
|
99
|
+
elsif value.is_a?(Hash)
|
|
100
|
+
value = deep_copy(value)
|
|
101
|
+
end
|
|
102
|
+
copy[key] = value
|
|
103
|
+
copy
|
|
104
|
+
end
|
|
105
|
+
hash.merge(fields)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
def recompute_accessors
|
|
110
|
+
ruby_name = Inflection.ruby_name((self[:renamed] ||
|
|
111
|
+
self[:name]).to_s)
|
|
112
|
+
self[:getter] =
|
|
113
|
+
if self[:boolean] && ruby_name != "value"
|
|
114
|
+
"#{ruby_name}?"
|
|
115
|
+
else
|
|
116
|
+
ruby_name
|
|
117
|
+
end
|
|
118
|
+
self[:setter] = "#{ruby_name}="
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @private
|
|
124
|
+
class << self
|
|
125
|
+
|
|
126
|
+
def parse xml, options = {}
|
|
127
|
+
|
|
128
|
+
context = options[:context] || Context.new
|
|
129
|
+
|
|
130
|
+
if defined? Nokogiri
|
|
131
|
+
parser = Parser.new(context, customizations)
|
|
132
|
+
parser.extend(NokogiriAdapter)
|
|
133
|
+
xml = "<foo/>" if xml.empty?
|
|
134
|
+
Nokogiri::XML::SAX::Parser.new(parser).parse(xml.strip)
|
|
135
|
+
else
|
|
136
|
+
parser = Parser.new(context, customizations)
|
|
137
|
+
parser.extend(REXMLSaxParserAdapter)
|
|
138
|
+
REXML::Parsers::StreamParser.new(REXML::Source.new(xml), parser).parse
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def simulate context = nil
|
|
146
|
+
StubResponse.new(customizations, context)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def customize config = nil, &block
|
|
150
|
+
grammar = Class.new(self)
|
|
151
|
+
grammar.customizations = customizations.deep_copy
|
|
152
|
+
grammar.config_eval(config) if config
|
|
153
|
+
grammar.module_eval(&block) if block_given?
|
|
154
|
+
grammar
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def element element_name, &block
|
|
158
|
+
eval_customization_context(element_name, &block)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def add_method method_name, &block
|
|
162
|
+
format_value do |value|
|
|
163
|
+
MetaUtils.extend_method(value = super(value), method_name, &block)
|
|
164
|
+
value
|
|
165
|
+
end
|
|
166
|
+
# different strategey, slightly different behavior
|
|
167
|
+
# element(method_name.to_s) do
|
|
168
|
+
# format_value(&block)
|
|
169
|
+
# force
|
|
170
|
+
# end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def ignore
|
|
174
|
+
@current[:ignored] = true
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def rename new_name
|
|
178
|
+
@current[:renamed] = new_name.to_s
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def force
|
|
182
|
+
@current[:forced] = true
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def collect_values
|
|
186
|
+
@current[:collected] = true
|
|
187
|
+
@current[:initial_collection] = lambda { [] }
|
|
188
|
+
@current[:add_to_collection] =
|
|
189
|
+
lambda { |ary, val| ary << val }
|
|
190
|
+
force
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def index(name, &block)
|
|
194
|
+
(@customizations[:index_names] ||= []) << name
|
|
195
|
+
@current[:indexed] = [name, block]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def boolean_value
|
|
199
|
+
@current[:boolean] = true
|
|
200
|
+
format_value {|value| super(value) == 'true' }
|
|
201
|
+
end
|
|
202
|
+
# required by the hash configuration method
|
|
203
|
+
alias_method :boolean, :boolean_value
|
|
204
|
+
|
|
205
|
+
TRANSLATE_DIGITS = ['0123456789'.freeze, ('X'*10).freeze]
|
|
206
|
+
EASY_FORMAT = "XXXX-XX-XXTXX:XX:XX.XXXZ".freeze
|
|
207
|
+
DATE_PUNCTUATION = ['-:.TZ'.freeze, (' '*5).freeze]
|
|
208
|
+
|
|
209
|
+
def datetime_value
|
|
210
|
+
datetime_like_value(DateTime, :civil)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def time_value
|
|
214
|
+
datetime_like_value(Time, :utc)
|
|
215
|
+
end
|
|
216
|
+
alias_method :timestamp, :time_value
|
|
217
|
+
|
|
218
|
+
def datetime_like_value(klass, parts_constructor)
|
|
219
|
+
format_value do |value|
|
|
220
|
+
value = super(value)
|
|
221
|
+
if value and value.tr(*TRANSLATE_DIGITS) == EASY_FORMAT
|
|
222
|
+
|
|
223
|
+
# it's way faster to parse this specific format manually
|
|
224
|
+
# vs. DateTime#parse, and this happens to be the format
|
|
225
|
+
# that AWS uses almost (??) everywhere.
|
|
226
|
+
|
|
227
|
+
parts = value.tr(*DATE_PUNCTUATION).
|
|
228
|
+
chop.split.map { |elem| elem.to_i }
|
|
229
|
+
klass.send(parts_constructor, *parts)
|
|
230
|
+
elsif value
|
|
231
|
+
# fallback in case we have to handle another date format
|
|
232
|
+
klass.parse(value)
|
|
233
|
+
else
|
|
234
|
+
nil
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def integer_value
|
|
240
|
+
format_value do |value|
|
|
241
|
+
value = super(value)
|
|
242
|
+
value.nil? ? nil : value.to_i
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
# required by the hash configuration method
|
|
246
|
+
alias_method :integer, :integer_value
|
|
247
|
+
alias_method :long, :integer_value
|
|
248
|
+
|
|
249
|
+
def float_value
|
|
250
|
+
format_value do |value|
|
|
251
|
+
value = super(value)
|
|
252
|
+
value.nil? ? nil : value.to_f
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
alias_method :float, :float_value
|
|
257
|
+
|
|
258
|
+
def symbol_value
|
|
259
|
+
format_value do |value|
|
|
260
|
+
value = super(value)
|
|
261
|
+
['', nil].include?(value) ? nil : Inflection.ruby_name(value).to_sym
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def format_value &block
|
|
266
|
+
@current[:value_formatter] ||= ValueFormatter.new
|
|
267
|
+
@current[:value_formatter].extend_format_value(&block)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def list child_element_name = nil, &block
|
|
271
|
+
if child_element_name
|
|
272
|
+
ignore
|
|
273
|
+
parent_element_name = @current_name
|
|
274
|
+
element(child_element_name) do
|
|
275
|
+
rename(parent_element_name)
|
|
276
|
+
collect_values
|
|
277
|
+
yield if block_given?
|
|
278
|
+
end
|
|
279
|
+
else
|
|
280
|
+
collect_values
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def map_entry(key, value)
|
|
285
|
+
collect_values
|
|
286
|
+
element(key) { rename :key }
|
|
287
|
+
element(value) { rename :value }
|
|
288
|
+
@current[:initial_collection] = lambda { {} }
|
|
289
|
+
@current[:add_to_collection] = lambda do |hash, entry|
|
|
290
|
+
hash[entry.key] = entry.value
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def map entry_name, key, value
|
|
295
|
+
parent_element_name = @current_name
|
|
296
|
+
ignore
|
|
297
|
+
element(entry_name) do
|
|
298
|
+
rename(parent_element_name)
|
|
299
|
+
map_entry(key, value)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def wrapper method_name, options = {}, &blk
|
|
304
|
+
if block_given?
|
|
305
|
+
customizations =
|
|
306
|
+
eval_customization_context(method_name,
|
|
307
|
+
CustomizationContext.new(method_name),
|
|
308
|
+
&blk)
|
|
309
|
+
raise NotImplementedError.new("can't customize wrapped " +
|
|
310
|
+
"elements within the wrapper") unless
|
|
311
|
+
customizations[:children].empty?
|
|
312
|
+
@current[:wrapper_frames] ||= {}
|
|
313
|
+
@current[:wrapper_frames][method_name] = customizations
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
(options[:for] || []).each do |element_name|
|
|
317
|
+
element element_name do
|
|
318
|
+
@current[:wrapper] ||= []
|
|
319
|
+
@current[:wrapper] << method_name
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def construct_value &block
|
|
325
|
+
@current[:construct_value] = block
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def eql? other
|
|
329
|
+
self.customizations == other.customizations
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
protected
|
|
333
|
+
def initial_customizations(element_name = nil)
|
|
334
|
+
CustomizationContext.new(element_name)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
protected
|
|
338
|
+
def eval_customization_context name, initial = nil, &block
|
|
339
|
+
current_name = @current_name
|
|
340
|
+
current = @current
|
|
341
|
+
parent = @parent
|
|
342
|
+
begin
|
|
343
|
+
@current_name = name
|
|
344
|
+
@parent = @current
|
|
345
|
+
initial ||= customizations_for(name)
|
|
346
|
+
@current = initial
|
|
347
|
+
yield if block_given?
|
|
348
|
+
ensure
|
|
349
|
+
@current_name = current_name
|
|
350
|
+
@current = current
|
|
351
|
+
@parent = parent
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# will be modified to include the customizations defined in
|
|
355
|
+
# the block
|
|
356
|
+
initial
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
protected
|
|
360
|
+
def config_eval(config)
|
|
361
|
+
config.each do |item|
|
|
362
|
+
(type, identifier, args) = parse_config_item(item)
|
|
363
|
+
case type
|
|
364
|
+
when :method
|
|
365
|
+
validate_config_method(identifier)
|
|
366
|
+
validate_args(identifier, args)
|
|
367
|
+
send(identifier, *args)
|
|
368
|
+
when :element
|
|
369
|
+
element(identifier) do
|
|
370
|
+
config_eval(args)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
protected
|
|
377
|
+
def validate_args(identifier, args)
|
|
378
|
+
arity = method(identifier).arity
|
|
379
|
+
if args.length > 0
|
|
380
|
+
raise "#{identifier} does not accept an argument" if
|
|
381
|
+
arity == 0
|
|
382
|
+
else
|
|
383
|
+
raise "#{identifier} requires an argument" unless
|
|
384
|
+
arity == 0 || arity == -1
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
protected
|
|
389
|
+
def parse_config_item(item)
|
|
390
|
+
case item
|
|
391
|
+
when Symbol
|
|
392
|
+
[:method, item, []]
|
|
393
|
+
when Hash
|
|
394
|
+
(method, arg) = item.to_a.first
|
|
395
|
+
if method.kind_of?(Symbol)
|
|
396
|
+
[:method, method, [arg].flatten]
|
|
397
|
+
else
|
|
398
|
+
[:element, method, arg]
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
protected
|
|
404
|
+
def validate_config_method(method)
|
|
405
|
+
allow_methods = %w(
|
|
406
|
+
rename attribute_name boolean integer long float list force
|
|
407
|
+
ignore collect_values symbol_value timestamp map_entry map
|
|
408
|
+
)
|
|
409
|
+
unless allow_methods.include?(method.to_s)
|
|
410
|
+
raise "#{method} cannot be used in configuration"
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
protected
|
|
415
|
+
def customizations
|
|
416
|
+
@customizations ||= CustomizationContext.new
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
protected
|
|
420
|
+
def customizations_for element_name
|
|
421
|
+
if @parent
|
|
422
|
+
@parent[:children][element_name] ||=
|
|
423
|
+
CustomizationContext.new(element_name)
|
|
424
|
+
else
|
|
425
|
+
customizations[:children][element_name] ||=
|
|
426
|
+
CustomizationContext.new(element_name)
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
protected
|
|
431
|
+
def customizations= customizations
|
|
432
|
+
@customizations = customizations
|
|
433
|
+
@current = customizations
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# @private
|
|
439
|
+
class ValueFormatter
|
|
440
|
+
|
|
441
|
+
def extend_format_value &block
|
|
442
|
+
MetaUtils.extend_method(self, :format_value, &block)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def format_value value
|
|
446
|
+
value
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# @private
|
|
452
|
+
class Parser
|
|
453
|
+
|
|
454
|
+
def initialize context, customizations
|
|
455
|
+
@context = context
|
|
456
|
+
@customizations = customizations
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def start_element element_name, attrs
|
|
460
|
+
|
|
461
|
+
if @frame
|
|
462
|
+
@frame = @frame.build_child_frame(element_name)
|
|
463
|
+
else
|
|
464
|
+
@frame = RootFrame.new(@context, @customizations)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# consume attributes the same way we consume nested xml elements
|
|
468
|
+
attrs.each do |(attr_name, attr_value)|
|
|
469
|
+
attr_frame = @frame.build_child_frame(attr_name)
|
|
470
|
+
attr_frame.add_text(attr_value)
|
|
471
|
+
@frame.consume_child_frame(attr_frame)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def end_element name
|
|
477
|
+
@frame.close
|
|
478
|
+
if @frame.parent_frame
|
|
479
|
+
child_frame = @frame
|
|
480
|
+
parent_frame = @frame.parent_frame
|
|
481
|
+
parent_frame.consume_child_frame(child_frame)
|
|
482
|
+
end
|
|
483
|
+
@frame = @frame.parent_frame
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def characters chars
|
|
487
|
+
@frame.add_text(chars) if @frame
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
module REXMLSaxParserAdapter
|
|
493
|
+
|
|
494
|
+
include REXML::StreamListener
|
|
495
|
+
|
|
496
|
+
def tag_start(name, attrs)
|
|
497
|
+
start_element(name, attrs)
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def tag_end(name)
|
|
501
|
+
end_element(name)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def text(chars)
|
|
505
|
+
characters(chars)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
module NokogiriAdapter
|
|
511
|
+
|
|
512
|
+
def xmldecl(*args); end
|
|
513
|
+
def start_document; end
|
|
514
|
+
def end_document; end
|
|
515
|
+
def start_element_namespace(name, attrs = [], prefix = nil, uri = nil, ns = [])
|
|
516
|
+
start_element(name, attrs.map { |att| [att.localname, att.value] })
|
|
517
|
+
end
|
|
518
|
+
def end_element_namespace(name, prefix = nil, uri = nil)
|
|
519
|
+
end_element(name)
|
|
520
|
+
end
|
|
521
|
+
def error(*args); end
|
|
522
|
+
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# @private
|
|
526
|
+
class Frame
|
|
527
|
+
|
|
528
|
+
attr_reader :parent_frame
|
|
529
|
+
|
|
530
|
+
attr_reader :root_frame
|
|
531
|
+
|
|
532
|
+
attr_reader :element_name
|
|
533
|
+
|
|
534
|
+
attr_accessor :customizations
|
|
535
|
+
|
|
536
|
+
def initialize element_name, options = {}
|
|
537
|
+
|
|
538
|
+
@element_name = element_name
|
|
539
|
+
@context = options[:context]
|
|
540
|
+
@parent_frame = options[:parent_frame]
|
|
541
|
+
@root_frame = options[:root_frame]
|
|
542
|
+
@wrapper_frames = {}
|
|
543
|
+
|
|
544
|
+
if @parent_frame
|
|
545
|
+
@customizations = @parent_frame.customizations_for_child(element_name)
|
|
546
|
+
else
|
|
547
|
+
@customizations = options[:customizations]
|
|
548
|
+
@root_frame ||= self
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
if @root_frame == self and
|
|
552
|
+
indexes = @customizations[:index_names]
|
|
553
|
+
indexes.each do |name|
|
|
554
|
+
if context.kind_of?(Context)
|
|
555
|
+
context.__set_data__(name, {})
|
|
556
|
+
else
|
|
557
|
+
add_mutators(name)
|
|
558
|
+
context.send("#{name}=", {})
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# we build and discard child frames here so we can know
|
|
564
|
+
# which children should always add a method to this
|
|
565
|
+
# frame's context (forced elements, like collected arrays)
|
|
566
|
+
@customizations[:children].keys.each do |child_element_name|
|
|
567
|
+
consume_initial_frame(build_child_frame(child_element_name))
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
if @customizations[:wrapper_frames]
|
|
571
|
+
@customizations[:wrapper_frames].keys.each do |method_name|
|
|
572
|
+
consume_initial_frame(wrapper_frame_for(method_name))
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def build_child_frame(child_element_name)
|
|
579
|
+
Frame.new(child_element_name,
|
|
580
|
+
:parent_frame => self,
|
|
581
|
+
:root_frame => root_frame)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def consume_child_frame child_frame
|
|
585
|
+
|
|
586
|
+
return if child_frame.ignored?
|
|
587
|
+
|
|
588
|
+
if child_frame.wrapped?
|
|
589
|
+
child_frame.wrapper_methods.each do |method_name|
|
|
590
|
+
consume_in_wrapper(method_name, child_frame)
|
|
591
|
+
end
|
|
592
|
+
else
|
|
593
|
+
# forced child frames have already added mutators to this context
|
|
594
|
+
add_mutators_for(child_frame) unless child_frame.forced?
|
|
595
|
+
|
|
596
|
+
if child_frame.collected?
|
|
597
|
+
child_frame.add_to_collection(context.send(child_frame.getter),
|
|
598
|
+
child_frame.value)
|
|
599
|
+
else
|
|
600
|
+
invoke_setter(child_frame, child_frame.value)
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def close
|
|
607
|
+
if indexed = @customizations[:indexed]
|
|
608
|
+
(name, block) = indexed
|
|
609
|
+
key = block.call(context)
|
|
610
|
+
[key].flatten.each do |k|
|
|
611
|
+
index(name)[k] = context
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
def add_text text
|
|
617
|
+
@text ||= ''
|
|
618
|
+
@text << text
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def value
|
|
622
|
+
@customizations[:value_formatter] ?
|
|
623
|
+
@customizations[:value_formatter].format_value(default_value) :
|
|
624
|
+
default_value
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def context
|
|
628
|
+
@context ||= (self.ignored? ? parent_frame.context : construct_context)
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def setter
|
|
632
|
+
@customizations[:setter]
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def getter
|
|
636
|
+
@customizations[:getter]
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def initial_collection
|
|
640
|
+
@customizations[:initial_collection].call
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def add_to_collection(collection, value)
|
|
644
|
+
@customizations[:add_to_collection].call(collection, value)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def index(name)
|
|
648
|
+
return root_frame.index(name) unless root_frame == self
|
|
649
|
+
context.send(name)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
protected
|
|
653
|
+
def consume_initial_frame(child_frame)
|
|
654
|
+
if child_frame.forced?
|
|
655
|
+
add_mutators_for(child_frame)
|
|
656
|
+
if child_frame.collected?
|
|
657
|
+
invoke_setter(child_frame, child_frame.initial_collection)
|
|
658
|
+
else
|
|
659
|
+
# this allows nested forced elements to appear
|
|
660
|
+
invoke_setter(child_frame, child_frame.value)
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
protected
|
|
666
|
+
def construct_context
|
|
667
|
+
if @customizations[:construct_value]
|
|
668
|
+
instance_eval(&@customizations[:construct_value])
|
|
669
|
+
else
|
|
670
|
+
Context.new
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
protected
|
|
675
|
+
def consume_in_wrapper method_name, child_frame
|
|
676
|
+
wrapper_frame = wrapper_frame_for(method_name)
|
|
677
|
+
add_mutators(method_name)
|
|
678
|
+
|
|
679
|
+
# the wrapper consumes the unwrapped child
|
|
680
|
+
customizations = child_frame.customizations.merge(:wrapper => nil)
|
|
681
|
+
child_frame = child_frame.dup
|
|
682
|
+
child_frame.customizations = customizations
|
|
683
|
+
|
|
684
|
+
wrapper_frame.consume_child_frame(child_frame)
|
|
685
|
+
consume_child_frame(wrapper_frame)
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
protected
|
|
689
|
+
def wrapper_frame_for(method_name)
|
|
690
|
+
@wrapper_frames[method_name] ||=
|
|
691
|
+
Frame.new(method_name.to_s,
|
|
692
|
+
:customizations => wrapper_customizations(method_name))
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
protected
|
|
696
|
+
def wrapper_customizations(method_name)
|
|
697
|
+
customizations = CustomizationContext.new(method_name)
|
|
698
|
+
customizations[:children] = @customizations[:children]
|
|
699
|
+
if wrapper_frames = @customizations[:wrapper_frames] and
|
|
700
|
+
additional = wrapper_frames[method_name]
|
|
701
|
+
additional[:children] = @customizations[:children].merge(additional[:children]) if
|
|
702
|
+
additional[:children]
|
|
703
|
+
customizations.merge!(additional)
|
|
704
|
+
end
|
|
705
|
+
customizations
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
protected
|
|
709
|
+
def invoke_setter(child_frame, value)
|
|
710
|
+
if context.kind_of?(Context)
|
|
711
|
+
context.__set_data__(child_frame.getter, value)
|
|
712
|
+
else
|
|
713
|
+
context.send(child_frame.setter, value)
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
protected
|
|
718
|
+
def add_mutators_for child_frame
|
|
719
|
+
return if context.kind_of?(Context)
|
|
720
|
+
add_mutators(child_frame.ruby_name,
|
|
721
|
+
child_frame.setter,
|
|
722
|
+
child_frame.getter)
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
protected
|
|
726
|
+
def add_mutators(variable_name,
|
|
727
|
+
setter = nil,
|
|
728
|
+
getter = nil)
|
|
729
|
+
return if context.kind_of?(Context)
|
|
730
|
+
variable_name = variable_name.to_s.gsub(/\?$/, '')
|
|
731
|
+
setter ||= "#{variable_name}="
|
|
732
|
+
getter ||= variable_name
|
|
733
|
+
return if context.respond_to?(getter) && context.respond_to?(setter)
|
|
734
|
+
MetaUtils.extend_method(context, setter) do |val|
|
|
735
|
+
instance_variable_set("@#{variable_name}", val)
|
|
736
|
+
end
|
|
737
|
+
MetaUtils.extend_method(context, getter) do
|
|
738
|
+
instance_variable_get("@#{variable_name}")
|
|
739
|
+
end
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
protected
|
|
743
|
+
def forced?
|
|
744
|
+
@customizations[:forced]
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
protected
|
|
748
|
+
def ignored?
|
|
749
|
+
@customizations[:ignored]
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
protected
|
|
753
|
+
def collected?
|
|
754
|
+
@customizations[:collected]
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
protected
|
|
758
|
+
def wrapped?
|
|
759
|
+
@customizations[:wrapper]
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
protected
|
|
763
|
+
def wrapper_methods
|
|
764
|
+
@customizations[:wrapper]
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
protected
|
|
768
|
+
def default_value
|
|
769
|
+
if
|
|
770
|
+
# TODO : move this out of the default value method
|
|
771
|
+
@context and
|
|
772
|
+
@context.respond_to?(:encoding) and
|
|
773
|
+
@context.encoding == 'base64'
|
|
774
|
+
then
|
|
775
|
+
Base64.decode64(@text.strip)
|
|
776
|
+
else
|
|
777
|
+
@context || @text
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
protected
|
|
782
|
+
def ruby_name
|
|
783
|
+
Inflection.ruby_name(@customizations[:renamed] || element_name)
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
protected
|
|
787
|
+
def customizations_for_child child_element_name
|
|
788
|
+
@customizations[:children][child_element_name] ||
|
|
789
|
+
CustomizationContext.new(child_element_name)
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
protected
|
|
793
|
+
def initial_customizations(element_name = nil)
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
# @private
|
|
799
|
+
class RootFrame < Frame
|
|
800
|
+
|
|
801
|
+
def initialize context, customizations
|
|
802
|
+
super('ROOT', :context => context, :customizations => customizations)
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# @private
|
|
808
|
+
class StubResponse
|
|
809
|
+
|
|
810
|
+
def initialize customizations, context = nil
|
|
811
|
+
@customizations = customizations
|
|
812
|
+
stub_methods(customizations, context || self)
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
def inspect
|
|
816
|
+
methods = public_methods - Object.public_methods
|
|
817
|
+
"<Stub #{methods.collect{|m| ":#{m}" }.sort.join(', ')}>"
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
# @private
|
|
821
|
+
private
|
|
822
|
+
def stub_methods customizations, context
|
|
823
|
+
add_wrappers_to_context(customizations, context)
|
|
824
|
+
add_child_elements_to_context(customizations, context)
|
|
825
|
+
add_indexes_to_context(customizations, context)
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
# @private
|
|
829
|
+
private
|
|
830
|
+
def add_wrappers_to_context customizations, context
|
|
831
|
+
wrappers(customizations) do |wrapper_name,wrapper_customizations|
|
|
832
|
+
MetaUtils.extend_method(context, wrapper_name) do
|
|
833
|
+
StubResponse.new(wrapper_customizations)
|
|
834
|
+
end
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# @private
|
|
839
|
+
private
|
|
840
|
+
def add_child_elements_to_context customizations, context
|
|
841
|
+
without_wrapper(customizations) do |child_name,child_rules|
|
|
842
|
+
|
|
843
|
+
ruby_name = Inflection.ruby_name(child_rules[:renamed] || child_name)
|
|
844
|
+
|
|
845
|
+
# we stop at any collected elements
|
|
846
|
+
if child_rules[:collected]
|
|
847
|
+
MetaUtils.extend_method(context, ruby_name) { [] }
|
|
848
|
+
next
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
if child_rules[:construct_value]
|
|
852
|
+
|
|
853
|
+
MetaUtils.extend_method(context, ruby_name) do
|
|
854
|
+
child_rules[:construct_value].call
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
elsif child_rules[:children].empty? # has no child elements
|
|
858
|
+
|
|
859
|
+
unless child_rules[:ignored]
|
|
860
|
+
|
|
861
|
+
method_name = child_rules[:boolean] ? "#{ruby_name}?" : ruby_name
|
|
862
|
+
|
|
863
|
+
MetaUtils.extend_method(context, method_name) do
|
|
864
|
+
if child_rules[:value_formatter]
|
|
865
|
+
child_rules[:value_formatter].format_value('')
|
|
866
|
+
else
|
|
867
|
+
nil
|
|
868
|
+
end
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
else # it has one or more child elements
|
|
873
|
+
|
|
874
|
+
if child_rules[:ignored]
|
|
875
|
+
stub_methods(child_rules, context)
|
|
876
|
+
else
|
|
877
|
+
MetaUtils.extend_method(context, ruby_name) do
|
|
878
|
+
StubResponse.new(child_rules)
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
# @private
|
|
888
|
+
def add_indexes_to_context(customizations, context)
|
|
889
|
+
if indexes = customizations[:index_names]
|
|
890
|
+
indexes.each do |index|
|
|
891
|
+
MetaUtils.extend_method(context, index) { {} }
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
# @private
|
|
897
|
+
def wrappers customizations, &block
|
|
898
|
+
wrappers = {}
|
|
899
|
+
customizations[:children].each_pair do |child_name,child_rules|
|
|
900
|
+
if child_rules[:wrapper]
|
|
901
|
+
wrapper_name = child_rules[:wrapper].first
|
|
902
|
+
wrappers[wrapper_name] ||= { :children => {} }
|
|
903
|
+
wrappers[wrapper_name][:children][child_name] = child_rules.merge(:wrapper => nil)
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
wrappers.each_pair do |wrapper_name, wrapper_customizations|
|
|
908
|
+
yield(wrapper_name, wrapper_customizations)
|
|
909
|
+
end
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
# @private
|
|
913
|
+
private
|
|
914
|
+
def without_wrapper customizations, &block
|
|
915
|
+
customizations[:children].each_pair do |child_name,child_rules|
|
|
916
|
+
unless child_rules[:wrapper]
|
|
917
|
+
yield(child_name, child_rules)
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
end
|