adapi 0.0.2 → 0.0.3
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/README.rdoc +74 -39
- data/adapi.gemspec +4 -0
- data/examples/add_ad_group.rb +6 -22
- data/examples/add_bare_ad_group.rb +11 -6
- data/examples/add_bare_campaign.rb +5 -7
- data/examples/add_campaign.rb +12 -29
- data/examples/add_campaign_targets.rb +16 -7
- data/examples/add_invalid_ad_group.rb +35 -0
- data/examples/add_invalid_text_ad.rb +30 -0
- data/examples/add_keywords.rb +14 -0
- data/examples/add_text_ad.rb +23 -0
- data/examples/log_to_specific_account.rb +26 -13
- data/examples/rollback_campaign.rb +56 -0
- data/examples/update_campaign.rb +14 -15
- data/examples/update_campaign_status.rb +6 -6
- data/lib/adapi.rb +19 -1
- data/lib/adapi/ad.rb +55 -42
- data/lib/adapi/ad/text_ad.rb +126 -0
- data/lib/adapi/ad_group.rb +80 -42
- data/lib/adapi/ad_group_criterion.rb +13 -55
- data/lib/adapi/api.rb +107 -1
- data/lib/adapi/campaign.rb +144 -94
- data/lib/adapi/campaign_target.rb +93 -45
- data/lib/adapi/config.rb +16 -2
- data/lib/adapi/keyword.rb +109 -0
- data/lib/adapi/version.rb +12 -1
- data/lib/httpi_request_monkeypatch.rb +35 -0
- data/test/factories/ad_group_factory.rb +20 -0
- data/test/factories/ad_text_factory.rb +9 -0
- data/test/test_helper.rb +3 -3
- data/test/unit/ad/ad_text_test.rb +30 -0
- data/test/unit/ad_group_test.rb +34 -0
- data/test/unit/ad_test.rb +12 -0
- data/test/unit/campaign_target_test.rb +18 -3
- metadata +122 -109
- data/examples/add_ad.rb +0 -17
- data/examples/add_ad_group_criteria.rb +0 -20
- data/examples/update_campaign_name.rb +0 -14
@@ -1,20 +1,33 @@
|
|
1
1
|
|
2
2
|
require 'adapi'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
Adapi::Config.load_settings(:in_hash => {
|
5
|
+
:coca_cola => {
|
6
|
+
:authentication => {
|
7
|
+
:method => 'ClientLogin',
|
8
|
+
:email => 'coca_cola_email@gmail.com',
|
9
|
+
:password => 'coca_cola_password',
|
10
|
+
:developer_token => 'coca_cola_developer_token',
|
11
|
+
:user_agent => 'Coca-Cola Adwords API Test'
|
12
|
+
},
|
13
|
+
:service => {
|
14
|
+
:environment => 'SANDBOX'
|
15
|
+
}
|
13
16
|
},
|
14
|
-
|
15
|
-
:
|
17
|
+
:pepsi => {
|
18
|
+
:authentication => {
|
19
|
+
:method => 'ClientLogin',
|
20
|
+
:email => 'pepsi_email@gmail.com',
|
21
|
+
:password => 'pepsi_password',
|
22
|
+
:developer_token => 'pepsi_developer_token',
|
23
|
+
:user_agent => 'Pepsi Adwords API Test'
|
24
|
+
},
|
25
|
+
:service => {
|
26
|
+
:environment => 'SANDBOX'
|
27
|
+
}
|
16
28
|
}
|
17
29
|
})
|
18
30
|
|
19
|
-
|
20
|
-
|
31
|
+
Adapi::Config.set(:pepsi, :client_customer_id => '555-666-7777')
|
32
|
+
|
33
|
+
p Adapi::Config.read
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
require 'adapi'
|
3
|
+
|
4
|
+
# create campaign by single command, with campaing targets, with ad_groups
|
5
|
+
# including keywords and ads
|
6
|
+
|
7
|
+
campaign_data = {
|
8
|
+
:name => "Campaign #%d" % (Time.new.to_f * 1000).to_i,
|
9
|
+
:status => 'PAUSED',
|
10
|
+
# Automatic CPC: BudgetOptimizer or ManualCPC
|
11
|
+
:bidding_strategy => { :xsi_type => 'BudgetOptimizer', :bid_ceiling => 100 },
|
12
|
+
:budget => { :amount => 50, :delivery_method => 'STANDARD' },
|
13
|
+
|
14
|
+
:network_setting => {
|
15
|
+
:target_google_search => true,
|
16
|
+
:target_search_network => true,
|
17
|
+
:target_content_network => false,
|
18
|
+
:target_content_contextual => false
|
19
|
+
},
|
20
|
+
|
21
|
+
:targets => {
|
22
|
+
:language => [ 'en', 'cs' ],
|
23
|
+
# TODO test together with city target
|
24
|
+
:geo => { :proximity => { :geo_point => '38.89859,-77.035971', :radius => '10 km' } }
|
25
|
+
},
|
26
|
+
|
27
|
+
:ad_groups => [
|
28
|
+
{
|
29
|
+
:name => "AdGroup #%d" % (Time.new.to_f * 1000).to_i,
|
30
|
+
:status => 'ENABLED',
|
31
|
+
|
32
|
+
:keywords => [ 'dem codez', '"top coder"', "[-code]" ],
|
33
|
+
|
34
|
+
:ads => [
|
35
|
+
{
|
36
|
+
:headline => "Code like Neo",
|
37
|
+
:description1 => 'Need mad coding skills?',
|
38
|
+
:description2 => 'Check out my new blog!',
|
39
|
+
:url => '', # THIS FAILS
|
40
|
+
:display_url => 'http://www.demcodez.com'
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
]
|
45
|
+
|
46
|
+
}
|
47
|
+
|
48
|
+
$campaign = Adapi::Campaign.create(campaign_data)
|
49
|
+
|
50
|
+
p "Campaign ID #{$campaign.id} created"
|
51
|
+
p "with status DELETED and changed name"
|
52
|
+
pp $campaign.attributes
|
53
|
+
|
54
|
+
p "with errors:"
|
55
|
+
pp $campaign.errors.to_a
|
56
|
+
|
data/examples/update_campaign.rb
CHANGED
@@ -1,23 +1,22 @@
|
|
1
|
-
|
2
1
|
require 'adapi'
|
3
2
|
|
4
3
|
# create campaign
|
5
|
-
require 'add_bare_campaign'
|
6
|
-
|
7
|
-
p 'original status: %s' % $campaign[:status]
|
8
|
-
|
9
|
-
campaign_updates = {
|
10
|
-
:id => $campaign[:id],
|
11
|
-
:status => 'ACTIVE'
|
12
|
-
}
|
13
|
-
|
14
|
-
$campaign = Adapi::Campaign.update(:data => campaign_updates)
|
4
|
+
require File.join(File.dirname(__FILE__), 'add_bare_campaign')
|
15
5
|
|
16
|
-
p
|
6
|
+
p "ORIGINAL CAMPAIGN:"
|
7
|
+
pp $campaign.attributes
|
17
8
|
|
18
|
-
$
|
9
|
+
$updated_campaign = Adapi::Campaign.update(
|
19
10
|
:id => $campaign[:id],
|
20
|
-
:
|
11
|
+
:status => 'ACTIVE',
|
12
|
+
:name => "UPDATED_#{$campaign[:name]}",
|
13
|
+
:network_setting => {
|
14
|
+
:target_google_search => false,
|
15
|
+
:target_search_network => false,
|
16
|
+
:target_content_network => true,
|
17
|
+
:target_content_contextual => true
|
18
|
+
}
|
21
19
|
)
|
22
20
|
|
23
|
-
p
|
21
|
+
p "UPDATED CAMPAIGN:"
|
22
|
+
pp $updated_campaign.attributes
|
@@ -2,14 +2,14 @@
|
|
2
2
|
require 'adapi'
|
3
3
|
|
4
4
|
# create campaign
|
5
|
-
require 'add_bare_campaign'
|
5
|
+
require File.join(File.dirname(__FILE__), 'add_bare_campaign')
|
6
6
|
|
7
|
-
p
|
7
|
+
p "ORIGINAL STATUS: %s" % $campaign.status
|
8
8
|
|
9
|
-
$campaign
|
9
|
+
$campaign.activate
|
10
10
|
|
11
|
-
p
|
11
|
+
p "STATUS UPDATE 1: %s" % $campaign.status
|
12
12
|
|
13
|
-
$campaign
|
13
|
+
$campaign.delete
|
14
14
|
|
15
|
-
p
|
15
|
+
p "STATUS UPDATE 2: %s" % $campaign.status
|
data/lib/adapi.rb
CHANGED
@@ -3,15 +3,33 @@ require 'rubygems'
|
|
3
3
|
require 'adwords_api'
|
4
4
|
require 'collection'
|
5
5
|
require 'yaml'
|
6
|
+
require 'pp'
|
6
7
|
|
8
|
+
require 'active_model'
|
9
|
+
# require only ActiveSupport parts that we actually use
|
10
|
+
require 'active_support/all'
|
11
|
+
|
12
|
+
require 'adapi/version'
|
7
13
|
require 'adapi/config'
|
8
14
|
require 'adapi/api'
|
9
15
|
require 'adapi/campaign'
|
10
16
|
require 'adapi/campaign_target'
|
11
17
|
require 'adapi/ad_group'
|
12
18
|
require 'adapi/ad_group_criterion'
|
19
|
+
require 'adapi/keyword'
|
13
20
|
require 'adapi/ad'
|
14
|
-
require 'adapi/
|
21
|
+
require 'adapi/ad/text_ad'
|
22
|
+
|
23
|
+
# monkeypatch HTTPI
|
24
|
+
require 'httpi_request_monkeypatch'
|
25
|
+
|
26
|
+
HTTPI.adapter = :curb
|
27
|
+
# supress HTTPI output
|
28
|
+
# HTTPI.log = false
|
29
|
+
|
30
|
+
# load factories for development environment
|
31
|
+
# require 'factory_girl'
|
32
|
+
# Dir[ File.join(File.dirname(__FILE__), '../test/factories/*.rb') ].each { |f| require f }
|
15
33
|
|
16
34
|
module Adapi
|
17
35
|
API_VERSION = :v201101
|
data/lib/adapi/ad.rb
CHANGED
@@ -1,63 +1,76 @@
|
|
1
1
|
module Adapi
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Ad == AdGroupAd
|
4
|
+
# wraps all types of ads: text ads, image ads...
|
5
5
|
class Ad < Api
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
super(params)
|
10
|
-
end
|
7
|
+
attr_accessor :ad_group_id, :url, :display_url, :approval_status,
|
8
|
+
:disapproval_reasons, :trademark_disapproved
|
11
9
|
|
12
|
-
|
13
|
-
ad_service = Ad.new
|
10
|
+
validates_presence_of :ad_group_id
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
response = ad_service.service.mutate([operation])
|
12
|
+
# PS: create won't work with id and ad_group_id
|
13
|
+
# 'id' => id, 'ad_group_id' => ad_group_id,
|
14
|
+
def attributes
|
15
|
+
super.merge('url' => url, 'display_url' => display_url)
|
16
|
+
end
|
22
17
|
|
23
|
-
|
18
|
+
def initialize(params = {})
|
19
|
+
params[:service_name] = :AdGroupAdService
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
ad = response[:value].first
|
28
|
-
puts " Ad ID is #{ad[:ad][:id]}, type is '#{ad[:ad][:xsi_type]}' and status is '#{ad[:status]}'."
|
21
|
+
%w{ id ad_group_id url display_url status }.each do |param_name|
|
22
|
+
self.send "#{param_name}=", params[param_name.to_sym]
|
29
23
|
end
|
30
24
|
|
31
|
-
|
25
|
+
super(params)
|
32
26
|
end
|
33
27
|
|
34
|
-
|
35
|
-
|
28
|
+
# deletes ad
|
29
|
+
#
|
30
|
+
def destroy
|
31
|
+
response = self.mutate(
|
32
|
+
:operator => 'REMOVE',
|
33
|
+
:operand => {
|
34
|
+
:ad_group_id => self.ad_group_id,
|
35
|
+
:ad => { :id => self.id, :xsi_type => 'Ad' }
|
36
|
+
}
|
37
|
+
)
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
(response and response[:value]) ? true : false
|
40
|
+
end
|
39
41
|
|
40
|
-
selector = {
|
41
|
-
:fields => ['Id', 'Headline'],
|
42
|
-
:ordering => [{:field => 'Id', :sort_order => 'ASCENDING'}],
|
43
|
-
:predicates => [
|
44
|
-
{:field => 'AdGroupId', :operator => 'EQUALS', :values => ad_group_id }
|
45
|
-
# { :field => 'Status', :operator => 'IN', :values => ['ENABLED', 'PAUSED', 'DISABLED'] }
|
46
|
-
]
|
47
|
-
}
|
48
42
|
|
49
|
-
|
43
|
+
# ad-specific mutate wrapper, deals with PolicyViolations for ads
|
44
|
+
#
|
45
|
+
def mutate(operation)
|
46
|
+
operation = [operation] unless operation.is_a?(Array)
|
47
|
+
|
48
|
+
# fix to save space during specifyng operations
|
49
|
+
operation = operation.map do |op|
|
50
|
+
op[:operand].delete(:status) if op[:operand][:status].nil?
|
51
|
+
op
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
response = @service.mutate(operation)
|
56
|
+
|
57
|
+
rescue AdsCommon::Errors::HttpError => e
|
58
|
+
self.errors.add(:base, e.message)
|
50
59
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
# traps any exceptions raised by AdWords API
|
61
|
+
rescue AdwordsApi::Errors::ApiException => e
|
62
|
+
# return PolicyViolations so they can be sent again
|
63
|
+
e.errors.each do |error|
|
64
|
+
if (error[:api_error_type] == 'PolicyViolationError') && error[:is_exemptable]
|
65
|
+
self.errors.add(error[:api_error_type], error[:key])
|
66
|
+
else
|
67
|
+
# otherwise, just report the errors
|
68
|
+
self.errors.add( "[#{self.xsi_type.underscore}]", "#{error[:error_string]} @ #{error[:field_path]}")
|
69
|
+
end
|
57
70
|
end
|
58
|
-
else
|
59
|
-
puts "No ads found for ad group ##{ad_group_id}."
|
60
71
|
end
|
72
|
+
|
73
|
+
response
|
61
74
|
end
|
62
75
|
|
63
76
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Adapi
|
2
|
+
# Ad::TextAd == AdGroupAd::TextAd
|
3
|
+
#
|
4
|
+
# http://code.google.com/apis/adwords/docs/reference/latest/AdGroupAdService.TextAd.html
|
5
|
+
#
|
6
|
+
class Ad::TextAd < Ad
|
7
|
+
|
8
|
+
attr_accessor :headline, :description1, :description2
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
super.merge('headline' => headline, 'description1' => description1, 'description2' => description2)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(params = {})
|
15
|
+
params[:service_name] = :AdGroupAdService
|
16
|
+
|
17
|
+
@xsi_type = 'TextAd'
|
18
|
+
|
19
|
+
%w{ headline description1 description2 }.each do |param_name|
|
20
|
+
self.send "#{param_name}=", params[param_name.to_sym]
|
21
|
+
end
|
22
|
+
|
23
|
+
super(params)
|
24
|
+
end
|
25
|
+
|
26
|
+
def save
|
27
|
+
self.new? ? self.create : self.update
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
operation = {
|
32
|
+
:operator => 'ADD',
|
33
|
+
:operand => {
|
34
|
+
:ad_group_id => @ad_group_id,
|
35
|
+
:status => @status,
|
36
|
+
:ad => self.data
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
response = self.mutate(operation)
|
41
|
+
|
42
|
+
# check for PolicyViolationError(s)
|
43
|
+
# PS: check google-adwords-api/examples/handle_policy_violation_error.rb
|
44
|
+
if (self.errors['PolicyViolationError'].size > 0)
|
45
|
+
# set exemptions and try again
|
46
|
+
operation[:exemption_requests] = errors['PolicyViolationError'].map do |error|
|
47
|
+
{ :key => error }
|
48
|
+
end
|
49
|
+
|
50
|
+
self.errors.clear
|
51
|
+
|
52
|
+
response = self.mutate(operation)
|
53
|
+
end
|
54
|
+
|
55
|
+
return false unless (response and response[:value])
|
56
|
+
|
57
|
+
self.id = response[:value].first[:ad][:id] rescue nil
|
58
|
+
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
# params - specify hash of params and values to update
|
63
|
+
# PS: I think it's possible to edit only status, but not headline,
|
64
|
+
# descriptions... instead you should delete existing ad and create a new one
|
65
|
+
#
|
66
|
+
def update(params = {})
|
67
|
+
# set params (:status param makes it a little complicated)
|
68
|
+
#
|
69
|
+
updated_params = (params || self.attributes).symbolize_keys
|
70
|
+
updated_status = updated_params.delete(:status)
|
71
|
+
|
72
|
+
response = self.mutate(
|
73
|
+
:operator => 'SET',
|
74
|
+
:operand => {
|
75
|
+
:ad_group_id => self.ad_group_id,
|
76
|
+
:ad => updated_params.merge(:id => self.id),
|
77
|
+
:status => updated_status
|
78
|
+
}
|
79
|
+
)
|
80
|
+
|
81
|
+
(response and response[:value]) ? true : false
|
82
|
+
end
|
83
|
+
|
84
|
+
def find # == refresh
|
85
|
+
TextAd.find(:first, :ad_group_id => self.ad_group_id, :id => self.id)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.find(amount = :all, params = {})
|
89
|
+
params.symbolize_keys!
|
90
|
+
first_only = (amount.to_sym == :first)
|
91
|
+
|
92
|
+
# for ActiveRecord compatibility, we don't use anything besides conditions
|
93
|
+
# params for now
|
94
|
+
params = params[:conditions] if params[:conditions]
|
95
|
+
|
96
|
+
# we need ad_group_id
|
97
|
+
raise ArgumentError, "AdGroup ID is required" unless params[:ad_group_id]
|
98
|
+
|
99
|
+
# supported condition parameters: ad_group_id and id
|
100
|
+
predicates = [ :ad_group_id, :id ].map do |param_name|
|
101
|
+
if params[param_name]
|
102
|
+
{:field => param_name.to_s.camelcase, :operator => 'EQUALS', :values => params[param_name] }
|
103
|
+
end
|
104
|
+
end.compact
|
105
|
+
|
106
|
+
selector = {
|
107
|
+
:fields => ['Id', 'Headline'],
|
108
|
+
:ordering => [{:field => 'Id', :sort_order => 'ASCENDING'}],
|
109
|
+
:predicates => predicates
|
110
|
+
}
|
111
|
+
|
112
|
+
response = TextAd.new.service.get(selector)
|
113
|
+
|
114
|
+
response = (response and response[:entries]) ? response[:entries] : []
|
115
|
+
|
116
|
+
response.map! do |data|
|
117
|
+
TextAd.new(data[:ad].merge(:ad_group_id => data[:ad_group_id], :status => data[:status]))
|
118
|
+
end
|
119
|
+
|
120
|
+
# TODO convert to TextAd instances
|
121
|
+
# PS: we already have ad_group_id parameter
|
122
|
+
first_only ? response.first : response
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
data/lib/adapi/ad_group.rb
CHANGED
@@ -1,69 +1,107 @@
|
|
1
1
|
module Adapi
|
2
2
|
class AdGroup < Api
|
3
|
+
|
4
|
+
attr_accessor :campaign_id, :name, :bids, :keywords, :ads
|
5
|
+
|
6
|
+
validates_presence_of :campaign_id, :name, :status
|
7
|
+
validates_inclusion_of :status, :in => %w{ ENABLED PAUSED DELETED }
|
8
|
+
|
9
|
+
def attributes
|
10
|
+
super.merge('campaign_id' => campaign_id, 'name' => name, 'bids' => bids)
|
11
|
+
end
|
3
12
|
|
4
13
|
def initialize(params = {})
|
5
14
|
params[:service_name] = :AdGroupService
|
6
|
-
super(params)
|
7
|
-
end
|
8
15
|
|
9
|
-
|
10
|
-
ad_group_service = AdGroup.new
|
16
|
+
@xsi_type = 'AdGroup'
|
11
17
|
|
12
|
-
|
13
|
-
|
18
|
+
%w{ campaign_id name status bids keywords ads }.each do |param_name|
|
19
|
+
self.send "#{param_name}=", params[param_name.to_sym]
|
20
|
+
end
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
response = ad_group_service.service.mutate([operation])
|
22
|
+
@keywords ||= []
|
23
|
+
@ads ||= []
|
19
24
|
|
20
|
-
|
25
|
+
super(params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create
|
29
|
+
return false unless self.valid?
|
30
|
+
|
31
|
+
operand = Hash[
|
32
|
+
[:campaign_id, :name, :status, :bids].map do |k|
|
33
|
+
[ k.to_sym, self.send(k) ] if self.send(k)
|
34
|
+
end.compact
|
35
|
+
]
|
21
36
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
response = self.mutate(
|
38
|
+
:operator => 'ADD',
|
39
|
+
:operand => operand
|
40
|
+
)
|
41
|
+
|
42
|
+
return false unless (response and response[:value])
|
43
|
+
|
44
|
+
self.id = response[:value].first[:id] rescue nil
|
45
|
+
|
46
|
+
if @keywords.size > 0
|
47
|
+
keyword = Adapi::Keyword.create(
|
48
|
+
:ad_group_id => @id,
|
49
|
+
:keywords => @keywords
|
26
50
|
)
|
51
|
+
|
52
|
+
if (keyword.errors.size > 0)
|
53
|
+
self.errors.add("[keyword]", keyword.errors.to_a)
|
54
|
+
return false
|
55
|
+
end
|
27
56
|
end
|
28
57
|
|
29
|
-
ads.each do |ad_data|
|
30
|
-
Adapi::Ad.create(
|
31
|
-
|
32
|
-
)
|
58
|
+
@ads.each do |ad_data|
|
59
|
+
ad = Adapi::Ad::TextAd.create( ad_data.merge(:ad_group_id => @id) )
|
60
|
+
|
61
|
+
if (ad.errors.size > 0)
|
62
|
+
self.errors.add("[ad] \"#{ad.headline}\"", ad.errors.to_a)
|
63
|
+
return false
|
64
|
+
end
|
33
65
|
end
|
34
66
|
|
35
|
-
|
36
|
-
ad_group
|
67
|
+
true
|
37
68
|
end
|
69
|
+
|
70
|
+
def self.find(amount = :all, params = {})
|
71
|
+
params.symbolize_keys!
|
72
|
+
first_only = (amount.to_sym == :first)
|
38
73
|
|
39
|
-
|
40
|
-
#
|
41
|
-
def self.find(params = {})
|
42
|
-
ad_group_service = AdGroup.new
|
74
|
+
raise "No Campaign ID is required" unless params[:campaign_id]
|
43
75
|
|
44
|
-
|
45
|
-
|
76
|
+
predicates = [ :campaign_id, :id ].map do |param_name|
|
77
|
+
if params[param_name]
|
78
|
+
{:field => param_name.to_s.camelcase, :operator => 'EQUALS', :values => params[param_name] }
|
79
|
+
end
|
80
|
+
end.compact
|
46
81
|
|
47
82
|
selector = {
|
48
|
-
:fields => ['Id', 'Name'],
|
49
|
-
|
50
|
-
:predicates =>
|
51
|
-
:field => 'CampaignId', :operator => 'EQUALS', :values => campaign_id
|
52
|
-
}]
|
83
|
+
:fields => ['Id', 'Name', 'Status'],
|
84
|
+
:ordering => [{:field => 'Name', :sort_order => 'ASCENDING'}],
|
85
|
+
:predicates => predicates
|
53
86
|
}
|
54
87
|
|
55
|
-
response =
|
88
|
+
response = AdGroup.new.service.get(selector)
|
56
89
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
90
|
+
response = (response and response[:entries]) ? response[:entries] : []
|
91
|
+
|
92
|
+
#response.map! do |data|
|
93
|
+
# TextAd.new(data[:ad].merge(:ad_group_id => data[:ad_group_id], :status => data[:status]))
|
94
|
+
#end
|
95
|
+
|
96
|
+
first_only ? response.first : response
|
97
|
+
end
|
98
|
+
|
99
|
+
def find_keywords(first_only = false)
|
100
|
+
Keyword.find( (first_only ? :first : :all), :ad_group_id => self.id )
|
101
|
+
end
|
66
102
|
|
103
|
+
def find_ads(first_only = false)
|
104
|
+
Ad::TextAd.find( (first_only ? :first : :all), :ad_group_id => self.id )
|
67
105
|
end
|
68
106
|
|
69
107
|
end
|