facebook_ads 0.1.5 → 0.1.7

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +23 -0
  4. data/.travis.yml +1 -1
  5. data/Gemfile +4 -5
  6. data/Gemfile.lock +34 -82
  7. data/{README.markdown → README.md} +43 -10
  8. data/Rakefile +6 -0
  9. data/bin/console +15 -1
  10. data/facebook_ads.gemspec +11 -13
  11. data/lib/facebook_ads.rb +11 -7
  12. data/lib/facebook_ads/ad.rb +6 -8
  13. data/lib/facebook_ads/ad_account.rb +65 -54
  14. data/lib/facebook_ads/ad_audience.rb +22 -0
  15. data/lib/facebook_ads/ad_campaign.rb +21 -17
  16. data/lib/facebook_ads/ad_creative.rb +7 -9
  17. data/lib/facebook_ads/ad_image.rb +3 -5
  18. data/lib/facebook_ads/ad_insight.rb +4 -5
  19. data/lib/facebook_ads/ad_product.rb +6 -0
  20. data/lib/facebook_ads/ad_product_catalog.rb +60 -0
  21. data/lib/facebook_ads/ad_product_feed.rb +6 -0
  22. data/lib/facebook_ads/ad_product_set.rb +6 -0
  23. data/lib/facebook_ads/ad_set.rb +9 -11
  24. data/lib/facebook_ads/ad_targeting.rb +19 -21
  25. data/lib/facebook_ads/base.rb +107 -62
  26. data/test/ad_account_test.rb +27 -0
  27. data/test/ad_campaign_test.rb +32 -0
  28. data/test/ad_creative_test.rb +7 -0
  29. data/test/ad_image_test.rb +27 -0
  30. data/test/ad_insight_test.rb +7 -0
  31. data/test/ad_product_catalog_test.rb +28 -0
  32. data/test/ad_product_feed_test.rb +7 -0
  33. data/test/ad_product_set_test.rb +7 -0
  34. data/test/ad_product_test.rb +22 -0
  35. data/test/ad_set_test.rb +7 -0
  36. data/test/ad_targeting_test.rb +7 -0
  37. data/test/ad_test.rb +7 -0
  38. data/test/facebook_ads_test.rb +8 -0
  39. data/test/test_helper.rb +64 -0
  40. data/test/vcr_cassettes/AdAccountTest-test_all.yml +70 -0
  41. data/test/vcr_cassettes/AdAccountTest-test_applications.yml +130 -0
  42. data/test/vcr_cassettes/AdAccountTest-test_find_by.yml +71 -0
  43. data/test/vcr_cassettes/AdCampaignTest-test_create.yml +295 -0
  44. data/test/vcr_cassettes/AdCampaignTest-test_list.yml +133 -0
  45. data/test/vcr_cassettes/AdImageTest-test_create.yml +2963 -0
  46. data/test/vcr_cassettes/AdImageTest-test_list.yml +137 -0
  47. data/test/vcr_cassettes/AdProductCatalogTest-test_all.yml +60 -0
  48. data/test/vcr_cassettes/AdProductCatalogTest-test_create.yml +256 -0
  49. data/test/vcr_cassettes/AdProductTest-test_list.yml +130 -0
  50. metadata +44 -49
  51. data/spec/ad_account_spec.rb +0 -78
  52. data/spec/ad_campaign_spec.rb +0 -13
  53. data/spec/ad_creative_spec.rb +0 -14
  54. data/spec/ad_image_spec.rb +0 -11
  55. data/spec/ad_insight_spec.rb +0 -11
  56. data/spec/ad_set_spec.rb +0 -13
  57. data/spec/ad_spec.rb +0 -13
  58. data/spec/ad_targeting_spec.rb +0 -4
  59. data/spec/spec_helper.rb +0 -15
  60. data/spec/support/fixtures.sh +0 -17
  61. data/spec/support/fixtures/6057330925170.json +0 -1
  62. data/spec/support/fixtures/6057810634370.json +0 -1
  63. data/spec/support/fixtures/6057810946970.json +0 -1
  64. data/spec/support/fixtures/6057824295570.json +0 -1
  65. data/spec/support/fixtures/act_861827983860489.json +0 -1
  66. data/spec/support/fixtures/act_861827983860489/adcreatives.json +0 -1
  67. data/spec/support/fixtures/act_861827983860489/adimages.json +0 -1
  68. data/spec/support/fixtures/act_861827983860489/ads.json +0 -1
  69. data/spec/support/fixtures/act_861827983860489/adsets.json +0 -1
  70. data/spec/support/fixtures/act_861827983860489/campaigns.json +0 -1
  71. data/spec/support/fixtures/me/adaccounts.json +0 -1
  72. data/spec/support/rack_facebook.rb +0 -22
