twitter-ads 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CONTRIBUTING.md +77 -0
  5. data/LICENSE +22 -0
  6. data/README.md +111 -0
  7. data/Rakefile +86 -0
  8. data/bin/twitter-ads +42 -0
  9. data/lib/twitter-ads.rb +54 -0
  10. data/lib/twitter-ads/account.rb +229 -0
  11. data/lib/twitter-ads/audiences/tailored_audience.rb +177 -0
  12. data/lib/twitter-ads/campaign/app_list.rb +42 -0
  13. data/lib/twitter-ads/campaign/campaign.rb +40 -0
  14. data/lib/twitter-ads/campaign/funding_instrument.rb +33 -0
  15. data/lib/twitter-ads/campaign/line_item.rb +91 -0
  16. data/lib/twitter-ads/campaign/promotable_user.rb +28 -0
  17. data/lib/twitter-ads/campaign/targeting_criteria.rb +77 -0
  18. data/lib/twitter-ads/campaign/tweet.rb +83 -0
  19. data/lib/twitter-ads/client.rb +92 -0
  20. data/lib/twitter-ads/creative/app_download_card.rb +44 -0
  21. data/lib/twitter-ads/creative/image_app_download_card.rb +44 -0
  22. data/lib/twitter-ads/creative/image_conversation_card.rb +44 -0
  23. data/lib/twitter-ads/creative/lead_gen_card.rb +46 -0
  24. data/lib/twitter-ads/creative/promoted_account.rb +38 -0
  25. data/lib/twitter-ads/creative/promoted_tweet.rb +87 -0
  26. data/lib/twitter-ads/creative/video.rb +43 -0
  27. data/lib/twitter-ads/creative/video_app_download_card.rb +47 -0
  28. data/lib/twitter-ads/creative/video_conversation_card.rb +46 -0
  29. data/lib/twitter-ads/creative/website_card.rb +48 -0
  30. data/lib/twitter-ads/cursor.rb +127 -0
  31. data/lib/twitter-ads/enum.rb +135 -0
  32. data/lib/twitter-ads/error.rb +93 -0
  33. data/lib/twitter-ads/http/request.rb +127 -0
  34. data/lib/twitter-ads/http/response.rb +74 -0
  35. data/lib/twitter-ads/http/ton_upload.rb +140 -0
  36. data/lib/twitter-ads/legacy.rb +7 -0
  37. data/lib/twitter-ads/resources/analytics.rb +90 -0
  38. data/lib/twitter-ads/resources/dsl.rb +108 -0
  39. data/lib/twitter-ads/resources/persistence.rb +43 -0
  40. data/lib/twitter-ads/resources/resource.rb +92 -0
  41. data/lib/twitter-ads/targeting/reach_estimate.rb +69 -0
  42. data/lib/twitter-ads/utils.rb +76 -0
  43. data/lib/twitter-ads/version.rb +6 -0
  44. data/spec/fixtures/accounts_all.json +65 -0
  45. data/spec/fixtures/accounts_features.json +18 -0
  46. data/spec/fixtures/accounts_load.json +19 -0
  47. data/spec/fixtures/app_lists_all.json +22 -0
  48. data/spec/fixtures/app_lists_load.json +31 -0
  49. data/spec/fixtures/campaigns_all.json +208 -0
  50. data/spec/fixtures/campaigns_load.json +27 -0
  51. data/spec/fixtures/funding_instruments_all.json +74 -0
  52. data/spec/fixtures/funding_instruments_load.json +28 -0
  53. data/spec/fixtures/line_items_all.json +292 -0
  54. data/spec/fixtures/line_items_load.json +36 -0
  55. data/spec/fixtures/placements.json +35 -0
  56. data/spec/fixtures/promotable_users_all.json +57 -0
  57. data/spec/fixtures/promotable_users_load.json +18 -0
  58. data/spec/fixtures/promoted_tweets_all.json +212 -0
  59. data/spec/fixtures/promoted_tweets_load.json +19 -0
  60. data/spec/fixtures/reach_estimate.json +19 -0
  61. data/spec/fixtures/tailored_audiences_all.json +67 -0
  62. data/spec/fixtures/tailored_audiences_load.json +29 -0
  63. data/spec/fixtures/tweet_preview.json +24 -0
  64. data/spec/fixtures/videos_all.json +50 -0
  65. data/spec/fixtures/videos_load.json +22 -0
  66. data/spec/quality_spec.rb +15 -0
  67. data/spec/shared/properties.rb +20 -0
  68. data/spec/spec_helper.rb +61 -0
  69. data/spec/support/helpers.rb +42 -0
  70. data/spec/twitter-ads/account_spec.rb +315 -0
  71. data/spec/twitter-ads/audiences/tailored_audience_spec.rb +45 -0
  72. data/spec/twitter-ads/campaign/app_list_spec.rb +108 -0
  73. data/spec/twitter-ads/campaign/line_item_spec.rb +95 -0
  74. data/spec/twitter-ads/campaign/reach_estimate_spec.rb +98 -0
  75. data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +39 -0
  76. data/spec/twitter-ads/campaign/tweet_spec.rb +83 -0
  77. data/spec/twitter-ads/client_spec.rb +115 -0
  78. data/spec/twitter-ads/creative/app_download_card_spec.rb +44 -0
  79. data/spec/twitter-ads/creative/image_app_download_card_spec.rb +43 -0
  80. data/spec/twitter-ads/creative/image_conversation_card_spec.rb +40 -0
  81. data/spec/twitter-ads/creative/lead_gen_card_spec.rb +46 -0
  82. data/spec/twitter-ads/creative/promoted_account_spec.rb +30 -0
  83. data/spec/twitter-ads/creative/promoted_tweet_spec.rb +46 -0
  84. data/spec/twitter-ads/creative/video_app_download_card_spec.rb +42 -0
  85. data/spec/twitter-ads/creative/video_conversation_card_spec.rb +52 -0
  86. data/spec/twitter-ads/creative/video_legacy_spec.rb +43 -0
  87. data/spec/twitter-ads/creative/video_spec.rb +43 -0
  88. data/spec/twitter-ads/creative/website_card_spec.rb +37 -0
  89. data/spec/twitter-ads/cursor_spec.rb +67 -0
  90. data/spec/twitter-ads/placements_spec.rb +36 -0
  91. data/spec/twitter-ads/utils_spec.rb +101 -0
  92. data/twitter-ads.gemspec +37 -0
  93. metadata +247 -0
  94. metadata.gz.sig +0 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::TailoredAudience do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ # check model properties
