fb_graph 2.1.7 → 2.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/VERSION +1 -1
  2. data/lib/fb_graph.rb +8 -2
  3. data/lib/fb_graph/achievement.rb +44 -0
  4. data/lib/fb_graph/ad_account.rb +2 -0
  5. data/lib/fb_graph/ad_connection_object.rb +38 -0
  6. data/lib/fb_graph/application.rb +27 -6
  7. data/lib/fb_graph/broad_targeting_category.rb +13 -0
  8. data/lib/fb_graph/connections/achievements.rb +21 -0
  9. data/lib/fb_graph/connections/ad_connection_objects.rb +14 -0
  10. data/lib/fb_graph/connections/broad_targeting_categories.rb +14 -0
  11. data/lib/fb_graph/connections/mutual_friends.rb +22 -0
  12. data/lib/fb_graph/connections/user_achievements.rb +22 -0
  13. data/lib/fb_graph/exception.rb +41 -1
  14. data/lib/fb_graph/node.rb +2 -11
  15. data/lib/fb_graph/user.rb +2 -0
  16. data/lib/fb_graph/user_achievement.rb +35 -0
  17. data/spec/fb_graph/achievement_spec.rb +59 -0
  18. data/spec/fb_graph/ad_connection_object_spec.rb +30 -0
  19. data/spec/fb_graph/application_spec.rb +3 -1
  20. data/spec/fb_graph/broad_targeting_category_spec.rb +17 -0
  21. data/spec/fb_graph/connections/achievements_spec.rb +27 -0
  22. data/spec/fb_graph/connections/ad_connection_objects_spec.rb +20 -0
  23. data/spec/fb_graph/connections/broad_targeting_categories_spec.rb +18 -0
  24. data/spec/fb_graph/connections/mutual_friends_spec.rb +28 -0
  25. data/spec/fb_graph/connections/user_achievements_spec.rb +37 -0
  26. data/spec/fb_graph/exception_spec.rb +97 -1
  27. data/spec/fb_graph/user_achievement_spec.rb +53 -0
  28. data/spec/mock_json/ad_accounts/ad_connection_objects/test_connection_objects.json +34 -0
  29. data/spec/mock_json/ad_accounts/broad_targeting_categories/test_bct.json +19 -0
  30. data/spec/mock_json/applications/achievements/sample.json +24 -0
  31. data/spec/mock_json/users/mutual_friends/me_and_agektmr.json +126 -0
  32. data/spec/mock_json/users/user_achievements/created.json +1 -0
  33. data/spec/mock_json/users/user_achievements/sample.json +31 -0
  34. metadata +41 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.7
1
+ 2.1.8
@@ -61,14 +61,17 @@ require 'fb_graph/venue'
61
61
  require 'fb_graph/work'
62
62
 
63
63
  require 'fb_graph/node'
64
+ require 'fb_graph/achievement'
64
65
  require 'fb_graph/ad_account'
65
66
  require 'fb_graph/ad_campaign'
66
67
  require 'fb_graph/ad_campaign_stat'
68
+ require 'fb_graph/ad_connection_object'
67
69
  require 'fb_graph/ad_group'
68
70
  require 'fb_graph/ad_group_stat'
69
71
  require 'fb_graph/ad_keyword'
70
72
  require 'fb_graph/ad_keyword_suggestion'
71
73
  require 'fb_graph/ad_keyword_valid'
74
+ require 'fb_graph/broad_targeting_category'
72
75
  require 'fb_graph/reach_estimate.rb'
73
76
  require 'fb_graph/album'
74
77
  require 'fb_graph/app_request'
@@ -98,10 +101,13 @@ require 'fb_graph/tab'
98
101
  require 'fb_graph/tag'
99
102
  require 'fb_graph/thread'
100
103
  require 'fb_graph/user'
101
- require 'fb_graph/ad_user'
102
- require 'fb_graph/test_user' # Load after FbGraph::User
104
+ require 'fb_graph/user_achievement'
103
105
  require 'fb_graph/video'