data/lib/facebook_ads.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # External requires.
2
- require 'active_support/all'
3
- require 'httmultiparty'
2
+ require 'json'
3
+ require 'rest-client'
4
4
  require 'hashie'
5
+ require 'logger'
5
6
 
6
7
  # Internal requires.
7
8
  require 'facebook_ads/base'
@@ -9,7 +10,6 @@ Dir[File.expand_path('../facebook_ads/*.rb', __FILE__)].each { |f| require f }
9
10
 
10
11
  # The primary namespace for this gem.
11
12
  module FacebookAds
12
-
13
13
  def self.logger=(logger)
14
14
  @logger = logger
15
15
  end
@@ -28,10 +28,7 @@ module FacebookAds
28
28
  end
29
29
 
30
30
  def self.base_uri
31
- unless defined?(@base_uri)
32
- @base_uri = 'https://graph.facebook.com/v2.6'
33
- end
34
-
31
+ @base_uri = 'https://graph.facebook.com/v2.8' unless defined?(@base_uri)
35
32
  @base_uri
36
33
  end
37
34
 
@@ -43,4 +40,11 @@ module FacebookAds
43
40
  @access_token
44
41
  end
45
42
 
43
+ def self.business_id=(business_id)
44
+ @business_id = business_id
45
+ end
46
+
47
+ def self.business_id
48
+ @business_id
49
+ end
46
50
  end
@@ -2,33 +2,31 @@ module FacebookAds
2
2
  # An ad belongs to an ad set. It is created using an ad creative.
3
3
  # https://developers.facebook.com/docs/marketing-api/reference/adgroup
4
4
  class Ad < Base
5
-
6
- FIELDS = %w(id account_id campaign_id adset_id adlabels bid_amount bid_info bid_type configured_status conversion_specs created_time creative effective_status last_updated_by_app_id name tracking_specs updated_time ad_review_feedback)
7
- STATUSES = %w(ACTIVE PAUSED DELETED PENDING_REVIEW DISAPPROVED PREAPPROVED PENDING_BILLING_INFO CAMPAIGN_PAUSED ARCHIVED ADSET_PAUSED)
5
+ FIELDS = %w(id account_id campaign_id adset_id adlabels bid_amount bid_info bid_type configured_status conversion_specs created_time creative effective_status last_updated_by_app_id name tracking_specs updated_time ad_review_feedback).freeze
6
+ STATUSES = %w(ACTIVE PAUSED DELETED PENDING_REVIEW DISAPPROVED PREAPPROVED PENDING_BILLING_INFO CAMPAIGN_PAUSED ARCHIVED ADSET_PAUSED).freeze
8
7
 
9
8
  # belongs_to ad_account
10
9
 
11
10
  def ad_account
12
- @ad_set ||= FacebookAds::AdAccount.find(account_id)
11
+ @ad_set ||= AdAccount.find(account_id)
13
12
  end
14
13
 
15
14
  # belongs_to ad_campaign
16
15
 
17
16
  def ad_campaign
18
- @ad_set ||= FacebookAds::AdCampaign.find(campaign_id)
17
+ @ad_set ||= AdCampaign.find(campaign_id)
19
18
  end
20
19
 
21
20
  # belongs_to ad_set
22
21
 
23
22
  def ad_set
24
- @ad_set ||= FacebookAds::AdSet.find(adset_id)
23
+ @ad_set ||= AdSet.find(adset_id)
25
24
  end
26
25
 
