doesopengraph 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,14 @@
6
6
  #
7
7
 
8
8
  module DoesOpenGraph
9
+
10
+ class OpenGraphException < Exception; end
11
+ class IncapableOfUpdateMethods < OpenGraphException; end
12
+ class InvalidRequestMethod < OpenGraphException; end
13
+ class InvalidResponseFromFacebook < OpenGraphException; end
14
+
15
+
16
+
9
17
  class GraphAPI
10
18
 
11
19
  require "typhoeus"
@@ -15,99 +23,70 @@ module DoesOpenGraph
15
23
  HTTP_GRAPH_ENDPOINT = "http://graph.facebook.com/"
16
24
  HTTPS_GRAPH_ENDPOINT = "https://graph.facebook.com/"
17
25
 
18
- attr_reader :access_token
26
+ attr_reader :access_token, :history
19
27
 
20
28
  def initialize(acctok=nil)
21
29
  @access_token = acctok
30
+ @history = Array.new
22
31
  end
23
-
24
-
32
+
33
+ def renew(acctok)
34
+ @access_token = acctok
35
+ end
36
+
37
+
25
38
  def node(id, connection=nil, params={})
26
- # Inject an access_token if we plan to make an authorized request:
27
- params[:access_token] = @access_token if @access_token
28
-
29
- # Stringify tokens:
30
- id = id.to_s
31
- connection = connection.to_s unless connection.nil?
32
-
33
- # Smoosh the URL components together:
34
- base = @access_token.nil? ? HTTP_GRAPH_ENDPOINT : HTTPS_GRAPH_ENDPOINT
35
- path = connection.nil? ? id : File.join(id, connection)
36
- href = File.join(base, path)
37
-
38
- # Make a request and parse JSON result:
39
- begin
40
- response = Typhoeus::Request.get(href, :params=>params)
41
- data = JSON.parse(response.body)
42
- return GraphNode.new(data, self)
43
- rescue JSON::ParserError => jsone
44
- raise "Invalid JSON or poorly formed JSON returned for #{path}" and return nil
45
- end
39
+ path = connection.nil? ? id.to_s : File.join(id.to_s, connection.to_s)
40
+ return request(:get, path, params)
46
41
  end
47
42
  alias_method :get, :node
48
43
 
49
44
 
50
45
  def update(id, connection, params={})
51
- # Inject the access token if we have it and err if we don't
52
46
  return nil unless @access_token
53
- params[:access_token] = @access_token
54
-
55
- # Smoosh the URL components together:
56
- base = HTTPS_GRAPH_ENDPOINT
57
- path = File.join(id, connection)
58
- href = File.join(base, path)
59
-
60
- # Make our POST request and check the results:
61
- begin
62
- response = Typhoeus::Request.post(href, :params=>params)
63
- data = JSON.parse(response.body)
64
- return GraphResponse.new(data)
65
- rescue JSON::ParserError => jsone
66
- # A JSON.parse on "true" triggers an error, so let's build it straight from body:
67
- return GraphResponse.new(response.body)
68
- end
47
+ path = connection.nil? ? id.to_s : File.join(id.to_s, connection.to_s)
48
+ return request(:post, path, params)
69
49
  end
70
50
  alias_method :post, :update
71
51
 
72
52
 
73
53
  def delete(id, connection=nil)
74
- # Inject the access token if we have it and err if we don't
75
54
  return nil unless @access_token
76
- params = Hash.new and params[:access_token] = @access_token
77
-
78
- # Smoosh the URL components together:
79
- base = HTTPS_GRAPH_ENDPOINT
80
- path = connection.nil? ? id : File.join(id, connection)
81
- href = File.join(base, path)
82
-
83
- # Make our DELETE request and return the results:
84
- begin
85
- response = Typhoeus::Request.delete(href, :params=>params)
86
- data = JSON.parse(response.body)
87
- return GraphResponse.new(data)
88
- rescue JSON::ParserError => jsone
89
- # A JSON.parse on "true" triggers an error, so let's build it straight from body:
90
- return GraphResponse.new(response.body)
91
- end
55
+ path = connection.nil? ? id.to_s : File.join(id.to_s, connection.to_s)
56
+ return request(:delete, path)
92
57
  end
93
58
 
94
-
59
+
95
60
  def search(query, type, params={})
96
- # Inject the access token if we have it and err if we don't
97
61
  return nil unless @access_token
98
- params[:access_token] = @access_token
99
-
100
- # Build the search query and its target:
101
- params[:q] = query
102
- params[:type] = type
103
- href = File.join(HTTPS_GRAPH_ENDPOINT, "search")
104
-
105
- # Make the request:
106
- response = Typhoeus::Request.get(href, :params=>params)
107
- data = JSON.parse(response.body)
108
- return GraphResponse.new(data)
62
+ params[:q] = query.to_s
63
+ params[:type] = type.to_s
64
+ request(:get, "search", params)
65
+ end
66
+
67
+
68
+ def num_requests
69
+ @history.length
109
70
  end
