zuck 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.rvmrc +1 -0
  2. data/.travis.yml +7 -0
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.markdown +4 -0
  5. data/Gemfile +35 -0
  6. data/Gemfile.lock +110 -0
  7. data/Guardfile.dist +45 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.markdown +138 -0
  10. data/Rakefile +39 -0
  11. data/VERSION +1 -0
  12. data/console +26 -0
  13. data/lib/zuck/facebook/ad_account.rb +40 -0
  14. data/lib/zuck/facebook/ad_campaign.rb +24 -0
  15. data/lib/zuck/facebook/ad_creative.rb +30 -0
  16. data/lib/zuck/facebook/ad_group.rb +39 -0
  17. data/lib/zuck/facebook/targeting_spec.rb +200 -0
  18. data/lib/zuck/fb_object/dsl.rb +110 -0
  19. data/lib/zuck/fb_object/error.rb +8 -0
  20. data/lib/zuck/fb_object/hash_delegator.rb +111 -0
  21. data/lib/zuck/fb_object/helpers.rb +57 -0
  22. data/lib/zuck/fb_object/read.rb +147 -0
  23. data/lib/zuck/fb_object/read_only.rb +0 -0
  24. data/lib/zuck/fb_object/write.rb +75 -0
  25. data/lib/zuck/fb_object.rb +53 -0
  26. data/lib/zuck/koala/koala_methods.rb +27 -0
  27. data/lib/zuck.rb +9 -0
  28. data/spec/fixtures/a_single_account.yml +75 -0
  29. data/spec/fixtures/a_single_campaign.yml +48 -0
  30. data/spec/fixtures/create_ad_campaign.yml +49 -0
  31. data/spec/fixtures/create_ad_group.yml +47 -0
  32. data/spec/fixtures/delete_ad_group.yml +50 -0
  33. data/spec/fixtures/find_a_single_campaign_and_update_it.yml +247 -0
  34. data/spec/fixtures/list_of_ad_accounts.yml +75 -0
  35. data/spec/fixtures/list_of_ad_campaigns.yml +76 -0
  36. data/spec/fixtures/list_of_ad_creatives.yml +51 -0
  37. data/spec/fixtures/list_of_ad_groups.yml +49 -0
  38. data/spec/fixtures/list_of_all_ad_creatives_of_account.yml +86 -0
  39. data/spec/fixtures/reach_for_invalid_keyword.yml +95 -0
  40. data/spec/fixtures/reach_for_valid_keywords.yml +93 -0
  41. data/spec/fixtures/reach_for_valid_keywords_male_young.yml +93 -0
  42. data/spec/lib/zuck/facebook/ad_account_spec.rb +26 -0
  43. data/spec/lib/zuck/facebook/ad_campaign_spec.rb +4 -0
  44. data/spec/lib/zuck/facebook/targeting_spec_spec.rb +174 -0
  45. data/spec/lib/zuck/fb_object/helpers_spec.rb +67 -0
  46. data/spec/lib/zuck/koala/koala_methods_spec.rb +30 -0
  47. data/spec/lib/zuck/util/hash_delegator_spec.rb +54 -0
  48. data/spec/lib/zuck_spec.rb +165 -0
  49. data/spec/spec_helper.rb +47 -0
  50. data/spec/vcr_setup.rb +15 -0
  51. data/zuck.gemspec +141 -0
  52. metadata +389 -0
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.9.3@zuck
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ script: bundle exec rspec spec
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --hide-void-return
2
+ --no-output
3
+ --markup-provider=redcarpet
4
+ --markup=markdown
@@ -0,0 +1,4 @@
1
+ 0.0.4
2
+ -----
3
+
4
+ - integrated targeting spec to fetch reach estimates
data/Gemfile ADDED
@@ -0,0 +1,35 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rvm'
4
+ gem 'bundler'
5
+ gem 'koala', '>=1.5'
6
+ gem 'activesupport'
7
+
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "jeweler", "~> 1.8.4"
12
+ gem "simplecov", ">= 0", :require => false
13
+ end
14
+
15
+ group :development, :test do
16
+ gem 'webmock'
17
+ gem 'rspec'
18
+ gem 'vcr'
19
+ end
20
+
21
+ platform :ruby do
22
+ group :development do
23
+ gem 'guard'
24
+ gem 'guard-rspec'
25
+ gem 'guard-bundler'
26
+ gem 'guard-yard'
27
+ gem 'growl'
28
+ gem 'redcarpet' # Markdown for yard
29
+ gem 'rb-fsevent'
30
+ end
31
+ end
32
+
33
+ # platform :jruby do
34
+ # # gem 'jruby-openssl'
35
+ # end
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.9)
5
+ i18n (~> 0.6)
6
+ multi_json (~> 1.0)
7
+ addressable (2.3.2)
8
+ bourne (1.1.2)
9
+ mocha (= 0.10.5)
10
+ coderay (1.0.8)
11
+ crack (0.3.1)
12
+ diff-lcs (1.1.3)
13
+ faraday (0.8.4)
14
+ multipart-post (~> 1.1)
15
+ git (1.2.5)
16
+ growl (1.0.3)
17
+ guard (1.6.1)
18
+ listen (>= 0.6.0)
19
+ lumberjack (>= 1.0.2)
20
+ pry (>= 0.9.10)
21
+ thor (>= 0.14.6)
22
+ guard-bundler (1.0.0)
23
+ bundler (~> 1.0)
24
+ guard (~> 1.1)
25
+ guard-rspec (2.3.3)
26
+ guard (>= 1.1)
27
+ rspec (~> 2.11)
28
+ guard-yard (2.0.1)
29
+ guard (>= 1.1.0)
30
+ yard (>= 0.7.0)
31
+ i18n (0.6.1)
32
+ jeweler (1.8.4)
33
+ bundler (~> 1.0)
34
+ git (>= 1.2.5)
35
+ rake
36
+ rdoc
37
+ json (1.7.5)
38
+ json (1.7.5-java)
39
+ koala (1.6.0)
40
+ addressable (~> 2.2)
41
+ faraday (~> 0.8)
42
+ multi_json (~> 1.3)
43
+ listen (0.6.0)
44
+ lumberjack (1.0.2)
45
+ metaclass (0.0.1)
46
+ method_source (0.8.1)
47
+ mocha (0.10.5)
48
+ metaclass (~> 0.0.1)
49
+ multi_json (1.5.0)
50
+ multipart-post (1.1.5)
51
+ pry (0.9.10)
52
+ coderay (~> 1.0.5)
53
+ method_source (~> 0.8)
54
+ slop (~> 3.3.1)
55
+ rake (10.0.3)
56
+ rb-fsevent (0.9.1)
57
+ rdoc (3.12)
58
+ json (~> 1.4)
59
+ redcarpet (2.2.2)
60
+ rspec (2.12.0)
61
+ rspec-core (~> 2.12.0)
62
+ rspec-expectations (~> 2.12.0)
63
+ rspec-mocks (~> 2.12.0)
64
+ rspec-core (2.12.2)
65
+ rspec-expectations (2.12.1)
66
+ diff-lcs (~> 1.1.3)
67
+ rspec-mocks (2.12.1)
68
+ rvm (1.11.3.5)
69
+ shoulda (3.3.2)
70
+ shoulda-context (~> 1.0.1)
71
+ shoulda-matchers (~> 1.4.1)
72
+ shoulda-context (1.0.2)
73
+ shoulda-matchers (1.4.2)
74
+ activesupport (>= 3.0.0)
75
+ bourne (~> 1.1.2)
76
+ simplecov (0.7.1)
77
+ multi_json (~> 1.0)
78
+ simplecov-html (~> 0.7.1)
79
+ simplecov-html (0.7.1)
80
+ slop (3.3.3)
81
+ thor (0.16.0)
82
+ vcr (2.3.0)
83
+ webmock (1.9.0)
84
+ addressable (>= 2.2.7)
85
+ crack (>= 0.1.7)
86
+ yard (0.8.3)
87
+
88
+ PLATFORMS
89
+ java
90
+ ruby
91
+
92
+ DEPENDENCIES
93
+ activesupport
94
+ bundler
95
+ growl
96
+ guard
97
+ guard-bundler
98
+ guard-rspec
99
+ guard-yard
100
+ jeweler (~> 1.8.4)
101
+ koala (>= 1.5)
102
+ rb-fsevent
103
+ rdoc (~> 3.12)
104
+ redcarpet
105
+ rspec
106
+ rvm
107
+ shoulda
108
+ simplecov
109
+ vcr
110
+ webmock
data/Guardfile.dist ADDED
@@ -0,0 +1,45 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ # Uncomment next line if Gemfile contain `gemspec' command
7
+ # watch(/^.+\.gemspec/)
8
+ end
9
+
10
+ guard 'rspec', :version => 2 do
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch('spec/spec_helper.rb') { "spec" }
13
+ watch(%r{^lib/zuck/(.+)\.rb$}) { "spec" }
14
+
15
+ # Rails example
16
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
17
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
18
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
19
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
20
+ watch('config/routes.rb') { "spec/routing" }
21
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
22
+
23
+ # Turnip features and steps
24
+ watch(%r{^spec/acceptance/(.+)\.feature$})
25
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
26
+ end
27
+
28
+
29
+ guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
30
+ watch('config/application.rb')
31
+ watch('config/environment.rb')
32
+ watch('config/environments/test.rb')
33
+ watch(%r{^config/initializers/.+\.rb$})
34
+ watch('Gemfile')
35
+ watch('Gemfile.lock')
36
+ watch('spec/spec_helper.rb') { :rspec }
37
+ watch('test/test_helper.rb') { :test_unit }
38
+ watch(%r{features/support/}) { :cucumber }
39
+ end
40
+
41
+ guard 'yard' do
42
+ watch(%r{app/.+\.rb})
43
+ watch(%r{lib/.+\.rb})
44
+ watch(%r{ext/.+\.c})
45
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Jannis Hermanns
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,138 @@
1
+ [![Build Status](https://secure.travis-ci.org/moviepilot/zuck.png?branch=master)](https://travis-ci.org/moviepilot/zuck)
2
+
3
+ Zuck; use facebook's advertisement API with ruby
4
+ ================
5
+
6
+ This is a little gem that makes access to facebook's
7
+ ads API a little easier. Check out facebook's
8
+ [documentation](https://developers.facebook.com/docs/reference/ads-api/)
9
+ for a nice diagram that explains how things work.
10
+
11
+ ![](https://dl.dropbox.com/u/1953503/kw29_koelner_gelierzucker_31_oder_diamant_feinster_zucker_462982.jpeg)
12
+
13
+ We just hacked this up and are not using it in production yet,
14
+ so handle with care. On the other hand, simplecov reports 100%
15
+ coverage. But as it is a gem in a very early stage, a warning was
16
+ due.
17
+
18
+ Usage
19
+ =====
20
+
21
+ Not everything is supported yet. Here's what you can do currently.
22
+
23
+
24
+ Reading
25
+ --------
26
+ ```ruby
27
+ # Let's set a default graph object with an access token
28
+ Zuck.graph = Koala::Facebook::API.new('my_access_token')
29
+
30
+ # Fetching all ad accounts associated to that user
31
+ accounts = Zuck::AdAccount.all
32
+
33
+ # Let's look at an account
34
+ my_account = accounts.first
35
+ => #<Zuck::AdAccount id: "act_10150585630710217", account_id: "10150585630710217", name: "", account_status: 1, currency: "USD", timezone_id: 47, timezone_name: "Europe/Berlin", timezone_offset_hours_utc: 2, is_personal: 0, business_name: "Big Mike Alright UG (haftungsbeschr\u00e4nkt)", business_street: "Big Mike Alright UG (haftungsbeschr\u00e4nkt)", business_street2: "J\u00e4gerndorfer Zeile 61", business_city: "Berlin", business_state: "Berlin", business_zip: "12209", business_country_code: "DE", vat_status: 3, daily_spend_limit: 25000, users: [{"uid":501730216,"permissions":[1,2,3,4,5,7],"role":1001}], notification_settings: {"501730216":{"1000":{"1":1},"1001":{"1":1},"1002":{"1":1,"2":60},"1003":{"1":1,"2":60},"1004":{"1":1},"1005":{"1":1},"1006":{"1":1},"1009":{"1":1},"1010":{"1":1},"1011":{"1":1},"2000":{"1":1,"2":60},"2001":{"1":1,"2":60},"2002":{"2":60},"2003":{"1":1,"2":60},"2004":{"1":1,"2":60},"2005":{"1":1,"2":60},"3000":{"1":1,"2":60},"3001":{"1":1,"2":60},"3002":{"2":60},"3003":{"1":1,"2":60},"5000":{"1":1},"6000":{"1":1},"6001":{"1":1},"9000":{"1":1,"2":60},"8000":{"1":1,"2":60}}}, capabilities: [], balance: 0, moo_default_conversion_bid: 1000, moo_default_bid: 1000>
36
+
37
+ # Aha. How do I access properties? The documented properties
38
+ # have getters:
39
+ my_account.currency
40
+ => "USD"
41
+
42
+ # But facebook also returns some non documented stuff
43
+ my_account[:moo_default_bid]
44
+ => 1000
45
+
46
+ # Let's fetch the campaigns for this account
47
+ my_campaign = my_account.ad_campaigns.first
48
+
49
+ # Aha! Does this campaign have ad groups?
50
+ my_group = my_campaign.ad_groups.first
51
+ my_group.name
52
+ => "Group names are silly"
53
+
54
+ # That was surprising. Just like the fact that ad groups
55
+ # have ad creatives associated to them:
56
+ my_creative = my_group.ad_creatives.first
57
+
58
+ # Let's go back up to the parent
59
+ my_creative.ad_group.name
60
+ => "Group names are silly"
61
+ ```
62
+
63
+ Writing
64
+ --------
65
+
66
+ ```ruby
67
+ # Directly defining the creative as JSON
68
+ creative = '{"type":25,"action_spec":{"action.type":"like", "post":10150420410887685}}'
69
+
70
+ # Options for the ad group we want to create
71
+ o = { bid_type: 1,
72
+ max_bid: 1,
73
+ name: "My first ad group",
74
+ targeting: '{"countries":["US"]}',
75
+ creative: creative}
76
+
77
+ # Create it in the context of my_campaign
78
+ group = my_campaign.create_ad_group(o)
79
+ => #<Zuck::AdGroup adgroup_id: 6005851390151, ad_id: 6005851390151, campaign_id: 6005851032951, name: "My first ad group", adgroup_status: 4, bid_type: 1, max_bid: "1", bid_info: {"1":"1"}, ad_status: 4, account_id: "10150585630710217", id: "6005851390151", creative_ids: [6005851371551], targeting: {"countries":["US"],"friends_of_connections":[{"id":"6005851366351","name":null}]}, conversion_specs: [{"action.type":"like","post":"10150420410887685"}], start_time: null, end_time: null, updated_time: 1343916568, created_time: 1343916568>
80
+
81
+ # Shoot, that was the wrong name
82
+ group.name = "My serious ad group"
83
+ group.save
84
+ => true
85
+
86
+ # No wait, let's not spend money on facebook
87
+ group.destroy
88
+ => true
89
+
90
+ # What does destroy mean? Changing the status!
91
+ group.ad_status
92
+ => 3
93
+ ```
94
+
95
+ Supported objects
96
+ -----------------
97
+
98
+ This gem supports basic CRUD on the objects of the facebook ads api.
99
+ Here's a support chart:
100
+
101
+ <table>
102
+ <tr>
103
+ <th style="text-align:right">Object</th>
104
+ <th style="text-align:center">.all</th>
105
+ <th style="text-align:center">.create</th>
106
+ <th style="text-align:center">.save</th>
107
+ <th style="text-align:center">.destroy</th>
108
+ <th style="text-align:center">parent.create_obj*</th>
109
+ <th style="text-align:center">Convenience methods**</th>
110
+ </tr>
111
+ <tr><td style="text-align: right">Ad account</td> <td>✔</td><td>-</td><td>✔</td><td>✔</td><td>-</td><td>-</td></tr>
112
+ <tr><td style="text-align: right">Ad account group</td> <td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
113
+ <tr><td style="text-align: right">Ad campaign</td> <td>✔</td><td>✔</td><td>✔</td><td>✔</td><td>✔</td><td>-</td></tr>
114
+ <tr><td style="text-align: right">Ad creative</td> <td>✔</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
115
+ <tr><td style="text-align: right">Ad group</td> <td>✔</td><td>✔</td><td>✔</td><td>✔</td><td>✔</td><td>-</td></tr>
116
+ <tr><td style="text-align: right">Ad image</td> <td>●</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
117
+ <tr><td style="text-align: right">Ad user</td> <td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
118
+ </table>
119
+
120
+ (*) This means that you can, for example, create a new ad group by calling
121
+ `my_campaign.create_ad_group(data)` or not.
122
+
123
+ (**) Right now, everything goes right to facebook, but we'll want some
124
+ convenience methods that tell you, for example, what
125
+ `ad_group.ad_status == 3` actually means
126
+
127
+ ( ) These don't exist as their own objects in this gem but live in their
128
+ parents. This means you can, for now, only read them:
129
+
130
+ Users don't exist as objects yet, but you can list all ad users of
131
+ an account via `my_ad_account.users` and you will get an array of hashes.
132
+
133
+ To-Do
134
+ -----
135
+
136
+ Add convenience stuff, right now everything is quite raw and directly
137
+ sent over to facebook. Also, more tests directly to the api with a test
138
+ user. Consolidate and test create code with other objects than AdGroup.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "zuck"
18
+ gem.homepage = "http://github.com/jayniz/zuck"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Ruby adapter to facebook's ad api}
21
+ gem.description = %Q{This gem allows to easily access facebook's ads api in ruby. See https://developers.facebook.com/docs/reference/ads-api/}
22
+ gem.email = "jannis@gmail.com"
23
+ gem.authors = ["Jannis Hermanns"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+
29
+ task :default => :test
30
+
31
+ require 'rdoc/task'
32
+ Rake::RDocTask.new do |rdoc|
33
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
34
+
35
+ rdoc.rdoc_dir = 'rdoc'
36
+ rdoc.title = "zuck #{version}"
37
+ rdoc.rdoc_files.include('README*')
38
+ rdoc.rdoc_files.include('lib/**/*.rb')
39
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/console ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
3
+
4
+ require 'bundler'
5
+ Bundler.require
6
+
7
+ require 'irb'
8
+ require 'zuck'
9
+
10
+ def reload!
11
+ @loaded_files ||= {}
12
+ count = 0
13
+
14
+ Dir['./lib/**/*.rb'].each do |file|
15
+ mtime = File.stat(file).mtime
16
+ if !@loaded_files.has_key?(file) or mtime > @loaded_files[file]
17
+ STDERR.puts "mtime for #{file} changed, reloading"
18
+ load file
19
+ @loaded_files[file] = mtime
20
+ count += 1
21
+ end
22
+ end
23
+ "reloaded #{count} files"
24
+ end
25
+
26
+ IRB.start
@@ -0,0 +1,40 @@
1
+ module Zuck
2
+ class AdAccount < RawFbObject
3
+
4
+ # The [fb docs](https://developers.facebook.com/docs/reference/ads-api/adaccount/)
5
+ # were incomplete, so I added here what the graph explorer
6
+ # actually returned.
7
+ known_keys :account_id,
8
+ :account_status,
9
+ :balance,
10
+ :business_city,
11
+ :business_country_code,
12
+ :business_name,
13
+ :business_state,
14
+ :business_street,
15
+ :business_street2,
16
+ :business_zip,
17
+ :capabilities,
18
+ :currency,
19
+ :daily_spend_limit,
20
+ :id,
21
+ :is_personal,
22
+ :moo_default_bid,
23
+ :moo_default_conversion_bid,
24
+ :name,
25
+ :notification_settings,
26
+ :timezone_id,
27
+ :timezone_name,
28
+ :timezone_offset_hours_utc,
29
+ :users,
30
+ :vat_status
31
+
32
+
33
+ list_path 'me/adaccounts'
34
+ connections :ad_campaigns
35
+
36
+ def self.all(graph = Zuck.graph)
37
+ super(graph)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Zuck
2
+ class AdCampaign < RawFbObject
3
+
4
+ # The [fb docs](https://developers.facebook.com/docs/reference/ads-api/adaccount/)
5
+ # were incomplete, so I added here what the graph explorer
6
+ # actually returned.
7
+ known_keys :account_id,
8
+ :campaign_id,
9
+ :campaign_status,
10
+ :created_time,
11
+ :daily_imps,
12
+ :end_time,
13
+ :id,
14
+ :lifetime_budget,
15
+ :name,
16
+ :start_time,
17
+ :updated_time
18
+
19
+ parent_object :ad_account
20
+ list_path :adcampaigns
21
+ connections :ad_groups
22
+
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module Zuck
2
+ class AdCreative < RawFbObject
3
+
4
+ # Can't create this directly (yet)
5
+ read_only
6
+
7
+ # The [fb docs](https://developers.facebook.com/docs/reference/ads-api/adaccount/)
8
+ # were incomplete, so I added here what the graph explorer
9
+ # actually returned.
10
+ known_keys :alt_view_tags,
11
+ :body,
12
+ :count_current_adgroups,
13
+ :creative_id,
14
+ :id,
15
+ :image_hash,
16
+ :image_url,
17
+ :link_url,
18
+ :name,
19
+ :object_id,
20
+ :preview_url,
21
+ :run_status,
22
+ :title,
23
+ :type,
24
+ :view_tag
25
+
26
+ parent_object :ad_group
27
+ list_path :adcreatives
28
+
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ require 'zuck/facebook/ad_creative'
2
+
3
+ module Zuck
4
+ class AdGroup < RawFbObject
5
+
6
+ # The [fb docs](https://developers.facebook.com/docs/reference/ads-api/adaccount/)
7
+ # were incomplete, so I added here what the graph explorer
8
+ # actually returned.
9
+ known_keys :account_id,
10
+ :ad_id,
11
+ :ad_status,
12
+ :adgroup_id,
13
+ :adgroup_status,
14
+ :bid_info,
15
+ :bid_type,
16
+ :campaign_id,
17
+ :conversion_specs,
18
+ :created_time,
19
+ :creative_ids,
20
+ :end_time,
21
+ :id,
22
+ :max_bid,
23
+ :name,
24
+ :start_time,
25
+ :targeting,
26
+ :updated_time
27
+
28
+ parent_object :ad_campaign
29
+ list_path :adgroups
30
+ connections :ad_creatives
31
+
32
+ def self.create(graph, data, ad_campaign)
33
+ path = ad_campaign.ad_account.path
34
+ data['campaign_id'] = ad_campaign.id
35
+ super(graph, data, ad_campaign, path)
36
+ end
37
+
38
+ end
39
+ end