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,177 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class TailoredAudience
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ attr_reader :account
11
+
12
+ property :id, read_only: true
13
+ property :created_at, type: :time, read_only: true
14
+ property :updated_at, type: :time, read_only: true
15
+ property :deleted, type: :bool, read_only: true
16
+
17
+ property :name
18
+ property :list_type
19
+
20
+ property :audience_size, read_only: true
21
+ property :audience_type, read_only: true
22
+ property :metadata, read_only: true
23
+ property :partner_source, read_only: true
24
+ property :reasons_not_targetable, read_only: true
25
+ property :targetable, type: :bool, read_only: true
26
+ property :targetable_types, read_only: true
27
+
28
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/tailored_audiences'.freeze # @api private
29
+ RESOURCE = '/0/accounts/%{account_id}/tailored_audiences/%{id}'.freeze # @api private
30
+ RESOURCE_UPDATE = '/0/accounts/%{account_id}/tailored_audience_changes'.freeze # @api private
31
+ GLOBAL_OPT_OUT =
32
+ '/0/accounts/%{account_id}/tailored_audiences/global_opt_out'.freeze # @api private
33
+
34
+ LIST_TYPES = %w(
35
+ EMAIL
36
+ DEVICE_ID
37
+ TWITTER_ID
38
+ HANDLE
39
+ PHONE_NUMBER
40
+ ).freeze
41
+
42
+ OPERATIONS = %w(
43
+ ADD
44
+ REMOVE
45
+ REPLACE
46
+ ).freeze
47
+
48
+ def initialize(account)
49
+ @account = account
50
+ self
51
+ end
52
+
53
+ class << self
54
+
55
+ # Creates a new tailored audience.
56
+ #
57
+ # @example
58
+ # audience = TailoredAudience.create(account, '/path/to/file', 'my list', 'EMAIL')
59
+ #
60
+ # @param account [Account] The account object instance.
61
+ # @param file_path [String] The path to the file to be uploaded.
62
+ # @param name [String] The tailored audience name.
63
+ # @param list_type [String] The tailored audience list type.
64
+ #
65
+ # @since 0.3.0
66
+ #
67
+ # @return [TailoredAudience] The newly created tailored audience instance.
68
+ def create(account, file_path, name, list_type)
69
+ upload = TwitterAds::TONUpload.new(account.client, file_path)
70
+
71
+ audience = new(account)
72
+ audience.send(:create_audience, name, list_type)
73
+
74
+ begin
75
+ audience.send(:update_audience, audience, upload.perform, list_type, 'ADD')
76
+ audience.reload!
77
+ rescue TwitterAds::ClientError => e
78
+ audience.delete!
79
+ raise e
80
+ end
81
+ end
82
+
83
+ # Updates the global opt-out list for the specified advertiser account.
84
+ #
85
+ # @example
86
+ # TailoredAudience.opt_out(account, '/path/to/file', 'EMAIL')
87
+ #
88
+ # @param account [Account] The account object instance.
89
+ # @param file_path [String] The path to the file to be uploaded.
90
+ # @param list_type [String] The tailored audience list type.
91
+ #
92
+ # @since 0.3.0
93
+ #
94
+ # @return [Boolean] The result of the opt-out update.
95
+ def opt_out(account, file_path, list_type)
96
+ upload = TwitterAds::TONUpload.new(account.client, file_path)
97
+ params = { input_file_path: upload.perform, list_type: list_type }
98
+ resource = GLOBAL_OPT_OUT % { account_id: account.id }
99
+ Request.new(account.client, :put, resource, params: params).perform
100
+ true
101
+ end
102
+
103
+ end
104
+
105
+ # Updates the current tailored audience instance.
106
+ #
107
+ # @example
108
+ # audience = account.tailored_audiences('xyz')
109
+ # audience.update('/path/to/file', 'EMAIL', 'REPLACE')
110
+ #
111
+ # @param file_path [String] The path to the file to be uploaded.
112
+ # @param list_type [String] The tailored audience list type.
113
+ # @param operation [String] The update operation type (Default: 'ADD').
114
+ #
115
+ # @since 0.3.0
116
+ #
117
+ # @return [TailoredAudience] [description]
118
+ def update(file_path, list_type, operation = 'ADD')
119
+ upload = TwitterAds::TONUpload.new(account.client, file_path)
120
+ update_audience(self, upload.perform, list_type, operation)
121
+ reload!
122
+ end
123
+
124
+ # Deletes the current tailored audience instance.
125
+ #
126
+ # @example
127
+ # audience.delete!
128
+ #
129
+ # Note: calls to this method are destructive and irreverisble.
130
+ #
131
+ # @since 0.3.0
132
+ #
133
+ # @return [self] Returns the tailored audience instance refreshed from the API.
134
+ def delete!
135
+ resource = RESOURCE % { account_id: account.id, id: id }
136
+ response = Request.new(account.client, :delete, resource).perform
137
+ from_response(response.body[:data])
138
+ end
139
+
140
+ # Returns the status of all changes for the current tailored audience instance.
141
+ #
142
+ # @example
143
+ # audience.status
144
+ #
145
+ # @since 0.3.0
146
+ #
147
+ # @return [Hash] Returns a hash object representing the tailored audience status.
148
+ def status
149
+ return nil unless id
150
+ resource = RESOURCE_UPDATE % { account_id: account.id }
151
+ request = Request.new(account.client, :get, resource, params: to_params)
152
+ Cursor.new(nil, request).to_a.select { |change| change[:tailored_audience_id] == id }
153
+ end
154
+
155
+ private
156
+
157
+ def create_audience(name, list_type)
158
+ params = { name: name, list_type: list_type }
159
+ resource = RESOURCE_COLLECTION % { account_id: account.id }
160
+ response = Request.new(account.client, :post, resource, params: params).perform
161
+ from_response(response.body[:data])
162
+ end
163
+
164
+ def update_audience(audience, location, list_type, operation)
165
+ params = {
166
+ tailored_audience_id: audience.id,
167
+ input_file_path: location,
168
+ list_type: list_type,
169
+ operation: operation
170
+ }
171
+
172
+ resource = RESOURCE_UPDATE % { account_id: audience.account.id }
173
+ Request.new(audience.account.client, :post, resource, params: params).perform
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class AppList
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ attr_reader :account
11
+
12
+ property :id, read_only: true
13
+ property :apps, read_only: true
14
+ property :name, read_only: true
15
+
16
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/app_lists'.freeze # @api private
17
+ RESOURCE = '/0/accounts/%{account_id}/app_lists/%{id}'.freeze # @api private
18
+
19
+ def initialize(account)
20
+ @account = account
21
+ self
22
+ end
23
+
24
+ # Creates a new App List
25
+ #
26
+ # @param name [String] The name for the app list to be created.
27
+ # @param ids [String] String or String Array of app IDs.
28
+ #
29
+ # @return [self] Returns the instance refreshed from the API
30
+ def create(name, *ids)
31
+ resource = self.class::RESOURCE_COLLECTION % { account_id: account.id }
32
+ params = to_params.merge!(app_store_identifiers: ids.join(','), name: name)
33
+ response = Request.new(account.client, :post, resource, params: params).perform
34
+ from_response(response.body[:data])
35
+ end
36
+
37
+ def apps
38
+ reload! if @id && !@apps
39
+ @apps
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class Campaign
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+ include TwitterAds::Persistence
10
+
11
+ attr_reader :account
12
+
13
+ property :id, read_only: true
14
+ property :reasons_not_servable, read_only: true
15
+ property :servable, read_only: true
16
+ property :deleted, type: :bool, read_only: true
17
+ property :created_at, type: :time, read_only: true
18
+ property :updated_at, type: :time, read_only: true
19
+
20
+ property :name
21
+ property :funding_instrument_id
22
+ property :end_time, type: :time
23
+ property :start_time, type: :time
24
+ property :paused, type: :bool
25
+ property :currency
26
+ property :standard_delivery
27
+ property :daily_budget_amount_local_micro
28
+ property :total_budget_amount_local_micro
29
+
30
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/campaigns'.freeze # @api private
31
+ RESOURCE_STATS = '/0/stats/accounts/%{account_id}/campaigns'.freeze # @api private
32
+ RESOURCE = '/0/accounts/%{account_id}/campaigns/%{id}'.freeze # @api private
33
+
34
+ def initialize(account)
35
+ @account = account
36
+ self
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class FundingInstrument
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ attr_reader :account
11
+
12
+ property :id, read_only: true
13
+ property :name, read_only: true
14
+ property :cancelled, read_only: true
15
+ property :credit_limit_local_micro, read_only: true
16
+ property :currency, read_only: true
17
+ property :description, read_only: true
18
+ property :funded_amount_local_micro, read_only: true
19
+ property :type, read_only: true
20
+ property :created_at, type: :time, read_only: true
21
+ property :updated_at, type: :time, read_only: true
22
+ property :deleted, type: :bool, read_only: true
23
+
24
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/funding_instruments'.freeze # @api private
25
+ RESOURCE = '/0/accounts/%{account_id}/funding_instruments/%{id}'.freeze # @api private
26
+
27
+ def initialize(account)
28
+ @account = account
29
+ self
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class LineItem
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+ include TwitterAds::Persistence
10
+ include TwitterAds::Analytics
11
+
12
+ attr_reader :account
13
+
14
+ property :id, read_only: true
15
+ property :deleted, type: :bool, read_only: true
16
+ property :created_at, type: :time, read_only: true
17
+ property :updated_at, type: :time, read_only: true
18
+
19
+ property :name
20
+ property :campaign_id
21
+ property :advertiser_domain
22
+ property :categories
23
+ property :charge_by
24
+ property :include_sentiment
25
+ property :objective
26
+ property :optimization
27
+ property :paused, type: :bool
28
+ property :primary_web_event_tag
29
+ property :product_type
30
+ property :placements
31
+ property :bid_unit
32
+ property :automatically_select_bid
33
+ property :bid_amount_local_micro
34
+ property :total_budget_amount_local_micro
35
+
36
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/line_items'.freeze # @api private
37
+ RESOURCE_STATS = '/0/stats/accounts/%{account_id}/line_items'.freeze # @api private
38
+ RESOURCE = '/0/accounts/%{account_id}/line_items/%{id}'.freeze # @api private
39
+
40
+ def initialize(account)
41
+ @account = account
42
+ self
43
+ end
44
+
45
+ # Overload for CUSTOM objective deprecation warning.
46
+ # @private
47
+ def objective=(value)
48
+ if value == TwitterAds::Objective::CUSTOM
49
+ TwitterAds::Utils.deprecated('TwitterAds::Objective::CUSTOM')
50
+ end
51
+ @objective = value
52
+ end
53
+
54
+ class << self
55
+
56
+ # Helper method to return a list a valid placement combinations by Product.
57
+ #
58
+ # @example
59
+ # LineItem.placements(Product::PROMOTED_TWEETS)
60
+ #
61
+ # @param product_type [Product] The enum value for the Product type being targeted.
62
+ #
63
+ # @return [Array] An array of valid placement combinations.
64
+ #
65
+ # @since 0.3.2
66
+ # @see https://dev.twitter.com/ads/reference/get/line_items/placements
67
+ def placements(client, product_type = nil)
68
+ resource = '/0/line_items/placements'
69
+ params = { product_type: product_type } if product_type
70
+ response = TwitterAds::Request.new(client, :get, resource, params: params).perform
71
+ response.body[:data][0][:placements]
72
+ end
73
+
74
+ end
75
+
76
+ # Returns a collection of targeting criteria available to the current line item.
77
+ #
78
+ # @param id [String] The TargetingCriteria ID value.
79
+ # @param opts [Hash] A Hash of extended options.
80
+ # @option opts [Boolean] :with_deleted Indicates if deleted items should be included.
81
+ # @option opts [String] :sort_by The object param to sort the API response by.
82
+ #
83
+ # @since 0.3.1
84
+ #
85
+ # @return A Cursor or object instance.
86
+ def targeting_criteria(id = nil, opts = {})
87
+ id ? TargetingCriteria.load(account, id, opts) : TargetingCriteria.all(account, @id, opts)
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class PromotableUser
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ attr_reader :account
11
+
12
+ property :id, read_only: true
13
+ property :promotable_user_type, read_only: true
14
+ property :user_id, read_only: true
15
+ property :created_at, type: :time, read_only: true
16
+ property :updated_at, type: :time, read_only: true
17
+ property :deleted, type: :bool, read_only: true
18
+
19
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/promotable_users'.freeze # @api private
20
+ RESOURCE = '/0/accounts/%{account_id}/promotable_users/%{id}'.freeze # @api private
21
+
22
+ def initialize(account)
23
+ @account = account
24
+ self
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ class TargetingCriteria
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Persistence
9
+ include TwitterAds::Resource::InstanceMethods
10
+
11
+ attr_reader :account
12
+
13
+ property :id, read_only: true
14
+ property :name, read_only: true
15
+ property :localized_name, read_only: true
16
+ property :created_at, type: :time, read_only: true
17
+ property :updated_at, type: :time, read_only: true
18
+ property :deleted, type: :bool, read_only: true
19
+
20
+ property :line_item_id
21
+ property :targeting_type
22
+ property :targeting_value
23
+ property :tailored_audience_expansion, type: :bool
24
+ property :tailored_audience_type
25
+
26
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/targeting_criteria'.freeze # @api private
27
+ RESOURCE = '/0/accounts/%{account_id}/targeting_criteria/%{id}'.freeze # @api private
28
+
29
+ def initialize(account)
30
+ @account = account
31
+ self
32
+ end
33
+
34
+ class << self
35
+
36
+ # Returns a Cursor instance for a given resource.
37
+ #
38
+ # @param account [Account] The Account object instance.
39
+ # @param line_item_id [String] The line item ID string.
40
+ # @param opts [Hash] An optional Hash of extended options.
41
+ # @option opts [Boolean] :with_deleted Indicates if deleted items should be included.
42
+ # @option opts [String] :sort_by The object param to sort the API response by.
43
+ #
44
+ # @return [Cursor] A Cusor object ready to iterate through the API response.
45
+ #
46
+ # @since 0.3.1
47
+ # @see Cursor
48
+ # @see https://dev.twitter.com/ads/basics/sorting Sorting
49
+ def all(account, line_item_id, opts = {})
50
+ params = { line_item_id: line_item_id }.merge!(opts)
51
+ resource = RESOURCE_COLLECTION % { account_id: account.id }
52
+ request = Request.new(account.client, :get, resource, params: params)
53
+ Cursor.new(self, request, init_with: [account])
54
+ end
55
+
56
+ # Returns an object instance for a given resource.
57
+ #
58
+ # @param account [Account] The Account object instance.
59
+ # @param id [String] The ID of the specific object to be loaded.
60
+ # @param opts [Hash] An optional Hash of extended options.
61
+ # @option opts [Boolean] :with_deleted Indicates if deleted items should be included.
62
+ # @option opts [String] :sort_by The object param to sort the API response by.
63
+ #
64
+ # @return [self] The object instance for the specified resource.
65
+ #
66
+ # @since 0.3.1
67
+ def load(account, id, opts = {})
68
+ params = { with_deleted: true }.merge!(opts)
69
+ resource = RESOURCE % { account_id: account.id, id: id }
70
+ response = Request.new(account.client, :get, resource, params: params).perform
71
+ new(account).from_response(response.body[:data])
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end