110
71
 
72
+ def previous_request
73
+ @history.last
74
+ end
75
+
76
+ def repeat
77
+ previous_request.request()
78
+ end
79
+
80
+
81
+
82
+ private
83
+
84
+
85
+ def request(method, path, params={})
86
+ api_request = GraphRequest.new(self, method, path, params)
87
+ @history << api_request
88
+ return api_request.request()
89
+ end
111
90
 
112
91
 
113
92
  end # GraphAPI
@@ -0,0 +1,59 @@
1
+ # AWEXOME LABS
2
+ # DoesOpenGraph
3
+ #
4
+ # GraphRequest - A request to the OpenGraph API.
5
+ #
6
+
7
+ module DoesOpenGraph
8
+ class GraphRequest
9
+
10
+ attr_reader :api, :method, :path, :params, :href
11
+
12
+ # Build a Request object from its component parts
13
+ def initialize(api, method, path, params={})
14
+ @api = api
15
+ @method = method
16
+ @path = path
17
+ @params = params
18
+ @href = nil
19
+ end
20
+
21
+
22
+ # Perform the request
23
+ def request
24
+ base = @api.access_token ? GraphAPI::HTTPS_GRAPH_ENDPOINT : GraphAPI::HTTP_GRAPH_ENDPOINT
25
+ @href = File.join(base, @path)
26
+
27
+ if !%w(get post delete).include?(@method.to_s)
28
+ raise InvalidRequestMethod.new("Invalid HTTP method #{@method} passed to request") and return nil
29
+ end
30
+
31
+ params[:access_token] = @api.access_token if @api.access_token
32
+
33
+ begin
34
+ response = Typhoeus::Request.send(@method, @href, :params=>@params)
35
+ puts "RESPONSE RECEIVED FROM FACEBOOK ON REQUEST TO PATH #{@path}:\n#{response.body}\n\n"
36
+
37
+ return GraphResponse.new(response.body, self)
38
+
39
+ # TODO: Parse known error responses from Facebook, such as:
40
+ # TODO: {"error":{"message":"Unknown path components: \/status","type":"OAuthException"}}
41
+
42
+ rescue Exception => e
43
+ raise OpenGraphException.new("Error in OpenGraph response: #{e}") and return nil
44
+ end
45
+ end
46
+
47
+
48
+ # Repeat the same request with optionally different parameters
49
+ def repeat(params={})
50
+ @params.merge(params)
51
+ request()
52
+ end
53
+
54
+
55
+ end # GraphRequest
56
+ end # DoesOpenGraph
57
+
58
+
59
+
@@ -7,17 +7,131 @@
7
7
  module DoesOpenGraph
8
8
  class GraphResponse
9
9
 
10
- attr_reader :content, :api
10
+ attr_reader :content, :object, :request
11
11
 
12
- # Build a Response object from request content and store the api
13
- def initialize(content, api=nil)
14
- @content = content.is_a?(Hash) ? content : Hash.new
15
- @api = api
12
+ # Build a Response object from raw JSON HTTP response
13
+ def initialize(raw_content, request=nil)
14
+ @request = request
15
+ self.update(raw_content)
16
16
  end
