google-ads-common 0.3.0 → 0.4.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/ChangeLog CHANGED
@@ -1,6 +1,11 @@
1
+ 0.4.0:
2
+ - Extracted soap4r specific code from main path.
3
+ - APIs with Savon backend are now ruby1.9 - compatible.
4
+ - Fixed possible statement corruption after call.
5
+
1
6
  0.3.0:
2
7
  - Fixes towards Ruby1.9 compatibility.
3
- - Support for method names with CAPSed words (Savon),
8
+ - Support for method names with CAPSed words (Savon).
4
9
  - New single-Logger logging system.
5
10
 
6
11
  0.2.1:
data/Rakefile CHANGED
@@ -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,109 +20,58 @@
20
20
  # Rakefile for the ads_common package.
21
21
 
22
22
  require 'rubygems'
23
- gem 'rake'
24
- require 'rake/gempackagetask'
25
- require 'rake/rdoctask'
26
- require 'rake/clean'
23
+ require 'rubygems/package_task'
27
24
  require 'rake/testtask'
28
- require 'lib/ads_common/api_config'
29
-
30
- $WSDLDIR = 'wsdl'
31
- $LIBDIR = 'lib'
32
- $DOCDIR = 'doc'
33
- $TESTDIR = 'test'
34
-
35
- $PROJECT_NAME = 'ads_common'
36
- $GEM_NAME = 'google-ads-common'
37
-
38
- $CURRENT_VERSION = AdsCommon::ApiConfig::ADS_COMMON_VERSION
39
- $PKG_VERSION = ENV['REL'] ? ENV['REL'] : $CURRENT_VERSION
40
-
41
- # Configure gem details
42
- $GEM_SUMMARY = "Common code for Google Ads APIs."
43
- $GEM_DESCRIPTION = "#{$PROJECT_NAME} provides essential utilities shared by " +
44
- "all Ads Ruby client libraries."
45
- $GEM_AUTHORS = ['Sergio Gomes', 'Danial Klimkin']
46
- $GEM_EMAIL = 'api.sgomes@gmail.com'
47
- $GEM_HOMEPAGE = 'http://code.google.com/p/google-api-ads-ruby/'
25
+ require 'rdoc/task'
26
+ require './lib/ads_common/api_config'
27
+
28
+ GEM_NAME = 'google-ads-common'
29
+
30
+ files = FileList['lib/**/*', 'Rakefile'].to_a
31
+ tests = FileList['test/**/test_*.rb']
32
+ docs = ['README', 'COPYING', 'ChangeLog']
33
+ extra_files = ['test/test_config.yml']
34
+
35
+ spec = Gem::Specification.new do |s|
36
+ s.platform = Gem::Platform::RUBY
37
+ s.name = GEM_NAME
38
+ s.version = AdsCommon::ApiConfig::ADS_COMMON_VERSION
39
+ s.summary = 'Common code for Google Ads APIs.'
40
+ s.description = ("%s provides essential utilities shared by all Ads Ruby " +
41
+ "client libraries.") % GEM_NAME
42
+ s.authors = ['Sergio Gomes', 'Danial Klimkin']
43
+ s.email = 'api.dklimkin@gmail.com'
44
+ s.homepage = 'http://code.google.com/p/google-api-ads-ruby/'
45
+ s.require_path = 'lib'
46
+ s.files = files
47
+ s.test_files = tests
48
+ s.has_rdoc = true
49
+ s.extra_rdoc_files = docs
50
+ s.add_dependency('savon', '~> 0.9.1')
51
+ s.add_dependency('soap4r', '= 1.5.8')
52
+ s.add_dependency('httpclient', '>= 2.1.6')
53
+ s.add_dependency('httpi', '~> 0.9.2')
54
+ end
48
55
 
49
- # ====================================================================
50
- # Default task - call package
56
+ desc 'Default target - build'
51
57
  task :default => [:package]
52
58
 
53
- # ====================================================================
59
+ # Create a task that will package the Common library into a gem file.
60
+ Gem::PackageTask.new(spec) do |pkg|
61
+ pkg.need_tar = true
62
+ pkg.package_files.include(extra_files)
63
+ end
64
+
54
65
  # Create a task to build the RDOC documentation tree.
55
- Rake::RDocTask.new("rdoc") do |rdoc|
56
- # Try to use SDoc to generate the docs
57
- begin
58
- require 'sdoc'
59
- rdoc.options << '--fmt' << 'shtml'
60
- rdoc.template = 'direct'
61
- rescue LoadError
62
- # Do nothing, give up on SDoc and continue with whatever is the default.
63
- end
64
- rdoc.rdoc_dir = $DOCDIR
65
- rdoc.title = "#{$PROJECT_NAME} -- Common code for Google Ads APIs"
66
+ RDoc::Task.new do |rdoc|
67
+ rdoc.rdoc_dir = 'doc'
68
+ rdoc.title = "%s -- Common code for Google Ads APIs" % GEM_NAME
66
69
  rdoc.main = 'README'
67
- rdoc.rdoc_files.include('README', 'COPYING', 'ChangeLog')
68
- rdoc.rdoc_files.include("#{$LIBDIR}/**/*.rb")
70
+ rdoc.rdoc_files.include(docs)
71
+ rdoc.rdoc_files.include(files)
69
72
  end
70
73
 
71
- # ====================================================================
72
74
  # Create a task to perform the unit testing.
