Skip to content

Commit f194d1f

Browse files
committed
Adding source code for new ads_savon gem, to be supported by future client library versions.
1 parent 77d38a0 commit f194d1f

27 files changed

Lines changed: 1536 additions & 0 deletions

ads_savon/BUILD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Description:
2+
# GoogleAdsSavon, a soap library forked from Savon v1.2.0.
3+
package(default_visibility = ["//visibility:public"])
4+
5+
licenses(["notice"]) # MIT
6+
7+
exports_files(["LICENSE"])

ads_savon/CONTRIBUTING

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Want to contribute? Great! First, read this page (including the small print at the end).
2+
3+
### Before you contribute
4+
Before we can use your code, you must sign the
5+
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
6+
(CLA), which you can do online. The CLA is necessary mainly because you own the
7+
copyright to your changes, even after your contribution becomes part of our
8+
codebase, so we need your permission to use and distribute your code. We also
9+
need to be sure of various other things—for instance that you'll tell us if you
10+
know that your code infringes on other people's patents. You don't have to sign
11+
the CLA until after you've submitted your code for review and a member has
12+
approved it, but you must do it before we can put your code into our codebase.
13+
Before you start working on a larger contribution, you should get in touch with
14+
us first through the issue tracker with your idea so that we can help out and
15+
possibly guide you. Coordinating up front makes it much easier to avoid
16+
frustration later on.
17+
18+
### Code reviews
19+
All submissions, including submissions by project members, require review. We
20+
use Github pull requests for this purpose.
21+
22+
### The small print
23+
Contributions made by corporations are covered by a different agreement than
24+
the one above, the Software Grant and Corporate Contributor License Agreement.

ads_savon/ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1.0.0:
2+
- Initial release of Google Ads Savon. This is a fork of Savon version 1,
3+
which can be found here: https://github.com/savonrb/savon/tree/version1

