google-ads-common 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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