73
- Rake::TestTask.new("test") do |test|
74
- test.libs << $TESTDIR
75
- test.pattern = "#{$TESTDIR}/**/test_*.rb"
76
- test.verbose = true
77
- end
78
-
79
- # ====================================================================
80
- # Create a task that will package the Rake software into distributable
81
- # gem files.
82
- PKG_FILES = FileList[
83
- 'Rakefile',
84
- "#{$LIBDIR}/**/*.rb",
85
- "#{$DOCDIR}/**/*.*"
86
- ]
87
-
88
- PKG_FILES.exclude(/\._/)
89
-
90
- if ! defined?(Gem)
91
- puts "Package Target requires RubyGems"
92
- else
93
- spec = Gem::Specification.new do |s|
94
-
95
- # Basic information
96
- s.name = $GEM_NAME
97
- s.version = $PKG_VERSION
98
- s.summary = $GEM_SUMMARY
99
- s.description = $GEM_DESCRIPTION
100
-
101
- # Files and dependencies
102
- s.files = PKG_FILES.to_a
103
- s.require_path = $LIBDIR
104
- s.add_dependency('soap4r', '= 1.5.8')
105
- s.add_dependency('savon', '~> 0.9.1')
106
- s.add_dependency('httpclient', '>= 2.1.6')
107
- s.add_dependency('httpi', '~> 0.9.2')
108
-
109
- # RDoc information
110
- s.has_rdoc = true
111
- s.extra_rdoc_files = ['README', 'ChangeLog', 'COPYING']
112
- s.rdoc_options << '--main' << 'README'
113
-
114
- # Metadata
115
- s.authors = $GEM_AUTHORS
116
- s.email = $GEM_EMAIL
117
- s.homepage = $GEM_HOMEPAGE
118
- s.rubyforge_project = $GEM_NAME
119
- s.requirements << 'soap4r v1.5.8'
120
- s.requirements << 'savon v0.9.1 or greater'
121
- s.requirements << 'httpclient v2.1.6 or greater'
122
- s.requirements << 'httpi v0.9.2 or greater'
123
- end
124
-
125
- Rake::GemPackageTask.new(spec) do |t|
126
- t.need_tar = true
127
- end
75
+ Rake::TestTask.new do |t|
76
+ t.test_files = tests
128
77
  end
@@ -1,8 +1,8 @@
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
- # Copyright:: Copyright 2010, Google Inc. All Rights Reserved.
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
6
  #
7
7
  # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
8
  # you may not use this file except in compliance with the License.
@@ -24,33 +24,39 @@ require 'logger'
24
24
  require 'ads_common/errors'
25
25
  require 'ads_common/auth/base_handler'
26
26
  require 'ads_common/auth/client_login_handler'
27
- require 'ads_common/soap4r_headers/nested_header_handler'
28
- require 'ads_common/soap4r_headers/single_header_handler'
29
- require 'ads_common/soap4r_response_handler'
30
- require 'ads_common/soap4r_logger'
31
27
 
32
28
  module AdsCommon
33
29
  class Api
34
30
 
35
- # Methods that return the service configuration and the client library
36
- # configuration. They need to be redefined in subclasses.
37
- attr_reader :api_config, :config
31
+ # Methods that return the client library configuration. Needs to be
32
+ # redefined in subclasses.
33
+ attr_reader :config
38
34
 
39
- # Credential handler objects used for authentication
35
+ # Credential handler objects used for authentication.
40
36
  attr_reader :credential_handler
41
37
 
42
- # The configuration for this Api object
43
- attr_reader :config
44
-
45
- # The logger for this Api object.
38
+ # The logger for this API object.
46
39
  attr_reader :logger
47
40
 
48
- # Constructor for Api.
41
+ # Constructor for API.
49
42
  def initialize(provided_config = nil)
50
43
  load_config(provided_config)
51
44
  provided_logger = @config.read('library.logger')
52
45
  self.logger = (provided_logger.nil?) ?
53
46
  create_default_logger() : provided_logger
47
+
48
+ # Check for valid environment.
49
+ env_string = config.read('service.environment')
50
+ environment = (env_string.nil?) ? api_config.default_environment :
51
+ env_string.to_s.upcase.to_sym
52
+ if api_config.environments.include?(environment)
53
+ config.set('service.environment', environment)
54
+ else
55
+ raise AdsCommon::Errors::Error,
56
+ "Unknown or unspecified environment: \"%s\"" % env_string
57
+ end
58
+
59
+ @wrappers = Hash.new
54
60
  end
55
61
 
56
62
  # Sets the logger to use.
@@ -59,6 +65,11 @@ module AdsCommon
59
65
  @config.set('library.logger', @logger)
60
66
  end
61
67
 
68
+ # Getter for the API service configurations.
69
+ def api_config
70
+ AdsCommon::ApiConfig
71
+ end
72
+
62
73
  # Obtain an API service, given a version and its name.
63
74
  #
64
75
  # Args:
@@ -69,34 +80,40 @@ module AdsCommon
69
80
  # - the service wrapper for the intended service.
70
81
  #
71
82
  def service(name, version = nil)
72
- version = api_config.default_version if version == nil
73
- # Check if version exists
74
- if !api_config.versions.include?(version.to_sym)
75
- if version.is_a? Integer
76
- raise AdsCommon::Errors::Error, "Unknown version '#{version}'. " +
77
- "Please note that version numbers should be symbols; check the" +
78
- "Readme or the included examples for more information."
79
- else
80
- raise AdsCommon::Errors::Error, "Unknown version #{version}"
81
- end
82
- end
83
- version = version.to_sym
84
83
  name = name.to_sym