27
26
  # belongs_to ad_creative
28
27
 
29
28
  def ad_creative
30
- @ad_creative ||= FacebookAds::AdCreative.find(creative['id'])
29
+ @ad_creative ||= AdCreative.find(creative['id'])
31
30
  end
32
-
33
31
  end
34
32
  end
@@ -2,8 +2,7 @@ module FacebookAds
2
2
  # An ad account has many ad campaigns, ad images, and ad creatives.
3
3
  # https://developers.facebook.com/docs/marketing-api/reference/ad-account
4
4
  class AdAccount < Base
5
-
6
- FIELDS = %w(id account_id account_status age created_time currency name last_used_time)
5
+ FIELDS = %w(id account_id account_status age created_time currency name).freeze
7
6
 
8
7
  class << self
9
8
  def all
@@ -11,9 +10,9 @@ module FacebookAds
11
10
  end
12
11
 
13
12
  def find_by(conditions)
14
- all.detect do |ad_account|
13
+ all.detect do |object|
15
14
  conditions.all? do |key, value|
16
- ad_account.send(key) == value
15
+ object.send(key) == value
17
16
  end
18
17
  end
19
18
  end
@@ -22,90 +21,63 @@ module FacebookAds
22
21
  # has_many campaigns
23
22
 
24
23
  def ad_campaigns(effective_status: ['ACTIVE'], limit: 100)
25
- FacebookAds::AdCampaign.paginate("/#{id}/campaigns", query: { effective_status: effective_status, limit: limit })
24
+ AdCampaign.paginate("/#{id}/campaigns", query: { effective_status: effective_status, limit: limit })
26
25
  end
27
26
 
28
27
  def create_ad_campaign(name:, objective:, status: 'ACTIVE')
29
- raise Exception, "Objective must be one of: #{FacebookAds::AdCampaign::OBJECTIVES.to_sentence}" unless FacebookAds::AdCampaign::OBJECTIVES.include?(objective)
30
- raise Exception, "Status must be one of: #{FacebookAds::AdCampaign::STATUSES.to_sentence}" unless FacebookAds::AdCampaign::STATUSES.include?(status)
31
- campaign = FacebookAds::AdCampaign.post("/#{id}/campaigns", query: { name: name, objective: objective, status: status }, objectify: true)
32
- FacebookAds::AdCampaign.find(campaign.id)
28
+ raise Exception, "Objective must be one of: #{AdCampaign::OBJECTIVES.to_sentence}" unless AdCampaign::OBJECTIVES.include?(objective)
29
+ raise Exception, "Status must be one of: #{AdCampaign::STATUSES.to_sentence}" unless AdCampaign::STATUSES.include?(status)
30
+ campaign = AdCampaign.post("/#{id}/campaigns", query: { name: name, objective: objective, status: status }, objectify: true)
31
+ AdCampaign.find(campaign.id)
33
32
  end
34
33
 
35
34
  # has_many ad_images
36
35
 
37
36
  def ad_images(hashes: nil, limit: 100)
38
- if hashes.present?
39
- FacebookAds::AdImage.get("/#{id}/adimages", query: { hashes: hashes }, objectify: true)
37
+ if !hashes.nil?
38
+ AdImage.get("/#{id}/adimages", query: { hashes: hashes }, objectify: true)
40
39
  else
41
- FacebookAds::AdImage.paginate("/#{id}/adimages", query: { limit: limit })
40
+ AdImage.paginate("/#{id}/adimages", query: { limit: limit })
42
41
  end
43
42
  end
44
43
 
45
44
  def create_ad_images(urls)
46
- files = urls.collect do |url|
47
- pathname = Pathname.new(url)
48
- name = "#{pathname.dirname.basename}.jpg"
49
- data = HTTParty.get(url).body
50
- file = File.open("/tmp/#{name}", 'w') # Assume *nix-based system.
51
- file.binmode
52
- file.write(data)
53
- file.close
54
- [name, File.open(file.path)]
45
+ files = urls.map do |url|
46
+ name, path = download(url)
47
+ [name, File.open(path)]
55
48
  end.to_h
56
49
 
