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
@@ -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,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zuck::AdCampaign do
4
+ 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