85
- environment = config.read('service.environment')
86
- # Check if the current environment supports the requested version
84
+ version = (version.nil?) ? api_config.default_version : version.to_sym
85
+
86
+ # Check if version exists.
87
+ if !api_config.versions.include?(version)
88
+ raise AdsCommon::Errors::Error, "Unknown version '%s'." % version
89
+ end
90
+
91
+ # Check if the current environment supports the requested version.
92
+ environment = @config.read('service.environment')
87
93
  if !api_config.environment_has_version(environment, version)
88
94
  raise AdsCommon::Errors::Error,
89
- "Environment #{environment} does not support version #{version}"
95
+ "Environment '%s' does not support version '%s'." %
96
+ [environment, version]
90
97
  end
91
- # Check if the specified version has the requested service
98
+
99
+ # Check if the specified version has the requested service.
92
100
  if !api_config.version_has_service(version, name)
93
- raise AdsCommon::Errors::Error, "Version #{version} does not contain " +
94
- "service #{name}"
101
+ raise AdsCommon::Errors::Error,
102
+ "Version '%s' does not contain service '%s'", [version, name]
103
+ end
104
+
105
+ # Try to re-use the service for this version if it was requested before.
106
+ wrapper = if @wrappers.include?(version) && @wrappers[version][name]
107
+ @wrappers[version][name]
108
+ else
109
+ @wrappers[version] ||= {}
110
+ @wrappers[version][name] = prepare_wrapper(version, name)
95
111
  end
96
- prepare_driver(version, name)
97
- return @wrappers[[version, name]]
112
+ return wrapper
98
113
  end
99
114
 
115
+ private
116
+
100
117
  # Retrieve the SOAP header handlers to plug into the drivers. Needs to
101
118
  # be implemented on the specific API, because of the different types of
102
119
  # SOAP headers (optional parameter specifying API version).
@@ -111,80 +128,37 @@ module AdsCommon
111
128
  # Returns:
112
129
  # - a list of SOAP header handlers; one per provided header
113
130
  #
114
- def soap_header_handlers(auth_handler, header_list, version, wrapper = nil)
131
+ def soap_header_handlers(auth_handler, header_list, version, wrapper)
132
+ raise NotImplementedError, 'soap_header_handlers not overriden.'
115
133
  end
116
134
 
117
- private
118
-
119
135
  # Auxiliary method to create an authentication handler. Needs to be
120
136
  # implemented on the specific API because of the different nesting models
121
137
  # used in different APIs and API versions.
122
138
  #
123
139
  # Args:
124
- # - version: intended API version
125
140
  # - environment: the current working environment (production, sandbox, etc.)
141
+ # - version: intended API version
126
142
  #
127
143
  # Returns:
128
144
  # - auth handler
129
145
  #
130
- def create_auth_handler(version = nil, environment = nil)
146
+ def create_auth_handler(environment, version)
147
+ raise NotImplementedError, 'create_auth_handler not overriden.'
131
148
  end
132
149
 
133
- # Handle loading of a single service.
134
- # Creates the driver, sets up handlers and logger, declares the appropriate
135
- # wrapper class and creates an instance of it.
150
+ # Handle loading of a single service wrapper. Needs to be implemented on
151
+ # specific API level.
136
152
  #
137
153
  # Args:
138
- # - version: intended API version. Must be an integer.
139
- # - service: name for the intended service
154
+ # - version: intended API version. Must be a symbol.
155
+ # - service: name for the intended service. Must be a symbol.
140
156
  #
141
157
  # Returns:
142
- # - the driver for the service
143
- # - the simplified wrapper generated for the driver
158
+ # - a wrapper generated for the service.
144
159
  #
145
- def prepare_driver(version, service)
146
- version = version.to_sym
147
- service = service.to_sym
148
- environment = config.read('service.environment')
149
- api_config.do_require(version, service)
150
- endpoint = api_config.endpoint(environment, version, service)
151
- # Set endpoint if not using the default
152
- if endpoint.nil? then
153
- driver = eval("#{api_config.interface_name(version, service)}.new")
154
- else
155
- endpoint_url = endpoint.to_s + service.to_s
156
- driver = eval("#{api_config.interface_name(version, service)}.new" +
157
- "(\"#{endpoint_url}\")")
158
- end
159
-
160
- # Create an instance of the wrapper class for this service.
161
- wrapper_class = api_config.wrapper_name(version, service)
162
- wrapper = eval("#{wrapper_class}.new(driver, self)")
163
-
164
- auth_handler = create_auth_handler(version, environment)
165
- header_list =
166
- auth_handler.header_list(@credential_handler.credentials(version))
167
-
168
- soap_handlers = soap_header_handlers(auth_handler, header_list, version,
169
- wrapper)
170
-
171
- soap_handlers.each do |handler|
172
- driver.headerhandler << handler
173
- end
174
-
175
- # Add response handler to this driver for API unit usage processing.
176
- driver.callbackhandler = create_callback_handler
177
- # Plug the wiredump to our XML logger
178
- driver.wiredump_dev = AdsCommon::Soap4rLogger.new(@logger, Logger::DEBUG)
179
- driver.options['protocol.http.ssl_config.verify_mode'] = nil
180
- proxy = config.read('connection.proxy')
181
- if proxy
182
- driver.options['protocol.http.proxy'] = proxy
183
- end
184
-
185
- @drivers[[version, service]] = driver
186
- @wrappers[[version, service]] = wrapper
187
- return driver, wrapper
160
+ def prepare_wrapper(version, service)
161
+ raise NotImplementedError, 'prepare_wrapper not overriden.'
188
162
  end
189
163
 
190
164
  # Auxiliary method to create a default Logger.