57
- response = FacebookAds::AdImage.post("/#{id}/adimages", query: files, objectify: false)
50
+ response = AdImage.post("/#{id}/adimages", query: files, objectify: false)
58
51
  files.values.each { |file| File.delete(file.path) }
59
-
60
- if response['images'].present?
61
- hashes = response['images'].map { |key, hash| hash['hash'] }
62
- ad_images(hashes: hashes)
63
- else
64
- []
65
- end
52
+ !response['images'].nil? ? ad_images(hashes: response['images'].map { |_key, hash| hash['hash'] }) : []
66
53
  end
67
54
 
68
55
  # has_many ad_creatives
69
56
 
70
57
  def ad_creatives(limit: 100)
71
- FacebookAds::AdCreative.paginate("/#{id}/adcreatives", query: { limit: limit })
58
+ AdCreative.paginate("/#{id}/adcreatives", query: { limit: limit })
72
59
  end
73
60
 
74
61
  def create_ad_creative(creative, carousel: true)
75
- optional = %i(instagram_actor_id)
76
-
77
- required = if carousel
78
- %i(name page_id link message assets call_to_action_type multi_share_optimized multi_share_end_card)
79
- else
80
- %i(name page_id message link link_title image_hash call_to_action_type)
81
- end
82
-
83
- if (keys = required - creative.keys).present?
84
- raise Exception, "Creative is missing the following: #{keys.to_sentence}"
85
- end
86
-
87
- raise Exception, "Creative call_to_action_type must be one of: #{FacebookAds::AdCreative::CALL_TO_ACTION_TYPES.to_sentence}" unless FacebookAds::AdCreative::CALL_TO_ACTION_TYPES.include?(creative[:call_to_action_type])
88
-
89
- query = if carousel
90
- FacebookAds::AdCreative.carousel(creative)
91
- else
92
- FacebookAds::AdCreative.photo(creative)
93
- end
94
-
95
- creative = FacebookAds::AdCreative.post("/#{id}/adcreatives", query: query, objectify: true) # Returns a FacebookAds::AdCreative instance.
96
- FacebookAds::AdCreative.find(creative.id)
62
+ carousel ? create_carousel_ad_creative(creative) : create_image_ad_creative(creative)
97
63
  end
98
64
 
99
65
  # has_many ad_sets
100
66
 
101
67
  def ad_sets(effective_status: ['ACTIVE'], limit: 100)
102
- FacebookAds::AdSet.paginate("/#{id}/adsets", query: { effective_status: effective_status, limit: limit })
68
+ AdSet.paginate("/#{id}/adsets", query: { effective_status: effective_status, limit: limit })
103
69
  end
104
70
 
105
71
  # has_many ads
106
72
 
107
73
  def ads(effective_status: ['ACTIVE'], limit: 100)
108
- FacebookAds::Ad.paginate("/#{id}/ads", query: { effective_status: effective_status, limit: limit })
74
+ Ad.paginate("/#{id}/ads", query: { effective_status: effective_status, limit: limit })
75
+ end
76
+
77
+ # has_many ad_audiences
78
+
79
+ def ad_audiences
80
+ AdAudience.paginate("/#{id}/customaudiences")
109
81
  end
110
82
 
111
83
  # has_many ad_insights
@@ -122,5 +94,44 @@ module FacebookAds
122
94
  self.class.get("/#{id}/advertisable_applications", objectify: false)
123
95
  end
124
96
 
