doesopengraph 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
-