@@ -214,7 +188,7 @@ module AdsCommon
214
188
  if log_level.is_a?(String)
215
189
  result = case log_level.upcase
216
190
  when 'FATAL' then Logger::FATAL
217
- when 'ERROR' then Logger:ERROR
191
+ when 'ERROR' then Logger::ERROR
218
192
  when 'WARN' then Logger::WARN
219
193
  when 'INFO' then Logger::INFO
220
194
  when 'DEBUG' then Logger::DEBUG
@@ -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.3.0'
29
+ ADS_COMMON_VERSION = '0.4.0'
30
30
 
31
31
  # Get the available API versions.
32
32
  #
@@ -53,10 +53,8 @@ module AdsCommon
53
53
  # given version
54
54
  #
55
55
  def environment_has_version(environment, version)
56
- environment = environment.upcase.to_sym
57
- version = version.to_sym
58
- !environment_config[environment].nil? and
59
- !environment_config[environment][version].nil?
56
+ return environment_config.include?(environment) &&
57
+ environment_config[environment].include?(version)
60
58
  end
61
59
 
62
60
  # Does the given version exist and contain the given service?
@@ -66,10 +64,8 @@ module AdsCommon
66
64
  # given service
67
65
  #
68
66
  def version_has_service(version, service)
69
- version = version.to_sym
70
- service = service.to_sym
71
- (!service_config[version].nil? and
72
- service_config[version].include?(service))
67
+ return service_config.include?(version) &&
68
+ service_config[version].include?(service)
73
69
  end
74
70
 
75
71
  # Get the default API version.
@@ -127,9 +123,6 @@ module AdsCommon
127
123
  # The endpoint URL (as a string)
128
124
  #
129
125
  def endpoint(environment, version, service)
130
- environment = environment.upcase.to_sym
131
- version = version.to_sym
132
- service = service.to_sym
133
126
  base = get_wsdl_base(environment, version)
134
127
  if !subdir_config().nil?
135
128
  base = base.to_s + subdir_config()[[version, service]].to_s
@@ -148,8 +141,6 @@ module AdsCommon
148
141
  #
149
142
  def subdir(version, service)
150
143
  return nil if subdir_config().nil?
151
- version = version.to_sym
152
- service = service.to_sym
153
144
  subdir_config()[[version, service]]
154
145
  end
155
146
 
@@ -163,39 +154,19 @@ module AdsCommon
163
154
  # The full URL for the auth server.
164
155
  #
165
156
  def auth_server(environment)
166
- auth_server_url = ENV['ADSAPI_AUTH_URL']
167
- if auth_server_url.nil?
168
- environment = environment.upcase.to_sym
169
- auth_server_url = auth_server_config[environment]
170
- end
171
- if auth_server_url.nil?
172
- # If we don't have an entry for this environment, we just return the
173
- # default server (the same one being used for the default environment)
174
- auth_server_url = auth_server_config[default_environment()]
175
- end
157
+ auth_server_url =
158
+ ENV['ADSAPI_AUTH_URL'] ||
159
+ auth_server_config[environment] ||
160
+ auth_server_config[default_environment()]
176
161
  return auth_server_url
177
162
  end
178
163
 
179
- # Add a new environment to the list.
180
- #
181
- # Args:
182
- # - name: the name for the new environment
183
- # - endpoint_hash: a hash of base endpoint URLs, indexed by version number,
184
- # e.g.:
185
- # { :v13 => 'URL_FOR_v13', :v200906 => 'URL_FOR_v200906' }
186
- #
187
- def add_environment(name, endpoint_hash)
188
- name = name.upcase.to_sym
189
- environment_config[name] = endpoint_hash
190
- end
191
-
192
164
  # Perform the loading of the necessary source files for a version
193
165
  #
194
166
  # Args:
195
167
  # - version: the API version (as an integer)
196
168
  #
197
169
  def do_require(version, service)
198
- version = version.to_sym
199
170
  eval("require '#{api_path}/#{version}/#{service}Wrapper.rb'")
200
171
  end
201
172
 
@@ -209,8 +180,6 @@ module AdsCommon
209
180
  # The full module name for the given service (as a string)
210
181
  #
211
182
  def module_name(version, service)
212
- version = version.to_sym
213
- service = service.to_sym
214
183
  return "#{api_name}::#{version.to_s.upcase}::#{service}"
215
184
  end
216
185
 
@@ -224,8 +193,6 @@ module AdsCommon
224
193
  # The full interface class name for the given service (as a string)
225
194
  #
226
195
  def interface_name(version, service)
227
- version = version.to_sym
228
- service = service.to_sym
229
196
  return module_name(version, service) + "::#{service}Interface"
230
197
  end
231
198
 
@@ -239,8 +206,6 @@ module AdsCommon
239
206
  # The full wrapper class name for the given service (as a string)
240
207
  #
241
208
  def wrapper_name(version, service)
242
- version = version.to_sym
243
- service = service.to_sym
244
209
  return module_name(version, service) + "::#{service}Wrapper"
245
210
  end
246
211
 
@@ -279,10 +244,8 @@ module AdsCommon
279
244
  # Returns:
280
245
  # String containing base URL.
281
246
  def get_wsdl_base(environment, version)
282
- wsdl_base = ENV['ADSAPI_BASE_URL']
283
- if wsdl_base.nil?
284
- wsdl_base = environment_config[environment][version]
285
- end
247
+ wsdl_base = ENV['ADSAPI_BASE_URL'] ||
248
+ wsdl_base = environment_config[environment][version]
286
249
  return wsdl_base
