google-ads-common 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ 0.6.0:
2
+ - Better namespace handling in complex cases.
3
+ - Large refactoring on parameter processing code.
4
+ - Support for SimpleTypes in wsdl.
5
+
1
6
  0.5.5:
2
7
  - Now support Savon 0.9.7.
3
8
 
@@ -26,7 +26,7 @@ module AdsCommon
26
26
  # Contains helper methods for loading and managing the available services.
27
27
  # This module is meant to be imported into API-specific modules.
28
28
  module ApiConfig
29
- ADS_COMMON_VERSION = '0.5.5'
29
+ ADS_COMMON_VERSION = '0.6.0'
30
30
 
31
31
  # Get the available API versions.
32
32
  #
@@ -63,12 +63,16 @@ module AdsCommon
63
63
  def process_types(doc)
64
64
  REXML::XPath.each(doc, '//schema') do |schema|
65
65
  ns_index = process_namespace(schema)
66
- get_complex_types(schema).each do |ctype|
66
+ complex_types = get_complex_types(schema)
67
+ simple_types = get_simple_types(schema)
68
+ (complex_types + simple_types).each do |ctype|
67
69
  ctype_name = get_element_name(ctype)
68
70
  if ctype_name.match('.+Exception$')
69
71
  @soap_exceptions << extract_exception(ctype)
70
72
  elsif ctype_name.match('.+Error$')
71
73
  # We don't use it at the moment.
74
+ elsif ctype_name.match('.+\.Reason$')
75
+ # We don't use it at the moment.
72
76
  else
73
77
  @soap_types << extract_type(ctype, ns_index)
74
78
  end
@@ -104,6 +108,11 @@ module AdsCommon
104
108
  return REXML::XPath.each(node, 'complexType').to_a
105
109
  end
106
110
 
111
+ # Extracts SimpleTypes from node into an array.
112
+ def get_simple_types(node)
113
+ return REXML::XPath.each(node, 'simpleType').to_a
114
+ end
115
+
107
116
  # Extracts exception parameters from ComplexTypes element.
108
117
  def extract_exception(exception_element)
