zuck 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/.travis.yml +7 -0
- data/.yardopts +4 -0
- data/CHANGELOG.markdown +4 -0
- data/Gemfile +35 -0
- data/Gemfile.lock +110 -0
- data/Guardfile.dist +45 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +138 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/console +26 -0
- data/lib/zuck/facebook/ad_account.rb +40 -0
- data/lib/zuck/facebook/ad_campaign.rb +24 -0
- data/lib/zuck/facebook/ad_creative.rb +30 -0
- data/lib/zuck/facebook/ad_group.rb +39 -0
- data/lib/zuck/facebook/targeting_spec.rb +200 -0
- data/lib/zuck/fb_object/dsl.rb +110 -0
- data/lib/zuck/fb_object/error.rb +8 -0
- data/lib/zuck/fb_object/hash_delegator.rb +111 -0
- data/lib/zuck/fb_object/helpers.rb +57 -0
- data/lib/zuck/fb_object/read.rb +147 -0
- data/lib/zuck/fb_object/read_only.rb +0 -0
- data/lib/zuck/fb_object/write.rb +75 -0
- data/lib/zuck/fb_object.rb +53 -0
- data/lib/zuck/koala/koala_methods.rb +27 -0
- data/lib/zuck.rb +9 -0
- data/spec/fixtures/a_single_account.yml +75 -0
- data/spec/fixtures/a_single_campaign.yml +48 -0
- data/spec/fixtures/create_ad_campaign.yml +49 -0
- data/spec/fixtures/create_ad_group.yml +47 -0
- data/spec/fixtures/delete_ad_group.yml +50 -0
- data/spec/fixtures/find_a_single_campaign_and_update_it.yml +247 -0
- data/spec/fixtures/list_of_ad_accounts.yml +75 -0
- data/spec/fixtures/list_of_ad_campaigns.yml +76 -0
- data/spec/fixtures/list_of_ad_creatives.yml +51 -0
- data/spec/fixtures/list_of_ad_groups.yml +49 -0
- data/spec/fixtures/list_of_all_ad_creatives_of_account.yml +86 -0
- data/spec/fixtures/reach_for_invalid_keyword.yml +95 -0
- data/spec/fixtures/reach_for_valid_keywords.yml +93 -0
- data/spec/fixtures/reach_for_valid_keywords_male_young.yml +93 -0
- data/spec/lib/zuck/facebook/ad_account_spec.rb +26 -0
- data/spec/lib/zuck/facebook/ad_campaign_spec.rb +4 -0
- data/spec/lib/zuck/facebook/targeting_spec_spec.rb +174 -0
- data/spec/lib/zuck/fb_object/helpers_spec.rb +67 -0
- data/spec/lib/zuck/koala/koala_methods_spec.rb +30 -0
- data/spec/lib/zuck/util/hash_delegator_spec.rb +54 -0
- data/spec/lib/zuck_spec.rb +165 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/vcr_setup.rb +15 -0
- data/zuck.gemspec +141 -0
- metadata +389 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/search?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&keyword_list=eminem,invalidsssssssssssssss&type=adkeywordvalid
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- ! '*/*'
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- ! '*'
|
23
|
+
Cache-Control:
|
24
|
+
- private, no-cache, no-store, must-revalidate
|
25
|
+
Content-Type:
|
26
|
+
- text/javascript; charset=UTF-8
|
27
|
+
Etag:
|
28
|
+
- ! '"1b6af91a08d48aeee5ffa0cf3437f38b61281b68"'
|
29
|
+
Expires:
|
30
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
31
|
+
Pragma:
|
32
|
+
- no-cache
|
33
|
+
X-Fb-Rev:
|
34
|
+
- '698469'
|
35
|
+
X-Fb-Debug:
|
36
|
+
- 7so4yUGhkG2hb0KiZDAFNlrgtEmcB0xQ9Xilxl/LfRw=
|
37
|
+
Date:
|
38
|
+
- Wed, 19 Dec 2012 14:36:48 GMT
|
39
|
+
Connection:
|
40
|
+
- keep-alive
|
41
|
+
Content-Length:
|
42
|
+
- '108'
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ! '{"data":[{"name":"Eminem","valid":true,"id":6003170652434},{"name":"invalidsssssssssssssss","valid":false}]}'
|
46
|
+
http_version:
|
47
|
+
recorded_at: Wed, 19 Dec 2012 14:36:48 GMT
|
48
|
+
- request:
|
49
|
+
method: get
|
50
|
+
uri: https://graph.facebook.com/act_10150585630710217/reachestimate?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&targeting_spec=%7B%22countries%22:%5B%22US%22%5D,%22keywords%22:%5B%22eminem%22,%22invalidsssssssssssssss%22%5D,%22age_min%22:13,%22connections%22:%5B%5D%7D
|
51
|
+
body:
|
52
|
+
encoding: US-ASCII
|
53
|
+
string: ''
|
54
|
+
headers:
|
55
|
+
Accept-Encoding:
|
56
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
57
|
+
Accept:
|
58
|
+
- ! '*/*'
|
59
|
+
User-Agent:
|
60
|
+
- Ruby
|
61
|
+
response:
|
62
|
+
status:
|
63
|
+
code: 400
|
64
|
+
message: Bad Request
|
65
|
+
headers:
|
66
|
+
Access-Control-Allow-Origin:
|
67
|
+
- ! '*'
|
68
|
+
Cache-Control:
|
69
|
+
- no-store
|
70
|
+
Content-Type:
|
71
|
+
- text/javascript; charset=UTF-8
|
72
|
+
Expires:
|
73
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
74
|
+
Pragma:
|
75
|
+
- no-cache
|
76
|
+
Www-Authenticate:
|
77
|
+
- ! 'OAuth "Facebook Platform" "invalid_token" "Error validating access token:
|
78
|
+
Session has expired at unix time 1355932800. The current unix time is 1355934833."'
|
79
|
+
X-Fb-Rev:
|
80
|
+
- '698469'
|
81
|
+
X-Fb-Debug:
|
82
|
+
- Kkjlojh5eu19FxaW5yu6N5A+5xmmbKyfKsCpa+y2eOc=
|
83
|
+
Date:
|
84
|
+
- Wed, 19 Dec 2012 16:33:53 GMT
|
85
|
+
Connection:
|
86
|
+
- keep-alive
|
87
|
+
Content-Length:
|
88
|
+
- '191'
|
89
|
+
body:
|
90
|
+
encoding: US-ASCII
|
91
|
+
string: ! '{"error":{"message":"Error validating access token: Session has expired
|
92
|
+
at unix time 1355932800. The current unix time is 1355934833.","type":"OAuthException","code":190,"error_subcode":463}}'
|
93
|
+
http_version:
|
94
|
+
recorded_at: Wed, 19 Dec 2012 16:33:53 GMT
|
95
|
+
recorded_with: VCR 2.2.4
|
@@ -0,0 +1,93 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/search?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&keyword_list=eminem,sting&type=adkeywordvalid
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- ! '*/*'
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- ! '*'
|
23
|
+
Cache-Control:
|
24
|
+
- private, no-cache, no-store, must-revalidate
|
25
|
+
Content-Type:
|
26
|
+
- text/javascript; charset=UTF-8
|
27
|
+
Etag:
|
28
|
+
- ! '"a684dbf074d5467a7775189984027b177954edef"'
|
29
|
+
Expires:
|
30
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
31
|
+
Pragma:
|
32
|
+
- no-cache
|
33
|
+
X-Fb-Rev:
|
34
|
+
- '698469'
|
35
|
+
X-Fb-Debug:
|
36
|
+
- vtS1+m1EAAr/YCY4TKxUueU+DfmYhcASPsoK5LMzkW8=
|
37
|
+
Date:
|
38
|
+
- Wed, 19 Dec 2012 15:13:41 GMT
|
39
|
+
Connection:
|
40
|
+
- keep-alive
|
41
|
+
Content-Length:
|
42
|
+
- '109'
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ! '{"data":[{"name":"Eminem","valid":true,"id":6003170652434},{"name":"Sting","valid":true,"id":6003164299249}]}'
|
46
|
+
http_version:
|
47
|
+
recorded_at: Wed, 19 Dec 2012 15:13:42 GMT
|
48
|
+
- request:
|
49
|
+
method: get
|
50
|
+
uri: https://graph.facebook.com/act_10150585630710217/reachestimate?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&targeting_spec=%7B%22countries%22:%5B%22US%22%5D,%22keywords%22:%5B%22eminem%22,%22sting%22%5D,%22age_min%22:13,%22connections%22:%5B%5D,%22broad_age%22:false%7D
|
51
|
+
body:
|
52
|
+
encoding: US-ASCII
|
53
|
+
string: ''
|
54
|
+
headers:
|
55
|
+
Accept-Encoding:
|
56
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
57
|
+
Accept:
|
58
|
+
- ! '*/*'
|
59
|
+
User-Agent:
|
60
|
+
- Ruby
|
61
|
+
response:
|
62
|
+
status:
|
63
|
+
code: 200
|
64
|
+
message: OK
|
65
|
+
headers:
|
66
|
+
Access-Control-Allow-Origin:
|
67
|
+
- ! '*'
|
68
|
+
Cache-Control:
|
69
|
+
- private, no-cache, no-store, must-revalidate
|
70
|
+
Content-Type:
|
71
|
+
- text/javascript; charset=UTF-8
|
72
|
+
Etag:
|
73
|
+
- ! '"486581ad6483292ae854bf5e25d36d1b564b1865"'
|
74
|
+
Expires:
|
75
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
76
|
+
Pragma:
|
77
|
+
- no-cache
|
78
|
+
X-Fb-Rev:
|
79
|
+
- '698469'
|
80
|
+
X-Fb-Debug:
|
81
|
+
- m+Eupy9dFg5wCI+Ej1LdCnZ73lJ1YHyikHIHA1+ET5Y=
|
82
|
+
Date:
|
83
|
+
- Wed, 19 Dec 2012 15:13:43 GMT
|
84
|
+
Connection:
|
85
|
+
- keep-alive
|
86
|
+
Content-Length:
|
87
|
+
- '322'
|
88
|
+
body:
|
89
|
+
encoding: US-ASCII
|
90
|
+
string: ! '{"users":16830580,"bid_estimations":[{"location":3,"cpc_min":43,"cpc_median":71,"cpc_max":145,"cpm_min":2,"cpm_median":15,"cpm_max":502}],"imp_estimates":[],"data":{"users":16830580,"bid_estimations":[{"location":3,"cpc_min":43,"cpc_median":71,"cpc_max":145,"cpm_min":2,"cpm_median":15,"cpm_max":502}],"imp_estimates":[]}}'
|
91
|
+
http_version:
|
92
|
+
recorded_at: Wed, 19 Dec 2012 15:13:43 GMT
|
93
|
+
recorded_with: VCR 2.2.4
|
@@ -0,0 +1,93 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/search?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&keyword_list=sting&type=adkeywordvalid
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- ! '*/*'
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- ! '*'
|
23
|
+
Cache-Control:
|
24
|
+
- private, no-cache, no-store, must-revalidate
|
25
|
+
Content-Type:
|
26
|
+
- text/javascript; charset=UTF-8
|
27
|
+
Etag:
|
28
|
+
- ! '"66bd8a85c55324c07c1c2afcb1eac4e2fd358bf5"'
|
29
|
+
Expires:
|
30
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
31
|
+
Pragma:
|
32
|
+
- no-cache
|
33
|
+
X-Fb-Rev:
|
34
|
+
- '698469'
|
35
|
+
X-Fb-Debug:
|
36
|
+
- XuqZnN5WjRA/b3sTn4Nz0jyvTkw2S0OCP5x8OKrUO54=
|
37
|
+
Date:
|
38
|
+
- Wed, 19 Dec 2012 15:20:53 GMT
|
39
|
+
Connection:
|
40
|
+
- keep-alive
|
41
|
+
Content-Length:
|
42
|
+
- '59'
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ! '{"data":[{"name":"Sting","valid":true,"id":6003164299249}]}'
|
46
|
+
http_version:
|
47
|
+
recorded_at: Wed, 19 Dec 2012 15:20:53 GMT
|
48
|
+
- request:
|
49
|
+
method: get
|
50
|
+
uri: https://graph.facebook.com/act_10150585630710217/reachestimate?access_token=AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD&targeting_spec=%7B%22countries%22:%5B%22US%22%5D,%22keywords%22:%5B%22sting%22%5D,%22age_min%22:13,%22age_max%22:24,%22genders%22:%5B2%5D,%22connections%22:%5B%5D,%22broad_age%22:false%7D
|
51
|
+
body:
|
52
|
+
encoding: US-ASCII
|
53
|
+
string: ''
|
54
|
+
headers:
|
55
|
+
Accept-Encoding:
|
56
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
57
|
+
Accept:
|
58
|
+
- ! '*/*'
|
59
|
+
User-Agent:
|
60
|
+
- Ruby
|
61
|
+
response:
|
62
|
+
status:
|
63
|
+
code: 200
|
64
|
+
message: OK
|
65
|
+
headers:
|
66
|
+
Access-Control-Allow-Origin:
|
67
|
+
- ! '*'
|
68
|
+
Cache-Control:
|
69
|
+
- private, no-cache, no-store, must-revalidate
|
70
|
+
Content-Type:
|
71
|
+
- text/javascript; charset=UTF-8
|
72
|
+
Etag:
|
73
|
+
- ! '"430ebe9f4489fdfc008a2c301d51e68d633c8978"'
|
74
|
+
Expires:
|
75
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
76
|
+
Pragma:
|
77
|
+
- no-cache
|
78
|
+
X-Fb-Rev:
|
79
|
+
- '698469'
|
80
|
+
X-Fb-Debug:
|
81
|
+
- H+RabGZXmTXmsUVynJuDJRCthPTjpJLfPD7iwdJA3WA=
|
82
|
+
Date:
|
83
|
+
- Wed, 19 Dec 2012 15:20:55 GMT
|
84
|
+
Connection:
|
85
|
+
- keep-alive
|
86
|
+
Content-Length:
|
87
|
+
- '314'
|
88
|
+
body:
|
89
|
+
encoding: US-ASCII
|
90
|
+
string: ! '{"users":39400,"bid_estimations":[{"location":3,"cpc_min":38,"cpc_median":58,"cpc_max":115,"cpm_min":1,"cpm_median":6,"cpm_max":383}],"imp_estimates":[],"data":{"users":39400,"bid_estimations":[{"location":3,"cpc_min":38,"cpc_median":58,"cpc_max":115,"cpm_min":1,"cpm_median":6,"cpm_max":383}],"imp_estimates":[]}}'
|
91
|
+
http_version:
|
92
|
+
recorded_at: Wed, 19 Dec 2012 15:20:55 GMT
|
93
|
+
recorded_with: VCR 2.2.4
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zuck::AdAccount do
|
4
|
+
|
5
|
+
let(:graph){ Koala::Facebook::API.new(:token) }
|
6
|
+
let(:acc){ Zuck::AdAccount.new(graph, name: :dance_in_style) }
|
7
|
+
|
8
|
+
it "initializes graph correctly" do
|
9
|
+
acc.graph.should == graph
|
10
|
+
end
|
11
|
+
|
12
|
+
it "initializes data correctly" do
|
13
|
+
acc[:name].should == :dance_in_style
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defines getters" do
|
17
|
+
acc.name.should == :dance_in_style
|
18
|
+
end
|
19
|
+
|
20
|
+
it "defines setters" do
|
21
|
+
acc.name = :bazinga
|
22
|
+
acc.name.should == :bazinga
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zuck::TargetingSpec do
|
4
|
+
let(:ad_account){ "2ijdsfoij" }
|
5
|
+
let(:graph){ mock('koala') }
|
6
|
+
let(:reach_response){
|
7
|
+
{ # These can probably go since we have
|
8
|
+
"users" => 23688420, # vcr cassetes with http requests and
|
9
|
+
"bid_estimations" => [ # responses in place
|
10
|
+
{
|
11
|
+
"location" => 3,
|
12
|
+
"cpc_min" => 37,
|
13
|
+
"cpc_median" => 44,
|
14
|
+
"cpc_max" => 57,
|
15
|
+
"cpm_min" => 6,
|
16
|
+
"cpm_median" => 12,
|
17
|
+
"cpm_max" => 33
|
18
|
+
}
|
19
|
+
],
|
20
|
+
"imp_estimates" => [
|
21
|
+
],
|
22
|
+
"data" => {
|
23
|
+
"users" => 23688420,
|
24
|
+
"bid_estimations" => [
|
25
|
+
{
|
26
|
+
"location" => 3,
|
27
|
+
"cpc_min" => 37,
|
28
|
+
"cpc_median" => 44,
|
29
|
+
"cpc_max" => 57,
|
30
|
+
"cpm_min" => 6,
|
31
|
+
"cpm_median" => 12,
|
32
|
+
"cpm_max" => 33
|
33
|
+
}
|
34
|
+
],
|
35
|
+
"imp_estimates" => [
|
36
|
+
]
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
describe "validating keywords" do
|
43
|
+
|
44
|
+
let(:valid_keyword_result){ [{"name" => "foo", "valid" => true }] }
|
45
|
+
let(:invalid_keyword_result){ [{"name" => "sdjf", "valid" => false }] }
|
46
|
+
|
47
|
+
it "escapes commas" do
|
48
|
+
o = {type: 'adkeywordvalid', keyword_list: 'foo%2Cbar' }
|
49
|
+
graph.should_receive(:search).with(nil, o).and_return []
|
50
|
+
fts = Zuck::TargetingSpec.new(graph, ad_account, keywords: 'foo,bar')
|
51
|
+
fts.validate_keyword('foo,bar').should == false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "acknowledges valid keywords" do
|
55
|
+
o = {type: 'adkeywordvalid', keyword_list: 'foo' }
|
56
|
+
graph.should_receive(:search).with(nil, o).and_return valid_keyword_result
|
57
|
+
fts = Zuck::TargetingSpec.new(graph, ad_account)
|
58
|
+
|
59
|
+
fts.validate_keyword('foo').should == true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "refuses invalid keywords" do
|
63
|
+
o = {type: 'adkeywordvalid', keyword_list: 'sdjf' }
|
64
|
+
graph.should_receive(:search).with(nil, o).and_return invalid_keyword_result
|
65
|
+
fts = Zuck::TargetingSpec.new(graph, ad_account)
|
66
|
+
|
67
|
+
fts.validate_keyword('sdjf').should == false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "options given in spec" do
|
72
|
+
it "accepts male as gender" do
|
73
|
+
expect{
|
74
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keywords: ['foo'], gender: 'male')
|
75
|
+
}.to_not raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
it "accepts male as gender for young people" do
|
79
|
+
expect{
|
80
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keywords: ['foo'], gender: 'male', age_class: 'young')
|
81
|
+
}.to_not raise_error
|
82
|
+
end
|
83
|
+
|
84
|
+
it "accepts male as gender for old people" do
|
85
|
+
expect{
|
86
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keywords: ['foo'], gender: 'male', age_class: 'old')
|
87
|
+
}.to_not raise_error
|
88
|
+
end
|
89
|
+
|
90
|
+
it "accepts without gender" do
|
91
|
+
expect{
|
92
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keywords: ['foo'])
|
93
|
+
}.to_not raise_error
|
94
|
+
end
|
95
|
+
|
96
|
+
it "accepts single keywrod" do
|
97
|
+
expect{
|
98
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keyword: 'foo')
|
99
|
+
}.to_not raise_error
|
100
|
+
end
|
101
|
+
|
102
|
+
it "does not accept invalid genders" do
|
103
|
+
expect{
|
104
|
+
Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], keywords: ['foo'], gender: 'gemale')
|
105
|
+
}.to raise_error("Gender can only be male or female")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "does not accept targetings with neither :keywords nor :connections" do
|
109
|
+
expect{
|
110
|
+
ts = Zuck::TargetingSpec.new(graph, ad_account, countries: ['US'], gender: 'female')
|
111
|
+
ts.fetch_reach
|
112
|
+
}.to raise_error("Need to set :keywords or :connections")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "fetching reach" do
|
117
|
+
let(:graph){ Koala::Facebook::API.new('AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD') }
|
118
|
+
let(:ad_account){ 'act_10150585630710217' }
|
119
|
+
|
120
|
+
it "bugs out when trying to use an invalid keyword" do
|
121
|
+
VCR.use_cassette('reach_for_invalid_keyword') do
|
122
|
+
spec = {countries: ['us'], keywords: ['eminem', 'invalidsssssssssssssss'] }
|
123
|
+
ts = Zuck::TargetingSpec.new(graph, ad_account, spec)
|
124
|
+
expect{
|
125
|
+
ts.validate_keywords
|
126
|
+
}.to raise_error(Zuck::InvalidKeywordError, 'invalidsssssssssssssss')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "works without gender or age" do
|
131
|
+
VCR.use_cassette('reach_for_valid_keywords') do
|
132
|
+
spec = {countries: ['us'], keywords: ['eminem', 'sting'] }
|
133
|
+
ts = Zuck::TargetingSpec.new(graph, ad_account, spec)
|
134
|
+
reach = ts.fetch_reach
|
135
|
+
reach[:users].should == 16830580
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "works with gender and age" do
|
140
|
+
VCR.use_cassette('reach_for_valid_keywords_male_young') do
|
141
|
+
spec = {countries: ['us'], keywords: ['sting'], gender: :female, age_class: :young }
|
142
|
+
ts = Zuck::TargetingSpec.new(graph, ad_account, spec)
|
143
|
+
reach = ts.fetch_reach
|
144
|
+
reach[:users].should == 39400
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
it "without instanciating manually" do
|
149
|
+
x = stub()
|
150
|
+
x.should_receive(:fetch_reach).and_return 9
|
151
|
+
Zuck::TargetingSpec.should_receive(:new).with(:graph, :ad_account, :options).and_return x
|
152
|
+
|
153
|
+
Zuck::TargetingSpec.fetch_reach(:graph, :ad_account, :options)
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "Batch processing" do
|
159
|
+
let(:graph){ Koala::Facebook::API.new('AAAEvJ5vzhl8BAPLr6fQgNy2wdUHDJ7ZAoX9PTZCFnebwuTBZBEqO7lNTVZA3XNsTHPTATpTmVFs6o6Jp1pZAL8ZA54BRBXWYtztVug8bm2BAZDZD') }
|
160
|
+
let(:ad_account){ 'act_10150585630710217' }
|
161
|
+
|
162
|
+
it "doesn't split up small bunches" do
|
163
|
+
requests = [{some: :thing}] * 50
|
164
|
+
graph.should_receive(:batch).once.and_return([])
|
165
|
+
Zuck::TargetingSpec.batch_reaches(graph, ad_account, requests)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "splits up into 50 request bunches" do
|
169
|
+
requests = [{some: :thing}] * 51
|
170
|
+
graph.should_receive(:batch).twice.and_return([])
|
171
|
+
Zuck::TargetingSpec.batch_reaches(graph, ad_account, requests)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class HTest
|
4
|
+
extend Zuck::FbObject::Helpers
|
5
|
+
include Zuck::FbObject::Helpers
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Zuck::FbObject::Helpers do
|
9
|
+
|
10
|
+
let(:graph_mock){ mock('graph') }
|
11
|
+
|
12
|
+
context "get" do
|
13
|
+
it "forwards to koala" do
|
14
|
+
graph_mock.should_receive(:get_object).with('/foo').and_return(true)
|
15
|
+
HTest.send(:get, graph_mock, "/foo")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "does not swallow exceptions" do
|
19
|
+
graph_mock.should_receive(:get_object).with('/foo').and_raise("broken")
|
20
|
+
expect{
|
21
|
+
HTest.send(:get, graph_mock, "/foo")
|
22
|
+
}.to raise_error("broken")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "create_connection" do
|
27
|
+
it "forwards to koala" do
|
28
|
+
graph_mock.should_receive(:put_connections).with(:parent, :connection, :args, :opts).and_return(true)
|
29
|
+
HTest.send(:create_connection, graph_mock, :parent, :connection, :args, :opts)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "does not swallow exceptions" do
|
33
|
+
graph_mock.should_receive(:put_connections).with(:parent, :connection, :args, :opts).and_raise("broken")
|
34
|
+
expect{
|
35
|
+
HTest.send(:create_connection, graph_mock, :parent, :connection, :args, :opts)
|
36
|
+
}.to raise_error("broken")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "post" do
|
41
|
+
it "forwards to koala" do
|
42
|
+
graph_mock.should_receive(:graph_call).with("path", :data, "post", :opts).and_return(true)
|
43
|
+
HTest.send(:post, graph_mock, :path, :data, :opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not swallow exceptions" do
|
47
|
+
graph_mock.should_receive(:graph_call).with("path", :data, "post", :opts).and_raise("broken")
|
48
|
+
expect{
|
49
|
+
HTest.send(:post, graph_mock, :path, :data, :opts)
|
50
|
+
}.to raise_error("broken")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "delete" do
|
55
|
+
it "forwards to koala" do
|
56
|
+
graph_mock.should_receive(:delete_object).with(:id).and_return(true)
|
57
|
+
HTest.send(:delete, graph_mock, :id)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "does not swallow exceptions" do
|
61
|
+
graph_mock.should_receive(:delete_object).with(:id).and_raise("broken")
|
62
|
+
expect{
|
63
|
+
HTest.send(:delete, graph_mock, :id)
|
64
|
+
}.to raise_error("broken")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class KMTest
|
4
|
+
extend Zuck::KoalaMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Zuck::KoalaMethods do
|
8
|
+
describe "assigning a graph instance" do
|
9
|
+
|
10
|
+
it "raises when not a Koala instance" do
|
11
|
+
expect{
|
12
|
+
KMTest.graph = :something_else
|
13
|
+
}.to raise_error "Symbol is not a Koala::Facebook::API"
|
14
|
+
KMTest.graph.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "raises when not a Koala instance" do
|
18
|
+
expect{
|
19
|
+
KMTest.graph = Koala::Facebook::API.new()
|
20
|
+
}.to raise_error
|
21
|
+
KMTest.graph.should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "that's valid" do
|
25
|
+
KMTest.graph.should be_nil
|
26
|
+
KMTest.graph = Koala::Facebook::API.new(:some_token)
|
27
|
+
KMTest.graph.should_not be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class HDTest
|
4
|
+
include Zuck::HashDelegator
|
5
|
+
known_keys :bar
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Zuck::HashDelegator do
|
9
|
+
|
10
|
+
let(:del){ HDTest.new }
|
11
|
+
|
12
|
+
it "assigns a value" do
|
13
|
+
del[:foo] = :bar
|
14
|
+
del[:foo].should == :bar
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has a shortcut getter" do
|
18
|
+
del[:foo] = :bar
|
19
|
+
del.data.should == {foo: :bar}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "transforms keys to symbols" do
|
23
|
+
del['bar'] = :foo
|
24
|
+
del['bar'].should == :foo
|
25
|
+
del[:bar].should == :foo
|
26
|
+
end
|
27
|
+
|
28
|
+
it "becomes a pretty string" do
|
29
|
+
x = HDTest.new
|
30
|
+
x[:some] = "thing"
|
31
|
+
x[:bar] = 1
|
32
|
+
x.to_s.should == '#<HDTest some: "thing", bar: 1>'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "allows to assign a hash" do
|
36
|
+
x = HDTest.new
|
37
|
+
x.set_data('some' => "thing", bar: 1)
|
38
|
+
x.to_s.should == '#<HDTest some: "thing", bar: 1>'
|
39
|
+
x[:some].should == "thing"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "allows to assign a hash using a shortcut" do
|
43
|
+
x = HDTest.new
|
44
|
+
x.data = {'some' => "thing", bar: 1}
|
45
|
+
x.to_s.should == '#<HDTest some: "thing", bar: 1>'
|
46
|
+
x[:some].should == "thing"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "has methods defined for the known keys" do
|
50
|
+
del[:bar] = :foo
|
51
|
+
del.bar.should == :foo
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|