287
250
  end
288
251
  end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: 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
+ # Handles SOAP headers and namespaces definition for ClientLogin type header.
21
+
22
+ require 'ads_common/savon_headers/simple_header_handler'
23
+
24
+ module AdsCommon
25
+ module SavonHeaders
26
+ class ClientLoginHeaderHandler < SimpleHeaderHandler
27
+ private
28
+
29
+ # Generates SOAP request header with login credentials and namespace
30
+ # definition.
31
+ #
32
+ # Args:
33
+ # - None.
34
+ #
35
+ # Returns:
36
+ # - Hash containing a header with filled in credentials.
37
+ #
38
+ def generate_request_header()
39
+ request_header = {}
40
+ credentials = @credential_handler.credentials(@version)
41
+ headers = @auth_handler.headers(credentials)
42
+ headers.each do |header, value|
43
+ if header == :authToken
44
+ auth_header = prepend_namespace('authentication')
45
+ request_header[auth_header] = {
46
+ prepend_namespace('token') => value
47
+ }
48
+ request_header[:attributes!] ||= Hash.new
49
+ request_header[:attributes!][auth_header] = {
50
+ 'xsi:type' => prepend_namespace('ClientLogin'),
51
+ }
52
+ else
53
+ request_header[prepend_namespace(header)] = value
54
+ end
55
+ end
56
+ return request_header
57
+ end
58
+ end
59
+ end
60
+ end
@@ -78,7 +78,7 @@ module AdsCommon
78
78
  in_params = get_service_registry.get_method_signature(action_name)[:input]
79
79
  in_params.each_with_index do |in_param, index|
80
80
  key = in_param[:name]
81
- value = args[index]
81
+ value = deep_copy(args[index])
82
82
  validated_args[key] = (value.nil?) ? nil :
83
83
  validate_arg(value, validated_args, key)
84
84
  end
@@ -223,14 +223,12 @@ module AdsCommon
223
223
  # Converts XML input string into a native format.
224
224
  def normalize_type(data, field)
225
225
  type_name = field[:type]
226
- result = (data.nil?) ? data :
227
- case type_name
228
- when 'long', 'int' then Integer(data)
229
- when 'double' then Float(data)
230
- when 'boolean' then data.kind_of?(String) ?
231
- data.casecmp('true') == 0 : data
232
- else data
233
- end
226
+ result = case data
227
+ when Array
228
+ data.map {|item| normalize_item(type_name, item)}
229
+ else
230
+ normalize_item(type_name, data)
231
+ end
234
232
  # If field signature allows an array, forcing array structure even for one
235
233
  # item.
236
234
  if !field[:min_occurs].nil? and
@@ -240,6 +238,18 @@ module AdsCommon
240
238
  return result
241
239
  end
242
240
 
241
+ # Converts one leaf item to a built-in type.
242
+ def normalize_item(type_name, item)
243
+ return (item.nil?) ? item :
244
+ case type_name
245
+ when 'long', 'int' then Integer(item)
246
+ when 'double' then Float(item)
247
+ when 'boolean' then item.kind_of?(String) ?
248
+ item.casecmp('true') == 0 : item
249
+ else item
250
+ end
251
+ end
252
+
243
253
  # Finds a field in a list by its name.
244
254
  def get_field_by_name(fields_list, name)
245
255
  fields_array = arrayize(fields_list)
@@ -271,5 +281,10 @@ module AdsCommon
271
281
  end
272
282
  return result
273
283
  end
284
+
285
+ # Returns copy of object and its sub-objects ("deep" copy).
286
+ def deep_copy(data)
287
+ return Marshal.load(Marshal.dump(data))
288
+ end
274
289
  end
275
290
  end
