hyper-graph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Chris Dinn
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.md ADDED
@@ -0,0 +1,55 @@
1
+ HyperGraph
2
+ ===========
3
+
4
+ HyperGraph is a simple Ruby library for accessing [Facebook's Graph API](http://developers.facebook.com/docs/api)
5
+
6
+ Usage
7
+ -----
8
+
9
+ Supports id and connection get and post requests, parsing the response into a Ruby-friendly format. [Read up on the API](http://developers.facebook.com/docs/api) to learn how it works.
10
+
11
+ Create a graph to store the session key:
12
+ irb > graph = HyperGraph.new('my-access-token')
13
+ => #<HyperGraph:0x1943b98 @access_token="my-access-token">
14
+ irb > graph.get('me')
15
+ => {:updated_time=>Wed Mar 17 16:19:03 -0400 2010, :first_name=>"Chris", :last_name=>"Dinn", :name=>"Chris Dinn", :link=>"http://www.facebook.com/chrisdinn", :timezone=>-4, :birthday=>"05/28/1983", :id=>518018845, :verified=>true}
16
+
17
+ Or, make requests directly from HyperGraph, though you'll need an access token for most requests:
18
+ irb > HyperGraph.get('518018845')
19
+ => {:first_name=>"Chris", :last_name=>"Dinn", :name=>"Chris Dinn", :link=>"http://www.facebook.com/chrisdinn", :id=>518018845}
20
+ irb > HyperGraph.get('518018845', :access_token => 'my-access-token')
21
+ => {:updated_time=>Wed Mar 17 16:19:03 -0400 2010, :first_name=>"Chris", :last_name=>"Dinn", :name=>"Chris Dinn", :link=>"http://www.facebook.com/chrisdinn", :timezone=>-4, :birthday=>"05/28/1983", :id=>518018845, :verified=>true}
22
+
23
+ You can also request connections:
24
+ irb > graph.get('me/photos', :limit => 1)
25
+ => [{:source=>"http://sphotos.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_n.jpg", :picture=>"http://photos-b.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_s.jpg",
26
+ :updated_time=>Sun Jan 10 18:06:36 -0500 2010, :from=>{:name=>"Jennifer Byrne", :id=>511872444}, :name=>"Finally a day off....xmas dinner, a few days late", :link=>"http://www.facebook.com/photo.php?pid=3217612&id=511872444",
27
+ :icon=>"http://static.ak.fbcdn.net/rsrc.php/z2E5Y/hash/8as8iqdm.gif", :tags=>[{:name=>"Chris Dinn", :y=>15.3846, :created_time=>Sun Jan 10 18:06:41 -0500 2010, :id=>518018845, :x=>83.8889}], :created_time=>Sun Jan 10 18:00:10 -0500 2010,
28
+ :id=>248026512444, :width=>604, :height=>483}]
29
+
30
+ Similarly, make a post or delete request:
31
+ irb > graph.post('514569082_115714061789461/likes')
32
+ => true
33
+ irb > graph.post('514569082_115714061789461/comments', :message => 'durian is disgustingly delicious')
34
+ => true
35
+ irb > graph.delete('514569082_115714061789461/likes')
36
+ => true
37
+
38
+ Time variables are converted into Time objects, IDs are converted to integers. Paging information is discarded from requests that return an array, so be sure to managed paging manually.
39
+
40
+
41
+ Note on Patches/Pull Requests
42
+ -----------------------------
43
+
44
+ * Fork the project.
45
+ * Make your feature addition or bug fix.
46
+ * Add tests for it. This is important so I don't break it in a
47
+ future version unintentionally.
48
+ * Commit, do not mess with rakefile, version, or history.
49
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
50
+ * Send me a pull request. Bonus points for topic branches.
51
+
52
+ Copyright
53
+ -----------------------------
54
+
55
+ © 2010 Chris Dinn. See [LICENSE](http://github.com/chrisdinn/SocialGraph/blob/master/LICENSE) for details.
@@ -0,0 +1,117 @@
1
+ require 'net/http'
2
+ require 'time'
3
+ require 'json'
4
+
5
+ class FacebookError<StandardError;end
6
+
7
+ class HyperGraph
8
+ API_URL = 'graph.facebook.com'
9
+
10
+ # Class methods
11
+ class << self
12
+ #Request an object from the social graph
13
+ def get(requested_object_id, options = {})
14
+ http = Net::HTTP.new(API_URL)
15
+ request_path = "/#{requested_object_id}"
16
+
17
+ query = build_query(options)
18
+ request_path << "?#{query}" unless query==""
19
+
20
+ http_response = http.get(request_path)
21
+ data = extract_data(JSON.parse(http_response.body))
22
+ normalize_response(data)
23
+ end
24
+
25
+ def post(requested_object_id, options = {})
26
+ http = Net::HTTP.new(API_URL)
27
+ request_path = "/#{requested_object_id}"
28
+ http_response = http.post(request_path, build_query(options))
29
+ if http_response.body=='true'
30
+ return true
31
+ else
32
+ data = extract_data(JSON.parse(http_response.body))
33
+ return normalize_response(data)
34
+ end
35
+ end
36
+
37
+ def delete(requested_object_id, options = {})
38
+ post(requested_object_id, options.merge(:method => 'delete'))
39
+ end
40
+
41
+ protected
42
+
43
+ def build_query(options)
44
+ options.to_a.collect{ |i| "#{i[0].to_s}=#{i[1]}" }.sort.join('&')
45
+ end
46
+
47
+ def normalize_response(response)
48
+ normalized_response = {}
49
+ case response
50
+ when Hash
51
+ normalized_response = normalize_hash(response)
52
+ when Array
53
+ normalized_response = normalize_array(response)
54
+ end
55
+ normalized_response
56
+ end
57
+
58
+ private
59
+
60
+ # Converts JSON-parsed hash keys and values into a Ruby-friendly format
61
+ # Convert :id into integer and :updated_time into Time and all keys into symbols
62
+ def normalize_hash(hash)
63
+ normalized_hash = {}
64
+ hash.each do |k, v|
65
+ case k
66
+ when "error"
67
+ raise FacebookError.new("#{hash['error']} - #{hash['message']}")
68
+ when "id"
69
+ normalized_hash[k.to_sym] = v.to_i
70
+ when /_time$/
71
+ normalized_hash[k.to_sym] = Time.parse(v)
72
+ else
73
+ data = extract_data(v)
74
+ case data
75
+ when Hash
76
+ normalized_hash[k.to_sym] = normalize_hash(data)
77
+ when Array
78
+ normalized_hash[k.to_sym] = normalize_array(data)
79
+ else
80
+ normalized_hash[k.to_sym] = data
81
+ end
82
+ end
83
+ end
84
+ normalized_hash
85
+ end
86
+
87
+ def normalize_array(array)
88
+ array.collect{ |item| normalize_response(item) }.sort{ |a, b| a[:id] <=> b[:id] }
89
+ end
90
+
91
+ # Extracts data from "data" key in Hash, if present
92
+ def extract_data(object)
93
+ if object.is_a?(Hash)&&object.keys.include?('data')
94
+ return object['data']
95
+ else
96
+ return object
97
+ end
98
+ end
99
+ end
100
+
101
+ # Instance methods
102
+ def initialize(access_token)
103
+ @access_token = access_token
104
+ end
105
+
106
+ def get(requested_object_id, options = {})
107
+ self.class.get(requested_object_id, options.merge(:access_token => @access_token))
108
+ end
109
+
110
+ def post(requested_object_id, options = {})
111
+ self.class.post(requested_object_id, options.merge(:access_token => @access_token))
112
+ end
113
+
114
+ def delete(requested_object_id, options = {})
115
+ self.class.delete(requested_object_id, options.merge(:access_token => @access_token))
116
+ end
117
+ end
@@ -0,0 +1,154 @@
1
+ require 'test_helper'
2
+
3
+ class HyperGraphTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @mock_connection = mock('api-connection')
7
+ Net::HTTP.stubs(:new).returns(@mock_connection)
8
+ end
9
+
10
+ def test_get_object_info_while_unauthenticated
11
+ json_api_response = '{"id":"518018845","name":"Chris Dinn","first_name":"Chris","last_name":"Dinn","link":"http://www.facebook.com/chrisdinn"}'
12
+ expected_parsed_response = { :id => 518018845,
13
+ :name => "Chris Dinn",
14
+ :first_name => "Chris",
15
+ :last_name => "Dinn",
16
+ :link => "http://www.facebook.com/chrisdinn"}
17
+
18
+ mock_response = stub(:body => json_api_response)
19
+ @mock_connection.stubs(:get).with("/518018845").returns(mock_response)
20
+
21
+ assert_equal expected_parsed_response, HyperGraph.get('518018845')
22
+ end
23
+
24
+ def test_get_object_info_when_authenticated
25
+ json_api_response = '{"id":"518018845","name":"Chris Dinn","first_name":"Chris","last_name":"Dinn","link":"http://www.facebook.com/chrisdinn","birthday":"05/28/1983","email":"expected-app-specific-email@proxymail.facebook.com","timezone":-4,"verified":true,"updated_time":"2010-03-17T20:19:03+0000"}'
26
+ expected_parsed_response = { :id => 518018845,
27
+ :name => "Chris Dinn",
28
+ :first_name => "Chris",
29
+ :last_name => "Dinn",
30
+ :link => "http://www.facebook.com/chrisdinn",
31
+ :birthday => "05/28/1983",
32
+ :email => "expected-app-specific-email@proxymail.facebook.com",
33
+ :timezone => -4,
34
+ :verified => true,
35
+ :updated_time => Time.parse("2010-03-17T20:19:03+0000")}
36
+
37
+ access_token = "test-access-token"
38
+ mock_response = stub(:body => json_api_response)
39
+ @mock_connection.stubs(:get).with("/518018845?access_token=#{access_token}").returns(mock_response)
40
+
41
+ assert_equal expected_parsed_response, HyperGraph.get('518018845', :access_token => access_token)
42
+ end
43
+
44
+ def test_get_object_connection_when_authenticated
45
+ json_api_response = '{ "data":[{"name": "Andrew Louis", "id": "28103622"},{"name": "Joey Coleman","id": "72600429"},{"name": "Kathryn Kinley","id": "28125421"},{"name": "Vanessa Larkey", "id": "28112600"}] }'
46
+ expected_sorted_array = [{:name => "Andrew Louis", :id => 28103622},{:name => "Vanessa Larkey", :id => 28112600},{:name => "Kathryn Kinley", :id => 28125421}, {:name => "Joey Coleman", :id => 72600429}]
47
+
48
+ access_token = "test-access-token"
49
+ mock_response = stub(:body => json_api_response)
50
+ @mock_connection.stubs(:get).with("/518018845/friends?access_token=#{access_token}").returns(mock_response)
51
+
52
+ assert_equal expected_sorted_array, HyperGraph.get('518018845/friends', :access_token => access_token)
53
+ end
54
+
55
+ def test_object_pagination
56
+ json_api_response = '{ "data": [ { "id": "248026512444",
57
+ "from": { "name": "Jennifer Byrne", "id": "511872444" },
58
+ "tags": { "data": [ { "id": "518018845", "name": "Chris Dinn", "x": 83.8889, "y": 15.3846, "created_time": "2010-01-10T23:06:41+0000" } ] },
59
+ "name": "Finally a day off....xmas dinner, a few days late", "picture": "http://photos-b.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_s.jpg", "source": "http://sphotos.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_n.jpg",
60
+ "height": 483, "width": 604, "link": "http://www.facebook.com/photo.php?pid=3217612&id=511872444", "icon": "http://static.ak.fbcdn.net/rsrc.php/z2E5Y/hash/8as8iqdm.gif",
61
+ "created_time": "2010-01-10T23:00:10+0000", "updated_time": "2010-01-10T23:06:36+0000" },
62
+ { "id": "248026522444",
63
+ "from": { "name": "Jennifer Byrne", "id": "511872444" },
64
+ "tags": { "data": [ { "id": "518018845", "name": "Chris Dinn","x": 92.2222, "y": 29.6296, "created_time": "2010-01-10T23:06:42+0000"}, { "id": "691356318", "name": "Steve Durant","x": 34.4444,"y": 19.2593,"created_time": "2010-01-10T23:06:42+0000" } ] },
65
+ "name": "Steve cooking dinner", "picture": "http://photos-e.ak.fbcdn.net/hphotos-ak-snc3/hs219.snc3/22675_248026522444_511872444_3217614_8129653_s.jpg","source": "http://sphotos.ak.fbcdn.net/hphotos-ak-snc3/hs219.snc3/22675_248026522444_511872444_3217614_8129653_n.jpg",
66
+ "height": 604, "width": 402, "link": "http://www.facebook.com/photo.php?pid=3217614&id=511872444", "icon": "http://static.ak.fbcdn.net/rsrc.php/z2E5Y/hash/8as8iqdm.gif",
67
+ "created_time": "2010-01-10T23:00:10+0000", "updated_time": "2010-01-10T23:06:36+0000" } ]}'
68
+ expected_sorted_array = [ {:id => 248026512444, :from => { :name => "Jennifer Byrne", :id => 511872444}, :tags => [ { :id => 518018845, :name => "Chris Dinn", :x => 83.8889, :y => 15.3846, :created_time => Time.parse("2010-01-10T23:06:41+0000") } ],
69
+ :name => "Finally a day off....xmas dinner, a few days late",
70
+ :picture => "http://photos-b.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_s.jpg",
71
+ :source => "http://sphotos.ak.fbcdn.net/hphotos-ak-snc3/hs239.snc3/22675_248026512444_511872444_3217612_4249159_n.jpg",
72
+ :height => 483, :width => 604, :link => "http://www.facebook.com/photo.php?pid=3217612&id=511872444",
73
+ :icon => "http://static.ak.fbcdn.net/rsrc.php/z2E5Y/hash/8as8iqdm.gif",
74
+ :created_time => Time.parse("2010-01-10T23:00:10+0000"),
75
+ :updated_time => Time.parse("2010-01-10T23:06:36+0000") },
76
+ {:id => 248026522444, :from => { :name => "Jennifer Byrne", :id => 511872444}, :tags => [ { :id => 518018845, :name => "Chris Dinn", :x => 92.2222, :y => 29.6296, :created_time => Time.parse("2010-01-10T23:06:42+0000") }, { :id => 691356318, :name => "Steve Durant", :x => 34.4444, :y => 19.2593, :created_time => Time.parse("2010-01-10T23:06:42+0000") } ],
77
+ :name => "Steve cooking dinner",
78
+ :picture => "http://photos-e.ak.fbcdn.net/hphotos-ak-snc3/hs219.snc3/22675_248026522444_511872444_3217614_8129653_s.jpg",
79
+ :source => "http://sphotos.ak.fbcdn.net/hphotos-ak-snc3/hs219.snc3/22675_248026522444_511872444_3217614_8129653_n.jpg",
80
+ :height => 604, :width => 402, :link => "http://www.facebook.com/photo.php?pid=3217614&id=511872444",
81
+ :icon => "http://static.ak.fbcdn.net/rsrc.php/z2E5Y/hash/8as8iqdm.gif",
82
+ :created_time => Time.parse("2010-01-10T23:00:10+0000"), :updated_time => Time.parse("2010-01-10T23:06:36+0000")}]
83
+ access_token = "test-access-token"
84
+ limit = 2
85
+ mock_response = stub(:body => json_api_response)
86
+ @mock_connection.stubs(:get).with("/me/photos?access_token=#{access_token}&limit=#{limit}").returns(mock_response)
87
+
88
+ assert_equal expected_sorted_array, HyperGraph.get('me/photos', :access_token => access_token, :limit => limit)
89
+ end
90
+
91
+ def test_instance_with_stored_access_token
92
+ json_api_response = '{"id":"518018845","name":"Chris Dinn","first_name":"Chris","last_name":"Dinn","link":"http://www.facebook.com/chrisdinn","birthday":"05/28/1983","email":"expected-app-specific-email@proxymail.facebook.com","timezone":-4,"verified":true,"updated_time":"2010-03-17T20:19:03+0000"}'
93
+ expected_parsed_response = { :id => 518018845,
94
+ :name => "Chris Dinn",
95
+ :first_name => "Chris",
96
+ :last_name => "Dinn",
97
+ :link => "http://www.facebook.com/chrisdinn",
98
+ :birthday => "05/28/1983",
99
+ :email => "expected-app-specific-email@proxymail.facebook.com",
100
+ :timezone => -4,
101
+ :verified => true,
102
+ :updated_time => Time.parse("2010-03-17T20:19:03+0000")}
103
+ access_token = "test-access-token"
104
+ mock_response = stub(:body => json_api_response)
105
+ @mock_connection.stubs(:get).with("/me?access_token=#{access_token}").returns(mock_response)
106
+
107
+ graph = HyperGraph.new(access_token)
108
+
109
+ assert_equal expected_parsed_response, HyperGraph.get('me', :access_token => access_token)
110
+ assert_equal expected_parsed_response, graph.get('me')
111
+ end
112
+
113
+ def test_post_request_returning_true
114
+ json_api_response = 'true'
115
+ access_token = "test-access-token"
116
+ mock_response = stub(:body => json_api_response)
117
+ @mock_connection.stubs(:post).with("/115934485101003/maybe", "access_token=#{access_token}").returns(mock_response)
118
+
119
+ graph = HyperGraph.new(access_token)
120
+ assert_equal true, graph.post('115934485101003/maybe')
121
+ end
122
+
123
+ def test_post_request_raising_error
124
+ json_api_response = '{"error":{"type":"Exception","message":"(#210) User not visible"}}'
125
+ access_token = "test-access-token"
126
+ mock_response = stub(:body => json_api_response)
127
+ @mock_connection.stubs(:post).with('/514569082_115714061789461/comments', "access_token=#{access_token}").returns(mock_response)
128
+
129
+ graph = HyperGraph.new(access_token)
130
+ assert_raise FacebookError do
131
+ graph.post('514569082_115714061789461/comments')
132
+ end
133
+ end
134
+
135
+ def test_get_request_raising_error
136
+ json_api_response = '{"error":{"type":"QueryParseException", "message":"An active access token must be used to query information about the current user."}}'
137
+ mock_response = stub(:body => json_api_response)
138
+ @mock_connection.stubs(:get).with('/me/home').returns(mock_response)
139
+
140
+ assert_raise FacebookError do
141
+ HyperGraph.get('me/home')
142
+ end
143
+ end
144
+
145
+ def test_delete_request
146
+ json_api_response = 'true'
147
+ access_token = "test-access-token"
148
+ mock_response = stub(:body => json_api_response)
149
+ @mock_connection.stubs(:post).with("/514569082_115714061789461/likes", "access_token=#{access_token}&method=delete").returns(mock_response)
150
+
151
+ graph = HyperGraph.new(access_token)
152
+ assert_equal true, graph.delete('514569082_115714061789461/likes')
153
+ end
154
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'bundler'
4
+
5
+ Bundler.setup(:default, :test)
6
+ Bundler.require(:default, :test)
7
+
8
+ require 'hyper_graph'
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyper-graph
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Chris Dinn
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-23 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description:
33
+ email:
34
+ - chrisgdinn@gmail.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/hyper_graph.rb
43
+ - test/hyper_graph_test.rb
44
+ - test/test_helper.rb
45
+ - LICENSE
46
+ - README.md
47
+ has_rdoc: true
48
+ homepage: http://github.com/chrisdinn/hyper-graph
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.6
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: A gem for working with the Facebook Graph API
77
+ test_files: []
78
+