ads_savon/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Copyright (c) 2010 Daniel Harrington
2+
Copyright (c) 2015 Google, Inc.
3+
4+
Permission is hereby granted, free of charge, to any person obtaining
5+
a copy of this software and associated documentation files (the
6+
"Software"), to deal in the Software without restriction, including
7+
without limitation the rights to use, copy, modify, merge, publish,
8+
distribute, sublicense, and/or sell copies of the Software, and to
9+
permit persons to whom the Software is furnished to do so, subject to
10+
the following conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ads_savon/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Google Ads Savon
2+
3+
Google Ads Savon is a fork of [Savon 1](http://savonrb.com/version1/)
4+
specific for use with the Google Ads Ruby Client Library.
5+
6+
Google Ads Savon is available through [Rubygems](http://rubygems.org/gems/google-ads-savon) and can be installed via:
7+
8+
$ gem install google-ads-savon
9+
10+
This library is not intended for re-use outside of google-ads-common.
11+
12+
This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.
13+
14+
Documentation
15+
-------------
16+
17+
Read about the original library at [savonrb.com/version1/](http://savonrb.com/version1/)

ads_savon/Rakefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
desc 'Package the Google Ads Savon library into a gem file.'
2+
task :build do
3+
result = system('/usr/bin/env gem build google-ads-savon.gemspec')
4+
raise 'Build failed.' unless result
5+
end

ads_savon/google-ads-savon.gemspec

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -*- encoding : utf-8 -*-
2+
lib = File.expand_path("../lib", __FILE__)
3+
$:.unshift lib unless $:.include? lib
4+
5+
require "ads_savon/version"
6+
7+
Gem::Specification.new do |s|
8+
s.name = "google-ads-savon"
9+
s.version = GoogleAdsSavon::VERSION
10+
s.authors = "Daniel Harrington"
11+
s.email = "me@rubiii.com"
12+
s.homepage = "http://savonrb.com"
13+
s.summary = "Heavy metal SOAP client"
14+
s.description = "Delicious SOAP for the Ruby community"
15+
16+
s.rubyforge_project = s.name
17+
s.license = 'MIT'
18+
19+
s.add_dependency "nori", "~> 2.4"
20+
s.add_dependency "httpi", "~> 2.3"
21+
s.add_dependency "wasabi", "~> 3.4"
22+
s.add_dependency "akami", "~> 1.2"
23+
s.add_dependency "gyoku", "~> 1.2"
24+
25+
s.add_dependency "builder", ">= 2.1.2"
26+
s.add_dependency "nokogiri", ">= 1.4.0"
27+
28+
s.add_development_dependency "rake", "~> 10.1"
29+
s.add_development_dependency "rspec", "~> 2.14"
30+
s.add_development_dependency "mocha", "~> 0.14"
31+
s.add_development_dependency "timecop", "~> 0.3"
32+
33+
s.files = Dir["**/*"]
34+
s.require_path = "lib"
35+
end

ads_savon/lib/ads_savon.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require "ads_savon/version"
2+
require "ads_savon/config"
3+
require "ads_savon/client"
4+
require "ads_savon/model"
5+
6+
module GoogleAdsSavon
7+
extend self
8+
9+
def client(*args, &block)
10+
Client.new(*args, &block)
11+
end
12+
13+
def configure
14+
yield config
15+
end
16+
17+
def config
18+
@config ||= Config.default
19+
end
20+
21+
attr_writer :config
22+
23+
end

ads_savon/lib/ads_savon/client.rb

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
require "wasabi/document"
2+
require "httpi/request"
3+
require "akami"
4+
5+
require "ads_savon/soap/xml"
6+
require "ads_savon/soap/request"
7+
require "ads_savon/soap/response"
8+
require "ads_savon/soap/request_builder"
9+
10+
module GoogleAdsSavon
11+
12+
# = GoogleAdsSavon::Client
13+
#
14+
# GoogleAdsSavon::Client is the main object for connecting to a SOAP service.
15+
class Client
16+
17+
# Initializes the GoogleAdsSavon::Client for a SOAP service. Accepts a +block+ which is evaluated in the
18+
# context of this object to let you access the +wsdl+, +http+, and +wsse+ methods.
19+
#
20+
# == Examples
21+
#
22+
# # Using a remote WSDL
23+
# client = GoogleAdsSavon::Client.new("http://example.com/UserService?wsdl")
24+
#
25+
# # Using a local WSDL
26+
# client = GoogleAdsSavon::Client.new File.expand_path("../wsdl/service.xml", __FILE__)
27+
#
28+
# # Directly accessing a SOAP endpoint
29+
# client = GoogleAdsSavon::Client.new do
30+
# wsdl.endpoint = "http://example.com/UserService"
31+
# wsdl.namespace = "http://users.example.com"
32+
# end
33+
def initialize(wsdl_document = nil, &block)
34+
self.config = GoogleAdsSavon.config.clone
35+
wsdl.document = wsdl_document if wsdl_document
36+
37+
process 1, &block if block
38+
wsdl.request = http
39+
end
40+
41+
# Accessor for the <tt>GoogleAdsSavon::Config</tt>.
42+
attr_accessor :config
43+
44+
# Returns the <tt>Wasabi::Document</tt>.
45+
def wsdl
46+
@wsdl ||= Wasabi::Document.new
47+
end
48+
49+
# Returns the <tt>HTTPI::Request</tt>.
50+
def http
51+
@http ||= HTTPI::Request.new
52+
end
53+
54+
# Returns the <tt>Akami::WSSE</tt> object.
55+
def wsse
56+
@wsse ||= Akami.wsse
57+
end
58+
59+
# Executes a SOAP request for a given SOAP action. Accepts a +block+ which is evaluated in the
60+
# context of the <tt>SOAP::RequestBuilder</tt> object to let you access its +soap+, +wsdl+,
61+
# +http+ and +wsse+ methods.
62+
#
63+
# == Examples
64+
#
65+
# # Calls a "getUser" SOAP action with the payload of "<userId>123</userId>"
66+
# client.request(:get_user) { soap.body = { :user_id => 123 } }
67+
#
68+
# # Prefixes the SOAP input tag with a given namespace: "<wsdl:GetUser>...</wsdl:GetUser>"
69+
# client.request(:wsdl, "GetUser") { soap.body = { :user_id => 123 } }
70+
#
71+
# # SOAP input tag with attributes: <getUser xmlns:wsdl="http://example.com">...</getUser>"
72+
# client.request(:get_user, "xmlns:wsdl" => "http://example.com")
73+
def request(*args, &block)
74+
raise ArgumentError, "GoogleAdsSavon::Client#request requires at least one argument" if args.empty?
75+
76+
options = extract_options(args)
77+
78+
request_builder = SOAP::RequestBuilder.new(options.delete(:input), options)
79+
request_builder.wsdl = wsdl
80+
request_builder.http = http.dup
81+
request_builder.wsse = wsse.dup
82+
request_builder.config = config.dup
83+
84+
post_configuration = lambda { process(0, request_builder, &block) if block }
85+
86+
response = request_builder.request(&post_configuration).response
87+
http.set_cookies(response.http)
88+
89+
if wsse.verify_response
90+
WSSE::VerifySignature.new(response.http.body).verify!
91+
end
92+
93+
response
94+
end
95+
96+
private
97+
98+
# Expects an Array of +args+ and returns a Hash containing the SOAP input,
99+
# the namespace (might be +nil+), the SOAP action (might be +nil+),
100+
# the SOAP body (might be +nil+), and a Hash of attributes for the input
101+
# tag (which might be empty).
102+
def extract_options(args)
103+
attributes = Hash === args.last ? args.pop : {}
104+
body = attributes.delete(:body)
105+
soap_action = attributes.delete(:soap_action)
106+
107+
namespace_identifier = args.size > 1 ? args.shift.to_sym : nil
108+
input = args.first
109+
110+
remove_blank_values(
111+
:namespace_identifier => namespace_identifier,
112+
:input => input,
113+
:attributes => attributes,
114+
:body => body,
115+
:soap_action => soap_action
116+
)
117+
end
118+
119+
# Processes a given +block+. Yields objects if the block expects any arguments.
120+
# Otherwise evaluates the block in the context of +instance+.
121+
def process(offset = 0, instance = self, &block)
122+
block.arity > 0 ? yield_objects(offset, instance, &block) : evaluate(instance, &block)
123+
end
124+
125+
# Yields a number of objects to a given +block+ depending on how many arguments
126+
# the block is expecting.
127+
def yield_objects(offset, instance, &block)
128+
to_yield = [:soap, :wsdl, :http, :wsse]
129+
yield *(to_yield[offset, block.arity].map { |obj_name| instance.send(obj_name) })
130+
end
131+
132+
# Evaluates a given +block+ inside +instance+. Stores the original block binding.
133+
def evaluate(instance, &block)
134+
original_self = eval "self", block.binding
135+
136+
# A proxy that attemps to make method calls on +instance+. If a NoMethodError is
137+
# raised, the call will be made on +original_self+.
138+
proxy = Object.new
139+
proxy.instance_eval do
140+
class << self
141+
attr_accessor :original_self, :instance
142+
end
143+
144+
def method_missing(method, *args, &block)
145+
instance.send(method, *args, &block)
146+
rescue NoMethodError
147+
original_self.send(method, *args, &block)
148+
end
149+
end
150+
151+
proxy.instance = instance
152+
proxy.original_self = original_self
153+
154+
proxy.instance_eval &block
155+
end
156+
157+
# Removes all blank values from a given +hash+.
158+
def remove_blank_values(hash)
159+
hash.delete_if { |_, value| value.respond_to?(:empty?) ? value.empty? : !value }
160+
end
161+
162+
end
163+
end

ads_savon/lib/ads_savon/config.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require "ads_savon/logger"
2+
require "ads_savon/null_logger"
3+
require "ads_savon/hooks/group"
4+
require "ads_savon/soap"
5+
6+
module GoogleAdsSavon
7+
Config = Struct.new(:_logger, :pretty_print_xml, :raise_errors, :soap_version, :env_namespace, :soap_header) do
8+
9+
def self.default
10+
config = new
11+
config._logger = Logger.new
12+
config.raise_errors = true
13+
config.soap_version = SOAP::DEFAULT_VERSION
14+
config
15+
end
16+
17+
alias_method :logger, :_logger
18+
19+
def logger=(logger)
20+
_logger.subject = logger
21+
end
22+
23+
def log_level=(level)
24+
_logger.level = level
25+
end
26+
27+
def log=(log)
28+
if log == true
29+
self._logger = Logger.new
30+
else
31+
self._logger = NullLogger.new
32+
end
33+
end
34+
35+
def hooks
36+
@hooks ||= Hooks::Group.new
37+
end
38+
39+
def clone
40+
config = super
41+
config._logger = config._logger.clone
42+
config
43+
end
44+
45+
end
46+
end

0 commit comments

Comments
 (0)