@@ -0,0 +1,90 @@
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 the array replies from services.
21
+
22
+ require 'rubygems'
23
+ require 'test/unit'
24
+ require 'ads_common/config'
25
+
26
+ class TestConfig < Test::Unit::TestCase
27
+ DEFAULT_CONFIG_HASH = {
28
+ :service => {:use_ruby_names => false,
29
+ :environment => 'sandbox'},
30
+ :authentication => {:method => 'ClientLogin',
31
+ :email => 'root@example.com'}
32
+ }
33
+
34
+ DEFAULT_CONFIG_FILENAME = 'test/test_config.yml'
35
+
36
+ # Test initializer with no arguments.
37
+ def test_initialize_nil
38
+ assert_nothing_raised do
39
+ config = AdsCommon::Config.new
40
+ assert_nil(config.read('service.use_ruby_names'))
41
+ end
42
+ end
43
+
44
+ # Test initializer with hash argument.
45
+ def test_initialize_hash
46
+ config = AdsCommon::Config.new(DEFAULT_CONFIG_HASH)
47
+ assert_equal(false, config.read('service.use_ruby_names'))
48
+ assert_equal('sandbox', config.read('service.environment'))
49
+ assert_equal('ClientLogin', config.read('authentication.method'))
50
+ assert_equal('root@example.com', config.read('authentication.email'))
51
+ assert_nil(config.read('unexisting.entry'))
52
+ end
53
+
54
+ # Test initializer with filename argument.
55
+ def test_initialize_filename
56
+ config = AdsCommon::Config.new(DEFAULT_CONFIG_FILENAME)
57
+ assert_equal(false, config.read('service.use_ruby_names'))
58
+ assert_equal('sandbox', config.read('service.environment'))
59
+ assert_equal('ClientLogin', config.read('authentication.method'))
60
+ assert_equal('root@example.com', config.read('authentication.email'))
61
+ assert_nil(config.read('unexisting.entry'))
62
+ end
63
+
64
+ # Test default result.
65
+ def test_read_default_result
66
+ config = AdsCommon::Config.new(DEFAULT_CONFIG_HASH)
67
+ assert_nil(config.read('unexisting.entry'))
68
+ assert_equal('default', config.read('unexisting.entry', 'default'))
69
+ assert_equal('sandbox', config.read('service.environment', 'production'))
70
+ end
71
+
72
+ # Test setter.
73
+ def test_set
74
+ config = AdsCommon::Config.new(DEFAULT_CONFIG_HASH)
75
+ assert_equal('sandbox', config.read('service.environment'))
76
+ assert_nil(config.read('unexisting.entry'))
77
+ config.set('unexisting.entry', 'foobar')
78
+ assert_equal('sandbox', config.read('service.environment'))
79
+ assert_equal('foobar', config.read('unexisting.entry'))
80
+ end
81
+
82
+ # Test subhash.
83
+ def test_set
84
+ config = AdsCommon::Config.new(DEFAULT_CONFIG_HASH)
85
+ result = config.read('service')
86
+ assert_instance_of(Hash, result)
87
+ assert_equal('sandbox', result[:environment])
88
+ assert_equal(false, result[:use_ruby_names])
89
+ end
90
+ end
@@ -0,0 +1,231 @@
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 the array replies from services.
21
+
22
+ require 'rubygems'
23
+ require 'test/unit'
24
+ require 'pp'
25
+ require 'ads_common/savon_service'
26
+
27
+ # SavonService is abstract, defining a child class for the test.
28
+ class SomeService < AdsCommon::SavonService
29
+ def private_get_service_registry()
30
+ return get_service_registry
31
+ end
32
+ def private_get_module()
33
+ return get_module
34
+ end
35
+ def private_normalize_type(data, field)
36
+ return normalize_type(data, field)
37
+ end
38
+ def private_get_field_by_name(fields_list, name)
39
+ return get_field_by_name(fields_list, name)
40
+ end
41
+ def private_arrayize(object)
42
+ return arrayize(object)
43
+ end
44
+ def private_deep_copy(object)
45
+ return deep_copy(object)
46
+ end
47
+ end
48
+
49
+ class TestSavonService < Test::Unit::TestCase
50
+ TEST_NAMESPACE = 'namespace'
51
+ TEST_ENDPOINT = 'endpoint'
52
+
53
+ # Initialize tests.
54
+ def setup
55
+ @some_service = SomeService.new(TEST_NAMESPACE, TEST_ENDPOINT)
56
+ end
57
+
58
+ def test_initialize_abstract
59
+ assert_raises(NoMethodError) do
60
+ AdsCommon::SavonService.new(TEST_NAMESPACE, TEST_ENDPOINT)
61
+ end
62
+ assert_nothing_raised do
63
+ SomeService.new(TEST_NAMESPACE, TEST_ENDPOINT)
64
+ end
65
+ end
66
+
67
+ def test_get_service_registry_abstract
68
+ assert_raises(NoMethodError) { @some_service.private_get_service_registry }
69
+ end
70
+
71
+ def test_get_module_abstract
72
+ assert_raises(NoMethodError) { @some_service.private_get_module }
73
+ end
74
+
75
+ def test_arrayize_empty
76
+ result1 = @some_service.private_arrayize(nil)
77
+ assert_instance_of(Array, result1, 'returned object is not an Array')
78
+ assert_equal(0, result1.size, 'array is not empty')
79
+
80
+ result2 = @some_service.private_arrayize([])
81
+ assert_instance_of(Array, result2, 'returned object is not an Array')
82
+ assert_equal(0, result2.size, 'array is not empty')
83
+ end
84
+
85
+ def test_arrayize_on_array
86
+ result1 = @some_service.private_arrayize([nil])
87
+ assert_instance_of(Array, result1, 'returned object is not an Array')
88
+ assert_equal(1, result1.size, 'array changed size')
89
+ assert_equal(nil, result1[0], 'array changed data')
90
+
91
+ result2 = @some_service.private_arrayize(['a', 'b'])
92
+ assert_instance_of(Array, result2, 'returned object is not an Array')
93
+ assert_equal(2, result2.size, 'array changed size')
94
+ assert_equal('a', result2[0], 'array changed data')
95
+ assert_equal('b', result2[1], 'array changed data')
96
+ end
97
+
98
+ def test_normalize_type_int
99
+ result1 = @some_service.private_normalize_type(5, {:type => 'int'})
100
+ assert_kind_of(Integer, result1)
101
+ assert_equal(5, result1, 'bad conversion')
102
+
103
+ result2 = @some_service.private_normalize_type(2147483648, {:type => 'int'})
104
+ assert_kind_of(Integer, result2)
105
+ assert_equal(2147483648, result2, 'bad conversion')
106
+ end
107
+
108
+ def test_normalize_type_string
109
+ result1 = @some_service.private_normalize_type('foobar',
110
+ {:type => 'string'})
111
+ assert_kind_of(String, result1)
112
+ assert_equal('foobar', result1, 'bad conversion')
113
+
114
+ result2 = @some_service.private_normalize_type('', {:type => 'string'})
115
+ assert_kind_of(String, result2)
116
+ assert_equal('', result2, 'bad conversion')
117
+ end
118
+
119
+ def test_normalize_type_long
120
+ result1 = @some_service.private_normalize_type(2147483648,
121
+ {:type => 'long'})
122
+ assert_kind_of(Integer, result1)
123
+ assert_equal(2147483648, result1, 'bad conversion')
124
+
125
+ result2 = @some_service.private_normalize_type(-1, {:type => 'long'})
126
+ assert_kind_of(Integer, result2)
127
+ assert_equal(-1, result2, 'bad conversion')
128
+ end
129
+
130
+ def test_normalize_type_boolean
131
+ result1 = @some_service.private_normalize_type(true, {:type => 'boolean'})
132
+ assert_kind_of(TrueClass, result1)
133
+
134
+ result2 = @some_service.private_normalize_type(false, {:type => 'boolean'})
135
+ assert_kind_of(FalseClass, result2)
136
+
137
+ result3 = @some_service.private_normalize_type('true', {:type => 'boolean'})
138
+ assert_kind_of(TrueClass, result3)
139
+
140
+ result4 = @some_service.private_normalize_type('false',
141
+ {:type => 'boolean'})
142
+ assert_kind_of(FalseClass, result4)
143
+
144
+ result5 = @some_service.private_normalize_type('True',
145
+ {:type => 'boolean'})
146
+ assert_kind_of(TrueClass, result3)
147
+
148
+ result6 = @some_service.private_normalize_type('False',
149
+ {:type => 'boolean'})
150
+ assert_kind_of(FalseClass, result4)
151
+ end
152
+
153
+ def test_normalize_type_object
154
+ result1 = @some_service.private_normalize_type({:a => 'b'},
155
+ {:type => 'SomeClass'})
156
+ assert_equal('b', result1[:a], 'object corrupted')
157
+
158
+ result2 = @some_service.private_normalize_type(@some_service,
159
+ {:type => 'SavonService'})
160
+ assert_equal(@some_service.hash, result2.hash, 'object corrupted')
161
+ end
162
+
163
+ def test_normalize_type_double
164
+ result1 = @some_service.send(:private_normalize_type, 3.14,
165
+ {:type => 'double'})
166
+ assert_kind_of(Float, result1)
167
+ assert_equal(3.14, result1, 'bad conversion')
168
+
169
+ result2 = @some_service.send(:private_normalize_type, '-3.14',
170
+ {:type => 'double'})
171
+ assert_kind_of(Float, result2)
172
+ assert_equal(-3.14, result2, 'bad conversion')
173
+
174
+ result3 = @some_service.send(:private_normalize_type, '42',
175
+ {:type => 'double'})
176
+ assert_kind_of(Float, result3)
177
+ assert_equal(42.0, result3, 'bad conversion')
178
+ end
179
+
180
+ def test_normalize_type_single_array_item
181
+ result1 = @some_service.private_normalize_type('42',
182
+ {:type => 'double', :min_occurs => '0', :max_occurs => 1})
183
+ assert_kind_of(Float, result1)
184
+ assert_equal(42.0, result1, 'Float expected for max_occurs 1')
185
+
186
+ result2 = @some_service.private_normalize_type('42',
187
+ {:type => 'double', :min_occurs => '0', :max_occurs => nil})
188
+ assert_instance_of(Array, result2)
189
+ assert_equal(42.0, result2[0], 'Array is expected for undefined max_occurs')
190
+
191
+ result3 = @some_service.private_normalize_type('42',
192
+ {:type => 'double', :min_occurs => '0', :max_occurs => 2})
193
+ assert_instance_of(Array, result3)
194
+ assert_equal(42.0, result3[0], 'Array is expected for max_occurs > 1')
195
+ end
196
+
197
+ def test_deep_copy_simple
198
+ result1 = @some_service.private_deep_copy(42)
199
+ assert_equal(42, result1)
200
+
201
+ result2 = @some_service.private_deep_copy('Hello World')
202
+ assert_equal('Hello World', result2)
203
+
204
+ result3 = @some_service.private_deep_copy(nil)
205
+ assert_nil(result3)
206
+
207
+ result4 = @some_service.private_deep_copy([])
208
+ assert_equal([], result4)
209
+ assert_not_same([], result4)
210
+
211
+ result5 = @some_service.private_deep_copy({})
212
+ assert_equal({}, result5)
213
+ assert_not_same({}, result5)
214
+ end
215
+
216
+ def test_deep_copy_complex
217
+ data = {:ab => 'ab', :cd => ['cd', 'de', 'ef']}
218
+
219
+ result1 = @some_service.private_deep_copy(data)
220
+ assert_equal(data, result1)
221
+ assert_not_same(data, result1)
222
+
223
+ result2 = @some_service.private_deep_copy(data)
224
+ assert_equal(result2, result1)
225
+ assert_not_same(result2, result1)
226
+
227
+ result2[:cd] = nil
228
+ assert_not_equal(data, result2)
229
+ assert_equal(data, result1)
230
+ end
231
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-ads-common
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 15
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 3
8
+ - 4
8
9
  - 0
