google-ads-savon 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/BUILD +7 -0
- data/CONTRIBUTING +24 -0
- data/ChangeLog +3 -0
- data/LICENSE +21 -0
- data/README.md +17 -0
- data/Rakefile +5 -0
- data/google-ads-savon.gemspec +35 -0
- data/lib/ads_savon.rb +23 -0
- data/lib/ads_savon/client.rb +163 -0
- data/lib/ads_savon/config.rb +46 -0
- data/lib/ads_savon/core_ext/string.rb +30 -0
- data/lib/ads_savon/error.rb +6 -0
- data/lib/ads_savon/hooks/group.rb +68 -0
- data/lib/ads_savon/hooks/hook.rb +61 -0
- data/lib/ads_savon/http/error.rb +42 -0
- data/lib/ads_savon/log_message.rb +50 -0
- data/lib/ads_savon/logger.rb +39 -0
- data/lib/ads_savon/model.rb +102 -0
- data/lib/ads_savon/null_logger.rb +10 -0
- data/lib/ads_savon/soap.rb +21 -0
- data/lib/ads_savon/soap/fault.rb +72 -0
- data/lib/ads_savon/soap/invalid_response_error.rb +13 -0
- data/lib/ads_savon/soap/request.rb +86 -0
- data/lib/ads_savon/soap/request_builder.rb +205 -0
- data/lib/ads_savon/soap/response.rb +130 -0
- data/lib/ads_savon/soap/xml.rb +252 -0
- data/lib/ads_savon/version.rb +5 -0
- metadata +224 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e33b82251dab2fe6023fddf342d54c42baae2ad5
|
4
|
+
data.tar.gz: d45eadd1cb5bd5c857580018ca16a08f00616356
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7def73594ce63bdb09025dc8bd0d1bd8a1de1dcee582b425b880408cba3022d248659dce4d13c20771a41710057f1b7be578f2b5053184d2e8b6579839877c5c
|
7
|
+
data.tar.gz: 3d3ac185715f0f161f11a4cd98d034948576724ece7f6ee749685e5f75f00107eebbdb6f6e1217a47b48c4e95f365c46053b68b42ea05bc4850d96ff72946906
|
data/BUILD
ADDED
data/CONTRIBUTING
ADDED
@@ -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.
|
data/ChangeLog
ADDED
data/LICENSE
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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/)
|
data/Rakefile
ADDED
@@ -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
|
data/lib/ads_savon.rb
ADDED
@@ -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
|
@@ -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
|
@@ -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,0 +1,30 @@
|
|
1
|
+
require "ads_savon/soap"
|
2
|
+
|
3
|
+
module GoogleAdsSavon
|
4
|
+
module CoreExt
|
5
|
+
module String
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
unless "savon".respond_to?(:snakecase)
|
9
|
+
base.send(:include, Extension)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Extension
|
14
|
+
def snakecase
|
15
|
+
str = dup
|
16
|
+
str.gsub! /::/, '/'
|
17
|
+
str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
|
18
|
+
str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
|
19
|
+
str.tr! ".", "_"
|
20
|
+
str.tr! "-", "_"
|
21
|
+
str.downcase!
|
22
|
+
str
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
String.send :include, GoogleAdsSavon::CoreExt::String
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "ads_savon/hooks/hook"
|
2
|
+
|
3
|
+
module GoogleAdsSavon
|
4
|
+
module Hooks
|
5
|
+
|
6
|
+
# = GoogleAdsSavon::Hooks::Group
|
7
|
+
#
|
8
|
+
# Manages a list of hooks.
|
9
|
+
class Group
|
10
|
+
|
11
|
+
# Accepts an Array of +hooks+ to start with.
|
12
|
+
def initialize(hooks = [])
|
13
|
+
@hooks = hooks
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns whether this group contains hooks.
|
17
|
+
def empty?
|
18
|
+
hooks.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the number of hooks in this group.
|
22
|
+
def count
|
23
|
+
hooks.count
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds a new hook.
|
27
|
+
def define(id, hook, &block)
|
28
|
+
hooks << Hook.new(id, hook, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Removes hooks matching the given +ids+.
|
32
|
+
def reject(*ids)
|
33
|
+
ids = ids.flatten
|
34
|
+
hooks.reject! { |hook| ids.include? hook.id }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Fire a given +hook+ with any given +args+.
|
38
|
+
def fire(hook, *args, &callback)
|
39
|
+
callable = select(hook)
|
40
|
+
|
41
|
+
if callable.empty?
|
42
|
+
callback.call
|
43
|
+
else
|
44
|
+
args.unshift(callback) if callback
|
45
|
+
callable.call(*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Calls the hooks with the given +args+ and returns the
|
50
|
+
# value of the last hooks.
|
51
|
+
def call(*args)
|
52
|
+
hooks.inject(nil) { |memo, hook| hook.call(*args) }
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def hooks
|
58
|
+
@hooks ||= []
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a new group for a given +hook+.
|
62
|
+
def select(hook)
|
63
|
+
Group.new hooks.select { |h| h.hook == hook }
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|