97
+ private
98
+
99
+ def create_carousel_ad_creative(creative)
100
+ required = %i(name page_id link message assets call_to_action_type multi_share_optimized multi_share_end_card)
101
+
102
+ unless (keys = required - creative.keys).length.zero?
103
+ raise Exception, "Creative is missing the following: #{keys.to_sentence}"
104
+ end
105
+
106
+ raise Exception, "Creative call_to_action_type must be one of: #{AdCreative::CALL_TO_ACTION_TYPES.to_sentence}" unless AdCreative::CALL_TO_ACTION_TYPES.include?(creative[:call_to_action_type])
107
+ query = AdCreative.carousel(creative)
108
+ creative = AdCreative.post("/#{id}/adcreatives", query: query, objectify: true) # Returns an AdCreative instance.
109
+ AdCreative.find(creative.id)
110
+ end
111
+
112
+ def create_image_ad_creative(creative)
113
+ required = %i(name page_id message link link_title image_hash call_to_action_type)
114
+
115
+ unless (keys = required - creative.keys).length.zero?
116
+ raise Exception, "Creative is missing the following: #{keys.to_sentence}"
117
+ end
118
+
119
+ raise Exception, "Creative call_to_action_type must be one of: #{AdCreative::CALL_TO_ACTION_TYPES.to_sentence}" unless AdCreative::CALL_TO_ACTION_TYPES.include?(creative[:call_to_action_type])
120
+ query = AdCreative.photo(creative)
121
+ creative = AdCreative.post("/#{id}/adcreatives", query: query, objectify: true) # Returns an AdCreative instance.
122
+ AdCreative.find(creative.id)
123
+ end
124
+
125
+ def download(url)
126
+ pathname = Pathname.new(url)
127
+ name = "#{pathname.dirname.basename}.jpg"
128
+ # @FIXME: Need to handle exception here.
129
+ data = RestClient.get(url).body
130
+ file = File.open("/tmp/#{name}", 'w') # Assume *nix-based system.
131
+ file.binmode
132
+ file.write(data)
133
+ file.close
134
+ [name, file.path]
135
+ end
125
136
  end
126
137
  end
@@ -0,0 +1,22 @@
1
+ module FacebookAds
2
+ # https://developers.facebook.com/docs/marketing-api/reference/custom-audience
3
+ class AdAudience < Base
4
+ FIELDS = %w(id account_id subtype name description approximate_count data_source delivery_status external_event_source lookalike_audience_ids lookalike_spec operation_status opt_out_link permission_for_actions pixel_id retention_days rule time_content_updated time_created time_updated).freeze
5
+
6
+ # belongs_to ad_account
7
+
8
+ def ad_account
9
+ @ad_account ||= AdAccount.find("act_#{account_id}")
10
+ end
11
+
12
+ # actions
13
+
14
+ def share(account_id)
15
+ query = {
16
+ share_with_object_id: account_id,
17
+ share_with_object_type: 'Account'
18
+ }
19
+ AdAccount.post("/#{id}/share_with_objects", query: query, objectify: false)
20
+ end
21
+ end
22
+ end
@@ -2,53 +2,57 @@ module FacebookAds
2
2
  # An ad campaign has many ad sets and belongs to an ad account.
3
3
  # https://developers.facebook.com/docs/marketing-api/reference/ad-campaign-group
4
4
  class AdCampaign < Base
5
-
6
- FIELDS = %w(id account_id buying_type can_use_spend_cap configured_status created_time effective_status name objective start_time stop_time updated_time spend_cap)
7
- STATUSES = %w(ACTIVE PAUSED DELETED PENDING_REVIEW DISAPPROVED PREAPPROVED PENDING_BILLING_INFO CAMPAIGN_PAUSED ARCHIVED ADSET_PAUSED)
8
- OBJECTIVES = %w(CONVERSIONS MOBILE_APP_INSTALLS) # %w(BRAND_AWARENESS CANVAS_APP_ENGAGEMENT CANVAS_APP_INSTALLS CONVERSIONS EVENT_RESPONSES EXTERNAL LEAD_GENERATION LINK_CLICKS LOCAL_AWARENESS MOBILE_APP_ENGAGEMENT MOBILE_APP_INSTALLS OFFER_CLAIMS PAGE_LIKES POST_ENGAGEMENT PRODUCT_CATALOG_SALES REACH VIDEO_VIEWS)
5
+ FIELDS = %w(id account_id buying_type can_use_spend_cap configured_status created_time effective_status name objective start_time stop_time updated_time spend_cap).freeze
6
+ STATUSES = %w(ACTIVE PAUSED DELETED PENDING_REVIEW DISAPPROVED PREAPPROVED PENDING_BILLING_INFO CAMPAIGN_PAUSED ARCHIVED ADSET_PAUSED).freeze
7
+ OBJECTIVES = %w(CONVERSIONS MOBILE_APP_INSTALLS).freeze
9
8
 