9
- version: 0.3.0
10
+ version: 0.4.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Sergio Gomes
@@ -15,44 +16,50 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-06-09 00:00:00 +04:00
19
+ date: 2011-06-20 00:00:00 +04:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
- name: soap4r
23
+ name: savon
23
24
  prerelease: false
24
25
  requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
25
27
  requirements:
26
- - - "="
28
+ - - ~>
27
29
  - !ruby/object:Gem::Version
30
+ hash: 57
28
31
  segments:
32
+ - 0
33
+ - 9
29
34
  - 1
30
- - 5
31
- - 8
32
- version: 1.5.8
35
+ version: 0.9.1
33
36
  type: :runtime
34
37
  version_requirements: *id001
35
38
  - !ruby/object:Gem::Dependency
36
- name: savon
39
+ name: soap4r
37
40
  prerelease: false
38
41
  requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
39
43
  requirements:
40
- - - ~>
44
+ - - "="
41
45
  - !ruby/object:Gem::Version
46
+ hash: 19
42
47
  segments:
43
- - 0
44
- - 9
45
48
  - 1
46
- version: 0.9.1
49
+ - 5
50
+ - 8
51
+ version: 1.5.8
47
52
  type: :runtime
48
53
  version_requirements: *id002
49
54
  - !ruby/object:Gem::Dependency