109
118
  return {:name => get_element_name(exception_element),
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/ruby
2
2
  #
3
- # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
3
+ # Authors:: api.dklimkin@gmail.com (Danial Klimkin)
4
4
  #
5
5
  # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
6
  #
@@ -20,6 +20,8 @@
20
20
  # Contains common errors across APIs, as well as base classes to inherit from
21
21
  # in specific APIs.
22
22
 
23
+ require 'pp'
24
+
23
25
  module AdsCommon
24
26
  module Errors
25
27
 
@@ -30,31 +32,53 @@ module AdsCommon
30
32
  # Raised if an attempt is made to authenticate with missing or wrong
31
33
  # information.
32
34
  class AuthError < Error
33
- attr_reader :error
34
- attr_reader :info
35
+ attr_reader :error, :info
35
36
  def initialize(message = self.class.to_s, error = nil, info = nil)
36
37
  super(message)
37
- @error = error
38
- @info = info
38
+ @error, @info = error, info
39
39
  end
40
40
  end
41
41
 
42
42
  # Raised when OAuth access token is required.
43
43
  class OAuthVerificationRequired < AuthError
44
- attr_reader :oauth_url
45
- attr_reader :request_token
44
+ attr_reader :oauth_url, :request_token
46
45
  def initialize(oauth_url, request_token)
47
46
  super()
48
47
  @oauth_url, @request_token = oauth_url, request_token
49
48
  end
50
49
  end
51
50
 
52
- # Raised if setting a non-existant property on an object
51
+ # Raised if a required property on an object is missing.
53
52
  class MissingPropertyError < Error
54
53
  attr_reader :property, :object_type
55
54
  def initialize(property, object_type)
56
- @property = property
57
- @object_type = object_type
55
+ @property, @object_type = property, object_type
56
+ end
57
+ def to_s()
58
+ return "%s: name: %s, type: %s" % [super, @property, @object_type]
59
+ end
60
+ end
61
+
62
+ # Raised if the type of the object provided does not match expected type.
63
+ class TypeMismatchError < Error
64
+ attr_reader :expected, :provided, :field_name
65
+ def initialize(expected, provided, field_name)
66
+ @expected, @provided, @field_name = expected, provided, field_name
67
+ end
68
+ def to_s()
69
+ return "%s: expected: '%s', provided: '%s' for field '%s'" %
70
+ [super, @expected, @provided, @field_name]
71
+ end
72
+ end
73
+
74
+ # Raised if unexpected parameters encountered.
75
+ class UnexpectedParametersError < Error
76
+ attr_reader :parameters_list
77
+ def initialize(parameters_list)
78
+ @parameters_list = parameters_list
79
+ end
80
+ def to_s()
81
+ return "%s: %s" % [super, PP.singleline_pp(@parameters_list, '')]
58
82
  end
59
83
  end
60
84
 
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Author:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # This class validates input parameters before passing them to Savon.
21
+
22
+ module AdsCommon
23
+ class ParametersValidator
24
+ # Savon special keys.
25
+ IGNORED_HASH_KEYS = [:order!, :attributes!]
26
+
27
+ # We collect required namespaces into this hash during validation.
28
+ attr_reader :extra_namespaces
29
+
30
+ # Instance initializer.
31
+ #
32
+ # Args:
33
+ # - service: instance of savon_service to validate for
34
+ def initialize(registry)
35
+ @registry = registry
36
+ @extra_namespaces = {}
37
+ end
38
+
39
+ # Validates input parameters to:
40
+ # - add parameter names;
41
+ # - resolve xsi:type where required;
42
+ # - convert some native types to XML.
43
+ def validate_args(action_name, args)
44
+ in_params = @registry.get_method_signature(action_name)[:input]
45
+ # TODO: compare number of parameters.
46
+ args_hash = in_params.each_with_index.inject({}) do
47
+ |result, (in_param, index)|
48
+ result.merge({in_param[:name] => deep_copy(args[index])})
49
+ end
50
+ validate_arguments(args_hash, in_params)
51
+ return args_hash
52
+ end
53
+
54
+ private
55
+
56
+ # Validates given arguments based on provided fields list.
57
+ def validate_arguments(args_hash, fields_list, type_ns = nil)
58
+ check_extra_fields(args_hash, array_from_named_list(fields_list))
59
+ add_order_key(args_hash, fields_list)
60
+ fields_list.each do |field|
61
+ key = field[:name]
62
+ item = args_hash[key]
63
+ check_required_argument_present(item, field)
64
+ if item
65
+ item_type = get_full_type_signature(field[:type])
66
+ item_ns = field[:ns] || type_ns
67
+ key = handle_namespace_override(args_hash, key, item_ns) if item_ns
68
+ validate_arg(item, args_hash, key, item_type)
69
+ end
70
+ end
71
+ return args_hash
72
+ end
73
+
74
+ # Checks if no extra fields provided outside of known ones.
75
+ def check_extra_fields(args_hash, known_fields)
76
+ extra_fields = args_hash.keys - known_fields - IGNORED_HASH_KEYS
77
+ unless extra_fields.empty?
78
+ raise AdsCommon::Errors::UnexpectedParametersError.new(extra_fields)
79
+ end
80
+ end
81
+
82
+ # Generates order of XML elements for SOAP request. Adds :order! key to
83
+ # keep the correct order.
84
+ def add_order_key(args, fields_list)
85
+ all_args = fields_list.map {|field| field[:name]}
86
+ order_array = (all_args & args.keys)
87
+ args[:order!] = order_array unless order_array.empty?
88
+ end
89
+
90
+ # Checks the provided data structure matches wsdl definition.
91
+ def check_required_argument_present(arg, field)
92
+ # At least one item required, none passed.
93
+ if field[:min_occurs] > 0 and arg.nil?
94
+ raise AdsCommon::Errors::MissingPropertyError.new(
95
+ field[:name], field[:type])
96
+ end
97
+ # An object passed when an array is expected.
98
+ if (field[:max_occurs] == :unbounded) and
99
+ !(arg.nil? or arg.kind_of?(Array))
100
+ raise AdsCommon::Errors::TypeMismatchError.new(
101
+ Array, arg.class, field[:name])
102
+ end
103
+ # An array passed when an object is expected.
104
+ if (field[:max_occurs] == 1) and arg.kind_of?(Array)
105
+ raise AdsCommon::Errors::TypeMismatchError.new(
106
+ field[:type], Array)
107
+ end
108
+ end
109
+
110
+ # Overrides non-default namespace if requested.
111
+ def handle_namespace_override(args, key, ns)
112
+ add_extra_namespace(ns)
113
+ new_key = prefix_key_with_namespace(key.to_s.lower_camelcase, ns)
114
+ rename_hash_key(args, key, new_key)
115
+ replace_array_item(args[:order!], key, new_key)
116
+ return new_key
117
+ end
118
+
119
+ # Validates single argument.
120
+ def validate_arg(arg, parent, key, arg_type)
121
+ result = case arg
122
+ when Array
123
+ validate_array_arg(arg, parent, key, arg_type)
124
+ when Hash
125
+ validate_hash_arg(arg, parent, key, arg_type)
126
+ when Time
127
+ validate_time_arg(arg, parent, key)
128
+ else
129
+ arg
130
+ end
131
+ return result
132
+ end
133
+
134
+ # Validates Array argument.
135
+ def validate_array_arg(arg, parent, key, arg_type)
136
+ result = arg.map do |item|
137
+ validate_arg(item, parent, key, arg_type)
138
+ end
139
+ return result
140
+ end
141
+
142
+ # Validates Hash argument.
143
+ def validate_hash_arg(arg, parent, key, arg_type)
144
+ arg_type = handle_xsi_type(arg, parent, key, arg_type)
145
+ validate_arguments(arg, arg_type[:fields], arg_type[:ns])
146
+ end
147
+
148
+ # Validates Time argument.
149
+ def validate_time_arg(arg, parent, key)
150
+ xml_value = time_to_xml_hash(arg)
151
+ parent[key] = xml_value
152
+ return xml_value
153
+ end
154
+
155
+ # Handles custom xsi:type.
156
+ def handle_xsi_type(arg, parent, key, arg_type)
157
+ xsi_type = arg.delete('xsi:type') || arg.delete(:xsi_type)
158
+ if xsi_type
159
+ xsi_field_type = get_full_type_signature(xsi_type)
160
+ if xsi_field_type.nil?
161
+ raise AdsCommon::Errors::ApiException.new(
162
+ "Incorrect xsi:type specified: '%s'" % [xsi_type])
163
+ else
164
+ # TODO: make sure xsi_type is derived from arg_type.
165
+ arg_type = xsi_field_type
166
+ # xsi:type needs to be from a correct namespace.
167
+ if xsi_field_type[:ns]
168
+ xsi_type = prefix_key_with_namespace(xsi_type, xsi_field_type[:ns])
169
+ end
170
+ end
171
+ add_xsi_type(parent, key, xsi_type)
172
+ end
173
+ return arg_type
174
+ end
175
+
176
+ # Replaces an item in an array with a different one into the same position.
177
+ def replace_array_item(data, old_item, new_item)
178
+ data.map! {|item| (item == old_item) ? new_item : item}
179
+ end
180
+
181
+ # Replaces an item in an array with a different one into the same position.
182
+ def rename_hash_key(data, old_key, new_key)
183
+ data[new_key] = data.delete(old_key)
184
+ return data
185
+ end
186
+
187
+ # Adds ":attributes!" record for Savon to specify xsi:type.
188
+ def add_xsi_type(parent, key, xsi_type)
189
+ add_attribute(parent, key, 'xsi:type', xsi_type)
190
+ end
191
+
192
+ # Adds Savon attribute for given node, key, name and value.
193
+ def add_attribute(node, key, name, value)
194
+ node[:attributes!] ||= {}
195
+ node[:attributes!][key] ||= {}
196
+ if node[:attributes!][key].include?(name)
197
+ node[:attributes!][key][name] = arrayize(node[:attributes!][key][name])
198
+ node[:attributes!][key][name] << value
199
+ else
200
+ node[:attributes!][key][name] = value
201
+ end
202
+ end
203
+
204
+ # Prefixes a key with a given namespace index or default namespace.
205
+ def prefix_key_with_namespace(key, ns_index = nil)
206
+ namespace = (ns_index.nil?) ? DEFAULT_NAMESPACE : ("ns%d" % ns_index)
207
+ return prefix_key(key, namespace)
208
+ end
209
+
210
+ # Prefixes with a given namespace.
211
+ def prefix_key(key, ns)
212
+ return [ns, key].join(':')
213
+ end
214
+
215
+ # Returns list of 'names' for objects in array.
216
+ def array_from_named_list(fields_list)
217
+ return fields_list.map {|field| field[:name]}
218
+ end
219
+
220
+ # Returns copy of object and its sub-objects ("deep" copy).
221
+ def deep_copy(data)
222
+ return Marshal.load(Marshal.dump(data))
223
+ end
224
+
225
+ # Returns type signature with all inherited fields.
226
+ def get_full_type_signature(type_name)
227
+ result = (type_name.nil?) ? nil : @registry.get_type_signature(type_name)
228
+ result[:fields] = implode_parent(result) if result and result[:base]
229
+ return result
230
+ end
231
+
232
+ # Returns all inherited fields of superclasses for given type.
233
+ def implode_parent(data_type)
234
+ result = []
235
+ if data_type[:base]
236
+ parent_type = @registry.get_type_signature(data_type[:base])
237
+ result += implode_parent(parent_type)
238
+ end
239
+ data_type[:fields].each do |field|
240
+ # If the parent type includes a field with the same name, overwrite it.
241
+ result.reject! {|parent_field| parent_field[:name].eql?(field[:name])}
242
+ # Storing field's namespace.
243
+ field[:ns] = data_type[:ns] if data_type[:ns]
244
+ result << field
245
+ end
246
+ return result
247
+ end
248
+
249
+ # Adds additional namespace for XML generation.
250
+ def add_extra_namespace(ns_index)
251
+ @extra_namespaces.merge!({
252
+ "xmlns:ns%d" % ns_index => @registry.get_namespace(ns_index)
253
+ })
254
+ end
255
+
256
+ # Makes sure object is an array.
257
+ def arrayize(object)
258
+ return [] if object.nil?
259
+ return object.is_a?(Array) ? object : [object]
260
+ end
261
+
262
+ # Converts Time to a hash for XML marshalling.
263
+ def time_to_xml_hash(time)
264
+ return {
265
+ :hour => time.hour, :minute => time.min, :second => time.sec,
266
+ :date => {:year => time.year, :month => time.month, :day => time.day}
267
+ }
268
+ end
269
+ end
270
+ end
@@ -87,8 +87,8 @@ module AdsCommon
87
87
  credentials = @credential_handler.credentials(@version)
88
88
  app_name = credentials[:userAgent] || credentials[:useragent]
89
89
  # We don't know the library version here. A breaking change needs to be
90
- # introduced. This is scheduled for 0.6.0, using Common version for now.
91
- lib_version = '0.5.5'
90
+ # introduced. This is scheduled for 0.7.0, using Common version for now.
91
+ lib_version = '0.6.0'
92
92
  soap_user_agent = "Common-Ruby-%s; %s" % [lib_version, app_name]
93
93
  return "Savon/%s (%s)" % [Savon::Version, soap_user_agent]
94
94
  end
@@ -22,11 +22,10 @@
22
22
  require 'httpi'
23
23
  require 'savon'
24
24
 
25
+ require 'ads_common/parameters_validator'
26
+
25
27
  module AdsCommon
26
28
  class SavonService
27
- # Default namespace name.
28
- DEFAULT_NAMESPACE = 'wsdl'
29
-
30
29
  # HTTP read timeout in seconds.
31
30
  HTTP_READ_TIMEOUT = 15 * 60
32
31
 
@@ -81,164 +80,31 @@ module AdsCommon
81
80
 
82
81
  # Executes SOAP action specified as a string with given arguments.
83
82
  def execute_action(action_name, args, &block)
84
- args = validate_args(action_name, args)
85
- response = execute_soap_request(action_name.to_sym, args)
83
+ validator = ParametersValidator.new(get_service_registry())
84
+ args = validator.validate_args(action_name, args)
85
+ response = execute_soap_request(
86
+ action_name.to_sym, args, validator.extra_namespaces)
86
87
  handle_errors(response)
87
88
  return extract_result(response, action_name, &block)
88
89
  end
89
90
 
90
91
  # Executes the SOAP request with original SOAP name.
91
- def execute_soap_request(action, args)
92
+ def execute_soap_request(action, args, extra_namespaces)
92
93
  original_action_name =
93
94
  get_service_registry.get_method_signature(action)[:original_name]
94
95
  original_action_name = action if original_action_name.nil?
95
96
  response = @client.request(original_action_name) do |soap|
96
- set_headers(soap, args)
97
+ set_headers(soap, args, extra_namespaces)
97
98
  end
98
99
  return response
99
100
  end
100
101
 
101
- # Validates input parameters to:
102
- # - add parameter names;
103
- # - resolve xsi:type where required;
104
- # - convert some native types to XML.
105
- def validate_args(action_name, args)
106
- validated_args = {}
107
- in_params = get_service_registry.get_method_signature(action_name)[:input]
108
- in_params.each_with_index do |in_param, index|
109
- key = in_param[:name]
110
- value = deep_copy(args[index])
111
- validated_args[key] = (value.nil?) ?
112
- nil : validate_arg(value, validated_args, key, in_param[:type])
113
- # Adding :order! key to keep correct order in SOAP elements.
114
- validated_args[:order!] =
115
- generate_order_for_args(validated_args, in_params)
116
- end
117
- return validated_args
118
- end
119
-
120
- # Validates method argument. Runs recursively if hash or array encountered.
121
- # Also handles some types that need special conversions.
122
- def validate_arg(arg, parent = nil, key = nil, field_type_name = nil)
123
- field_type = get_full_type_signature(field_type_name)
124
- result = case arg
125
- when Hash
126
- validate_hash_arg(arg, parent, key, field_type)
127
- when Array then arg.map do |item|
128
- validate_arg(item, parent, key, field_type_name)
129
- end
130
- when Time then time_to_xml_hash(arg)
131
- else arg
132
- end
133
- return result
134
- end
135
-
136
- # Generates order of XML elements for SOAP request. Returns only items
137
- # existing in arg.
138
- def generate_order_for_args(arg, fields)
139
- all_keys = fields.map {|field| field[:name]}
140
- return all_keys & arg.keys
141
- end
142
-
143
- # Validates hash argument recursively. Keeps tracking of correct place
144
- # for xsi:type and adds is when required.
145
- def validate_hash_arg(arg, parent = nil, key = nil, field_type = nil)
146
- # Non-default namespace should be used, overriding default.
147
- if field_type and field_type.include?(:ns)
148
- namespace = get_service_registry.get_namespace(field_type[:ns])
149
- key = prefix_key(key)
150
- add_attribute(parent, key, 'xmlns', namespace)
151
- end
152
-
153
- # Handling custom xsi:type.
154
- xsi_type = arg.delete('xsi:type') || arg.delete(:xsi_type)
155
- if xsi_type
156
- xsi_field_type = get_full_type_signature(xsi_type)
157
- if xsi_field_type.nil?
158
- raise AdsCommon::Errors::ApiException.new(
159
- "Incorrect xsi:type specified: '%s'" % [xsi_type])
160
- else
161
- # TODO: make sure xsi_type is derived from field_type.
162
- field_type = xsi_field_type
163
- end
164
- if parent and key
165
- add_xsi_type(parent, key, xsi_type)
166
- else
167
- raise AdsCommon::Errors::ApiException.new(
168
- "Can't find correct position for xsi:type (%s) [%s], [%s]" %
169
- [xsi_type, parent, key])
170
- end
171
- end
172
-
173
- # Adding :order! key to keep correct order in SOAP elements.
174
- if field_type and field_type.include?(:fields)
175
- arg[:order!] =
176
- generate_order_for_args(arg, field_type[:fields])
177
- end
178
-
179
- # Processing each key-value pair.
180
- return arg.inject({}) do |result, (k, v)|
181
- if (k == :attributes! or k == :order!)
182
- result[k] = v
183
- else
184
- subfield = (field_type.nil?) ? nil :
185
- get_field_by_name(field_type[:fields], k)
186
- # Here we will give up if the field is unknown. For full validation
187
- # we have to handle nil here.
188
- subtype_name, subtype = if subfield and subfield.include?(:type)
189
- subtype_name = subfield[:type]
190
- subtype = (subtype_name.nil?) ? nil :
191
- get_service_registry.get_type_signature(subtype_name)
192
- [subtype_name, subtype]
193
- end
194
- # In case of non-default namespace, the children should be in
195
- # overridden namespace but the node has to be in the default.
196
- # We also have to fix order! list if we alter the key name.
197
- new_key = if (subtype and subtype[:ns])
198
- prefixed_key = prefix_key(k)
199
- replace_item!(arg[:order!], k, prefixed_key)
200
- prefixed_key
201
- else
202
- k
203
- end
204
- result[new_key] = validate_arg(v, result, k, subtype_name)
205
- end
206
- result
207
- end
208
- end
209
-
210
- # Replaces an item in an array with a different one into the same position.
211
- def replace_item!(data, old_item, new_item)
212
- data.map! {|item| (item == old_item) ? new_item : item}
213
- end
214
-
215
- # Adds ":attributes!" record for Savon to specify xsi:type.
216
- def add_xsi_type(parent, key, xsi_type)
217
- add_attribute(parent, key, 'xsi:type', xsi_type)
218
- end
219
-
220
- # Adds Savon attribute for given node, key, name and value.
221
- def add_attribute(node, key, name, value)
222
- node[:attributes!] ||= {}
223
- node[:attributes!][key] ||= {}
224
- if node[:attributes!][key].include?(name)
225
- node[:attributes!][key][name] = arrayize(node[:attributes!][key][name])
226
- node[:attributes!][key][name] << value
227
- else
228
- node[:attributes!][key][name] = value
229
- end
230
- end
231
-
232
- # Prefixes default namespace.
233
- def prefix_key(key)
234
- return "%s:%s" % [DEFAULT_NAMESPACE, key.to_s.lower_camelcase]
235
- end
236
-
237
102
  # Executes each handler to generate SOAP headers.
238
- def set_headers(soap, args)
103
+ def set_headers(soap, args, extra_namespaces)
239
104
  @headerhandler.each do |handler|
240
105
  handler.prepare_request(@client.http, soap, args)
241
106
  end
107
+ soap.namespaces.merge!(extra_namespaces) unless extra_namespaces.nil?
242
108
  end
243
109
 
244
110
  # Checks for errors in response and raises appropriate exception.
@@ -405,14 +271,6 @@ module AdsCommon
405
271
  return object.is_a?(Array) ? object : [object]
406
272
  end
407
273
 
408
- # Converts Time to a hash for XML marshalling.
409
- def time_to_xml_hash(time)
410
- return {
411
- :hour => time.hour, :minute => time.min, :second => time.sec,
412
- :date => {:year => time.year, :month => time.month, :day => time.day}
413
- }
414
- end
415
-
416
274
  # Returns all inherited fields of superclasses for given type.
417
275
  def implode_parent(data_type)
418
276
  result = []
@@ -428,11 +286,6 @@ module AdsCommon
428
286
  return result
429
287
  end
430
288
 
431
- # Returns copy of object and its sub-objects ("deep" copy).
432
- def deep_copy(data)
433
- return Marshal.load(Marshal.dump(data))
434
- end
435
-
436
289
  # Returns type signature with all inherited fields.
437
290
  def get_full_type_signature(type_name)
438
291
  result = (type_name.nil?) ? nil :
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Author:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # Tests validator methods.
21
+
22
+ require 'rubygems'
23
+ require 'test/unit'
24
+
25
+ require 'ads_common/parameters_validator'
26
+
27
+ module AdsCommon
28
+ class ParametersValidator
29
+ public :deep_copy, :add_attribute, :array_from_named_list
30
+ end
31
+ end
32
+
33
+ class TestParametersValidator < Test::Unit::TestCase
34
+ def setup
35
+ @validator = AdsCommon::ParametersValidator.new(nil)
36
+ end
37
+
38
+ def test_deep_copy_simple
39
+ result1 = @validator.deep_copy(42)
40
+ assert_equal(42, result1)
41
+
42
+ result2 = @validator.deep_copy('Hello World')
43
+ assert_equal('Hello World', result2)
44
+
45
+ result3 = @validator.deep_copy(nil)
46
+ assert_nil(result3)
47
+
48
+ result4 = @validator.deep_copy([])
49
+ assert_equal([], result4)
50
+ assert_not_same([], result4)
51
+
52
+ result5 = @validator.deep_copy({})
53
+ assert_equal({}, result5)
54
+ assert_not_same({}, result5)
55
+ end
56
+
57
+ def test_deep_copy_complex
58
+ data = {:ab => 'ab', :cd => ['cd', 'de', 'ef']}
59
+
60
+ result1 = @validator.deep_copy(data)
61
+ assert_equal(data, result1)
62
+ assert_not_same(data, result1)
63
+
64
+ result2 = @validator.deep_copy(data)
65
+ assert_equal(result2, result1)
66
+ assert_not_same(result2, result1)
67
+
68
+ result2[:cd] = nil
69
+ assert_not_equal(data, result2)
70
+ assert_equal(data, result1)
71
+ end
72
+
73
+ def test_add_attribute
74
+ node = {}
75
+
76
+ key, name, value1, value2, value3 = 'key', 'name', 'Lorem', 'ipsum', 'dolor'
77
+
78
+ @validator.add_attribute(node, key, name, value1)
79
+ assert_kind_of(Hash, node)
80
+ assert_kind_of(Hash, node[:attributes!])
81
+ assert_kind_of(Hash, node[:attributes!][key])
82
+ assert_equal(value1, node[:attributes!][key][name])
83
+
84
+ @validator.add_attribute(node, key, name, value2)
85
+ assert_kind_of(Hash, node)
86
+ assert_kind_of(Hash, node[:attributes!])
87
+ assert_kind_of(Hash, node[:attributes!][key])
88
+ assert_kind_of(Array, node[:attributes!][key][name])
89
+ assert_equal(value1, node[:attributes!][key][name][0])
90
+ assert_equal(value2, node[:attributes!][key][name][1])
91
+
92
+ @validator.add_attribute(node, key, name, value3)
93
+ assert_equal(value1, node[:attributes!][key][name][0])
94
+ assert_equal(value2, node[:attributes!][key][name][1])
95
+ assert_equal(value3, node[:attributes!][key][name][2])
96
+ end
97
+
98
+ def test_array_from_named_list
99
+ src = [{:name => 'foo'}, {:name => 'bar', :bar => :baz}, {:name => 'ipsum'}]
100
+ result = @validator.array_from_named_list(src)
101
+ assert_equal(['foo', 'bar', 'ipsum'], result)
102
+ end
103
+ end
@@ -219,64 +219,4 @@ class TestSavonService < Test::Unit::TestCase
219
219
  assert_instance_of(Float, result4)
220
220
  assert_equal(42.0, result4, 'Float is expected for nil max_occurs')
221
221
  end
222
-
223
- def test_deep_copy_simple
224
- result1 = @stub_service.private_deep_copy(42)
225
- assert_equal(42, result1)
226
-
227
- result2 = @stub_service.private_deep_copy('Hello World')
228
- assert_equal('Hello World', result2)
229
-
230
- result3 = @stub_service.private_deep_copy(nil)
231
- assert_nil(result3)
232
-
233
- result4 = @stub_service.private_deep_copy([])
234
- assert_equal([], result4)
235
- assert_not_same([], result4)
236
-
237
- result5 = @stub_service.private_deep_copy({})
238
- assert_equal({}, result5)
239
- assert_not_same({}, result5)
240
- end
241
-
242
- def test_deep_copy_complex
243
- data = {:ab => 'ab', :cd => ['cd', 'de', 'ef']}
244
-
245
- result1 = @stub_service.private_deep_copy(data)
246
- assert_equal(data, result1)
247
- assert_not_same(data, result1)
248
-
249
- result2 = @stub_service.private_deep_copy(data)
250
- assert_equal(result2, result1)
251
- assert_not_same(result2, result1)
252
-
253
- result2[:cd] = nil
254
- assert_not_equal(data, result2)
255
- assert_equal(data, result1)
256
- end
257
-
258
- def test_add_attribute
259
- node = {}
260
-
261
- key, name, value1, value2, value3 = 'key', 'name', 'Lorem', 'ipsum', 'dolor'
262
-
263
- @stub_service.private_add_attribute(node, key, name, value1)
264
- assert_kind_of(Hash, node)
265
- assert_kind_of(Hash, node[:attributes!])
266
- assert_kind_of(Hash, node[:attributes!][key])
267
- assert_equal(value1, node[:attributes!][key][name])
268
-
269
- @stub_service.private_add_attribute(node, key, name, value2)
270
- assert_kind_of(Hash, node)
271
- assert_kind_of(Hash, node[:attributes!])
272
- assert_kind_of(Hash, node[:attributes!][key])
273
- assert_kind_of(Array, node[:attributes!][key][name])
274
- assert_equal(value1, node[:attributes!][key][name][0])
275
- assert_equal(value2, node[:attributes!][key][name][1])
276
-
277
- @stub_service.private_add_attribute(node, key, name, value3)
278
- assert_equal(value1, node[:attributes!][key][name][0])
279
- assert_equal(value2, node[:attributes!][key][name][1])
280
- assert_equal(value3, node[:attributes!][key][name][2])
281
- end
282
222
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 5
8
- - 5
9
- version: 0.5.5
7
+ - 6
8
+ - 0
9
+ version: 0.6.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Sergio Gomes
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-28 00:00:00 +04:00
18
+ date: 2011-12-02 00:00:00 +04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -89,27 +89,29 @@ extra_rdoc_files:
89
89
  - COPYING
90
90
  - ChangeLog
91
91
  files:
92
- - lib/ads_common/auth/base_handler.rb
93
92
  - lib/ads_common/auth/client_login_handler.rb
93
+ - lib/ads_common/auth/base_handler.rb
94
94
  - lib/ads_common/auth/oauth_handler.rb
95
- - lib/ads_common/build/savon_abstract_generator.rb
95
+ - lib/ads_common/credential_handler.rb
96
96
  - lib/ads_common/build/savon_generator.rb
97
- - lib/ads_common/build/savon_registry.rb
98
97
  - lib/ads_common/build/savon_registry_generator.rb
98
+ - lib/ads_common/build/savon_registry.rb
99
99
  - lib/ads_common/build/savon_service_generator.rb
100
- - lib/ads_common/savon_headers/base_header_handler.rb
101
- - lib/ads_common/savon_headers/httpi_request_proxy.rb
102
- - lib/ads_common/savon_headers/oauth_header_handler.rb
103
- - lib/ads_common/savon_headers/simple_header_handler.rb
100
+ - lib/ads_common/build/savon_abstract_generator.rb
104
101
  - lib/ads_common/api_config.rb
105
- - lib/ads_common/api.rb
106
102
  - lib/ads_common/config.rb
107
- - lib/ads_common/credential_handler.rb
108
- - lib/ads_common/errors.rb
109
- - lib/ads_common/http.rb
103
+ - lib/ads_common/api.rb
104
+ - lib/ads_common/savon_headers/base_header_handler.rb
105
+ - lib/ads_common/savon_headers/simple_header_handler.rb
106
+ - lib/ads_common/savon_headers/oauth_header_handler.rb
107
+ - lib/ads_common/savon_headers/httpi_request_proxy.rb
110
108
  - lib/ads_common/savon_service.rb
109
+ - lib/ads_common/http.rb
110
+ - lib/ads_common/parameters_validator.rb
111
+ - lib/ads_common/errors.rb
111
112
  - Rakefile
112
113
  - test/test_config.rb
114
+ - test/test_parameters_validator.rb
113
115
  - test/test_savon_service.rb
114
116
  - README
115
117
  - COPYING
@@ -148,4 +150,5 @@ specification_version: 3
148
150
  summary: Common code for Google Ads APIs.
149
151
  test_files:
150
152
  - test/test_config.rb
153
+ - test/test_parameters_validator.rb
151
154
  - test/test_savon_service.rb