10
9
  # belongs_to ad_account
11
10
 
12
11
  def ad_account
13
- @ad_account ||= FacebookAds::AdAccount.find("act_#{account_id}")
12
+ @ad_account ||= AdAccount.find("act_#{account_id}")
14
13
  end
15
14
 
16
15
  # has_many ad_sets
17
16
 
18
17
  def ad_sets(effective_status: ['ACTIVE'], limit: 100)
19
- FacebookAds::AdSet.paginate("/#{id}/adsets", query: { effective_status: effective_status, limit: limit })
18
+ AdSet.paginate("/#{id}/adsets", query: { effective_status: effective_status, limit: limit })
20
19
  end
21
20
 
22
21
  def create_ad_set(name:, promoted_object:, targeting:, daily_budget:, optimization_goal:, billing_event: 'IMPRESSIONS', status: 'ACTIVE', is_autobid: true)
23
- raise Exception, "Optimization goal must be one of: #{FacebookAds::AdSet::OPTIMIZATION_GOALS.to_sentence}" unless FacebookAds::AdSet::OPTIMIZATION_GOALS.include?(optimization_goal)
24
- raise Exception, "Billing event must be one of: #{FacebookAds::AdSet::BILLING_EVENTS.to_sentence}" unless FacebookAds::AdSet::BILLING_EVENTS.include?(billing_event)
22
+ raise Exception, "Optimization goal must be one of: #{AdSet::OPTIMIZATION_GOALS.to_sentence}" unless AdSet::OPTIMIZATION_GOALS.include?(optimization_goal)
23
+ raise Exception, "Billing event must be one of: #{AdSet::BILLING_EVENTS.to_sentence}" unless AdSet::BILLING_EVENTS.include?(billing_event)
25
24
 
26
- targeting.validate! # Will raise if invalid.
25
+ if targeting.is_a?(Hash)
26
+ # NOP
27
+ else
28
+ targeting.validate! # Will raise if invalid.
29
+ targeting = targeting.to_hash
30
+ end
27
31
 
28
- ad_set = FacebookAds::AdSet.post("/act_#{account_id}/adsets", query: { # Returns a FacebookAds::AdSet instance.
32
+ query = {
29
33
  campaign_id: id,
30
34
  name: name,
31
- targeting: targeting.to_hash.to_json,
35
+ targeting: targeting.to_json,
32
36
  promoted_object: promoted_object.to_json,
33
37
  optimization_goal: optimization_goal,
34
38
  daily_budget: daily_budget,
35
39
  billing_event: billing_event,
36
40
  status: status,
37
41
  is_autobid: is_autobid
38
- }, objectify: true)
39
-
40
- FacebookAds::AdSet.find(ad_set.id)
42
+ }
43
+ ad_set = AdSet.post("/act_#{account_id}/adsets", query: query, objectify: true) # Returns an AdSet instance.
44
+ AdSet.find(ad_set.id)
41
45
  end
42
46
 
43
47
  # has_many ad_insights
44
48
 
45
49
  def ad_insights(range: Date.today..Date.today, level: 'ad', time_increment: 1)
46
- FacebookAds::AdInsight.paginate("/#{id}/insights", query: {
50
+ query = {
47
51
  level: level,
48
52
  time_increment: time_increment,
49
53
  time_range: { 'since': range.first.to_s, 'until': range.last.to_s }
50
- })
54
+ }
55
+ AdInsight.paginate("/#{id}/insights", query: query)
51
56
  end
52
-
53
57
  end
54
58
  end