104
106
 
107
+ # Load after FbGraph::User
108
+ require 'fb_graph/ad_user'
109
+ require 'fb_graph/test_user'
110
+
105
111
  require 'fb_graph/klass'
106
112
  require 'fb_graph/project'
107
113
 
@@ -0,0 +1,44 @@
1
+ module FbGraph
2
+ class Achievement < Node
3
+ attr_accessor :type, :application, :title, :url, :description, :image, :images, :data, :updated_time, :context, :points, :display_order
4
+
5
+ def initialize(identifier, attributes = {})
6
+ super
7
+
8
+ # TODO: Handle data, context in smarter way.
9
+ [:type, :title, :url, :description, :data, :context].each do |key|
10
+ send "#{key}=", attributes[key]
11
+ end
12
+
13
+ if self.data
14
+ @points = self.data[:points]
15
+ end
16
+
17
+ if self.context
18
+ @display_order = self.context[:display_order]
19
+ end
20
+
21
+ @images = []
22
+ if _images_ = attributes[:image]
23
+ _images_.each do |_image_|
24
+ @images << _image_[:url]
25
+ end
26
+ end
27
+ @image = @images.first
28
+
29
+ if application = attributes[:application]
30
+ application[:link] = application[:url] # for some reason, FB uses "url" only here..
31
+ @application = Application.new(application[:id], application)
32
+ end
33
+
34
+ if attributes[:updated_time]
35
+ @updated_time = Time.parse(attributes[:updated_time]).utc
36
+ end
37
+ end
38
+
39
+ def destroy(options = {})
40
+ options[:access_token] ||= self.access_token
41
+ application.unregister_achievement!(url, options)
42
+ end
43
+ end
44
+ end
@@ -4,7 +4,9 @@ module FbGraph
4
4
  include Connections::AdGroups
5
5
  include Connections::AdCampaignStats
6
6
  include Connections::AdGroupStats
7
+ include Connections::BroadTargetingCategories
7
8
  include Connections::ReachEstimates
9
+ include Connections::AdConnectionObjects
8
10
 
9
11
  attr_accessor :account_id, :name, :account_status, :daily_spend_limit, :users, :currency, :timezone_id, :timezone_name, :capabilities, :account_groups
10
12
 
@@ -0,0 +1,38 @@
1
+ module FbGraph
2
+ class AdConnectionObject < Node
3
+ attr_accessor :name, :url, :type, :tabs, :picture, :object
4
+
5
+ TYPES = {
6
+ :page => 1,
7
+ :application => 2,
8
+ :event => 3,
9
+ :place => 6,
10
+ :domain => 7
11
+ }
12
+
13
+ def initialize(identifier, attributes = {})
14
+ super
15
+
16
+ %w(name url type tabs picture).each do |field|
17
+ send("#{field}=", attributes[field.to_sym])
18
+ end
19
+
20
+ self.object = if page?
21
+ FbGraph::Page.new(identifier)
22
+ elsif application?
23
+ FbGraph::Application.new(identifier)
24
+ elsif event?
25
+ FbGraph::Event.new(identifier)
26
+ elsif place?
27
+ FbGraph::Place.new(identifier)
28
+ elsif domain?
29
+ FbGraph::Domain.new(identifier)
30
+ end
31
+ end
32
+
33
+ # Defines methods for page?, application?, event? and so forth
34
+ TYPES.keys.each do |object_type|
35
+ define_method("#{object_type}?") { type == TYPES[object_type] }
36
+ end
37
+ end
38
+ end
@@ -1,6 +1,7 @@
1
1
  module FbGraph
2
2
  class Application < Node
3
3
  include Connections::Accounts
4
+ include Connections::Achievements
4
5
  include Connections::Albums
5
6
  include Connections::Events
6
7
  include Connections::Feed
