├── .bundle └── config ├── .gitignore ├── .rspec ├── Gemfile ├── LICENSE ├── Manifest ├── README.textile ├── Rakefile ├── TODO.list ├── fbgraph.gemspec ├── lib ├── fbgraph.rb └── fbgraph │ ├── authorization.rb │ ├── base.rb │ ├── cacert.pem │ ├── canvas.rb │ ├── client.rb │ ├── fql.rb │ ├── logger.rb │ ├── realtime.rb │ ├── result.rb │ ├── search.rb │ ├── selection.rb │ ├── timeline.rb │ └── version.rb └── spec ├── lib └── fbauth │ ├── authorization_spec.rb │ ├── base_spec.rb │ ├── canvas_spec.rb │ ├── client_spec.rb │ ├── realtime_spec.rb │ ├── search_spec.rb │ └── selection_spec.rb ├── spec_helper.rb └── support └── fbgraph_spec_helper.rb /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_DISABLE_SHARED_GEMS: '1' 3 | BUNDLE_PATH: vendor/bundler/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | *.log 3 | .DS_Store 4 | doc 5 | tmp 6 | pkg 7 | *.gem 8 | *.pid 9 | coverage 10 | coverage.data 11 | build/* 12 | *.pbxuser 13 | *.mode1v3 14 | .project 15 | .svn 16 | profile 17 | vendor/* 18 | gemspec/* 19 | .rvmrc 20 | .rbenv-version 21 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 20010-2012 Nicolas Santa 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Manifest: -------------------------------------------------------------------------------- 1 | Manifest 2 | README 3 | README.textile 4 | Rakefile 5 | TODO.list 6 | fbgraph.gemspec 7 | lib/fbgraph.rb 8 | lib/fbgraph/authorization.rb 9 | lib/fbgraph/base.rb 10 | lib/fbgraph/canvas.rb 11 | lib/fbgraph/client.rb 12 | lib/fbgraph/realtime.rb 13 | lib/fbgraph/search.rb 14 | lib/fbgraph/selection.rb 15 | specs/lib/fbauth/authorization_spec.rb 16 | specs/lib/fbauth/base_spec.rb 17 | specs/lib/fbauth/client_spec.rb 18 | specs/lib/fbauth/realtime_spec.rb 19 | specs/lib/fbauth/search_spec.rb 20 | specs/lib/fbauth/selection_spec.rb 21 | specs/spec_helper.rb 22 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h2. FBGRaph 2 | 3 | p. Facebook Open Graph API Gem for Rails 3. 4 | 5 | 6 | h2. Resources 7 | 8 | * "View RDoc on RDoc.info":http://rdoc.info/projects/nsanta/fbgraph 9 | 10 | * "View Source on GitHub":http://github.com/nsanta/fbgraph 11 | 12 | * "Report Issues on GitHub":http://github.com/nsanta/fbgraph/issues 13 | 14 | 15 | h2. Installation 16 | 17 | notextile. gem install fbgraph 18 | 19 | p. Be sure to require it 20 | 21 | notextile. require "fbgraph" 22 | 23 | p. or add this line into Gemfile for Rails 3 24 | 25 | notextile. gem "fbgraph" 26 | 27 | 28 | h2. Example Apps 29 | 30 | "Rails 3 Example":http://github.com/nsanta/fbgraph_example 31 | 32 | h2. Usage 33 | 34 | p. FBGraph supports most (no analytics yet) features of Facebook Open Graph API: developers.facebook.com/docs/reference/api/ 35 | 36 | **IMPORTANT!!** Facebook object IDs can be very large numbers--too large to fit in a regular SQL "INTEGER" column. If you use an integer column, your database will likely just store the largest number allowed resulting in a bug that may confound you beyond belief. 37 | 38 | **IF YOU PLAN TO STORE FACEBOOK GRAPH OBJECT IDS IN YOUR DATABASE, YOU MUST USE A BIGINT COLUMN, NOT A STANDARD INTEGER COLUMN!** 39 | 40 | h3. Initialization 41 | 42 | p. Without a token (for authorization) 43 | 44 | notextile. client = FBGraph::Client.new(:client_id => 'client_id',:secret_id =>'secret_id') 45 | 46 | p. With a token 47 | 48 | notextile. client = FBGraph::Client.new(:client_id => 'client_id',:secret_id =>'secret_id' ,:token => token) 49 | 50 | p. All methods are chainable 51 | 52 | Examples: 53 | 54 | notextile. client.selection.me.photos.until(Time.now.to_s).since(3.days.ago).limit(10).info! 55 | 56 | notextile. client.selection.user('id').videos.offset(10).info! 57 | 58 | notextile. client.search.query('q').on('users').limit(20).info! 59 | 60 | h3. Rails config file 61 | 62 | h2. TODO 63 | 64 | h3. Authorization 65 | 66 | h4. client.authorization.authorize_url 67 | 68 | p. returns the authorize url 69 | 70 | notextile. redirect_to client.authorization.authorize_url(:redirect_uri => callback_url , :scope => 'email,user_photos,friends_photos') 71 | 72 | h4. client.authorization.process_callback 73 | 74 | p. process the callback and returns the access token 75 | 76 | notextile. access_token = client.authorization.process_callback(params[:code], :redirect_uri => callback_url) 77 | 78 | h3. Exchange Sessions 79 | 80 | h2. TODO 81 | 82 | h3. Canvas 83 | 84 | Facebook send a signed_request as a parameter. Can be decoded with the method parse_signed_request of FBGraph::Canvas module 85 | 86 | > FBGraph::Canvas.parse_signed_request(app_secret, params[:signed_request]) 87 | 88 | h3. Selection 89 | 90 | h4. Accessing objects with connection types. 91 | 92 | p. All objects and their connections can be accesed 93 | 94 | Examples: 95 | 96 | notextile. client.selection.me.home.info! 97 | 98 | notextile. client.selection.user('id').photos.info! 99 | 100 | notextile. client.selection.photo('id').comments.info! 101 | 102 | notextile. client.selection.page('id').info! 103 | 104 | p. Also you can get results of more than 1 objects 105 | 106 | Example: 107 | 108 | notextile. client.selection.user([id1,id2,id3]).info! 109 | 110 | 111 | h4. client.selection.info! 112 | 113 | p. request with GET for information and return the response parsed with JSON. You can disable the parsing passing false as a first and unique parameter 114 | 115 | notextile. user_info = client.selection.me.info! 116 | 117 | 118 | h3. Publishing 119 | 120 | h4. client.selection.publish! 121 | 122 | p. request with POST for publishing and return the response parsed with JSON. You can disable the parsing passing false as a first and unique parameter 123 | 124 | notextile. client.selection.post('id').comments.params(:message => 'comment test').publish! 125 | 126 | p. OR 127 | 128 | notextile. client.selection.post('id').comments.publish!(:message => 'comment test') 129 | 130 | 131 | h3. Deletion 132 | 133 | h4. client.selection.delete! 134 | 135 | p. request with DELETE for deletion and return the response parsed with JSON. You can disable the parsing passing false as a first and unique parameter 136 | 137 | notextile. client.selection.post('id').delete! 138 | 139 | 140 | h3. Picture 141 | 142 | 143 | h4. client.selection.picture 144 | 145 | p. return the url of the object picture 146 | 147 | notextile. client.selection.me.picture 148 | 149 | 150 | h3. Paging 151 | 152 | h4. client.selection.limit 153 | 154 | 155 | notextile. client.selection.me.photos.limit(3).info! 156 | 157 | h4. client.selection.offset 158 | 159 | 160 | notextile. client.selection.me.photos.offset(10).info! 161 | 162 | h4. client.selection.until 163 | 164 | 165 | notextile. client.selection.me.photos.until(Time.now.to_s).info! 166 | 167 | h4. client.selection.since 168 | 169 | 170 | notextile. client.selection.me.photos.since(3.days.ago).info! 171 | 172 | 173 | h3. Search 174 | 175 | h4. client.search.query('query').info! 176 | 177 | p. Get the search results 178 | 179 | notextile. results = client.search.query('facebook').info! 180 | 181 | 182 | h4. client.search.query('query')on('type').info! 183 | 184 | p. Get the search results by type 185 | 186 | notextile. results = client.search.query('facebook').on('home').info! 187 | 188 | h3. RealTime Updates 189 | 190 | h4. client.realtime.user 191 | 192 | h4. client.realtime.permissions 193 | 194 | h4. client.realtime.errors 195 | 196 | p. Set the object to be subscribed, modified or unsubscribed 197 | 198 | h4. client.realtime.fields('email,picture') 199 | 200 | p. Set the objects fields 201 | 202 | 203 | h4. client.realtime.callback_url(url) 204 | 205 | p. Set the callback url 206 | 207 | 208 | h4. client.realtime.verify_token(token) 209 | 210 | p. Set the verify token (optional) 211 | 212 | h4. client.realtime.subscribe! 213 | 214 | p. Send the request for add/modify a subscription for realtime Updates. 215 | 216 | 217 | Examples: 218 | 219 | notextile. results = client.realtime.user.fields('email,picture').callback_url(url).verify_token('token').subscribe! 220 | 221 | notextile. results = client.realtime.permissions.fields('read_stream').callback_url(url).subscribe! 222 | 223 | If you want delete a subscirpition, you can use the delete! method. 224 | 225 | Examples: 226 | 227 | notextile. results = client.realtime.user.delete! 228 | 229 | 230 | h3. FQL 231 | 232 | h4. client.fql.query("SELECT name FROM user WHERE uid = me()") 233 | 234 | 235 | h3. Timeline 236 | 237 | h4. client.timeline.action('namespace', 'run').param(:location => [location object URL]) 238 | 239 | h4. client.timeline.reads.param(:article => [article object URL] ) 240 | 241 | 242 | h3. Analytics 243 | 244 | h4. TODO 245 | 246 | h3. Advanced 247 | 248 | h4. not documented yet 249 | 250 | 251 | h2. Credits 252 | 253 | Examples: 254 | 255 | notextile. results = client.selection.user(USER_ID).payments.info!(:status => STATUS). 256 | 257 | notextile. results = client.selection.order(ORDER_ID).publish!(:status => STATUS) 258 | 259 | 260 | h2. Contributions 261 | 262 | p. Just do a pull request with the repo in sync. 263 | 264 | 265 | 266 | h2. Maintainers 267 | 268 | "Nicolas Santa":http://github.com/nsanta 269 | 270 | "Matt Lightner (via RedRadiant)":http://github.com/redradiant 271 | 272 | "Victor Costan":http://github.com/pwnall 273 | 274 | h2. Contributors List 275 | 276 | "Mark Bates":http://github.com/markbates 277 | 278 | "Florent Guilleux":http://github.com/Florent2 279 | 280 | "Jan De Poorter":http://github.com/DefV 281 | 282 | "Thilo-Alexander Ginkel":http://github.com/ginkel 283 | 284 | "Matias Käkelä":http://github.com/massive 285 | 286 | "Max De Marzi":http://github.com/maxdemarzi 287 | 288 | "Peter Boling":https://github.com/pboling 289 | 290 | "Roberto Miranda":https://github.com/robertomiranda 291 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | #!/usr/bin/env rake 3 | require "bundler/gem_tasks" 4 | require 'rake' 5 | 6 | require 'rspec/core' 7 | require 'rspec/core/rake_task' 8 | RSpec::Core::RakeTask.new(:spec) do |spec| 9 | spec.pattern = FileList['spec/**/*_spec.rb'] 10 | end 11 | 12 | RSpec::Core::RakeTask.new(:rcov) do |spec| 13 | spec.pattern = 'specs/**/*_spec.rb' 14 | spec.rcov = true 15 | end 16 | 17 | task :default => :spec 18 | 19 | require 'rdoc/task' 20 | Rake::RDocTask.new do |rdoc| 21 | require File.expand_path('../lib/fbgraph/version', __FILE__) 22 | rdoc.rdoc_dir = 'rdoc' 23 | rdoc.title = "SanitizeEmail #{FBGraph::VERSION}" 24 | rdoc.options << '--line-numbers' 25 | rdoc.rdoc_files.include('README*') 26 | rdoc.rdoc_files.include('lib/**/*.rb') 27 | end 28 | 29 | Bundler::GemHelper.install_tasks 30 | -------------------------------------------------------------------------------- /TODO.list: -------------------------------------------------------------------------------- 1 | - Tests 2 | - README 3 | - Documentation 4 | - Analytics 5 | - Integrate with facebooker 6 | - Pluginizable 7 | 8 | -------------------------------------------------------------------------------- /fbgraph.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/fbgraph/version', __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "fbgraph" 6 | s.version = FBGraph::VERSION 7 | 8 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 9 | s.authors = ["Nicolas Santa"] 10 | s.date = "2012-04-27" 11 | s.description = "A Gem for Facebook Open Graph API" 12 | s.email = "nicolas55ar@gmail.com" 13 | s.extra_rdoc_files = [ 14 | "README.textile" 15 | ] 16 | s.files = `git ls-files`.split($\) 17 | s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 18 | s.test_files = s.files.grep(%r{^(test|spec|features)/}) 19 | 20 | s.extra_rdoc_files = [ 21 | "README.textile", 22 | ] 23 | s.files = [ 24 | ".bundle/config", 25 | "Gemfile", 26 | "Manifest", 27 | "README.textile", 28 | "Rakefile", 29 | "TODO.list", 30 | "fbgraph.gemspec", 31 | "lib/fbgraph.rb", 32 | "lib/fbgraph/authorization.rb", 33 | "lib/fbgraph/base.rb", 34 | "lib/fbgraph/cacert.pem", 35 | "lib/fbgraph/canvas.rb", 36 | "lib/fbgraph/client.rb", 37 | "lib/fbgraph/fql.rb", 38 | "lib/fbgraph/logger.rb", 39 | "lib/fbgraph/realtime.rb", 40 | "lib/fbgraph/result.rb", 41 | "lib/fbgraph/search.rb", 42 | "lib/fbgraph/selection.rb", 43 | "lib/fbgraph/timeline.rb", 44 | "spec/lib/fbauth/authorization_spec.rb", 45 | "spec/lib/fbauth/base_spec.rb", 46 | "spec/lib/fbauth/canvas_spec.rb", 47 | "spec/lib/fbauth/client_spec.rb", 48 | "spec/lib/fbauth/realtime_spec.rb", 49 | "spec/lib/fbauth/search_spec.rb", 50 | "spec/lib/fbauth/selection_spec.rb", 51 | "spec/spec_helper.rb" 52 | ] 53 | s.homepage = "http://github.com/nsanta/fbgraph" 54 | s.licenses = ["MIT"] 55 | s.require_paths = ["lib"] 56 | s.rubygems_version = "1.8.21" 57 | s.summary = "A Gem for Facebook Open Graph API" 58 | 59 | if s.respond_to? :specification_version then 60 | s.specification_version = 3 61 | 62 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 63 | s.add_runtime_dependency(%q, [">= 0"]) 64 | s.add_runtime_dependency(%q, [">= 1.0.0"]) 65 | s.add_runtime_dependency(%q, [">= 0.5.0"]) 66 | s.add_runtime_dependency(%q, [">= 0.7.5"]) 67 | s.add_runtime_dependency(%q, [">= 1.0.0"]) 68 | s.add_runtime_dependency(%q, [">= 0"]) 69 | s.add_runtime_dependency(%q, [">= 0"]) 70 | s.add_development_dependency(%q, ["~> 0.9.2"]) 71 | s.add_development_dependency(%q, ["> 1.0.0"]) 72 | s.add_development_dependency(%q, ["~> 1.3.0"]) 73 | s.add_development_dependency(%q, ["~> 2.6"]) 74 | s.add_development_dependency(%q, [">= 0"]) 75 | s.add_development_dependency(%q, [">= 3.9.0"]) 76 | else 77 | s.add_dependency(%q, [">= 0"]) 78 | s.add_dependency(%q, [">= 1.0.0"]) 79 | s.add_dependency(%q, [">= 0.5.0"]) 80 | s.add_dependency(%q, [">= 0.7.5"]) 81 | s.add_dependency(%q, [">= 1.0.0"]) 82 | s.add_dependency(%q, [">= 0"]) 83 | s.add_dependency(%q, [">= 0"]) 84 | s.add_dependency(%q, ["~> 0.9.2"]) 85 | s.add_dependency(%q, ["> 1.0.0"]) 86 | s.add_dependency(%q, ["~> 1.3.0"]) 87 | s.add_dependency(%q, ["~> 2.6"]) 88 | s.add_dependency(%q, [">= 0"]) 89 | s.add_dependency(%q, [">= 3.9.0"]) 90 | end 91 | else 92 | s.add_dependency(%q, [">= 0"]) 93 | s.add_dependency(%q, [">= 1.0.0"]) 94 | s.add_dependency(%q, [">= 0.5.0"]) 95 | s.add_dependency(%q, [">= 0.7.5"]) 96 | s.add_dependency(%q, [">= 1.0.0"]) 97 | s.add_dependency(%q, [">= 0"]) 98 | s.add_dependency(%q, [">= 0"]) 99 | s.add_dependency(%q, ["~> 0.9.2"]) 100 | s.add_dependency(%q, ["> 1.0.0"]) 101 | s.add_dependency(%q, ["~> 1.3.0"]) 102 | s.add_dependency(%q, ["~> 2.6"]) 103 | s.add_dependency(%q, [">= 0"]) 104 | s.add_dependency(%q, [">= 3.9.0"]) 105 | end 106 | end 107 | 108 | -------------------------------------------------------------------------------- /lib/fbgraph.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require "bundler/setup" 3 | require 'active_support/all' 4 | require 'oauth2' 5 | require 'json' 6 | require 'hashie' 7 | require 'rest_client' 8 | require 'uri' 9 | require "base64" 10 | require "openssl" 11 | 12 | require 'fbgraph/result' 13 | require 'fbgraph/client' 14 | require 'fbgraph/base' 15 | require 'fbgraph/authorization' 16 | require 'fbgraph/selection' 17 | require 'fbgraph/search' 18 | require 'fbgraph/realtime' 19 | require 'fbgraph/canvas' 20 | require 'fbgraph/logger' 21 | require 'fbgraph/fql' 22 | require 'fbgraph/timeline' 23 | 24 | module FBGraph 25 | @config = nil 26 | 27 | class << self 28 | def config 29 | @config ||= load_config(config_path).freeze 30 | end 31 | 32 | def config_path 33 | if defined?(Rails) 34 | File.join(Rails.root , 'config' , 'facebook.yml') 35 | else 36 | 'facebook.yml' 37 | end 38 | end 39 | 40 | def load_config(yaml_file) 41 | return {} unless File.exist?(yaml_file) 42 | cfg = YAML::load(File.open(yaml_file)) 43 | if defined? Rails 44 | cfg = cfg[Rails.env] 45 | end 46 | cfg 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/fbgraph/authorization.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | 3 | class Authorization 4 | 5 | def initialize(client) 6 | @client = client 7 | end 8 | 9 | def authorize_url(params = {}) 10 | params = { :redirect_uri => FBGraph.config[:canvas_url] }.merge(params) 11 | @client.oauth_client.auth_code.authorize_url(params) 12 | end 13 | 14 | 15 | def process_callback(code, options = {}) 16 | # HACK(pwnall): :parse => :query is added because Facebook's tarded OAuth 17 | # endpoint returns ContentType: text/plain instead of 18 | # application/x-www-form-urlencoded 19 | options = { :redirect_uri => FBGraph.config[:canvas_url], 20 | :parse => :query }.merge(options) 21 | @client.auth = @client.oauth_client.auth_code.get_token(code, options) 22 | @client.access_token = @client.auth.token 23 | end 24 | 25 | def upgrade_session!(key) 26 | token = upgrade_session_keys(key).first 27 | @client.access_token = token 28 | end 29 | 30 | def upgrade_session_keys(*keys) 31 | tokens = @client.oauth_client.request(:get, '/oauth/exchange_sessions', { 32 | :client_id => @client.client_id, 33 | :client_secret => @client.secret_id, 34 | :type => 'client_cred', 35 | :sessions => keys.flatten.join(',') 36 | }) 37 | JSON.parse(tokens).map { |hash| hash['access_token'] if hash} 38 | end 39 | 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/fbgraph/base.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | 3 | class Base 4 | 5 | attr_reader :objects , :connection_type , :logger, :fields, :last_result 6 | 7 | def initialize(client) 8 | @client = client 9 | @fields = [] 10 | @params = {} 11 | end 12 | 13 | def find(objects) 14 | @objects = objects 15 | return self 16 | end 17 | 18 | def connection(connection_type) 19 | @connection_type = connection_type 20 | return self 21 | end 22 | 23 | def params=(ps) 24 | @params = ps 25 | return self 26 | end 27 | 28 | def params 29 | @params 30 | end 31 | 32 | def param(pm) 33 | @params.merge!(pm) 34 | return self 35 | end 36 | 37 | def with_fields(*new_fields) 38 | @fields.concat(new_fields) if !(new_fields.blank? rescue true) 39 | @fields = sanitized_fields 40 | self 41 | end 42 | alias :fields :with_fields 43 | 44 | def info!(parsed = true, &block) 45 | self.instance_eval(&block) if block_given? 46 | @params.merge!(:fields => sanitized_fields.join(',')) unless sanitized_fields.blank? 47 | @params.merge!(:access_token => @client.access_token) unless @client.access_token.nil? 48 | if @objects.is_a? Array 49 | @params.merge!({:ids => @objects.join(',')}) 50 | path = build_open_graph_path(nil,nil, @params) 51 | elsif @objects.is_a? String 52 | path = build_open_graph_path(@objects , @connection_type, @params) 53 | else 54 | raise "No Facebook objects were recognized as selected; unable to build fb graph path." 55 | end 56 | show_log('GET' , path, @params) if @debug 57 | result = @client.consumer[path].get 58 | @last_result = ::FBGraph::Result.new(result, @params) 59 | end 60 | 61 | 62 | def publish!(data = {},parsed = true, &block) 63 | @params.merge!(data) 64 | self.instance_eval(&block) if block_given? 65 | @params.merge!(:fields => sanitized_fields.join(',')) unless sanitized_fields.blank? 66 | @params.merge!(:access_token => @client.access_token) if (@client.access_token) 67 | @path = build_open_graph_path(@objects , @connection_type) 68 | show_log('POST' , @path, @params) if @debug 69 | result = @client.consumer[@path].post(@params) 70 | @last_result = ::FBGraph::Result.new(result, @params) 71 | end 72 | 73 | def delete!(parsed = true, &block) 74 | self.instance_eval(&block) if block_given? 75 | @path = build_open_graph_path(@objects , nil) 76 | @params.merge!(:access_token => @client.access_token) if (@client.access_token) 77 | @params.merge!(:method => :delete) 78 | show_log('DELETE' , @path, @params) if @debug 79 | result = @client.consumer[@path].post(@params) 80 | @last_result = ::FBGraph::Result.new(result, @params) 81 | end 82 | 83 | %w(limit offset until since).each do |paging| 84 | class_eval <<-PAGING 85 | def #{paging}(value) 86 | @params[:#{paging}] = value 87 | self 88 | end 89 | PAGING 90 | end 91 | 92 | def debug=(att) 93 | @debug= att 94 | end 95 | 96 | private 97 | 98 | def sanitized_fields 99 | @fields.flatten.map(&:to_s).compact 100 | end 101 | 102 | def build_open_graph_path(objects, connection_type = nil , params = {}) 103 | request = [objects , connection_type].compact.join('/') 104 | request += "?"+params.to_a.map{|p| p.join('=')}.join('&') unless params.empty? 105 | URI.encode(request) 106 | end 107 | 108 | def show_log(ver, path, params) 109 | client.logger.info "FBGRAPH [#{verb}]: #{path}" 110 | client.logger.info "PARAMS: #{params.to_a.map{|p| p.join('=')}.join('&')}" 111 | end 112 | 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /lib/fbgraph/canvas.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | 3 | class Canvas 4 | 5 | class << self 6 | def parse_signed_request(secret_id,request) 7 | encoded_sig, payload = request.split('.', 2) 8 | sig = urldecode64(encoded_sig) 9 | data = JSON.parse(urldecode64(payload)) 10 | if data['algorithm'].to_s.upcase != 'HMAC-SHA256' 11 | raise "Bad signature algorithm: %s" % data['algorithm'] 12 | end 13 | expected_sig = OpenSSL::HMAC.digest('sha256', secret_id, payload) 14 | if expected_sig != sig 15 | raise "Bad signature" 16 | end 17 | data 18 | end 19 | 20 | private 21 | 22 | def urldecode64(str) 23 | encoded_str = str.tr('-_', '+/') 24 | encoded_str += '=' while !(encoded_str.size % 4).zero? 25 | Base64.decode64(encoded_str) 26 | end 27 | end 28 | 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/fbgraph/client.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | 3 | class Client 4 | 5 | attr_accessor :client_id , :secret_id , :facebook_uri , :access_token , :consumer , :auth , :logger 6 | 7 | def initialize(options = {}) 8 | @client_id = options[:client_id] || FBGraph.config[:client_id] 9 | @secret_id = options[:secret_id] || FBGraph.config[:secret_id] 10 | @ca_file = options[:ca_file] || FBGraph.config[:ca_file] || default_ca_file 11 | @facebook_uri = options[:facebook_uri] || 'https://graph.facebook.com' 12 | @consumer = RestClient::Resource.new(@facebook_uri, rest_client_ssl_options) 13 | @access_token = options.fetch :token, nil 14 | @auth = OAuth2::AccessToken.new(oauth_client, @access_token) 15 | @logger = options[:logger] || FBGraph::Logger 16 | return true 17 | end 18 | 19 | def set_token(new_token) 20 | @access_token = new_token 21 | @auth = OAuth2::AccessToken.new(oauth_client, @access_token) 22 | new_token 23 | end 24 | 25 | def authorization 26 | FBGraph::Authorization.new(self) 27 | end 28 | 29 | def selection 30 | FBGraph::Selection.new(self) 31 | end 32 | 33 | def search 34 | FBGraph::Search.new(self) 35 | end 36 | 37 | def realtime 38 | FBGraph::Realtime.new(self) 39 | end 40 | 41 | def fql 42 | FBGraph::FQL.new(self) 43 | end 44 | 45 | def timeline 46 | FBGraph::Timeline.new(self) 47 | end 48 | 49 | def oauth_client 50 | OAuth2::Client.new(client_id, secret_id, 51 | :site => { :url => facebook_uri }, 52 | :token_url => '/oauth/access_token', 53 | :authorize_url => '/oauth/authorize', 54 | :ssl => oauth_client_ssl_options) 55 | end 56 | 57 | def oauth_client_ssl_options 58 | { :ca_file => @ca_file, :verify => OpenSSL::SSL::VERIFY_PEER } 59 | end 60 | 61 | def rest_client_ssl_options 62 | { :ssl_ca_file => @ca_file, :verify_ssl => OpenSSL::SSL::VERIFY_PEER } 63 | end 64 | 65 | def default_ca_file 66 | File.join(File.dirname(__FILE__), 'cacert.pem') 67 | end 68 | end 69 | end 70 | 71 | # :nodoc: undo the clusterfuck that rest-client has done 72 | if Net::HTTP.method_defined? :__request__ 73 | module Net 74 | class HTTP 75 | undef request 76 | alias request __request__ 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/fbgraph/fql.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class FQL < Base 3 | 4 | 5 | def query(q) 6 | find('fql').param(:q => q).info! 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/fbgraph/logger.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class Logger 3 | def self.info(msg) 4 | puts msg 5 | end 6 | 7 | def self.warn(msg) 8 | puts msg 9 | end 10 | 11 | def self.error(msg) 12 | puts msg 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/fbgraph/realtime.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class Realtime < Base 3 | 4 | def initialize(client) 5 | @objects = 'subscriptions' 6 | super(client) 7 | end 8 | 9 | 10 | OBJECTS = %w(user permissions errors).freeze 11 | 12 | OBJECTS.each do |object| 13 | class_eval <<-METHOD 14 | def #{object} 15 | @params[:object] = "#{object}" 16 | self 17 | end 18 | METHOD 19 | end 20 | 21 | def fields(fs = "email,picture") 22 | @params[:fields] = fs 23 | self 24 | end 25 | 26 | def callback_url(url) 27 | @params[:callback_url] = url 28 | self 29 | end 30 | 31 | def verify_token(token) 32 | @params[:verify_token] = token 33 | self 34 | end 35 | 36 | 37 | alias_method :subscribe! , :publish! 38 | 39 | private 40 | 41 | def build_open_graph_path(objects,connection_type = nil , params = {}) 42 | request = "/" + [objects , connection_type].compact.join('/') 43 | request += "?"+params.to_a.map{|p| p.join('=')}.join('&') unless params.empty? 44 | request 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/fbgraph/result.rb: -------------------------------------------------------------------------------- 1 | require 'hashie/mash' 2 | 3 | module FBGraph 4 | class Result 5 | 6 | attr_accessor :params, :unparsed, :data 7 | 8 | def initialize(result, params = {}, &block) 9 | result = result.respond_to?(:body) ? result.body : result.to_s 10 | @data = Hashie::Mash.new(JSON.parse(result)) rescue result 11 | @unparsed = result 12 | @params = params.symbolize_keys 13 | self 14 | end 15 | 16 | def paging 17 | data.paging 18 | end 19 | 20 | def metadata 21 | data.metadata 22 | end 23 | 24 | # Implement enumerable 25 | def each(&block) 26 | return nil if data.blank? 27 | data.each(&block) 28 | end 29 | include Enumerable 30 | 31 | # Implement Comparable 32 | def <=>(other) 33 | (data <=> other.data) rescue 0 34 | end 35 | include Comparable 36 | 37 | def method_missing(method, *args, &block) 38 | data.send(method, *args, &block) rescue super(method, *args, &block) 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/fbgraph/search.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class Search < Base 3 | 4 | def initialize(client) 5 | @objects = 'search' 6 | super(client) 7 | end 8 | 9 | def query(q) 10 | @params = {:q => q} 11 | return self 12 | end 13 | 14 | def on(type) 15 | @params.merge!({:type => type}) 16 | return self 17 | end 18 | 19 | 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/fbgraph/selection.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class Selection < Base 3 | 4 | OBJECTS = %w(user album event group link note page photo post status 5 | video comment checkin friendlist thread order application 6 | apprequest subscription).freeze 7 | 8 | CONNECTION_TYPES = %w(home photos comments feed noreply 9 | maybe invited attending declined picture 10 | members tagged links groups albums 11 | statuses videos notes posts events friends 12 | activities interests music books movies television 13 | likes inbox outbox updates accounts checkins 14 | friendlists platformrequests threads participants 15 | former_participants senders messages insights 16 | subscriptions payments apprequests reviews 17 | mutualfriends family).freeze 18 | 19 | OBJECTS.each do |object| 20 | class_eval <<-METHOD 21 | def #{object}(object) 22 | find(object) 23 | self 24 | end 25 | METHOD 26 | end 27 | 28 | CONNECTION_TYPES.each do |object| 29 | class_eval <<-METHOD 30 | def #{object}(connection_id = nil) 31 | connection(['#{object}', connection_id]) 32 | self 33 | end 34 | METHOD 35 | end 36 | 37 | 38 | def me 39 | find('me') 40 | end 41 | 42 | def metadata 43 | @params.merge!({:metadata => '1'}) 44 | self 45 | end 46 | 47 | def picture(type='square') 48 | params = {:type => type} 49 | params.merge!(:access_token => @client.access_token) unless @client.access_token.blank? 50 | uri = [@client.facebook_uri , build_open_graph_path(@objects , 'picture' , params)].join('/') 51 | end 52 | 53 | end 54 | end 55 | 56 | -------------------------------------------------------------------------------- /lib/fbgraph/timeline.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | class Timeline < Base 3 | 4 | BUILT_IN_ACTIONS = { 5 | :listens => :music, 6 | :reads => :news, 7 | :watches => :movie, 8 | } 9 | 10 | 11 | def action(namespace,action) 12 | connection([namespace,action].join(':')) 13 | self 14 | end 15 | 16 | BUILT_IN_ACTIONS.each do |action,obj| 17 | class_eval <<-METHOD 18 | def #{action} 19 | find('me').connection("#{[obj,action].join('.')}") 20 | self 21 | end 22 | METHOD 23 | end 24 | 25 | 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/fbgraph/version.rb: -------------------------------------------------------------------------------- 1 | module FBGraph 2 | VERSION = '1.10.1' 3 | end 4 | -------------------------------------------------------------------------------- /spec/lib/fbauth/authorization_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Authorization do 5 | 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | @client = FBGraph::Client.new(:client_id => @client_id, 10 | :secret_id => @secret_id) 11 | @authorization = FBGraph::Authorization.new(@client) 12 | end 13 | 14 | describe 'initialization' do 15 | it 'should set the client' do 16 | @authorization.instance_variable_get("@client").should == @client 17 | end 18 | end 19 | 20 | describe "instance methods" do 21 | describe '.authorize_url' do 22 | it 'should return the authorization url' do 23 | @authorization.authorize_url(:redirect_uri => 'redirect/to/path' , 24 | :scope => 'email,user_photos,friends_photos').should =~ /redirect_uri=redirect%2Fto%2Fpath/ 25 | end 26 | end 27 | describe "process_callback" do 28 | before :each do 29 | @consumer = mock('Consumer' , :token => 'code') 30 | options = {:redirect_uri => 'redirect/to/path', :parse=>:query} 31 | @client.stub!(:oauth_client).and_return(Object.new) 32 | @client.oauth_client.stub!(:auth_code).and_return(Object.new) 33 | @client.oauth_client.auth_code.stub!(:get_token).with(@consumer.token, options).and_return(@consumer) 34 | @token = @authorization.process_callback(@consumer.token, options) 35 | end 36 | it 'should return the access_token' do 37 | @token.should == @consumer.token 38 | end 39 | it 'should set the consumer' do 40 | @client.auth.should == @consumer 41 | end 42 | end 43 | end 44 | 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/lib/fbauth/base_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Authorization do 5 | 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | @client = FBGraph::Client.new(:client_id => @client_id, 10 | :secret_id => @secret_id, 11 | :token => 'token') 12 | @base = FBGraph::Base.new(@client) 13 | 14 | end 15 | 16 | describe 'initialization' do 17 | it 'should set the client' do 18 | @base.instance_variable_get("@client").should == @client 19 | end 20 | it 'should params be empty' do 21 | @base.params.should be_empty 22 | end 23 | end 24 | 25 | describe "instance methods" do 26 | describe 'find' do 27 | it 'should objects set with "id"' do 28 | @base.find('id').objects.should == 'id' 29 | end 30 | end 31 | 32 | describe 'connection' do 33 | it 'should set connection_type to "type"' do 34 | @base.connection('type').connection_type.should == 'type' 35 | end 36 | end 37 | 38 | describe 'params=' do 39 | it 'should set params to {:fields => "fields"}' do 40 | @base.params = {:fields => "fields"} 41 | @base.params.should == {:fields => "fields"} 42 | end 43 | end 44 | 45 | describe 'params' do 46 | it 'should return params' do 47 | @base.params = {:fields => "fields"} 48 | @base.params.should == {:fields => "fields"} 49 | end 50 | end 51 | 52 | describe 'param' do 53 | it 'should merge the passed param' do 54 | @base.params = {:fields => "fields"} 55 | @base.param(:merged => true) 56 | @base.params.should == {:fields => "fields" , :merged => true} 57 | end 58 | end 59 | 60 | describe 'info!' do 61 | describe 'when object is an array' do 62 | it 'should request with the path "/?ids=1,2,3"' do 63 | uri = "?access_token=token&ids=1,2,3" 64 | @base.find([1,2,3]) 65 | expect_consumer(uri) 66 | @base.info!(false) 67 | end 68 | end 69 | 70 | describe 'when object is a string' do 71 | 72 | it 'should request with the path "/123"' do 73 | uri = "123?access_token=token" 74 | @base.find('123') 75 | expect_consumer(uri) 76 | @base.info!(false) 77 | end 78 | 79 | it "should parse the result by default" do 80 | uri = "123?access_token=token" 81 | @base.find('123') 82 | expect_consumer(uri, '{"me": [1, 2]}') 83 | @base.info!.me.should == [1, 2] 84 | end 85 | 86 | describe 'when a connection is passed' do 87 | it 'should request with the path "/123/home"' do 88 | uri = "123/home?access_token=token" 89 | @base.find('123').connection('home') 90 | expect_consumer(uri) 91 | @base.info!(false) 92 | end 93 | end 94 | 95 | describe 'when params are passed' do 96 | it 'should request with the path "/123?fields=name,picture"' do 97 | uri = "123?fields=name,picture&access_token=token" 98 | @base.find('123') 99 | @base.params = {:fields => "name,picture"} 100 | expect_consumer(uri) 101 | @base.info!(false) 102 | end 103 | end 104 | end 105 | end 106 | 107 | describe 'publish!' do 108 | describe 'when is passed params before invocation' do 109 | it 'should request with the path "/123" and params {:extra => "extra" }' do 110 | uri = "123" 111 | @base.find('123') 112 | @base.params = {:extra => "extra" } 113 | #@client.consumer.stub!(:post).with(uri , @base.params).and_return('') 114 | expect_consumer_post(uri, {:extra => "extra", :access_token => 'token'}) 115 | @base.publish!({}, false) 116 | end 117 | end 118 | describe 'when is passed params on invocation' do 119 | it 'should request with the path "/123" and params {:extra => "extra" }' do 120 | uri = "123" 121 | @base.find('123') 122 | ps = {:extra => "extra"} 123 | expect_consumer_post(uri, {:extra => "extra", :access_token => 'token'}) 124 | @base.publish!(ps, false) 125 | end 126 | end 127 | end 128 | 129 | describe 'delete!' do 130 | it 'should request with the path "/123" and params {:extra => "extra" }' do 131 | uri = "123" 132 | @base.find('123') 133 | @base.params = {:extra => "extra" } 134 | expect_consumer_post(uri, {:method => :delete, :extra => "extra", :access_token => 'token'}) 135 | @base.delete!(false) 136 | end 137 | end 138 | 139 | end 140 | 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /spec/lib/fbauth/canvas_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Canvas do 5 | before :each do 6 | @payload = { 'algorithm' => 'HMAC-SHA256', 'foo' => 'bar' } 7 | @secret = 'thisisasecret' 8 | 9 | encoded_payload = Base64.encode64(@payload.to_json) 10 | hash = OpenSSL::HMAC.digest('sha256', @secret, encoded_payload) 11 | encoded_sig = Base64.encode64(hash) 12 | 13 | @valid_request = "#{encoded_sig}.#{encoded_payload}" 14 | end 15 | 16 | it "should decode a valid request" do 17 | result = FBGraph::Canvas.parse_signed_request(@secret, @valid_request) 18 | result.should == @payload 19 | end 20 | 21 | it "should raise on an invalid request" do 22 | bad_secret = "notagoodsecret" 23 | 24 | lambda { 25 | FBGraph::Canvas.parse_signed_request(bad_secret, @valid_request) 26 | }.should raise_error 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/lib/fbauth/client_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Client do 5 | describe "initialization" do 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | end 10 | describe 'default' do 11 | 12 | before :each do 13 | @client = FBGraph::Client.new(:client_id => @client_id, 14 | :secret_id => @secret_id) 15 | end 16 | 17 | it 'should set the client_id' do 18 | @client.client_id.should == @client_id 19 | end 20 | it 'should set the secret_id' do 21 | @client.secret_id.should == @secret_id 22 | end 23 | it 'should set the facebook_uri ' do 24 | @client.facebook_uri.should == "https://graph.facebook.com" 25 | end 26 | end 27 | 28 | describe 'when token is present' do 29 | before :each do 30 | @token = 'token' 31 | @client = FBGraph::Client.new(:client_id => @client_id, 32 | :secret_id => @secret_id, 33 | :token => @token) 34 | end 35 | 36 | it 'should set the access_token' do 37 | @client.access_token.should == @token 38 | end 39 | it 'should set the consumer client' do 40 | @client.consumer.class.should == RestClient::Resource 41 | end 42 | end 43 | end 44 | 45 | 46 | describe "instance methods" do 47 | before :each do 48 | @client_id = 'client_id' 49 | @secret_id = 'secret_id' 50 | @client = FBGraph::Client.new(:client_id => @client_id, 51 | :secret_id => @secret_id) 52 | end 53 | describe '.authorization' do 54 | it 'should return a FBGraph::Authorization object' do 55 | @client.authorization.class.should == FBGraph::Authorization 56 | end 57 | end 58 | 59 | describe '.selection' do 60 | it 'should return a FBGraph::Selection object' do 61 | @client.selection.class.should == FBGraph::Selection 62 | end 63 | end 64 | 65 | describe '.search' do 66 | it 'should return a FBGraph::Search object' do 67 | @client.search.class.should == FBGraph::Search 68 | end 69 | end 70 | 71 | describe '.realtime' do 72 | it 'should return a FBGraph::Search object' do 73 | @client.realtime.class.should == FBGraph::Realtime 74 | end 75 | end 76 | 77 | describe '.oauth_client' do 78 | it 'should return a OAuth2::Client object' do 79 | @client.oauth_client.class.should == OAuth2::Client 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/lib/fbauth/realtime_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Realtime do 5 | 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | @client = FBGraph::Client.new(:client_id => @client_id, 10 | :secret_id => @secret_id) 11 | @search = FBGraph::Search.new(@client) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/lib/fbauth/search_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Search do 5 | 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | @client = FBGraph::Client.new(:client_id => @client_id, 10 | :secret_id => @secret_id) 11 | @search = FBGraph::Search.new(@client) 12 | end 13 | 14 | describe "initialization" do 15 | it "should set @object with 'search'" do 16 | @search.objects.should == 'search' 17 | end 18 | end 19 | 20 | 21 | describe 'instance methods' do 22 | 23 | describe '.query' do 24 | before(:each) do 25 | @result = @search.query('query') 26 | end 27 | it 'should return self' do 28 | @result.should == @search 29 | end 30 | it "should assign in params[:q] 'query'" do 31 | @search.params[:q].should == 'query' 32 | end 33 | end 34 | describe '.on' do 35 | before(:each) do 36 | @result = @search.on('type') 37 | end 38 | it 'should return self' do 39 | @result.should == @search 40 | end 41 | it "should assign in params[:type] 'type'" do 42 | @search.params[:type].should == 'type' 43 | end 44 | end 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/lib/fbauth/selection_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe FBGraph do 4 | describe FBGraph::Selection do 5 | 6 | before :each do 7 | @client_id = 'client_id' 8 | @secret_id = 'secret_id' 9 | @token = 'token' 10 | @client = FBGraph::Client.new(:client_id => @client_id, 11 | :secret_id => @secret_id, 12 | :token => @token) 13 | @selection = FBGraph::Selection.new(@client) 14 | end 15 | 16 | describe "when asking for an object's picture" do 17 | it "should append the access token" do 18 | @selection.me.picture.should == 19 | "https://graph.facebook.com/me/picture?type=square&access_token=#{@token}" 20 | end 21 | 22 | it "should not append an access token if none is available" do 23 | client = FBGraph::Client.new(:client_id => @client_id, 24 | :secret_id => @secret_id) 25 | selection = FBGraph::Selection.new(client) 26 | 27 | selection.me.picture.should == 28 | "https://graph.facebook.com/me/picture?type=square" 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper"` to ensure that it is only 4 | # loaded once. 5 | # 6 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 7 | 8 | require 'fbgraph' 9 | require 'support/fbgraph_spec_helper' 10 | begin 11 | require 'redgreen' 12 | rescue LoadError 13 | # redgreen is for pretty spec result colors on Windows... no biggie. 14 | end 15 | 16 | RSpec.configure do |config| 17 | config.treat_symbols_as_metadata_keys_with_true_values = true 18 | config.run_all_when_everything_filtered = true 19 | config.filter_run :focus 20 | 21 | # Run specs in random order to surface order dependencies. If you find an 22 | # order dependency and want to debug it, you can fix the order by providing 23 | # the seed, which is printed after each run. 24 | # --seed 1234 25 | config.order = 'random' 26 | 27 | config.include FBGraphSpecHelper 28 | end 29 | 30 | 31 | -------------------------------------------------------------------------------- /spec/support/fbgraph_spec_helper.rb: -------------------------------------------------------------------------------- 1 | module FBGraphSpecHelper 2 | def expect_consumer(uri, result = '') 3 | consumer = @client.consumer[uri] 4 | consumer.stub!(:get).and_return(Struct.new(:body).new(result)) 5 | @client.consumer.stub!(:[]).with(uri).and_return(consumer) 6 | end 7 | 8 | def expect_consumer_post(uri, params = {}, result = '') 9 | rest_client = @client.consumer[uri] 10 | rest_client.stub!(:post).with(params).and_return(Struct.new(:body).new(result)) 11 | @client.consumer.stub!(:[]).with(uri).and_return(rest_client) 12 | end 13 | end 14 | --------------------------------------------------------------------------------