25
+ subject { described_class.new(account) }
26
+
27
+ read = %w(
28
+ id
29
+ created_at
30
+ updated_at
31
+ deleted
32
+ audience_size
33
+ audience_type
34
+ metadata
35
+ partner_source
36
+ reasons_not_targetable
37
+ targetable
38
+ targetable_types
39
+ )
40
+
41
+ write = %w(name list_type)
42
+
43
+ include_examples 'object property check', read, write
44
+
45
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::AppList do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ # check model properties
25
+ subject { described_class.new(account) }
26
+ read = %w(id apps name)
27
+ write = []
28
+ include_examples 'object property check', read, write
29
+
30
+ describe '#create' do
31
+
32
+ let(:app_list) { TwitterAds::AppList.new(account) }
33
+ let!(:resource) { "#{ADS_API}/accounts/#{account.id}/app_lists" }
34
+ let!(:rel_path) { "/0/accounts/#{account.id}/app_lists" }
35
+
36
+ before(:each) do
37
+ stub_fixture(:post, :app_lists_load, /#{resource}\?.*/)
38
+ end
39
+
40
+ it 'creates post request with single app ID and name' do
41
+ name = 'Some test app list'
42
+ app = 'io.fabric.samples.cannonball'
43
+
44
+ params = { app_store_identifiers: app, name: name }
45
+ args = [account.client, :post, rel_path, params: params]
46
+
47
+ expect(Request).to receive(:new).with(*args).and_call_original
48
+ app_list.create(name, app)
49
+ end
50
+
51
+ it 'creates post request with multiple app IDs and name' do
52
+ name = 'Some test app list'
53
+ apps = ['com.supercell.clashofclans', 'com.functionx.viggle',
54
+ 'io.fabric.samples.cannonball', 'com.hoteltonight.android.prod']
55
+
56
+ params = { app_store_identifiers: apps.join(','), name: name }
57
+ args = [account.client, :post, rel_path, params: params]
58
+
59
+ expect(Request).to receive(:new).with(*args).and_call_original
60
+ app_list.create(name, apps)
61
+ end
62
+
63
+ end
64
+
65
+ describe '#apps' do
66
+
67
+ let(:app_list) { TwitterAds::AppList.new(account) }
68
+ let!(:resource) { "#{ADS_API}/accounts/#{account.id}/app_lists/abc2" }
69
+
70
+ context 'when @apps is nil' do
71
+
72
+ before(:each) do
73
+ app_list.instance_variable_set('@apps', nil)
74
+ app_list.instance_variable_set('@id', 'abc2')
75
+ stub_fixture(:get, :app_lists_load, /#{resource}/)
76
+ end
77
+
78
+ it 'reloads the object from the server' do
79
+ expect(app_list).to receive(:reload!).and_call_original
80
+ app_list.apps
81
+ expect(app_list.instance_variable_get('@apps')).not_to be_nil
82
+ end
83
+ end
84
+
85
+ context 'when @apps is not nil' do
86
+
87
+ let(:apps) {
88
+ [
89
+ {
90
+ 'app_store_identifier' => 'io.fabric.samples.cannonball',
91
+ 'os_type' => 'Android'
92
+ }
93
+ ]
94
+ }
95
+
96
+ before(:each) do
97
+ app_list.instance_variable_set('@apps', apps)
98
+ app_list.instance_variable_set('@id', 'abc2')
99
+ end
100
+
101
+ it 'does not reload the object from the server' do
102
+ expect(app_list).not_to receive(:reload!)
103
+ app_list.apps
104
+ expect(app_list.apps).to eq(apps)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ include TwitterAds::Enum
7
+
8
+ describe TwitterAds::LineItem do
9
+
10
+ before(:each) do
11
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
12
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
13
+ end
14
+
15
+ let(:client) do
16
+ Client.new(
17
+ Faker::Lorem.characters(15),
18
+ Faker::Lorem.characters(40),
19
+ "123456-#{Faker::Lorem.characters(40)}",
20
+ Faker::Lorem.characters(40)
21
+ )
22
+ end
23
+
24
+ let(:account) { client.accounts.first }
25
+
26
+ # check model properties
27
+ subject { described_class.new(account) }
28
+ read = %w(id created_at updated_at deleted)
29
+ write = %w(
30
+ name
31
+ campaign_id
32
+ advertiser_domain
33
+ categories
34
+ charge_by
35
+ include_sentiment
36
+ objective
37
+ paused
38
+ primary_web_event_tag
39
+ product_type
40
+ placements
41
+ bid_unit
42
+ automatically_select_bid
43
+ bid_amount_local_micro
44
+ total_budget_amount_local_micro
45
+ )
46
+ include_examples 'object property check', read, write
47
+
48
+ describe '#placements' do
49
+
50
+ before(:each) do
51
+ url = Addressable::Template.new "#{ADS_API}/line_items/placements{?product_type}"
52
+ stub_fixture(:get, :placements, url)
53
+ end
54
+
55
+ let(:product_type) { Product::PROMOTED_TWEETS }
56
+
57
+ it 'successfully fetches valid placement / product type combinations' do
58
+ expect(TwitterAds::Utils).not_to receive(:deprecated)
59
+ result = silence { described_class.placements(client, product_type) }
60
+ expect(result.size).not_to be_nil
61
+ expect(result).to all(be_a(Array))
62
+ end
63
+
64
+ it 'allows product_type to be an optional method argument' do
65
+ expect(TwitterAds::Utils).not_to receive(:deprecated)
66
+ result = silence { described_class.placements(client) }
67
+ expect(result.size).not_to be_nil
68
+ expect(result).to all(be_a(Array))
69
+ end
70
+
71
+ end
72
+
73
+ describe '#objective=' do
74
+
75
+ context 'when using TwitterAds::Objective::CUSTOM' do
76
+
77
+ it 'raises a warning message' do
78
+ expect(TwitterAds::Utils).to receive(:deprecated).with('TwitterAds::Objective::CUSTOM')
79
+ subject.objective = TwitterAds::Objective::CUSTOM
80
+ end
81
+
82
+ end
83
+
84
+ context 'when using any object other than TwitterAds::Objective::CUSTOM' do
85
+
86
+ it 'does not raise a warning message' do
87
+ expect(TwitterAds::Utils).not_to receive(:deprecated).with(any_args)
88
+ subject.objective = TwitterAds::Objective::VIDEO_VIEWS
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::ReachEstimate do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ describe '#fetch' do
25
+
26
+ let!(:resource) { "#{ADS_API}/accounts/#{account.id}/reach_estimate" }
27
+ let!(:rel_path) { "/0/accounts/#{account.id}/reach_estimate" }
28
+
29
+ before(:each) do
30
+ stub_fixture(:get, :reach_estimate, /#{resource}\?.*/)
31
+ end
32
+
33
+ it 'creates proper get request with no optional parameters specified' do
34
+ expected = { product_type: 'PROMOTED_TWEETS', objective: 'WEBSITE_CLICKS',
35
+ user_id: 12, bid_type: 'AUTO' }
36
+ args = [account.client, :get, rel_path, params: expected]
37
+
38
+ expect(Request).to receive(:new).with(*args).and_call_original
39
+ TwitterAds::ReachEstimate.fetch(account, 'PROMOTED_TWEETS', 'WEBSITE_CLICKS', 12)
40
+ end
41
+
42
+ it 'creates proper get request when optional paremeters are specified' do
43
+
44
+ expected = { product_type: 'PROMOTED_TWEETS', objective: 'WEBSITE_CLICKS',
45
+ user_id: 12, similar_to_followers_of_user: '12', gender: '2', bid_type: 'AUTO' }
46
+ args = [account.client, :get, rel_path, params: expected]
47
+
48
+ expect(Request).to receive(:new).with(*args).and_call_original
49
+ TwitterAds::ReachEstimate.fetch(
50
+ account, 'PROMOTED_TWEETS', 'WEBSITE_CLICKS', 12,
51
+ similar_to_followers_of_user: '12', gender: '2')
52
+
53
+ end
54
+
55
+ context 'without bid_type specified' do
56
+
57
+ it 'defaults bid_type to AUTO for backward compatibility' do
58
+ expected = { product_type: 'PROMOTED_TWEETS', objective: 'WEBSITE_CLICKS',
59
+ user_id: 12, bid_type: 'AUTO' }
60
+ args = [account.client, :get, rel_path, params: expected]
61
+
62
+ expect(Request).to receive(:new).with(*args).and_call_original
63
+ TwitterAds::ReachEstimate.fetch(account, 'PROMOTED_TWEETS', 'WEBSITE_CLICKS', 12)
64
+ end
65
+
66
+ end
67
+
68
+ context 'with bid_type specified' do
69
+
70
+ it 'does not default bid_type to AUTO' do
71
+ expected = { product_type: 'PROMOTED_TWEETS', objective: 'WEBSITE_CLICKS',
72
+ user_id: 12, bid_type: 'MAX' }
73
+ args = [account.client, :get, rel_path, params: expected]
74
+
75
+ expect(Request).to receive(:new).with(*args).and_call_original
76
+ TwitterAds::ReachEstimate.fetch(
77
+ account, 'PROMOTED_TWEETS', 'WEBSITE_CLICKS', 12, bid_type: 'MAX')
78
+ end
79
+
80
+ end
81
+
82
+ context 'with bid_amount_local_micro specified' do
83
+
84
+ it 'does not default bid_type to AUTO' do
85
+ expected = { product_type: 'PROMOTED_TWEETS', objective: 'WEBSITE_CLICKS',
86
+ user_id: 12, bid_amount_local_micro: 1500000 }
87
+ args = [account.client, :get, rel_path, params: expected]
88
+
89
+ expect(Request).to receive(:new).with(*args).and_call_original
90
+ TwitterAds::ReachEstimate.fetch(
91
+ account, 'PROMOTED_TWEETS', 'WEBSITE_CLICKS', 12, bid_amount_local_micro: 1500000)
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::TargetingCriteria do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ # check model properties
25
+ subject { described_class.new(account) }
26
+
27
+ read = %w(id name localized_name created_at updated_at deleted)
28
+
29
+ write = %w(
30
+ line_item_id
31
+ targeting_type
32
+ targeting_value
33
+ tailored_audience_expansion
34
+ tailored_audience_type
35
+ )
36
+
37
+ include_examples 'object property check', read, write
38
+
39
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::Tweet do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ describe '#tweet_preview' do
25
+
26
+ let!(:resource_collection) { "#{ADS_API}/accounts/#{account.id}/tweet/preview" }
27
+
28
+ before(:each) do
29
+ stub_fixture(:get, :tweet_preview, /#{resource_collection}.*/)
30
+ end
31
+
32
+ context 'with an existing tweet id' do
33
+
34
+ it 'successfully returns a preview of the specified tweet' do
35
+ params = { id: 634798319504617472 }
36
+ result = subject.preview(account, params)
37
+ expect(result.size).not_to be_nil
38
+ expect(result).to all(include(:platform, :preview))
39
+ end
40
+
41
+ end
42
+
43
+ context 'when previewing a new tweet' do
44
+
45
+ it 'url encodes the status content' do
46
+ params = { status: 'Hello World!', card_id: '19v69' }
47
+ expect(URI).to receive(:escape).at_least(:once).and_call_original
48
+ result = subject.preview(account, params)
49
+ expect(result.size).not_to be_nil
50
+ expect(result).to all(include(:platform, :preview))
51
+ end
52
+
53
+ it 'allows a single value for the media_ids param' do
54
+ resource = "/0/accounts/#{account.id}/tweet/preview"
55
+ expected = { status: 'Hello%20World!', media_ids: 634458428836962304 }
56
+
57
+ expect(TwitterAds::Request).to receive(:new).with(
58
+ account.client, :get, resource, params: expected).and_call_original
59
+
60
+ params = { status: 'Hello World!', media_ids: 634458428836962304 }
61
+ result = subject.preview(account, params)
62
+ expect(result.size).not_to be_nil
63
+ expect(result).to all(include(:platform, :preview))
64
+ end
65
+
66
+ it 'allows an array of values for the media_ids param' do
67
+ resource = "/0/accounts/#{account.id}/tweet/preview"
68
+ expected = { status: 'Hello%20World!', media_ids: '634458428836962304,634458428836962305' }
69
+
70
+ expect(TwitterAds::Request).to receive(:new).with(
71
+ account.client, :get, resource, params: expected).and_call_original
72
+
73
+ params = { status: 'Hello World!', media_ids: [634458428836962304, 634458428836962305] }
74
+ result = subject.preview(account, params)
75
+ expect(result.size).not_to be_nil
76
+ expect(result).to all(include(:platform, :preview))
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end