@@ -2,12 +2,10 @@ 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
-
6
- FIELDS = %w(id name object_story_id object_story_spec object_type thumbnail_url run_status)
7
- CALL_TO_ACTION_TYPES = %w(SHOP_NOW INSTALL_MOBILE_APP USE_MOBILE_APP SIGN_UP DOWNLOAD BUY_NOW) # %w(OPEN_LINK LIKE_PAGE SHOP_NOW PLAY_GAME INSTALL_APP USE_APP INSTALL_MOBILE_APP USE_MOBILE_APP BOOK_TRAVEL LISTEN_MUSIC WATCH_VIDEO LEARN_MORE SIGN_UP DOWNLOAD WATCH_MORE NO_BUTTON CALL_NOW BUY_NOW GET_OFFER GET_OFFER_VIEW GET_DIRECTIONS MESSAGE_PAGE SUBSCRIBE SELL_NOW DONATE_NOW GET_QUOTE CONTACT_US RECORD_NOW VOTE_NOW OPEN_MOVIES)
5
+ FIELDS = %w(id name object_story_id object_story_spec object_type thumbnail_url run_status).freeze
6
+ CALL_TO_ACTION_TYPES = %w(SHOP_NOW INSTALL_MOBILE_APP USE_MOBILE_APP SIGN_UP DOWNLOAD BUY_NOW).freeze
8
7
 
9
8
  class << self
10
-
11
9
  def photo(name:, page_id:, instagram_actor_id: nil, message:, link:, link_title:, image_hash:, call_to_action_type:)
12
10
  object_story_spec = {
13
11
  'page_id' => page_id, # 300664329976860
@@ -26,13 +24,14 @@ module FacebookAds
26
24
  }
27
25
  }
28
26
  }
27
+
29
28
  {
30
29
  name: name,
31
30
  object_story_spec: object_story_spec.to_json
32
31
  }
33
32
  end
34
33
 
35
- # https://developers.facebook.com/docs/marketing-api/guides/carousel-ads/v2.6
34
+ # https://developers.facebook.com/docs/marketing-api/guides/videoads
36
35
  def carousel(name:, page_id:, instagram_actor_id: nil, link:, message:, assets:, call_to_action_type:, multi_share_optimized:, multi_share_end_card:)
37
36
  object_story_spec = {
38
37
  'page_id' => page_id, # 300664329976860
@@ -41,7 +40,7 @@ module FacebookAds
41
40
  'link' => link, # https://tophatter.com/, https://itunes.apple.com/app/id619460348, http://play.google.com/store/apps/details?id=com.tophatter
42
41
  'message' => message,
43
42
  'call_to_action' => { 'type' => call_to_action_type },
44
- 'child_attachments' => assets.collect { |asset|
43
+ 'child_attachments' => assets.collect do |asset|
45
44
  {
46
45
  'link' => link,
47
46
  'image_hash' => asset[:hash],
@@ -49,18 +48,17 @@ module FacebookAds
49
48
  # 'description' => asset[:title],
50
49
  'call_to_action' => { 'type' => call_to_action_type } # Redundant?
51
50
  }
52
- },
51
+ end,
53
52
  'multi_share_optimized' => multi_share_optimized,
54
53
  'multi_share_end_card' => multi_share_end_card
55
54
  }
56
55
  }
56
+
57
57
  {
58
58
  name: name,
59
59
  object_story_spec: object_story_spec.to_json
60
60
  }
61
61
  end
62
-
63
62
  end
64
-
65
63
  end
66
64
  end
@@ -3,11 +3,10 @@ module FacebookAds
3
3
  # An image will always produce the same hash.
4
4
  # https://developers.facebook.com/docs/marketing-api/reference/ad-image
5
5
  class AdImage < Base
6
-
7
- FIELDS = %w(id hash account_id name permalink_url original_width original_height)
6
+ FIELDS = %w(id hash account_id name permalink_url original_width original_height).freeze
8
7
 
9
8
  class << self
10
- def find(id)
9
+ def find(_id)
11
10
  raise Exception, 'NOT IMPLEMENTED'
12
11
  end
13
12
  end
@@ -16,13 +15,12 @@ module FacebookAds
16
15
  self[:hash]
17
16
  end
18
17
 
19
- def update(data)
18
+ def update(_data)
20
19
  raise Exception, 'NOT IMPLEMENTED'
21
20
  end
22
21
 
23
22
  def destroy
24
23
  super(path: "/act_#{account_id}/adimages", query: { hash: hash })
25
24
  end
26
-
27
25
  end
28
26
  end