@@ -20,15 +21,35 @@ module FbGraph
20
21
  # include Connections::Translations
21
22
  include Connections::Videos
22
23
 
23
- attr_accessor :name, :description, :category, :link, :secret
24
+ @@attributes = [
25
+ :name,
26
+ :description,
27
+ :canvas_name,
28
+ :category,
29
+ :company,
30
+ :icon_url,
31
+ :subcategory,
32
+ :link,
33
+ :logo_url,
34
+ :daily_active_users,
35
+ :weekly_active_users,
36
+ :monthly_active_users,
37
+ :secret
38
+ ]
39
+ attr_accessor *@@attributes
24
40
 
25
41
  def initialize(client_id, attributes = {})
26
42
  super
27
- @name = attributes[:name]
28
- @description = attributes[:description]
29
- @category = attributes[:category]
30
- @link = attributes[:link]
31
- @secret = attributes[:secret]
43
+ @@attributes.each do |key|
44
+ # NOTE:
45
+ # For some reason, Graph API returns daily_active_users, weekly_active_users, monthly_active_users as JSON string.
46
+ value = if [:daily_active_users, :weekly_active_users, :monthly_active_users].include?(key)
47
+ attributes[key].to_i
48
+ else
49
+ attributes[key]
50
+ end
51
+ self.send("#{key}=", value)
52
+ end
32
53
  end
33
54
 
34
55
  def get_access_token(secret = nil)
@@ -0,0 +1,13 @@
1
+ module FbGraph
2
+ class BroadTargetingCategory < Node
3
+ attr_accessor :name, :parent_category
4
+
5
+ def initialize(identifier, attributes = {})
6
+ super
7
+
8
+ %w(name parent_category).each do |field|
9
+ self.send("#{field}=", attributes[field.to_sym])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module FbGraph
2
+ module Connections
3
+ module Achievements
4
+ def achievements(options = {})
5
+ options[:access_token] ||= self.access_token
6
+ achievements = self.connection(:achievements, options)
7
+ achievements.map! do |achievement|
8
+ Achievement.new(achievement[:id], achievement.merge(:access_token => options[:access_token]))
9
+ end
10
+ end
11
+
12
+ def achievement!(achievement_url, options = {})
13
+ post options.merge(:connection => :achievements, :achievement => achievement_url)
14
+ end
15
+
16
+ def unregister_achievement!(achievement_url, options = {})
17
+ delete options.merge(:connection => :achievements, :achievement => achievement_url)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module FbGraph
2
+ module Connections
3
+ module AdConnectionObjects
4
+ def connection_objects(options = {})
5
+ connection_objects = self.connection(:connectionobjects, options)
6
+ connection_objects.map! do |connection_object|
7
+ AdConnectionObject.new(connection_object[:id], connection_object.merge(
8
+ :access_token => options[:access_token] || self.access_token
9
+ ))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module FbGraph
2
+ module Connections
3
+ module BroadTargetingCategories
4
+ def broad_targeting_categories(options = {})
5
+ broad_targeting_categories = self.connection(:broadtargetingcategories, options)
6
+ broad_targeting_categories.map! do |btc|
7
+ BroadTargetingCategory.new(btc[:id], btc.merge(
8
+ :access_token => options[:access_token] || self.access_token
9
+ ))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ module FbGraph
2
+ module Connections
3
+ module MutualFriends
4
+ def mutual_friends(friend, options = {})
5
+ friends = self.connection(:mutualfriends, options.merge(:connection_scope => connection_scope(friend)))
6
+ friends.map! do |friend|
7
+ User.new(friend[:id], friend.merge(:access_token => options[:access_token] || self.access_token))
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def connection_scope(friend)
14
+ if friend.is_a?(User)
15
+ friend.identifier
16
+ else
17
+ friend
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module FbGraph
2
+ module Connections
3
+ module UserAchievements
4
+ def achievements(options = {})
5
+ options[:access_token] ||= self.access_token
6
+ achievements = self.connection(:achievements, options)
7
+ achievements.map! do |achievement|
8
+ UserAchievement.new(achievement[:id], achievement.merge(:access_token => options[:access_token]))
9
+ end
10
+ end
11
+
12
+ def achieve!(achievement_url, options = {})
13
+ achievement = post options.merge(:connection => :achievements, :achievement => achievement_url)
14
+ UserAchievement.new achievement[:id], achievement
15
+ end
16
+
17
+ def unachieve!(achievement_url, options = {})
18
+ delete options.merge(:connection => :achievements, :achievement => achievement_url)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,6 +1,40 @@
1
1
  module FbGraph