50
55
  name: httpclient
51
56
  prerelease: false
52
57
  requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
53
59
  requirements:
54
60
  - - ">="
55
61
  - !ruby/object:Gem::Version
62
+ hash: 7
56
63
  segments:
57
64
  - 2
58
65
  - 1
@@ -64,9 +71,11 @@ dependencies:
64
71
  name: httpi
65
72
  prerelease: false
66
73
  requirement: &id004 !ruby/object:Gem::Requirement
74
+ none: false
67
75
  requirements:
68
76
  - - ~>
69
77
  - !ruby/object:Gem::Version
78
+ hash: 63
70
79
  segments:
71
80
  - 0
72
81
  - 9
@@ -74,76 +83,80 @@ dependencies:
74
83
  version: 0.9.2
75
84
  type: :runtime
76
85
  version_requirements: *id004
77
- description: ads_common provides essential utilities shared by all Ads Ruby client libraries.
78
- email: api.sgomes@gmail.com
86
+ description: google-ads-common provides essential utilities shared by all Ads Ruby client libraries.
87
+ email: api.dklimkin@gmail.com
79
88
  executables: []
80
89
 
81
90
  extensions: []
82
91
 
83
92
  extra_rdoc_files:
84
93
  - README
85
- - ChangeLog
86
94
  - COPYING
95
+ - ChangeLog
87
96
  files:
88
- - Rakefile
89
97
  - lib/ads_common/auth/base_handler.rb
90
98
  - lib/ads_common/auth/client_login_handler.rb
91
- - lib/ads_common/build/savon_abstract_generator.rb
99
+ - lib/ads_common/api_config.rb
100
+ - lib/ads_common/config.rb
101
+ - lib/ads_common/credential_handler.rb
102
+ - lib/ads_common/errors.rb
103
+ - lib/ads_common/http.rb
104
+ - lib/ads_common/api.rb
105
+ - lib/ads_common/savon_service.rb
106
+ - lib/ads_common/soap4r_patches.rb
107
+ - lib/ads_common/soap4r_response_handler.rb
92
108
  - lib/ads_common/build/rake_common.rb
93
- - lib/ads_common/build/savon_registry_generator.rb
109
+ - lib/ads_common/build/savon_abstract_generator.rb
94
110
  - lib/ads_common/build/savon_generator.rb
95
111
  - lib/ads_common/build/savon_registry.rb
112
+ - lib/ads_common/build/savon_registry_generator.rb
96
113
  - lib/ads_common/build/savon_service_generator.rb
97
114
  - lib/ads_common/build/soap4r_generator.rb
98
115
  - lib/ads_common/savon_headers/simple_header_handler.rb
116
+ - lib/ads_common/savon_headers/client_login_header_handler.rb
99
117
  - lib/ads_common/soap4r_headers/nested_header_handler.rb
100
118
  - lib/ads_common/soap4r_headers/single_header_handler.rb
101
- - lib/ads_common/api_config.rb
102
- - lib/ads_common/api.rb
103
- - lib/ads_common/soap4r_response_handler.rb
104
- - lib/ads_common/config.rb
105
- - lib/ads_common/credential_handler.rb
106
- - lib/ads_common/errors.rb
107
- - lib/ads_common/http.rb
108
- - lib/ads_common/savon_service.rb
109
- - lib/ads_common/soap4r_patches.rb
110
119
  - lib/ads_common/soap4r_logger.rb
120
+ - Rakefile
121
+ - test/test_config.rb
122
+ - test/test_savon_service.rb
111
123
  - README
112
- - ChangeLog
113
124
  - COPYING
125
+ - ChangeLog
114
126
  has_rdoc: true
115
127
  homepage: http://code.google.com/p/google-api-ads-ruby/
116
128
  licenses: []
117
129
 
118
130
  post_install_message:
119
- rdoc_options:
120
- - --main
121
- - README
131
+ rdoc_options: []
132
+
122
133
  require_paths:
123
134
  - lib
124
135
  required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
125
137
  requirements:
126
138
  - - ">="
127
139
  - !ruby/object:Gem::Version
140
+ hash: 3
128
141
  segments:
129
142
  - 0
130
143
  version: "0"
131
144
  required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
132
146
  requirements:
133
147
  - - ">="
134
148
  - !ruby/object:Gem::Version
149
+ hash: 3
135
150
  segments:
136
151
  - 0
137
152
  version: "0"
138
- requirements:
139
- - soap4r v1.5.8
140
- - savon v0.9.1 or greater
141
- - httpclient v2.1.6 or greater
142
- - httpi v0.9.2 or greater
143
- rubyforge_project: google-ads-common
144
- rubygems_version: 1.3.6
153
+ requirements: []
154
+
155
+ rubyforge_project:
156
+ rubygems_version: 1.3.7
145
157
  signing_key:
146
158
  specification_version: 3
147
159
  summary: Common code for Google Ads APIs.
148
- test_files: []
149
-
160
+ test_files:
161
+ - test/test_config.rb
162
+ - test/test_savon_service.rb