facebook_ads 0.4 → 0.5
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +18 -5
- data/facebook_ads.gemspec +2 -2
- data/lib/facebook_ads.rb +10 -1
- data/lib/facebook_ads/ad.rb +11 -0
- data/lib/facebook_ads/ad_account.rb +44 -2
- data/lib/facebook_ads/ad_campaign.rb +12 -3
- data/lib/facebook_ads/ad_creative.rb +17 -5
- data/lib/facebook_ads/ad_set.rb +2 -2
- data/lib/facebook_ads/ad_targeting.rb +16 -5
- data/spec/facebook_ads/ad_account_spec.rb +20 -0
- data/spec/facebook_ads/ad_campaign_spec.rb +42 -2
- data/spec/facebook_ads/ad_targeting_spec.rb +20 -0
- data/spec/facebook_ads/facebook_ads_spec.rb +16 -5
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_ad_campaigns/lists_campaigns.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_ad_creatives/lists_creatives.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_ad_images/lists_images.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_ad_sets/lists_ad_sets.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_ads/lists_ads.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_all/lists_all_accounts.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_create_ad_campaign/creates_a_new_ad_campaign.yml +6 -6
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_create_ad_creative/creates_carousel_ad_creative.yml +7301 -0
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_create_ad_images/creates_an_image.yml +7 -7
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_find_by/finds_a_specific_account.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdAccount/_reach_estimate/estimates_the_reach_of_a_targeting_spec.yml +3 -3
- data/spec/fixtures/cassettes/FacebookAds_AdCampaign/_create_ad_set/creates_valid_ad_set.yml +114 -0
- data/spec/fixtures/cassettes/FacebookAds_AdCampaign/_destroy/{creates_a_new_ad_campaign.yml → sets_effective_status_to_deleted.yml} +9 -9
- data/spec/support/facebook_ads.rb +1 -0
- data/spec/support/vcr.rb +3 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a57756116ad7b5abac5c48ce3ac5d6f249f069e1
|
4
|
+
data.tar.gz: 5f4577a436b3e3b3099802a05eb4cfe1f2ec6280
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1bfa9b506e7321f4647f9ed61d7e1031a0f72543e13386ab21c1715edd098574f8c466394dba4e5a52338ef7f87330cc25052b3c7d8010970efa9a2a2db264b
|
7
|
+
data.tar.gz: 27f49b9e0bd2c6f09138a43f984b9b766b771320aa3ec155f933d9c67e3fd13e4d758c620eacf19e3a922ac9d1463dc801a19cc888fa881ee2ae393101304b68
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -36,10 +36,10 @@ FacebookAds.app_secret = '[YOUR_APP_SECRET]'
|
|
36
36
|
|
37
37
|
## API Version
|
38
38
|
|
39
|
-
This gem currently uses v2.
|
39
|
+
This gem currently uses v2.9 of the Marketing API (2.10 is released as of 7/18/2017). You can change the version as desired with the following:
|
40
40
|
|
41
41
|
```ruby
|
42
|
-
FacebookAds.base_uri = 'https://graph.facebook.com/v2.
|
42
|
+
FacebookAds.base_uri = 'https://graph.facebook.com/v2.10'
|
43
43
|
```
|
44
44
|
|
45
45
|
## Console
|
@@ -210,7 +210,7 @@ carousel_ad_creative = account.create_ad_creative({
|
|
210
210
|
call_to_action_type: 'SHOP_NOW',
|
211
211
|
multi_share_optimized: true,
|
212
212
|
multi_share_end_card: false
|
213
|
-
},
|
213
|
+
}, creative_type: 'carousel')
|
214
214
|
```
|
215
215
|
See FacebookAds::AdCreative::CALL_TO_ACTION_TYPES for a list of all call to action types.
|
216
216
|
|
@@ -224,8 +224,20 @@ image_ad_creative = account.create_ad_creative({
|
|
224
224
|
link_title: 'A link title.',
|
225
225
|
image_hash: ad_images.first.hash,
|
226
226
|
call_to_action_type: 'SHOP_NOW'
|
227
|
-
},
|
227
|
+
}, creative_type: 'image')
|
228
228
|
```
|
229
|
+
|
230
|
+
Create a single creative for a web link:
|
231
|
+
```ruby
|
232
|
+
image_ad_creative = account.create_ad_creative({
|
233
|
+
title: 'Test Link Title',
|
234
|
+
body: 'Link Description Text',
|
235
|
+
object_url: 'www.example.com/my-ad-link',
|
236
|
+
link_url: 'www.example.com/my-ad-link',
|
237
|
+
image_hash: ad_images.first.hash,
|
238
|
+
}, creative_type: 'link')
|
239
|
+
```
|
240
|
+
|
229
241
|
The options will be different depending on the thing being advertised (Android app, iOS app or website).
|
230
242
|
|
231
243
|
Find a creative by ID:
|
@@ -300,7 +312,8 @@ ad_set = campaign.create_ad_set(
|
|
300
312
|
optimization_goal: 'OFFSITE_CONVERSIONS',
|
301
313
|
daily_budget: 500, # This is in cents, so the daily budget here is $5.
|
302
314
|
billing_event: 'IMPRESSIONS',
|
303
|
-
status: 'PAUSED'
|
315
|
+
status: 'PAUSED',
|
316
|
+
is_autobid: true
|
304
317
|
)
|
305
318
|
```
|
306
319
|
See FacebookAds::AdSet::OPTIMIZATION_GOALS for a list of all optimization goals.
|
data/facebook_ads.gemspec
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
# To publish the next version:
|
4
4
|
# gem build facebook_ads.gemspec
|
5
|
-
# gem push facebook_ads-0.
|
5
|
+
# gem push facebook_ads-0.5.gem
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = 'facebook_ads'
|
8
|
-
s.version = '0.
|
8
|
+
s.version = '0.5'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.licenses = ['MIT']
|
11
11
|
s.authors = ['Chris Estreich']
|
data/lib/facebook_ads.rb
CHANGED
@@ -29,10 +29,19 @@ module FacebookAds
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.base_uri
|
32
|
-
@base_uri =
|
32
|
+
@base_uri = "https://graph.facebook.com/v#{api_version}" unless defined?(@base_uri)
|
33
33
|
@base_uri
|
34
34
|
end
|
35
35
|
|
36
|
+
def self.api_version=(api_version)
|
37
|
+
@api_version = api_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.api_version
|
41
|
+
@api_version = '2.9' unless defined?(@api_version)
|
42
|
+
@api_version
|
43
|
+
end
|
44
|
+
|
36
45
|
def self.access_token=(access_token)
|
37
46
|
@access_token = access_token
|
38
47
|
end
|
data/lib/facebook_ads/ad.rb
CHANGED
@@ -28,5 +28,16 @@ module FacebookAds
|
|
28
28
|
def ad_creative
|
29
29
|
@ad_creative ||= AdCreative.find(creative['id'])
|
30
30
|
end
|
31
|
+
|
32
|
+
# has_many ad_insights
|
33
|
+
|
34
|
+
def ad_insights(range: Date.today..Date.today, level: 'ad', time_increment: 1)
|
35
|
+
query = {
|
36
|
+
level: level,
|
37
|
+
time_increment: time_increment,
|
38
|
+
time_range: { 'since': range.first.to_s, 'until': range.last.to_s }
|
39
|
+
}
|
40
|
+
AdInsight.paginate("/#{id}/insights", query: query)
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
@@ -66,8 +66,19 @@ module FacebookAds
|
|
66
66
|
AdCreative.paginate("/#{id}/adcreatives", query: { limit: limit })
|
67
67
|
end
|
68
68
|
|
69
|
-
def create_ad_creative(creative, carousel:
|
70
|
-
|
69
|
+
def create_ad_creative(creative, creative_type: nil, carousel: false)
|
70
|
+
# Support old deprecated carousel param
|
71
|
+
return create_carousel_ad_creative(creative) if carousel
|
72
|
+
case creative_type
|
73
|
+
when 'carousel'
|
74
|
+
create_carousel_ad_creative(creative)
|
75
|
+
when 'link'
|
76
|
+
create_link_ad_creative(creative)
|
77
|
+
when 'image'
|
78
|
+
create_image_ad_creative(creative)
|
79
|
+
else
|
80
|
+
create_image_ad_creative(creative)
|
81
|
+
end
|
71
82
|
end
|
72
83
|
|
73
84
|
# has_many ad_sets
|
@@ -115,6 +126,25 @@ module FacebookAds
|
|
115
126
|
self.class.get("/#{id}/reachestimate", query: query, objectify: false)
|
116
127
|
end
|
117
128
|
|
129
|
+
def delivery_estimate(targeting:, optimization_goal:, currency: 'USD')
|
130
|
+
raise Exception, "Optimization goal must be one of: #{AdSet::OPTIMIZATION_GOALS.join(', ')}" unless AdSet::OPTIMIZATION_GOALS.include?(optimization_goal)
|
131
|
+
|
132
|
+
if targeting.is_a?(AdTargeting)
|
133
|
+
if targeting.validate!
|
134
|
+
targeting = targeting.to_hash
|
135
|
+
else
|
136
|
+
raise Exception, 'The provided targeting spec is not valid.'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
query = {
|
141
|
+
targeting_spec: targeting.to_json,
|
142
|
+
optimization_goal: optimization_goal,
|
143
|
+
currency: currency
|
144
|
+
}
|
145
|
+
self.class.get("/#{id}/delivery_estimate", query: query, objectify: false)
|
146
|
+
end
|
147
|
+
|
118
148
|
# has_many applications
|
119
149
|
|
120
150
|
def applications
|
@@ -179,6 +209,18 @@ module FacebookAds
|
|
179
209
|
AdCreative.find(result['id'])
|
180
210
|
end
|
181
211
|
|
212
|
+
def create_link_ad_creative(creative)
|
213
|
+
required = %i[name title body object_url link_url image_hash]
|
214
|
+
|
215
|
+
unless (keys = required - creative.keys).length.zero?
|
216
|
+
raise Exception, "Creative is missing the following: #{keys.join(', ')}"
|
217
|
+
end
|
218
|
+
|
219
|
+
query = AdCreative.link(creative)
|
220
|
+
result = AdCreative.post("/#{id}/adcreatives", query: query)
|
221
|
+
AdCreative.find(result['id'])
|
222
|
+
end
|
223
|
+
|
182
224
|
def download(url)
|
183
225
|
pathname = Pathname.new(url)
|
184
226
|
name = "#{pathname.dirname.basename}.jpg"
|
@@ -60,7 +60,7 @@ module FacebookAds
|
|
60
60
|
AdSet.paginate("/#{id}/adsets", query: { effective_status: effective_status, limit: limit })
|
61
61
|
end
|
62
62
|
|
63
|
-
def create_ad_set(name:, promoted_object
|
63
|
+
def create_ad_set(name:, promoted_object: {}, targeting:, daily_budget: nil, lifetime_budget: nil, end_time: nil, optimization_goal:, billing_event: 'IMPRESSIONS', status: 'ACTIVE', is_autobid: nil, bid_amount: nil)
|
64
64
|
raise Exception, "Optimization goal must be one of: #{AdSet::OPTIMIZATION_GOALS.join(', ')}" unless AdSet::OPTIMIZATION_GOALS.include?(optimization_goal)
|
65
65
|
raise Exception, "Billing event must be one of: #{AdSet::BILLING_EVENTS.join(', ')}" unless AdSet::BILLING_EVENTS.include?(billing_event)
|
66
66
|
|
@@ -77,12 +77,21 @@ module FacebookAds
|
|
77
77
|
targeting: targeting.to_json,
|
78
78
|
promoted_object: promoted_object.to_json,
|
79
79
|
optimization_goal: optimization_goal,
|
80
|
-
daily_budget: daily_budget,
|
81
80
|
billing_event: billing_event,
|
82
81
|
status: status,
|
83
82
|
is_autobid: is_autobid,
|
84
83
|
bid_amount: bid_amount
|
85
84
|
}
|
85
|
+
|
86
|
+
if daily_budget && lifetime_budget
|
87
|
+
raise Exception 'Only one budget may be set between daily_budget and life_budget'
|
88
|
+
elsif daily_budget
|
89
|
+
query[:daily_budget] = daily_budget
|
90
|
+
elsif lifetime_budget
|
91
|
+
query[:lifetime_budget] = lifetime_budget
|
92
|
+
query[:end_time] = end_time
|
93
|
+
end
|
94
|
+
|
86
95
|
result = AdSet.post("/act_#{account_id}/adsets", query: query)
|
87
96
|
AdSet.find(result['id'])
|
88
97
|
end
|
@@ -93,7 +102,7 @@ module FacebookAds
|
|
93
102
|
query = {
|
94
103
|
level: level,
|
95
104
|
time_increment: time_increment,
|
96
|
-
time_range: {
|
105
|
+
time_range: { since: range.first.to_s, until: range.last.to_s }
|
97
106
|
}
|
98
107
|
AdInsight.paginate("/#{id}/insights", query: query)
|
99
108
|
end
|
@@ -2,15 +2,17 @@ module FacebookAds
|
|
2
2
|
# Ad ad creative has many ad images and belongs to an ad account.
|
3
3
|
# https://developers.facebook.com/docs/marketing-api/reference/ad-creative
|
4
4
|
class AdCreative < Base
|
5
|
-
FIELDS = %w[id name object_story_id object_story_spec object_type thumbnail_url
|
6
|
-
CALL_TO_ACTION_TYPES = %w[SHOP_NOW INSTALL_MOBILE_APP USE_MOBILE_APP SIGN_UP DOWNLOAD BUY_NOW].freeze
|
5
|
+
FIELDS = %w[id name object_story_id object_story_spec object_type thumbnail_url].freeze
|
6
|
+
CALL_TO_ACTION_TYPES = %w[SHOP_NOW INSTALL_MOBILE_APP USE_MOBILE_APP SIGN_UP DOWNLOAD BUY_NOW NO_BUTTON].freeze
|
7
7
|
|
8
8
|
class << self
|
9
|
-
def photo(name:, page_id:, instagram_actor_id: nil, message:, link:, app_link: nil, link_title:, image_hash:, call_to_action_type:)
|
9
|
+
def photo(name:, page_id:, instagram_actor_id: nil, message:, link:, app_link: nil, link_title:, image_hash:, call_to_action_type:, link_description: nil)
|
10
10
|
object_story_spec = {
|
11
11
|
'page_id' => page_id, # 300664329976860
|
12
12
|
'instagram_actor_id' => instagram_actor_id, # 503391023081924
|
13
13
|
'link_data' => {
|
14
|
+
'name' => link_title,
|
15
|
+
'description' => link_description,
|
14
16
|
'link' => link, # https://tophatter.com/, https://itunes.apple.com/app/id619460348, http://play.google.com/store/apps/details?id=com.tophatter
|
15
17
|
'message' => message,
|
16
18
|
'image_hash' => image_hash,
|
@@ -19,8 +21,7 @@ module FacebookAds
|
|
19
21
|
'value' => {
|
20
22
|
# 'application' =>,
|
21
23
|
'link' => link,
|
22
|
-
'app_link' => app_link
|
23
|
-
'link_title' => link_title
|
24
|
+
'app_link' => app_link
|
24
25
|
}
|
25
26
|
}
|
26
27
|
}
|
@@ -89,6 +90,17 @@ module FacebookAds
|
|
89
90
|
product_set_id: product_set_id
|
90
91
|
}
|
91
92
|
end
|
93
|
+
|
94
|
+
def link(name:, title:, body:, object_url:, link_url:, image_hash:)
|
95
|
+
{
|
96
|
+
name: name,
|
97
|
+
title: title,
|
98
|
+
body: body,
|
99
|
+
object_url: object_url,
|
100
|
+
link_url: link_url,
|
101
|
+
image_hash: image_hash
|
102
|
+
}
|
103
|
+
end
|
92
104
|
end
|
93
105
|
end
|
94
106
|
end
|
data/lib/facebook_ads/ad_set.rb
CHANGED
@@ -72,8 +72,8 @@ module FacebookAds
|
|
72
72
|
Ad.paginate("/#{id}/ads", query: { effective_status: effective_status, limit: limit })
|
73
73
|
end
|
74
74
|
|
75
|
-
def create_ad(name:, creative_id:)
|
76
|
-
query = { name: name, adset_id: id, creative: { creative_id: creative_id }.to_json }
|
75
|
+
def create_ad(name:, creative_id:, status: 'PAUSED')
|
76
|
+
query = { name: name, adset_id: id, creative: { creative_id: creative_id }.to_json, status: status }
|
77
77
|
result = Ad.post("/act_#{account_id}/ads", query: query)
|
78
78
|
Ad.find(result['id'])
|
79
79
|
end
|
@@ -14,20 +14,30 @@ module FacebookAds
|
|
14
14
|
NOT_INSTALLED = 'not_installed'.freeze
|
15
15
|
APP_INSTALL_STATES = [INSTALLED, NOT_INSTALLED].freeze
|
16
16
|
|
17
|
-
attr_accessor :genders, :age_min, :age_max, :countries, :user_os, :user_device, :app_install_state
|
17
|
+
attr_accessor :genders, :age_min, :age_max, :countries, :user_os, :user_device, :app_install_state, :custom_locations, :income
|
18
18
|
|
19
19
|
def initialize
|
20
20
|
# self.genders = [WOMEN] # If nil, defaults to all genders.
|
21
21
|
# self.age_min = 18 # If nil, defaults to 18.
|
22
22
|
# self.age_max = 65 # If nil, defaults to 65+.
|
23
|
-
self.countries = ['US']
|
24
23
|
# self.user_os = [ANDROID_OS]
|
25
24
|
# self.user_device = ANDROID_DEVICES
|
26
25
|
# self.app_install_state = NOT_INSTALLED
|
26
|
+
self.income = [] # An a rray of objects with 'id' and optional 'name'
|
27
|
+
end
|
28
|
+
|
29
|
+
def geo_locations
|
30
|
+
if custom_locations
|
31
|
+
{ custom_locations: custom_locations }
|
32
|
+
elsif countries
|
33
|
+
{ countries: countries }
|
34
|
+
else
|
35
|
+
{ countries: ['US'] }
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
39
|
def validate!
|
30
|
-
{ gender: genders, countries: countries, user_os: user_os, user_device: user_device }.each_pair do |key, array|
|
40
|
+
{ gender: genders, countries: countries, user_os: user_os, user_device: user_device, custom_locations: custom_locations }.each_pair do |key, array|
|
31
41
|
if !array.nil? && !array.is_a?(Array)
|
32
42
|
raise Exception, "#{self.class.name}: #{key} must be an array"
|
33
43
|
end
|
@@ -49,10 +59,11 @@ module FacebookAds
|
|
49
59
|
genders: genders,
|
50
60
|
age_min: age_min,
|
51
61
|
age_max: age_max,
|
52
|
-
geo_locations:
|
62
|
+
geo_locations: geo_locations,
|
53
63
|
user_os: user_os,
|
54
64
|
user_device: user_device,
|
55
|
-
app_install_state: app_install_state
|
65
|
+
app_install_state: app_install_state,
|
66
|
+
income: income
|
56
67
|
}.reject { |_k, v| v.nil? }
|
57
68
|
end
|
58
69
|
end
|
@@ -65,6 +65,26 @@ describe FacebookAds::AdAccount do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
describe '.create_ad_creative' do
|
69
|
+
it 'creates carousel ad creative', :vcr do
|
70
|
+
ad_images = account.create_ad_images(%w[https://img0.etsystatic.com/108/1/13006112/il_570xN.1047856494_l2gp.jpg https://img1.etsystatic.com/143/0/13344107/il_570xN.1141249285_xciv.jpg])
|
71
|
+
carousel_ad_creative = account.create_ad_creative({
|
72
|
+
name: 'Test Carousel Creative',
|
73
|
+
page_id: '300664329976860', # Add your Facebook Page ID here.
|
74
|
+
link: 'http://play.google.com/store/apps/details?id=com.tophatter', # Add your Play Store ID here.
|
75
|
+
message: 'A message.',
|
76
|
+
assets: [
|
77
|
+
{ hash: ad_images[0].hash, title: 'Image #1 Title' },
|
78
|
+
{ hash: ad_images[1].hash, title: 'Image #2 Title' }
|
79
|
+
],
|
80
|
+
call_to_action_type: 'SHOP_NOW',
|
81
|
+
multi_share_optimized: true,
|
82
|
+
multi_share_end_card: false
|
83
|
+
}, creative_type: 'carousel')
|
84
|
+
expect(carousel_ad_creative.id).to eq('120330000008134415')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
68
88
|
describe '.ad_sets' do
|
69
89
|
it 'lists ad sets', :vcr do
|
70
90
|
ad_sets = account.ad_sets
|
@@ -2,14 +2,54 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
# FACEBOOK_ACCESS_TOKEN=... rspec spec/facebook_ads/ad_campaign_spec.rb
|
4
4
|
describe FacebookAds::AdCampaign do
|
5
|
+
let(:campaign) do
|
6
|
+
FacebookAds::AdCampaign.new(
|
7
|
+
id: '120330000008134915',
|
8
|
+
account_id: '10152335766987003',
|
9
|
+
buying_type: 'AUCTION',
|
10
|
+
can_use_spend_cap: true,
|
11
|
+
configured_status: 'PAUSED',
|
12
|
+
created_time: '2017-08-25T15:47:51-0700',
|
13
|
+
effective_status: 'PAUSED',
|
14
|
+
name: 'Test Campaign',
|
15
|
+
objective: 'CONVERSIONS',
|
16
|
+
start_time: '1969-12-31T15:59:59-0800',
|
17
|
+
updated_time: '2017-08-25T15:47:51-0700'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:targeting) do
|
22
|
+
targeting = FacebookAds::AdTargeting.new
|
23
|
+
targeting.genders = [FacebookAds::AdTargeting::WOMEN]
|
24
|
+
targeting.age_min = 29
|
25
|
+
targeting.age_max = 65
|
26
|
+
targeting.countries = ['US']
|
27
|
+
targeting.user_os = [FacebookAds::AdTargeting::ANDROID_OS]
|
28
|
+
targeting.user_device = FacebookAds::AdTargeting::ANDROID_DEVICES
|
29
|
+
targeting.app_install_state = FacebookAds::AdTargeting::NOT_INSTALLED
|
30
|
+
targeting
|
31
|
+
end
|
32
|
+
|
5
33
|
xdescribe '.ad_sets' do
|
6
34
|
end
|
7
35
|
|
8
|
-
|
36
|
+
describe '.create_ad_set' do
|
37
|
+
it 'creates_valid_ad_set', :vcr do
|
38
|
+
ad_set = campaign.create_ad_set(
|
39
|
+
name: 'Test Ad Set',
|
40
|
+
targeting: targeting,
|
41
|
+
optimization_goal: 'IMPRESSIONS',
|
42
|
+
daily_budget: 500, # This is in cents, so the daily budget here is $5.
|
43
|
+
billing_event: 'IMPRESSIONS',
|
44
|
+
status: 'PAUSED',
|
45
|
+
is_autobid: true
|
46
|
+
)
|
47
|
+
expect(ad_set.id).to eq('120330000008135715')
|
48
|
+
end
|
9
49
|
end
|
10
50
|
|
11
51
|
describe '.destroy' do
|
12
|
-
it '
|
52
|
+
it 'sets effective status to deleted', :vcr do
|
13
53
|
ad_campaign = FacebookAds::AdCampaign.find('6076262142242')
|
14
54
|
expect(ad_campaign.destroy).to be(true)
|
15
55
|
ad_campaign = FacebookAds::AdCampaign.find(ad_campaign.id)
|
@@ -1,4 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe FacebookAds::AdTargeting do
|
4
|
+
let(:targeting) { FacebookAds::AdTargeting.new }
|
5
|
+
|
6
|
+
describe '#geo_locations' do
|
7
|
+
let(:custom_locations) { [{ radius: 10, distance_unit: 'mile', address_string: '1601 Willow Road, Menlo Park, CA 94025' }] }
|
8
|
+
let(:countries) { ['JP'] }
|
9
|
+
|
10
|
+
it 'should return custom locations if specified' do
|
11
|
+
targeting.custom_locations = custom_locations
|
12
|
+
expect(targeting.geo_locations).to eq(custom_locations: custom_locations)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should return countries if specified' do
|
16
|
+
targeting.countries = countries
|
17
|
+
expect(targeting.geo_locations).to eq(countries: countries)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should default to US if nothing is specified' do
|
21
|
+
expect(targeting.geo_locations).to eq(countries: ['US'])
|
22
|
+
end
|
23
|
+
end
|
4
24
|
end
|