2
2
  class Exception < StandardError
3
3
  attr_accessor :code, :type, :message
4
+
5
+ ERROR_HEADER_MATCHERS = {
6
+ /invalid_token/ => "InvalidToken",
7
+ /invalid_request/ => "InvalidRequest"
8
+ }
9
+
10
+ def self.handle_httpclient_error(response, headers)
11
+ return nil unless response[:error]
12
+
13
+ # Check the WWW-Authenticate header, since it seems to be more standardized than the response
14
+ # body error information.
15
+ if www_authenticate = headers["WWW-Authenticate"]
16
+ # Session expiration/invalidation is very common. Check for that first.
17
+ if www_authenticate =~ /invalid_token/ && response[:error][:message] =~ /session has been invalidated/
18
+ raise InvalidSession.new("#{response[:error][:type]} :: #{response[:error][:message]}")
19
+ end
20
+
21
+ ERROR_HEADER_MATCHERS.keys.each do |matcher|
22
+ if matcher =~ www_authenticate
23
+ exception_class = FbGraph::const_get(ERROR_HEADER_MATCHERS[matcher])
24
+ raise exception_class.new("#{response[:error][:type]} :: #{response[:error][:message]}")
25
+ end
26
+ end
27
+ end
28
+
29
+ # If we can't match on WWW-Authenticate, use the type
30
+ case response[:error][:type]
31
+ when /OAuth/
32
+ raise Unauthorized.new("#{response[:error][:type]} :: #{response[:error][:message]}")
33
+ else
34
+ raise BadRequest.new("#{response[:error][:type]} :: #{response[:error][:message]}")
35
+ end
36
+ end
37
+
4
38
  def initialize(code, message, body = '')
5
39
  @code = code
6
40
  if body.blank?
@@ -30,4 +64,10 @@ module FbGraph
30
64
  super 404, message, body
31
65
  end
32
66
  end
33
- end
67
+
68
+ class InvalidToken < Unauthorized; end
69
+
70
+ class InvalidSession < InvalidToken; end
71
+
72
+ class InvalidRequest < BadRequest; end
73
+ end
@@ -120,21 +120,12 @@ module FbGraph
120
120
  _response_.map!(&:with_indifferent_access)
121
121
  when Hash
122
122
  _response_ = _response_.with_indifferent_access
123
- handle_httpclient_error(_response_) if _response_[:error]
123
+ Exception.handle_httpclient_error(_response_, response.headers) if _response_[:error]
124
124
  _response_
125
125
  end
126
126
  end
127
127
  rescue JSON::ParserError
128
128
  raise Exception.new(response.status, 'Unparsable Error Response')
129
129
  end
130
-
131
- def handle_httpclient_error(response)
132
- case response[:error][:type]
133
- when /OAuth/
134
- raise Unauthorized.new("#{response[:error][:type]} :: #{response[:error][:message]}")
135
- else
136
- raise BadRequest.new("#{response[:error][:type]} :: #{response[:error][:message]}")
137
- end
138
- end
139
130
  end
140
- end
131
+ end
@@ -22,6 +22,7 @@ module FbGraph
22
22
  include Connections::Links
23
23
  include Connections::Movies
24
24
  include Connections::Music