17
-
18
- # Use secret patching to provide accessors to connections within node
17
+
18
+ # Update the stored content of this node and parse
19
+ def update (raw_content)
20
+ @content = raw_content
21
+ self.parse
22
+ end
23
+
24
+ # Parse the stored raw content and translate into a usable object
25
+ def parse
26
+ begin
27
+ parsed_content = JSON.parse(@content)
28
+ @object = parsed_content.is_a?(Hash) ? Hashie::Mash.new(parsed_content) : parsed_content
29
+
30
+ rescue JSON::ParserError => parse_error
31
+ @object = nil
32
+ if parse_error.message.match("unexpected token")
33
+ @object = true if @content == "true"
34
+ @object = false if @content == "false"
35
+ end
36
+ if @object.nil?
37
+ raise InvalidResponseFromFacebook.new("Invalid JSON returned from Facebook: #{parse_error.message}")
38
+ end
39
+ end
40
+ end
41
+
42
+
43
+ # Update this node from theFetch an updated view of this node
44
+ def reload
45
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
46
+ up = request.request()
47
+ @content = up.content
48
+ @object = up.object
49
+ return self
50
+ end
51
+
52
+ # Is this response an error?
53
+ def error?
54
+ keys.include?(:error)
55
+ end
56
+
57
+ # What is the error return from Facebook in this response?
58
+ def error_message
59
+ @object.error ? @object.error.message : nil
60
+ end
61
+
62
+ # Introspect on the connections available to this node
63
+ def introspect
64
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
65
+ request.repeat(:metadata=>1)
66
+ end
67
+
68
+ # Get a connection of this node
69
+ def get(connection, params={})
70
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
71
+ request.api.get(object.id, connection, params)
72
+ end
73
+
74
+ # Post to a connection of this node
75
+ def post(connection, params={})
76
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
77
+ request.api.post(object.id, connection, params)
78
+ end
79
+
80
+
81
+ # Load the next page of the response if paging is available
82
+ def next_page; page("next"); end
83
+ def previous_page; page("previous"); end
84
+
85
+ # Load a specific page of results if available:
86
+ def page(pg, pp=25)
87
+ if pg.is_a?(String)
88
+ if object.paging
89
+ if page_url = object.paging[pg]
90
+ data = page_url.match(/\?(\S+)/)[1].split("&").collect{|pair| pair.split("=")}.select{|k,v| k!="access_token"}
91
+ params = Hash.new
92
+ data.each {|k,v| params[k.to_sym] = v}
93
+ return request.repeat(params)
94
+ end
95
+ end
96
+ else
97
+ return request.repeat(:limit=>pp, :offset=>(pg-1)*pp)
98
+ end
99
+ return nil
100
+ end
101
+
102
+
103
+ # Delete this node from the graph
104
+ def delete
105
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
106
+ request.api.delete(object.id)
107
+ end
108
+
109
+ # Like this node, if supported
110
+ def like
111
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
112
+ request.api.post(object.id, "likes")
113
+ end
114
+
115
+ # Unlike this node, if supported
116
+ def unlike
117
+ raise IncapableOfUpdateMethods.new("Cannot update content without stored request") if request.nil?
118
+ request.api.delete(object.id, "likes")
119
+ end
120
+
121
+
122
+ # What keys are available on this OpenGraph node?
123
+ def keys
124
+ object.is_a?(Hash) ? object.keys.collect{|k|k.to_sym} : Array.new
125
+ end
126
+
127
+ # Include our return top-level keys in the methods list:
128
+ def methods()
129
+ keys + super()
130
+ end
131
+
132
+ # Use method-missing to provide top-level keys of response Hash as methods
19
133
  def method_missing(m, *args, &block)
20
- content.include?(m.to_s) ? content[m.to_s] : nil
134
+ object.include?(m.to_s) ? object.send(m.to_s) : super(m, args)
21
135
  end
22
136
 
23
137
  end # GraphResponse
data/lib/doesopengraph.rb CHANGED
@@ -3,10 +3,17 @@
3
3
  #
4
4
  # DoesOpenGraph - Module definition and loader
5
5
 
6
+ require 'hashie'
7
+
6
8
  require "doesopengraph"
7
9
  require "doesopengraph/graph_api"
10
+ require "doesopengraph/graph_request"
8
11
  require "doesopengraph/graph_response"
9
- require "doesopengraph/graph_node"
10
12
 
11
13
  module DoesOpenGraph
14
+
15
+ def self.version
16
+ Gem.loaded_specs["doesopengraph"].version.to_s
17
+ end
18
+
12
19
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: doesopengraph
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.1
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - mccolin
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-03-25 00:00:00 -04:00
13
+ date: 2011-09-23 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -57,7 +57,7 @@ dependencies:
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: *id004
60
- description: The Awexome Labs library for accessing and manipulating the Facebook OpenGraph
60
+ description: Content-type-agnostic library for accessing and manipulating the Facebook OpenGraph with all major methods and search
61
61
  email: info@awexomelabs.com
62
62
  executables: []
63
63
 
@@ -69,7 +69,7 @@ extra_rdoc_files:
69
69
  files:
70
70
  - lib/doesopengraph.rb
71
71
  - lib/doesopengraph/graph_api.rb
72
- - lib/doesopengraph/graph_node.rb
72
+ - lib/doesopengraph/graph_request.rb
73
73
  - lib/doesopengraph/graph_response.rb
74
74
  - LICENSE
75
75
  - README.rdoc
@@ -1,42 +0,0 @@
1
- # AWEXOME LABS
2
- # DoesOpenGraph
3
- #
4
- # GraphNode - An instance of an item on the open graph
5
- #
6
-
7
- module DoesOpenGraph
8
- class GraphNode < GraphResponse
9
-
10
- # Fetch an updated view of this node
11
- def reload
12
- up = api.node(self.id)
13
- @content = up.content
14
- self
15
- end
16
-
17
- # Introspect on the connections available to this node
18
- def introspect
19
- api.node(self.id, nil, :metadata=>1)
20
- end
21
-
22
- # Delete this node from the graph
23
- def delete
24
- api.delete(self.id)
25
- end
26
-
27
- # Like this node, if supported
28
- def like
29
- api.post(self.id, "likes")
30
- end
31
-
32
- # Unlike this node, if supported
33
- def unlike
34
- api.delete(self.id, "likes")
35
- end
36
-
37
-
38
- end # GraphNode
39
- end # DoesOpenGraph
40
-
41
-
42
-