25
+ include Connections::MutualFriends
25
26
  include Connections::Notes
26
27
  include Connections::Outbox
27
28
  include Connections::Payments
@@ -33,6 +34,7 @@ module FbGraph
33
34
  include Connections::Tagged
34
35
  include Connections::Television
35
36
  include Connections::Threads
37
+ include Connections::UserAchievements
36
38
  include Connections::Videos
37
39
  extend Searchable
38
40
 
@@ -0,0 +1,35 @@
1
+ module FbGraph
2
+ class UserAchievement < Node
3
+ include Connections::Comments
4
+ include Connections::Likes
5
+
6
+ attr_accessor :from, :created_time, :application, :achievement
7
+
8
+ def initialize(identifier, attributes = {})
9
+ super
10
+
11
+ if from = attributes[:from]
12
+ @from = User.new(from[:id], from)
13
+ end
14
+ if created_time = attributes[:created_time] || attributes[:publish_time]
15
+ @created_time = Time.parse(created_time).utc
16
+ end
17
+ if application = attributes[:application]
18
+ application[:link] = application[:url] # for some reason, FB uses "url" only here..
19
+ @application = Application.new(application[:id], application)
20
+ end
21
+ if achievement = attributes[:achievement]
22
+ @achievement = Achievement.new(achievement[:id], achievement)
23
+ end
24
+
25
+ # cached connection
26
+ @_likes_ = Collection.new(attributes[:likes])
27
+ @_comments_ = Collection.new(attributes[:comments])
28
+ end
29
+
30
+ def destroy(options = {})
31
+ options[:access_token] ||= self.access_token
32
+ from.unachieve!(achievement.url, options)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe FbGraph::Achievement do
4
+ subject { achievement }
5
+ let(:achievement) { FbGraph::Achievement.new(attributes[:id], attributes) }
6
+ let(:attributes) do
7
+ {
8
+ :id => "10150310611431721",
9
+ :url => "http:\/\/fbgraphsample.heroku.com\/achievements\/1",
10
+ :type => "game.achievement",
11
+ :title => "1st Achievement",
12
+ :image => [{
13
+ :url => "http:\/\/matake.jp\/images\/nov.gif"
14
+ }],
15
+ :description => "For testing purpose",
16
+ :data => {
17
+ :points => 50
18
+ },
19
+ :updated_time => "2011-09-27T08:06:59+0000",
20
+ :application => {
21
+ :id => "134145643294322",
22
+ :name => "gem sample",
23
+ :url => "http:\/\/www.facebook.com\/apps\/application.php?id=134145643294322"
24
+ },
25
+ :context => {
26
+ :display_order => 0
27
+ }
28
+ }
29
+ end
30
+
31
+ [:type, :title, :url, :description, :data, :context].each do |key|
32
+ its(key) { should == attributes[key] }
33
+ end
34
+
35
+ its(:image) { should == attributes[:image].first[:url] }
36
+ its(:images) { should == attributes[:image].collect { |h| h[:url] } }
37
+ its(:points) { should == 50 }
38
+ its(:display_order) { should == 0 }
39
+ its(:updated_time) { should == Time.parse(attributes[:updated_time]).utc }
40
+ describe 'application' do
41
+ subject { achievement.application }
42
+ its(:name) { should == attributes[:application][:name] }
43
+ its(:link) { should == attributes[:application][:url] }
44
+ end
45
+
46
+ describe '#destroy' do
47
+ it 'should call DELETE /:app_id/achievements' do
48
+ expect { achievement.destroy }.should request_to('134145643294322/achievements', :delete)
49
+ end
50
+
51
+ it 'should delete achievement' do
52
+ mock_graph :delete, '134145643294322/achievements', 'true', :access_token => 'app_token', :params => {
53
+ :achievement => achievement.url
54
+ } do
55
+ achievement.destroy(:access_token => 'app_token').should be_true
56
+ end
57
+ end
58
+ end
59
+ end