jkarlsson-mini_fb 1.1.7
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.
- data/LICENSE.txt +20 -0
- data/README.markdown +222 -0
- data/lib/mini_fb.rb +811 -0
- data/test/test_mini_fb.rb +79 -0
- metadata +100 -0
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            Copyright (c) 2009 Appoxy LLC
         | 
| 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.markdown
    ADDED
    
    | @@ -0,0 +1,222 @@ | |
| 1 | 
            +
            MiniFB - the simple miniature facebook library
         | 
| 2 | 
            +
            ==============================================
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            MiniFB is a small, lightweight Ruby library for interacting with the [Facebook API](http://wiki.developers.facebook.com/index.php/API).
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Brought to you by: [](http://www.appoxy.com)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Support
         | 
| 9 | 
            +
            --------
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Join our Discussion Group at: <http://groups.google.com/group/mini_fb>
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            Demo Rails Application
         | 
| 14 | 
            +
            -------------------
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            There is a demo Rails app that uses mini_fb graph api at: [http://github.com/appoxy/mini_fb_demo](http://github.com/appoxy/mini_fb_demo)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            Installation
         | 
| 19 | 
            +
            -------------
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                gem install mini_fb
         | 
| 22 | 
            +
             | 
| 23 | 
            +
             | 
| 24 | 
            +
            Facebook Graph API
         | 
| 25 | 
            +
            ==================
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            For an overview of what this is all about, see <http://developers.facebook.com/docs/api>.
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            Authentication
         | 
| 30 | 
            +
            --------------
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            Facebook now uses Oauth 2 for authentication, but don't worry, this part is easy.
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                # Get your oauth url
         | 
| 35 | 
            +
                @oauth_url = MiniFB.oauth_url(FB_APP_ID, # your Facebook App ID (NOT API_KEY)
         | 
| 36 | 
            +
                                              "http://www.yoursite.com/sessions/create", # redirect url
         | 
| 37 | 
            +
                                              :scope=>MiniFB.scopes.join(",")) # This asks for all permissions
         | 
| 38 | 
            +
                # Have your users click on a link to @oauth_url
         | 
| 39 | 
            +
                .....
         | 
| 40 | 
            +
                # Then in your /sessions/create
         | 
| 41 | 
            +
                access_token_hash = MiniFB.oauth_access_token(FB_APP_ID, "http://www.yoursite.com/sessions/create", FB_SECRET, params[:code])
         | 
| 42 | 
            +
                @access_token = access_token_hash["access_token"]
         | 
| 43 | 
            +
                # TODO: This is where you'd want to store the token in your database
         | 
| 44 | 
            +
                # but for now, we'll just keep it in the cookie so we don't need a database
         | 
| 45 | 
            +
                cookies[:access_token] = @access_token
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            That's it. You now need to hold onto this access_token. We've put it in a cookie for now, but you probably
         | 
| 48 | 
            +
            want to store it in your database or something.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            Getting Data from Facebook
         | 
| 51 | 
            +
            --------------------------
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            It's very simple:
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                @id = {some ID of something in facebook} || "me"
         | 
| 56 | 
            +
                @type = {some facebook type like feed, friends, or photos} # (optional) nil will just return the object data directly
         | 
| 57 | 
            +
                @response_hash = MiniFB.get(@access_token, @id, :type=>@type)
         | 
| 58 | 
            +
                # @response_hash is a hash, but also allows object like syntax for instance, the following is true:
         | 
| 59 | 
            +
                @response_hash["user"] == @response_hash.user
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            See <http://developers.facebook.com/docs/api> for the available types.
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            Posting Data to Facebook
         | 
| 64 | 
            +
            ------------------------
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            Also pretty simple:
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                @id = {some ID of something in facebook}
         | 
| 69 | 
            +
                @type = {some type of post like comments, likes, feed} # required here
         | 
| 70 | 
            +
                @response_hash = MiniFB.post(@access_token, @id, :type=>@type)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            FQL
         | 
| 73 | 
            +
            ---
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                my_query = "select uid,a,b,c from users where ...."
         | 
| 76 | 
            +
                @res = MiniFB.fql(@access_token, my_query)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            Logging
         | 
| 79 | 
            +
            -------
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            To enabled logging:
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                MiniFB.enable_logging
         | 
| 84 | 
            +
             | 
| 85 | 
            +
             | 
| 86 | 
            +
            Original Facebook API
         | 
| 87 | 
            +
            =====================
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            This API will probably go away at some point, so you should use the Graph API above in most cases.
         | 
| 90 | 
            +
             | 
| 91 | 
            +
             | 
| 92 | 
            +
            General Usage
         | 
| 93 | 
            +
            -------------
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            The most general case is to use MiniFB.call method:
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                user_hash = MiniFB.call(FB_API_KEY, FB_SECRET, "Users.getInfo", "session_key"=>@session_key, "uids"=>@uid, "fields"=>User.all_fields)
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            Which simply returns the parsed json response from Facebook.
         | 
| 100 | 
            +
             | 
| 101 | 
            +
             | 
| 102 | 
            +
            Oauth 2.0 Authentication and Original Rest Api
         | 
| 103 | 
            +
            -------------
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            You can use the Graph api Oauth 2.0 token with original api methods. BEWARE: This has only been tested against stream.publish at present.
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                MiniFB.rest(@access_token, "rest.api.method", options)
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            eg:
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                response = MiniFB.rest(@access_token, "stream.publish", :params => {
         | 
| 112 | 
            +
                  :uid => @user_id, :target_id => @target_user_id,
         | 
| 113 | 
            +
                  :message => "Hello other user!"
         | 
| 114 | 
            +
                })
         | 
| 115 | 
            +
                
         | 
| 116 | 
            +
            all responses will be json. In the instance of 'bad json' methods, the response will formatted {'response': '#{bad_response_string}'}
         | 
| 117 | 
            +
             | 
| 118 | 
            +
             | 
| 119 | 
            +
            Some Higher Level Objects for Common Uses
         | 
| 120 | 
            +
            ----------------------
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            Get a MiniFB::Session:
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                @fb = MiniFB::Session.new(FB_API_KEY, FB_SECRET, @fb_session, @fb_uid)
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            Then it makes it a bit easier to use call for a particular user/session.
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                response = @fb.call("stream.get")
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            With the session, you can then get the user information for the session/uid.
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                user = @fb.user
         | 
| 133 | 
            +
             | 
| 134 | 
            +
            Then get info from the user:
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                first_name = user["first_name"]
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            Or profile photos:
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                photos = user.profile_photos
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            Or if you want other photos, try:
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                photos = @fb.photos("pids"=>[12343243,920382343,9208348])
         | 
| 145 | 
            +
             | 
| 146 | 
            +
             | 
| 147 | 
            +
            Higher Level Objects with OAuth2
         | 
| 148 | 
            +
            --------------------------------
         | 
| 149 | 
            +
             | 
| 150 | 
            +
            Get a MiniFB::OAuthSession with a Spanish locale:
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                @fb = MiniFB::OAuthSession.new(access_token, 'es_ES')
         | 
| 153 | 
            +
             | 
| 154 | 
            +
            Using the session object to make requests:
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                @fb.get('117199051648010')
         | 
| 157 | 
            +
                @fb.post('me', :type => :feed, :params => {
         | 
| 158 | 
            +
                  :message => "This is me from MiniFB"
         | 
| 159 | 
            +
                })
         | 
| 160 | 
            +
                @fb.fql('SELECT id FROM object_url WHERE url="http://www.imdb.com/title/tt1250777/"')
         | 
| 161 | 
            +
                @fb.rest('notes.create', :params => {
         | 
| 162 | 
            +
                  :title => "ToDo", :content => "Try MiniFB"
         | 
| 163 | 
            +
                })
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            Getting graph objects through the session:
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                @fb.me
         | 
| 168 | 
            +
                @fb.me.name
         | 
| 169 | 
            +
                @fb.me.connections
         | 
| 170 | 
            +
                @fb.me.feed
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                @ssp = @fb.graph_object('117199051648010')
         | 
| 173 | 
            +
                @ssp.mission
         | 
| 174 | 
            +
                @ssp.photos
         | 
| 175 | 
            +
             | 
| 176 | 
            +
             | 
| 177 | 
            +
            Facebook Connect
         | 
| 178 | 
            +
            ----------------
         | 
| 179 | 
            +
             | 
| 180 | 
            +
            This is actually very easy, first follow these instructions: http://wiki.developers.facebook.com/index.php/Connect/Setting_Up_Your_Site
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            Then add the following script to the page where you put the login button so it looks like this:
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                <script>
         | 
| 185 | 
            +
                    function facebook_onlogin(){
         | 
| 186 | 
            +
                        document.location.href = "<%= url_for :action=>"fb_connect" %>";
         | 
| 187 | 
            +
                    }
         | 
| 188 | 
            +
                </script>
         | 
| 189 | 
            +
                <fb:login-button onlogin="facebook_onlogin();"></fb:login-button>
         | 
| 190 | 
            +
             | 
| 191 | 
            +
            Define an fb_connect method in your login/sessions controller like so:
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                 def fb_connect
         | 
| 194 | 
            +
                    @fb_info = MiniFB.parse_cookie_information(FB_APP_ID, cookies) # some users may have to use their API rather than the app. ID.
         | 
| 195 | 
            +
                    puts "uid=#{@fb_info['uid']}"
         | 
| 196 | 
            +
                    puts "session=#{@fb_info['session_key']}"
         | 
| 197 | 
            +
                    
         | 
| 198 | 
            +
                    if MiniFB.verify_cookie_signature(FB_APP_ID, FB_SECRET, cookies)
         | 
| 199 | 
            +
                      # And here you would create the user if it doesn't already exist, then redirect them to wherever you want.
         | 
| 200 | 
            +
                    else
         | 
| 201 | 
            +
                      # The cookies may have been modified as the signature does not match
         | 
| 202 | 
            +
                    end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
             | 
| 207 | 
            +
            Photo Uploads
         | 
| 208 | 
            +
            -------------
         | 
| 209 | 
            +
             | 
| 210 | 
            +
            This is as simple as calling:
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                @fb.call("photos.upload", "filename"=>"<full path to file>")
         | 
| 213 | 
            +
             | 
| 214 | 
            +
            The file_name parameter will be used as the file data.
         | 
| 215 | 
            +
             | 
| 216 | 
            +
            Video Uploads
         | 
| 217 | 
            +
            -------------
         | 
| 218 | 
            +
             | 
| 219 | 
            +
            Similar to photos, but the correct [mime-type](http://en.wikipedia.org/wiki/Internet_media_type) is required:
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                @fb.call("video.upload", "filename"=>"<full path to file>", "mime_type" => "video/mp4")
         | 
| 222 | 
            +
             | 
    
        data/lib/mini_fb.rb
    ADDED
    
    | @@ -0,0 +1,811 @@ | |
| 1 | 
            +
            #MiniFB - the simple miniature facebook library
         | 
| 2 | 
            +
            #MiniFB is a small, lightweight Ruby library for interacting with the Facebook API.
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
            #Brought to you by: www.appoxy.com
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            #Support
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            #Join our Discussion Group at: http://groups.google.com/group/mini_fb
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            #Demo Rails Application
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            #There is a demo Rails app that uses mini_fb graph api at: http://github.com/appoxy/mini_fb_demo
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            require 'digest/md5'
         | 
| 15 | 
            +
            require 'erb'
         | 
| 16 | 
            +
            require 'json' unless defined? JSON
         | 
| 17 | 
            +
            require 'rest_client'
         | 
| 18 | 
            +
            require 'hashie'
         | 
| 19 | 
            +
            require 'base64'
         | 
| 20 | 
            +
            require 'openssl'
         | 
| 21 | 
            +
            require 'logger'
         | 
| 22 | 
            +
            require 'mime/types'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            module MiniFB
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # Global constants
         | 
| 27 | 
            +
                FB_URL = "http://api.facebook.com/restserver.php"
         | 
| 28 | 
            +
                FB_VIDEO_URL = "https://api-video.facebook.com/restserver.php"
         | 
| 29 | 
            +
                FB_API_VERSION = "1.0"
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                @@logging = false
         | 
| 32 | 
            +
                @@log = Logger.new(STDOUT)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def self.log_level=(level)
         | 
| 35 | 
            +
                    if level.is_a? Numeric
         | 
| 36 | 
            +
                        @@log.level = level
         | 
| 37 | 
            +
                    else
         | 
| 38 | 
            +
                        @@log.level = case level
         | 
| 39 | 
            +
                            when :fatal
         | 
| 40 | 
            +
                                @@log.level = Logger::FATAL
         | 
| 41 | 
            +
                            when :error
         | 
| 42 | 
            +
                                @@log.level = Logger::ERROR
         | 
| 43 | 
            +
                            when :warn
         | 
| 44 | 
            +
                                @@log.level = Logger::WARN
         | 
| 45 | 
            +
                            when :info
         | 
| 46 | 
            +
                                @@log.level = Logger::INFO
         | 
| 47 | 
            +
                            when :debug
         | 
| 48 | 
            +
                                @@log.level = Logger::DEBUG
         | 
| 49 | 
            +
                                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def self.enable_logging
         | 
| 54 | 
            +
                    @@logging = true
         | 
| 55 | 
            +
                    @@log.level = Logger::DEBUG
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def self.disable_logging
         | 
| 59 | 
            +
                    @@logging = false
         | 
| 60 | 
            +
                    @@log.level = Logger::ERROR
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                class FaceBookError < StandardError
         | 
| 64 | 
            +
                    attr_accessor :code
         | 
| 65 | 
            +
                    # Error that happens during a facebook call.
         | 
| 66 | 
            +
                    def initialize(error_code, error_msg)
         | 
| 67 | 
            +
                        @code = error_code
         | 
| 68 | 
            +
                        super("Facebook error #{error_code}: #{error_msg}")
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                class Session
         | 
| 73 | 
            +
                    attr_accessor :api_key, :secret_key, :session_key, :uid
         | 
| 74 | 
            +
             | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def initialize(api_key, secret_key, session_key, uid)
         | 
| 77 | 
            +
                        @api_key = api_key
         | 
| 78 | 
            +
                        @secret_key = FaceBookSecret.new secret_key
         | 
| 79 | 
            +
                        @session_key = session_key
         | 
| 80 | 
            +
                        @uid = uid
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    # returns current user
         | 
| 84 | 
            +
                    def user
         | 
| 85 | 
            +
                        return @user unless @user.nil?
         | 
| 86 | 
            +
                        @user = User.new(MiniFB.call(@api_key, @secret_key, "Users.getInfo", "session_key"=>@session_key, "uids"=>@uid, "fields"=>User.all_fields)[0], self)
         | 
| 87 | 
            +
                        @user
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    def photos
         | 
| 91 | 
            +
                        Photos.new(self)
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
             | 
| 95 | 
            +
                    def call(method, params={})
         | 
| 96 | 
            +
                        return MiniFB.call(api_key, secret_key, method, params.update("session_key"=>session_key))
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                class User
         | 
| 102 | 
            +
                    FIELDS = [:uid, :status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :birthday_date, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations, :locale, :profile_url, :proxied_email, :email, :email_hashes, :allowed_restrictions, :pic_with_logo, :pic_big_with_logo, :pic_small_with_logo, :pic_square_with_logo]
         | 
| 103 | 
            +
                    STANDARD_FIELDS = [:uid, :first_name, :last_name, :name, :timezone, :birthday, :sex, :affiliations, :locale, :profile_url, :proxied_email, :email]
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    def self.all_fields
         | 
| 106 | 
            +
                        FIELDS.join(",")
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    def self.standard_fields
         | 
| 110 | 
            +
                        STANDARD_FIELDS.join(",")
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    def initialize(fb_hash, session)
         | 
| 114 | 
            +
                        @fb_hash = fb_hash
         | 
| 115 | 
            +
                        @session = session
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    def [](key)
         | 
| 119 | 
            +
                        @fb_hash[key]
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    def uid
         | 
| 123 | 
            +
                        return self["uid"]
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                    def profile_photos
         | 
| 127 | 
            +
                        @session.photos.get("uid"=>uid, "aid"=>profile_pic_album_id)
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    def profile_pic_album_id
         | 
| 131 | 
            +
                        merge_aid(-3, uid)
         | 
| 132 | 
            +
                    end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    def merge_aid(aid, uid)
         | 
| 135 | 
            +
                        uid = uid.to_i
         | 
| 136 | 
            +
                        ret = (uid << 32) + (aid & 0xFFFFFFFF)
         | 
| 137 | 
            +
            #            puts 'merge_aid=' + ret.inspect
         | 
| 138 | 
            +
                        return ret
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                class Photos
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                    def initialize(session)
         | 
| 145 | 
            +
                        @session = session
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                    def get(params)
         | 
| 149 | 
            +
                        pids = params["pids"]
         | 
| 150 | 
            +
                        if !pids.nil? && pids.is_a?(Array)
         | 
| 151 | 
            +
                            pids = pids.join(",")
         | 
| 152 | 
            +
                            params["pids"] = pids
         | 
| 153 | 
            +
                        end
         | 
| 154 | 
            +
                        @session.call("photos.get", params)
         | 
| 155 | 
            +
                    end
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                BAD_JSON_METHODS = ["users.getloggedinuser", "auth.promotesession", "users.hasapppermission",
         | 
| 159 | 
            +
                                    "Auth.revokeExtendedPermission", "auth.revokeAuthorization",
         | 
| 160 | 
            +
                                    "pages.isAdmin", "pages.isFan",
         | 
| 161 | 
            +
                                    "stream.publish",
         | 
| 162 | 
            +
                                    "dashboard.addNews", "dashboard.addGlobalNews", "dashboard.publishActivity",
         | 
| 163 | 
            +
                                    "dashboard.incrementcount", "dashboard.setcount"
         | 
| 164 | 
            +
                ].collect { |x| x.downcase }
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                # THIS IS FOR THE OLD FACEBOOK API, NOT THE GRAPH ONE. See MiniFB.get and MiniFB.post for Graph API
         | 
| 167 | 
            +
                #
         | 
| 168 | 
            +
                # Call facebook server with a method request. Most keyword arguments
         | 
| 169 | 
            +
                # are passed directly to the server with a few exceptions.
         | 
| 170 | 
            +
                # The 'sig' value will always be computed automatically.
         | 
| 171 | 
            +
                # The 'v' version will be supplied automatically if needed.
         | 
| 172 | 
            +
                # The 'call_id' defaults to True, which will generate a valid
         | 
| 173 | 
            +
                # number. Otherwise it should be a valid number or False to disable.
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                # The default return is a parsed json object.
         | 
| 176 | 
            +
                # Unless the 'format' and/or 'callback' arguments are given,
         | 
| 177 | 
            +
                # in which case the raw text of the reply is returned. The string
         | 
| 178 | 
            +
                # will always be returned, even during errors.
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                # If an error occurs, a FacebookError exception will be raised
         | 
| 181 | 
            +
                # with the proper code and message.
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                # The secret argument should be an instance of FacebookSecret
         | 
| 184 | 
            +
                # to hide value from simple introspection.
         | 
| 185 | 
            +
                def MiniFB.call(api_key, secret, method, kwargs)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    puts 'kwargs=' + kwargs.inspect if @@logging
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                    if secret.is_a? String
         | 
| 190 | 
            +
                        secret = FaceBookSecret.new(secret)
         | 
| 191 | 
            +
                    end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                    # Prepare arguments for call
         | 
| 194 | 
            +
                    call_id = kwargs.fetch("call_id", true)
         | 
| 195 | 
            +
                    if call_id == true
         | 
| 196 | 
            +
                        kwargs["call_id"] = Time.now.tv_sec.to_s
         | 
| 197 | 
            +
                    else
         | 
| 198 | 
            +
                        kwargs.delete("call_id")
         | 
| 199 | 
            +
                    end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                    custom_format = kwargs.include?("format") || kwargs.include?("callback")
         | 
| 202 | 
            +
                    kwargs["format"] ||= "JSON"
         | 
| 203 | 
            +
                    kwargs["v"] ||= FB_API_VERSION
         | 
| 204 | 
            +
                    kwargs["api_key"]||= api_key
         | 
| 205 | 
            +
                    kwargs["method"] ||= method
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                    file_name = kwargs.delete("filename")
         | 
| 208 | 
            +
                    mime_type = kwargs.delete("mime_type") || 'image/jpeg'
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                    kwargs["sig"] = signature_for(kwargs, secret.value.call)
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                    fb_method = kwargs["method"].downcase
         | 
| 213 | 
            +
                    if (fb_method == "photos.upload" || fb_method == 'video.upload')
         | 
| 214 | 
            +
                        # Then we need a multipart post
         | 
| 215 | 
            +
                        response = MiniFB.post_upload(file_name, kwargs, mime_type)
         | 
| 216 | 
            +
                    else
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                        begin
         | 
| 219 | 
            +
                            response = Net::HTTP.post_form(URI.parse(FB_URL), post_params(kwargs))
         | 
| 220 | 
            +
                        rescue SocketError => err
         | 
| 221 | 
            +
                            # why are we catching this and throwing as different error?  hmmm..
         | 
| 222 | 
            +
                            # raise IOError.new( "Cannot connect to the facebook server: " + err )
         | 
| 223 | 
            +
                            raise err
         | 
| 224 | 
            +
                        end
         | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    # Handle response
         | 
| 228 | 
            +
                    return response.body if custom_format
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                    body = response.body
         | 
| 231 | 
            +
             | 
| 232 | 
            +
                    puts 'response=' + body.inspect if @@logging
         | 
| 233 | 
            +
                    begin
         | 
| 234 | 
            +
                        data = JSON.parse(body)
         | 
| 235 | 
            +
                        if data.include?("error_msg")
         | 
| 236 | 
            +
                            raise FaceBookError.new(data["error_code"] || 1, data["error_msg"])
         | 
| 237 | 
            +
                        end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    rescue JSON::ParserError => ex
         | 
| 240 | 
            +
                        if BAD_JSON_METHODS.include?(fb_method) # Little hack because this response isn't valid JSON
         | 
| 241 | 
            +
                            if body == "0" || body == "false"
         | 
| 242 | 
            +
                                return false
         | 
| 243 | 
            +
                            end
         | 
| 244 | 
            +
                            return body
         | 
| 245 | 
            +
                        else
         | 
| 246 | 
            +
                            raise ex
         | 
| 247 | 
            +
                        end
         | 
| 248 | 
            +
                    end
         | 
| 249 | 
            +
                    return data
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                def MiniFB.post_upload(filename, kwargs, mime_type = 'image/jpeg')
         | 
| 253 | 
            +
                    content = File.open(filename, 'rb') { |f| f.read }
         | 
| 254 | 
            +
                    boundary = "END_OF_PART_#{rand(1 << 64).to_s(16)}"
         | 
| 255 | 
            +
                    header = {'Content-type' => "multipart/form-data, boundary=#{boundary}"}
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                    # Make sure the filename has the correct extension.
         | 
| 258 | 
            +
                    # Facebook is really picky about this.
         | 
| 259 | 
            +
                    remote_filename = ensure_correct_extension(File.basename(filename), mime_type)
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                    # Build query
         | 
| 262 | 
            +
                    query = ''
         | 
| 263 | 
            +
                    kwargs.each { |a, v|
         | 
| 264 | 
            +
                        query <<
         | 
| 265 | 
            +
                                "--#{boundary}\r\n" <<
         | 
| 266 | 
            +
                                "Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" <<
         | 
| 267 | 
            +
                                "#{v}\r\n"
         | 
| 268 | 
            +
                    }
         | 
| 269 | 
            +
                    query <<
         | 
| 270 | 
            +
                            "--#{boundary}\r\n" <<
         | 
| 271 | 
            +
                            "Content-Disposition: form-data; filename=\"#{remote_filename}\"\r\n" <<
         | 
| 272 | 
            +
                            "Content-Transfer-Encoding: binary\r\n" <<
         | 
| 273 | 
            +
                            "Content-Type: #{mime_type}\r\n\r\n" <<
         | 
| 274 | 
            +
                            content <<
         | 
| 275 | 
            +
                            "\r\n" <<
         | 
| 276 | 
            +
                            "--#{boundary}--"
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                    # Call Facebook with POST multipart/form-data request
         | 
| 279 | 
            +
                    url = (mime_type.split('/').first == 'video') ? FB_VIDEO_URL : FB_URL
         | 
| 280 | 
            +
                    raw_post(url, query, header)
         | 
| 281 | 
            +
                end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                def MiniFB.raw_post(url, body, headers)
         | 
| 284 | 
            +
                    uri = URI.parse(url)
         | 
| 285 | 
            +
                    uri.port = (uri.scheme == 'https') ? 443 : 80
         | 
| 286 | 
            +
                    http = Net::HTTP.new(uri.host, uri.port)
         | 
| 287 | 
            +
                    if uri.scheme == 'https'
         | 
| 288 | 
            +
                        http.use_ssl = true
         | 
| 289 | 
            +
                        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         | 
| 290 | 
            +
                    end
         | 
| 291 | 
            +
                    http.start { |h| h.post(uri.path, body, headers) }
         | 
| 292 | 
            +
                end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                # Returns true is signature is valid, false otherwise.
         | 
| 295 | 
            +
                def MiniFB.verify_signature(secret, arguments)
         | 
| 296 | 
            +
                    if arguments.is_a? String
         | 
| 297 | 
            +
                        #new way: params[:session]
         | 
| 298 | 
            +
                        session = JSON.parse(arguments)
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                        signature = session.delete('sig')
         | 
| 301 | 
            +
                        return false if signature.nil?
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                        arg_string = String.new
         | 
| 304 | 
            +
                        session.sort.each { |k, v| arg_string << "#{k}=#{v}" }
         | 
| 305 | 
            +
                        if Digest::MD5.hexdigest(arg_string + secret) == signature
         | 
| 306 | 
            +
                            return true
         | 
| 307 | 
            +
                        end
         | 
| 308 | 
            +
                    else
         | 
| 309 | 
            +
                        #old way
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                        signature = arguments.delete("fb_sig")
         | 
| 312 | 
            +
                        return false if signature.nil?
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                        unsigned = Hash.new
         | 
| 315 | 
            +
                        signed = Hash.new
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                        arguments.each do |k, v|
         | 
| 318 | 
            +
                            if k =~ /^fb_sig_(.*)/ then
         | 
| 319 | 
            +
                                signed[$1] = v
         | 
| 320 | 
            +
                            else
         | 
| 321 | 
            +
                                unsigned[k] = v
         | 
| 322 | 
            +
                            end
         | 
| 323 | 
            +
                        end
         | 
| 324 | 
            +
             | 
| 325 | 
            +
                        arg_string = String.new
         | 
| 326 | 
            +
                        signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
         | 
| 327 | 
            +
                        if Digest::MD5.hexdigest(arg_string + secret) == signature
         | 
| 328 | 
            +
                            return true
         | 
| 329 | 
            +
                        end
         | 
| 330 | 
            +
                    end
         | 
| 331 | 
            +
                    return false
         | 
| 332 | 
            +
                end
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                # This function takes the app secret and the signed request, and verifies if the request is valid.
         | 
| 335 | 
            +
                def self.verify_signed_request(secret, req)
         | 
| 336 | 
            +
                    s, p = req.split(".")
         | 
| 337 | 
            +
                    sig = base64_url_decode(s)
         | 
| 338 | 
            +
                    expected_sig = OpenSSL::HMAC.digest('SHA256', secret, p.tr("-_", "+/"))
         | 
| 339 | 
            +
                    return sig == expected_sig
         | 
| 340 | 
            +
                end
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                # This function decodes the data sent by Facebook and returns a Hash.
         | 
| 343 | 
            +
                # See: http://developers.facebook.com/docs/authentication/canvas
         | 
| 344 | 
            +
                def self.signed_request_params(secret, req)
         | 
| 345 | 
            +
                    s, p = req.split(".")
         | 
| 346 | 
            +
                    p = base64_url_decode(p)
         | 
| 347 | 
            +
                    h = JSON.parse(p)
         | 
| 348 | 
            +
                    h.delete('algorithm') if h['algorithm'] == 'HMAC-SHA256'
         | 
| 349 | 
            +
                    h
         | 
| 350 | 
            +
                end
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                # Ruby's implementation of base64 decoding seems to be reading the string in multiples of 4 and ignoring
         | 
| 353 | 
            +
                # any extra characters if there are no white-space characters at the end. Since facebook does not take this
         | 
| 354 | 
            +
                # into account, this function fills any string with white spaces up to the point where it becomes divisible
         | 
| 355 | 
            +
                # by 4, then it replaces '-' with '+' and '_' with '/' (URL-safe decoding), and decodes the result.
         | 
| 356 | 
            +
                def self.base64_url_decode(str)
         | 
| 357 | 
            +
                    str = str + "=" * (4 - str.size % 4) unless str.size % 4 == 0
         | 
| 358 | 
            +
                    return Base64.decode64(str.tr("-_", "+/"))
         | 
| 359 | 
            +
                end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                # Parses cookies in order to extract the facebook cookie and parse it into a useable hash
         | 
| 362 | 
            +
                #
         | 
| 363 | 
            +
                # options:
         | 
| 364 | 
            +
                # * app_id - the connect applications app_id (some users may find they have to use their facebook API key)
         | 
| 365 | 
            +
                # * secret - the connect application secret
         | 
| 366 | 
            +
                # * cookies - the cookies given by facebook - it is ok to just pass all of the cookies, the method will do the filtering for you.
         | 
| 367 | 
            +
                def MiniFB.parse_cookie_information(app_id, cookies)
         | 
| 368 | 
            +
                    return nil if cookies["fbs_#{app_id}"].nil?
         | 
| 369 | 
            +
                    Hash[*cookies["fbs_#{app_id}"].split('&').map { |v| v.gsub('"', '').split('=', 2) }.flatten]
         | 
| 370 | 
            +
                end
         | 
| 371 | 
            +
             | 
| 372 | 
            +
                # Validates that the cookies sent by the user are those that were set by facebook. Since your
         | 
| 373 | 
            +
                # secret is only known by you and facebook it is used to sign all of the cookies set.
         | 
| 374 | 
            +
                #
         | 
| 375 | 
            +
                # options:
         | 
| 376 | 
            +
                # * app_id - the connect applications app_id (some users may find they have to use their facebook API key)
         | 
| 377 | 
            +
                # * secret - the connect application secret
         | 
| 378 | 
            +
                # * cookies - the cookies given by facebook - it is ok to just pass all of the cookies, the method will do the filtering for you.
         | 
| 379 | 
            +
                def MiniFB.verify_cookie_signature(app_id, secret, cookies)
         | 
| 380 | 
            +
                    fb_keys = MiniFB.parse_cookie_information(app_id, cookies)
         | 
| 381 | 
            +
                    return false if fb_keys.nil?
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                    signature = fb_keys.delete('sig')
         | 
| 384 | 
            +
                    return signature == Digest::MD5.hexdigest(fb_keys.map { |k, v| "#{k}=#{v}" }.sort.join + secret)
         | 
| 385 | 
            +
                end
         | 
| 386 | 
            +
             | 
| 387 | 
            +
                # <b>DEPRECATED:</b> Please use <tt>verify_cookie_signature</tt> instead.
         | 
| 388 | 
            +
                def MiniFB.verify_connect_signature(api_key, secret, cookies)
         | 
| 389 | 
            +
                    warn "DEPRECATION WARNING: 'verify_connect_signature' has been renamed to 'verify_cookie_signature' as Facebook no longer calls this 'connect'"
         | 
| 390 | 
            +
                    MiniFB.verify_cookie_signature(api_key, secret, cookies)
         | 
| 391 | 
            +
                end
         | 
| 392 | 
            +
             | 
| 393 | 
            +
                # Returns the login/add app url for your application.
         | 
| 394 | 
            +
                #
         | 
| 395 | 
            +
                # options:
         | 
| 396 | 
            +
                #    - :next => a relative next page to go to. relative to your facebook connect url or if :canvas is true, then relative to facebook app url
         | 
| 397 | 
            +
                #    - :canvas => true/false - to say whether this is a canvas app or not
         | 
| 398 | 
            +
                def self.login_url(api_key, options={})
         | 
| 399 | 
            +
                    login_url = "http://api.facebook.com/login.php?api_key=#{api_key}"
         | 
| 400 | 
            +
                    login_url << "&next=#{options[:next]}" if options[:next]
         | 
| 401 | 
            +
                    login_url << "&canvas" if options[:canvas]
         | 
| 402 | 
            +
                    login_url
         | 
| 403 | 
            +
                end
         | 
| 404 | 
            +
             | 
| 405 | 
            +
             | 
| 406 | 
            +
                def self.ensure_correct_extension(filename, mime_type)
         | 
| 407 | 
            +
                    allowed_extensions = MIME::Types[mime_type].first.extensions
         | 
| 408 | 
            +
                    extension = File.extname(filename)[1 .. -1]
         | 
| 409 | 
            +
                    if !allowed_extensions.include? extension
         | 
| 410 | 
            +
                      filename += '.' + allowed_extensions.first
         | 
| 411 | 
            +
                    end
         | 
| 412 | 
            +
                end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                # Manages access_token and locale params for an OAuth connection
         | 
| 415 | 
            +
                class OAuthSession
         | 
| 416 | 
            +
             | 
| 417 | 
            +
                    def initialize(access_token, locale="en_US")
         | 
| 418 | 
            +
                        @access_token = access_token
         | 
| 419 | 
            +
                        @locale = locale
         | 
| 420 | 
            +
                    end
         | 
| 421 | 
            +
             | 
| 422 | 
            +
                    def get(id, options={})
         | 
| 423 | 
            +
                        MiniFB.get(@access_token, id, session_options(options))
         | 
| 424 | 
            +
                    end
         | 
| 425 | 
            +
             | 
| 426 | 
            +
                    def post(id, options={})
         | 
| 427 | 
            +
                        MiniFB.post(@access_token, id, session_options(options))
         | 
| 428 | 
            +
                    end
         | 
| 429 | 
            +
             | 
| 430 | 
            +
                    def fql(fql_query, options={})
         | 
| 431 | 
            +
                        MiniFB.fql(@access_token, fql_query, session_options(options))
         | 
| 432 | 
            +
                    end
         | 
| 433 | 
            +
             | 
| 434 | 
            +
                    def multifql(fql_queries, options={})
         | 
| 435 | 
            +
                        MiniFB.multifql(@access_token, fql_queries, session_options(options))
         | 
| 436 | 
            +
                    end
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                    def rest(api_method, options={})
         | 
| 439 | 
            +
                        MiniFB.rest(@access_token, api_method, session_options(options))
         | 
| 440 | 
            +
                    end
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                    # Returns a GraphObject for the given id
         | 
| 443 | 
            +
                    def graph_object(id)
         | 
| 444 | 
            +
                        MiniFB::GraphObject.new(self, id)
         | 
| 445 | 
            +
                    end
         | 
| 446 | 
            +
             | 
| 447 | 
            +
                    # Returns and caches a GraphObject for the user
         | 
| 448 | 
            +
                    def me
         | 
| 449 | 
            +
                        @me ||= graph_object('me')
         | 
| 450 | 
            +
                    end
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                    private
         | 
| 453 | 
            +
                    def session_options(options)
         | 
| 454 | 
            +
                        (options[:params] ||= {})[:locale] ||= @locale
         | 
| 455 | 
            +
                        options
         | 
| 456 | 
            +
                    end
         | 
| 457 | 
            +
                end
         | 
| 458 | 
            +
             | 
| 459 | 
            +
                # Wraps a graph object for easily accessing its connections
         | 
| 460 | 
            +
                class GraphObject
         | 
| 461 | 
            +
                    # Creates a GraphObject using an OAuthSession or access_token
         | 
| 462 | 
            +
                    def initialize(session_or_token, id)
         | 
| 463 | 
            +
                        @oauth_session = if session_or_token.is_a?(MiniFB::OAuthSession)
         | 
| 464 | 
            +
                            session_or_token
         | 
| 465 | 
            +
                                         else
         | 
| 466 | 
            +
                                             MiniFB::OAuthSession.new(session_or_token)
         | 
| 467 | 
            +
                                         end
         | 
| 468 | 
            +
                        @id = id
         | 
| 469 | 
            +
                        @object = @oauth_session.get(id, :metadata => true)
         | 
| 470 | 
            +
                        @connections_cache = {}
         | 
| 471 | 
            +
                    end
         | 
| 472 | 
            +
             | 
| 473 | 
            +
                    def inspect
         | 
| 474 | 
            +
                        "<##{self.class.name} #{@object.inspect}>"
         | 
| 475 | 
            +
                    end
         | 
| 476 | 
            +
             | 
| 477 | 
            +
                    def connections
         | 
| 478 | 
            +
                        @object.metadata.connections.keys
         | 
| 479 | 
            +
                    end
         | 
| 480 | 
            +
             | 
| 481 | 
            +
                    unless RUBY_VERSION =~ /1\.9/
         | 
| 482 | 
            +
                        undef :id, :type
         | 
| 483 | 
            +
                    end
         | 
| 484 | 
            +
             | 
| 485 | 
            +
                    def methods
         | 
| 486 | 
            +
                        super + @object.keys.include?(key) + connections.include?(key)
         | 
| 487 | 
            +
                    end
         | 
| 488 | 
            +
             | 
| 489 | 
            +
                    def respond_to?(method)
         | 
| 490 | 
            +
                        @object.keys.include?(key) || connections.include?(key) || super
         | 
| 491 | 
            +
                    end
         | 
| 492 | 
            +
             | 
| 493 | 
            +
                    def keys
         | 
| 494 | 
            +
                        @object.keys
         | 
| 495 | 
            +
                    end
         | 
| 496 | 
            +
             | 
| 497 | 
            +
                    def [](key)
         | 
| 498 | 
            +
                        @object[key]
         | 
| 499 | 
            +
                    end
         | 
| 500 | 
            +
             | 
| 501 | 
            +
                    def method_missing(method, *args, &block)
         | 
| 502 | 
            +
                        key = method.to_s
         | 
| 503 | 
            +
                        if @object.keys.include?(key)
         | 
| 504 | 
            +
                            @object[key]
         | 
| 505 | 
            +
                        elsif @connections_cache.has_key?(key)
         | 
| 506 | 
            +
                            @connections_cache[key]
         | 
| 507 | 
            +
                        elsif connections.include?(key)
         | 
| 508 | 
            +
                            @connections_cache[key] = @oauth_session.get(@id, :type => key)
         | 
| 509 | 
            +
                        else
         | 
| 510 | 
            +
                            super
         | 
| 511 | 
            +
                        end
         | 
| 512 | 
            +
                    end
         | 
| 513 | 
            +
                end
         | 
| 514 | 
            +
             | 
| 515 | 
            +
                def self.graph_base
         | 
| 516 | 
            +
                    "https://graph.facebook.com/"
         | 
| 517 | 
            +
                end
         | 
| 518 | 
            +
             | 
| 519 | 
            +
                # options:
         | 
| 520 | 
            +
                #   - scope: comma separated list of extends permissions. see http://developers.facebook.com/docs/authentication/permissions
         | 
| 521 | 
            +
                def self.oauth_url(app_id, redirect_uri, options={})
         | 
| 522 | 
            +
                    oauth_url = "#{graph_base}oauth/authorize"
         | 
| 523 | 
            +
                    oauth_url << "?client_id=#{app_id}"
         | 
| 524 | 
            +
                    oauth_url << "&redirect_uri=#{CGI.escape(redirect_uri)}"
         | 
| 525 | 
            +
            #        oauth_url << "&scope=#{options[:scope]}" if options[:scope]
         | 
| 526 | 
            +
                    oauth_url << ("&" + options.map { |k, v| "%s=%s" % [k, v] }.join('&')) unless options.empty?
         | 
| 527 | 
            +
                    oauth_url
         | 
| 528 | 
            +
                end
         | 
| 529 | 
            +
             | 
| 530 | 
            +
                # returns a hash with one value being 'access_token', the other being 'expires'
         | 
| 531 | 
            +
                def self.oauth_access_token(app_id, redirect_uri, secret, code)
         | 
| 532 | 
            +
                    oauth_url = "#{graph_base}oauth/access_token"
         | 
| 533 | 
            +
                    oauth_url << "?client_id=#{app_id}"
         | 
| 534 | 
            +
                    oauth_url << "&redirect_uri=#{CGI.escape(redirect_uri)}"
         | 
| 535 | 
            +
                    oauth_url << "&client_secret=#{secret}"
         | 
| 536 | 
            +
                    oauth_url << "&code=#{CGI.escape(code)}"
         | 
| 537 | 
            +
                    resp = RestClient.get oauth_url
         | 
| 538 | 
            +
                    puts 'resp=' + resp.body.to_s if @@logging
         | 
| 539 | 
            +
                    params = {}
         | 
| 540 | 
            +
                    params_array = resp.split("&")
         | 
| 541 | 
            +
                    params_array.each do |p|
         | 
| 542 | 
            +
                        ps = p.split("=")
         | 
| 543 | 
            +
                        params[ps[0]] = ps[1]
         | 
| 544 | 
            +
                    end
         | 
| 545 | 
            +
                    return params
         | 
| 546 | 
            +
                end
         | 
| 547 | 
            +
             | 
| 548 | 
            +
                # Return a JSON object of working Oauth tokens from working session keys, returned in order given
         | 
| 549 | 
            +
                def self.oauth_exchange_session(app_id, secret, session_keys)
         | 
| 550 | 
            +
                    url = "#{graph_base}oauth/exchange_sessions"
         | 
| 551 | 
            +
                    params = {}
         | 
| 552 | 
            +
                    params["client_id"] = "#{app_id}"
         | 
| 553 | 
            +
                    params["client_secret"] = "#{secret}"
         | 
| 554 | 
            +
                    params["sessions"] = "#{session_keys}"
         | 
| 555 | 
            +
                    options = {}
         | 
| 556 | 
            +
                    options[:params] = params
         | 
| 557 | 
            +
                    options[:method] = :post
         | 
| 558 | 
            +
                    return fetch(url, options)
         | 
| 559 | 
            +
                end
         | 
| 560 | 
            +
             | 
| 561 | 
            +
                # Return a JSON object of working Oauth tokens from working session keys, returned in order given
         | 
| 562 | 
            +
                def self.authenticate_as_app(app_id, secret)
         | 
| 563 | 
            +
                    url = "#{graph_base}oauth/access_token"
         | 
| 564 | 
            +
                    params = {}
         | 
| 565 | 
            +
                    params["type"] = "client_cred"
         | 
| 566 | 
            +
                    params["client_id"] = "#{app_id}"
         | 
| 567 | 
            +
                    params["client_secret"] = "#{secret}"
         | 
| 568 | 
            +
            #      resp = RestClient.get url
         | 
| 569 | 
            +
                    options = {}
         | 
| 570 | 
            +
                    options[:params] = params
         | 
| 571 | 
            +
                    options[:method] = :get
         | 
| 572 | 
            +
                    options[:response_type] = :params
         | 
| 573 | 
            +
                    resp = fetch(url, options)
         | 
| 574 | 
            +
                    puts 'resp=' + resp.body.to_s if @@logging
         | 
| 575 | 
            +
                    resp
         | 
| 576 | 
            +
                end
         | 
| 577 | 
            +
             | 
| 578 | 
            +
                # Gets data from the Facebook Graph API
         | 
| 579 | 
            +
                # options:
         | 
| 580 | 
            +
                #   - type: eg: feed, home, etc
         | 
| 581 | 
            +
                #   - metadata: to include metadata in response. true/false
         | 
| 582 | 
            +
                #   - params: Any additional parameters you would like to submit
         | 
| 583 | 
            +
                def self.get(access_token, id, options={})
         | 
| 584 | 
            +
                    url = "#{graph_base}#{id}"
         | 
| 585 | 
            +
                    url << "/#{options[:type]}" if options[:type]
         | 
| 586 | 
            +
                    params = options[:params] || {}
         | 
| 587 | 
            +
                    params["access_token"] = "#{(access_token)}"
         | 
| 588 | 
            +
                    params["metadata"] = "1" if options[:metadata]
         | 
| 589 | 
            +
                    params["fields"] = options[:fields].join(",") if options[:fields]
         | 
| 590 | 
            +
                    options[:params] = params
         | 
| 591 | 
            +
                    return fetch(url, options)
         | 
| 592 | 
            +
                end
         | 
| 593 | 
            +
             | 
| 594 | 
            +
                # Posts data to the Facebook Graph API
         | 
| 595 | 
            +
                # options:
         | 
| 596 | 
            +
                #   - type: eg: feed, home, etc
         | 
| 597 | 
            +
                #   - metadata: to include metadata in response. true/false
         | 
| 598 | 
            +
                #   - params: Any additional parameters you would like to submit
         | 
| 599 | 
            +
                def self.post(access_token, id, options={})
         | 
| 600 | 
            +
                    url = "#{graph_base}#{id}"
         | 
| 601 | 
            +
                    url << "/#{options[:type]}" if options[:type]
         | 
| 602 | 
            +
                    options.delete(:type)
         | 
| 603 | 
            +
                    params = options[:params] || {}
         | 
| 604 | 
            +
                    options.each do |key, value|
         | 
| 605 | 
            +
                        if value.kind_of?(File)
         | 
| 606 | 
            +
                            params[key] = value
         | 
| 607 | 
            +
                        else
         | 
| 608 | 
            +
                            params[key] = "#{value}"
         | 
| 609 | 
            +
                        end
         | 
| 610 | 
            +
                    end
         | 
| 611 | 
            +
                    params["access_token"] = "#{(access_token)}"
         | 
| 612 | 
            +
                    params["metadata"] = "1" if options[:metadata]
         | 
| 613 | 
            +
                    options[:params] = params
         | 
| 614 | 
            +
                    options[:method] = :post
         | 
| 615 | 
            +
                    return fetch(url, options)
         | 
| 616 | 
            +
             | 
| 617 | 
            +
                end
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                # Executes an FQL query
         | 
| 620 | 
            +
                def self.fql(access_token, fql_query, options={})
         | 
| 621 | 
            +
                    url = "https://api.facebook.com/method/fql.query"
         | 
| 622 | 
            +
                    params = options[:params] || {}
         | 
| 623 | 
            +
                    params["access_token"] = "#{(access_token)}"
         | 
| 624 | 
            +
                    params["metadata"] = "1" if options[:metadata]
         | 
| 625 | 
            +
                    params["query"] = fql_query
         | 
| 626 | 
            +
                    params["format"] = "JSON"
         | 
| 627 | 
            +
                    options[:params] = params
         | 
| 628 | 
            +
                    return fetch(url, options)
         | 
| 629 | 
            +
                end
         | 
| 630 | 
            +
             | 
| 631 | 
            +
                # Executes multiple FQL queries
         | 
| 632 | 
            +
                # Example:
         | 
| 633 | 
            +
                #
         | 
| 634 | 
            +
                # MiniFB.multifql(access_token, { :statuses => "SELECT status_id, message FROM status WHERE uid = 12345",
         | 
| 635 | 
            +
                #                                 :privacy => "SELECT object_id, description FROM privacy WHERE object_id IN (SELECT status_id FROM #statuses)" })
         | 
| 636 | 
            +
                def self.multifql(access_token, fql_queries, options={})
         | 
| 637 | 
            +
                    url = "https://api.facebook.com/method/fql.multiquery"
         | 
| 638 | 
            +
                    params = options[:params] || {}
         | 
| 639 | 
            +
                    params["access_token"] = "#{(access_token)}"
         | 
| 640 | 
            +
                    params["metadata"] = "1" if options[:metadata]
         | 
| 641 | 
            +
                    params["queries"] = JSON[fql_queries]
         | 
| 642 | 
            +
                    params[:format] = "JSON"
         | 
| 643 | 
            +
                    options[:params] = params
         | 
| 644 | 
            +
                    return fetch(url, options)
         | 
| 645 | 
            +
                end
         | 
| 646 | 
            +
             | 
| 647 | 
            +
                # Uses new Oauth 2 authentication against old Facebook REST API
         | 
| 648 | 
            +
                # options:
         | 
| 649 | 
            +
                #   - params: Any additional parameters you would like to submit
         | 
| 650 | 
            +
                def self.rest(access_token, api_method, options={})
         | 
| 651 | 
            +
                    url = "https://api.facebook.com/method/#{api_method}"
         | 
| 652 | 
            +
                    params = options[:params] || {}
         | 
| 653 | 
            +
                    params[:access_token] = access_token
         | 
| 654 | 
            +
                    params[:format] = "JSON"
         | 
| 655 | 
            +
                    options[:params] = params
         | 
| 656 | 
            +
                    return fetch(url, options)
         | 
| 657 | 
            +
                end
         | 
| 658 | 
            +
             | 
| 659 | 
            +
                def self.fetch(url, options={})
         | 
| 660 | 
            +
             | 
| 661 | 
            +
                    begin
         | 
| 662 | 
            +
                        if options[:method] == :post
         | 
| 663 | 
            +
                            @@log.debug 'url_post=' + url if @@logging
         | 
| 664 | 
            +
                            resp = RestClient.post url, options[:params]
         | 
| 665 | 
            +
                        else
         | 
| 666 | 
            +
                            if options[:params] && options[:params].size > 0
         | 
| 667 | 
            +
                                url += '?' + options[:params].map { |k, v|  CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s) }.join('&')
         | 
| 668 | 
            +
                            end
         | 
| 669 | 
            +
                            @@log.debug 'url_get=' + url if @@logging
         | 
| 670 | 
            +
                            resp = RestClient.get url
         | 
| 671 | 
            +
                        end
         | 
| 672 | 
            +
             | 
| 673 | 
            +
                        @@log.debug 'resp=' + resp.to_s if @@log.debug?
         | 
| 674 | 
            +
             | 
| 675 | 
            +
                        if options[:response_type] == :params
         | 
| 676 | 
            +
                            # Some methods return a param like string, for example: access_token=11935261234123|rW9JMxbN65v_pFWQl5LmHHABC
         | 
| 677 | 
            +
                            params = {}
         | 
| 678 | 
            +
                            params_array = resp.split("&")
         | 
| 679 | 
            +
                            params_array.each do |p|
         | 
| 680 | 
            +
                                ps = p.split("=")
         | 
| 681 | 
            +
                                params[ps[0]] = ps[1]
         | 
| 682 | 
            +
                            end
         | 
| 683 | 
            +
                            return params
         | 
| 684 | 
            +
                        else
         | 
| 685 | 
            +
                            begin
         | 
| 686 | 
            +
                                res_hash = JSON.parse(resp.to_s)
         | 
| 687 | 
            +
                            rescue
         | 
| 688 | 
            +
                                # quick fix for things like stream.publish that don't return json
         | 
| 689 | 
            +
                                res_hash = JSON.parse("{\"response\": #{resp.to_s}}")
         | 
| 690 | 
            +
                            end
         | 
| 691 | 
            +
                        end
         | 
| 692 | 
            +
             | 
| 693 | 
            +
                        if res_hash.is_a? Array # fql  return this
         | 
| 694 | 
            +
                            res_hash.collect! { |x| x.is_a?(Hash) ? Hashie::Mash.new(x) : x }
         | 
| 695 | 
            +
                        else
         | 
| 696 | 
            +
                            res_hash = Hashie::Mash.new(res_hash)
         | 
| 697 | 
            +
                        end
         | 
| 698 | 
            +
             | 
| 699 | 
            +
                        if res_hash.include?("error_msg")
         | 
| 700 | 
            +
                            raise FaceBookError.new(res_hash["error_code"] || 1, res_hash["error_msg"])
         | 
| 701 | 
            +
                        end
         | 
| 702 | 
            +
             | 
| 703 | 
            +
                        return res_hash
         | 
| 704 | 
            +
                    rescue RestClient::Exception => ex
         | 
| 705 | 
            +
                        puts "ex.http_code=" + ex.http_code.to_s
         | 
| 706 | 
            +
                        puts 'ex.http_body=' + ex.http_body if @@logging
         | 
| 707 | 
            +
                        res_hash = JSON.parse(ex.http_body) # probably should ensure it has a good response
         | 
| 708 | 
            +
                        raise MiniFB::FaceBookError.new(ex.http_code, "#{res_hash["error"]["type"]}: #{res_hash["error"]["message"]}")
         | 
| 709 | 
            +
                    end
         | 
| 710 | 
            +
             | 
| 711 | 
            +
                end
         | 
| 712 | 
            +
             | 
| 713 | 
            +
                # Returns all available scopes.
         | 
| 714 | 
            +
                def self.scopes
         | 
| 715 | 
            +
                    scopes = %w{
         | 
| 716 | 
            +
                        about_me activities birthday checkins education_history
         | 
| 717 | 
            +
                        events groups hometown interests likes location notes
         | 
| 718 | 
            +
                        online_presence photo_video_tags photos relationships
         | 
| 719 | 
            +
                        religion_politics status videos website work_history
         | 
| 720 | 
            +
                    }
         | 
| 721 | 
            +
                    scopes.map! do |scope|
         | 
| 722 | 
            +
                        ["user_#{scope}", "friends_#{scope}"]
         | 
| 723 | 
            +
                    end.flatten!
         | 
| 724 | 
            +
             | 
| 725 | 
            +
                    scopes += %w{
         | 
| 726 | 
            +
                      read_insights read_stream read_mailbox read_friendlists read_requests
         | 
| 727 | 
            +
                      email ads_management xmpp_login
         | 
| 728 | 
            +
                      publish_stream create_event rsvp_event sms offline_access
         | 
| 729 | 
            +
                    }
         | 
| 730 | 
            +
                end
         | 
| 731 | 
            +
             | 
| 732 | 
            +
             | 
| 733 | 
            +
                # This function expects arguments as a hash, so
         | 
| 734 | 
            +
                # it is agnostic to different POST handling variants in ruby.
         | 
| 735 | 
            +
                #
         | 
| 736 | 
            +
                # Validate the arguments received from facebook. This is usually
         | 
| 737 | 
            +
                # sent for the iframe in Facebook's canvas. It is not necessary
         | 
| 738 | 
            +
                # to use this on the auth_token and uid passed to callbacks like
         | 
| 739 | 
            +
                # post-add and post-remove.
         | 
| 740 | 
            +
            #
         | 
| 741 | 
            +
                # The arguments must be a mapping of to string keys and values
         | 
| 742 | 
            +
                # or a string of http request data.
         | 
| 743 | 
            +
            #
         | 
| 744 | 
            +
                # If the data is invalid or not signed properly, an empty
         | 
| 745 | 
            +
                # dictionary is returned.
         | 
| 746 | 
            +
            #
         | 
| 747 | 
            +
                # The secret argument should be an instance of FacebookSecret
         | 
| 748 | 
            +
                # to hide value from simple introspection.
         | 
| 749 | 
            +
            #
         | 
| 750 | 
            +
                # DEPRECATED, use verify_signature instead
         | 
| 751 | 
            +
                def MiniFB.validate(secret, arguments)
         | 
| 752 | 
            +
             | 
| 753 | 
            +
                    signature = arguments.delete("fb_sig")
         | 
| 754 | 
            +
                    return arguments if signature.nil?
         | 
| 755 | 
            +
             | 
| 756 | 
            +
                    unsigned = Hash.new
         | 
| 757 | 
            +
                    signed = Hash.new
         | 
| 758 | 
            +
             | 
| 759 | 
            +
                    arguments.each do |k, v|
         | 
| 760 | 
            +
                        if k =~ /^fb_sig_(.*)/ then
         | 
| 761 | 
            +
                            signed[$1] = v
         | 
| 762 | 
            +
                        else
         | 
| 763 | 
            +
                            unsigned[k] = v
         | 
| 764 | 
            +
                        end
         | 
| 765 | 
            +
                    end
         | 
| 766 | 
            +
             | 
| 767 | 
            +
                    arg_string = String.new
         | 
| 768 | 
            +
                    signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
         | 
| 769 | 
            +
                    if Digest::MD5.hexdigest(arg_string + secret) != signature
         | 
| 770 | 
            +
                        unsigned # Hash is incorrect, return only unsigned fields.
         | 
| 771 | 
            +
                    else
         | 
| 772 | 
            +
                        unsigned.merge signed
         | 
| 773 | 
            +
                    end
         | 
| 774 | 
            +
                end
         | 
| 775 | 
            +
             | 
| 776 | 
            +
                class FaceBookSecret
         | 
| 777 | 
            +
                    # Simple container that stores a secret value.
         | 
| 778 | 
            +
                    # Proc cannot be dumped or introspected by normal tools.
         | 
| 779 | 
            +
                    attr_reader :value
         | 
| 780 | 
            +
             | 
| 781 | 
            +
                    def initialize(value)
         | 
| 782 | 
            +
                        @value = Proc.new { value }
         | 
| 783 | 
            +
                    end
         | 
| 784 | 
            +
                end
         | 
| 785 | 
            +
             | 
| 786 | 
            +
                private
         | 
| 787 | 
            +
                def self.post_params(params)
         | 
| 788 | 
            +
                    post_params = {}
         | 
| 789 | 
            +
                    params.each do |k, v|
         | 
| 790 | 
            +
                        k = k.to_s unless k.is_a?(String)
         | 
| 791 | 
            +
                        if Array === v || Hash === v
         | 
| 792 | 
            +
                            post_params[k] = JSON.dump(v)
         | 
| 793 | 
            +
                        else
         | 
| 794 | 
            +
                            post_params[k] = v
         | 
| 795 | 
            +
                        end
         | 
| 796 | 
            +
                    end
         | 
| 797 | 
            +
                    post_params
         | 
| 798 | 
            +
                end
         | 
| 799 | 
            +
             | 
| 800 | 
            +
                def self.signature_for(params, secret)
         | 
| 801 | 
            +
                    params.delete_if { |k, v| v.nil? }
         | 
| 802 | 
            +
                    raw_string = params.inject([]) do |collection, pair|
         | 
| 803 | 
            +
                        collection << pair.map { |x|
         | 
| 804 | 
            +
                            Array === x ? JSON.dump(x) : x
         | 
| 805 | 
            +
                        }.join("=")
         | 
| 806 | 
            +
                        collection
         | 
| 807 | 
            +
                    end.sort.join
         | 
| 808 | 
            +
                    Digest::MD5.hexdigest([raw_string, secret].join)
         | 
| 809 | 
            +
                end
         | 
| 810 | 
            +
             | 
| 811 | 
            +
            end
         | 
| @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            require 'test/unit'
         | 
| 2 | 
            +
            require 'rspec'
         | 
| 3 | 
            +
            require 'uri'
         | 
| 4 | 
            +
            require 'yaml'
         | 
| 5 | 
            +
            require 'active_support/core_ext'
         | 
| 6 | 
            +
            require_relative '../lib/mini_fb'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            describe "Some Feature" do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                before :all do
         | 
| 11 | 
            +
                    @is_setup = true
         | 
| 12 | 
            +
                    @config   = File.open(File.expand_path("~/.test_configs/mini_fb_tests.yml")) { |yf| YAML::load(yf) }
         | 
| 13 | 
            +
                    puts "@config=" + @config.inspect
         | 
| 14 | 
            +
                    MiniFB.log_level = :debug
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    @oauth_url       = MiniFB.oauth_url(@config['fb_app_id'], # your Facebook App ID (NOT API_KEY)
         | 
| 17 | 
            +
                                                        "http://localhost:3000", # redirect url
         | 
| 18 | 
            +
                                                        :scope=>MiniFB.scopes.join(","))
         | 
| 19 | 
            +
                    puts "If you need an access token, go here in your browser:"
         | 
| 20 | 
            +
                    puts "#{@oauth_url}"
         | 
| 21 | 
            +
                    puts "Then grab the 'code' parameter in the redirect url and add it to mini_fb_tests.yml."
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
                before :each do
         | 
| 27 | 
            +
                    # this code runs once per-test
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                it "should do something useful, rather than just being called test1" do
         | 
| 31 | 
            +
                    # el code here
         | 
| 32 | 
            +
                    puts 'whatup'
         | 
| 33 | 
            +
                    true.should be_true
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                it 'test_uri_escape' do
         | 
| 37 | 
            +
                    URI.escape("x=y").should eq("x=y")
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                it 'test_authenticate_as_app' do
         | 
| 41 | 
            +
                    res = MiniFB.authenticate_as_app(@config["fb_api_key"], @config["fb_secret"])
         | 
| 42 | 
            +
                    puts 'res=' + res.inspect
         | 
| 43 | 
            +
                    res.should include("access_token")
         | 
| 44 | 
            +
                    res["access_token"].should match(/^#{@config['fb_app_id']}/)#starts_with?(@config["fb_app_id"].to_s)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
             | 
| 48 | 
            +
                it 'test_signed_request_params' do
         | 
| 49 | 
            +
                    # Example request and secret taken from http://developers.facebook.com/docs/authentication/canvas
         | 
| 50 | 
            +
                    secret = 'secret'
         | 
| 51 | 
            +
                    req = 'vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0'
         | 
| 52 | 
            +
                    assert_equal MiniFB.signed_request_params(secret, req), {"0" => "payload"}
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
             | 
| 58 | 
            +
            def access_token
         | 
| 59 | 
            +
                @config['access_token']
         | 
| 60 | 
            +
            end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
             | 
| 63 | 
            +
            def test_me_with_fields
         | 
| 64 | 
            +
                fields = {
         | 
| 65 | 
            +
                        'interests' => [:name],
         | 
| 66 | 
            +
                        'activities'=> [:name],
         | 
| 67 | 
            +
                        'music'     => [:name],
         | 
| 68 | 
            +
                        'videos'    => [:name],
         | 
| 69 | 
            +
                        'television'=> [:name],
         | 
| 70 | 
            +
                        'movies'    => [:name],
         | 
| 71 | 
            +
                        'likes'     => [:name],
         | 
| 72 | 
            +
                        'work'      => [:name],
         | 
| 73 | 
            +
                        'education' => [:name],
         | 
| 74 | 
            +
                        'books'     => [:name]
         | 
| 75 | 
            +
                }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                snap   = MiniFB.get(access_token, 'me', :fields =>fields.keys)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,100 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: jkarlsson-mini_fb
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              prerelease: false
         | 
| 5 | 
            +
              segments: 
         | 
| 6 | 
            +
              - 1
         | 
| 7 | 
            +
              - 1
         | 
| 8 | 
            +
              - 7
         | 
| 9 | 
            +
              version: 1.1.7
         | 
| 10 | 
            +
            platform: ruby
         | 
| 11 | 
            +
            authors: 
         | 
| 12 | 
            +
            - Travis Reeder
         | 
| 13 | 
            +
            autorequire: 
         | 
| 14 | 
            +
            bindir: bin
         | 
| 15 | 
            +
            cert_chain: []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            date: 2011-02-17 00:00:00 +01:00
         | 
| 18 | 
            +
            default_executable: 
         | 
| 19 | 
            +
            dependencies: 
         | 
| 20 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 21 | 
            +
              name: rest-client
         | 
| 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 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 33 | 
            +
              name: hashie
         | 
| 34 | 
            +
              prerelease: false
         | 
| 35 | 
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 36 | 
            +
                requirements: 
         | 
| 37 | 
            +
                - - ">="
         | 
| 38 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 39 | 
            +
                    segments: 
         | 
| 40 | 
            +
                    - 0
         | 
| 41 | 
            +
                    version: "0"
         | 
| 42 | 
            +
              type: :runtime
         | 
| 43 | 
            +
              version_requirements: *id002
         | 
| 44 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 45 | 
            +
              name: mime-types
         | 
| 46 | 
            +
              prerelease: false
         | 
| 47 | 
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 48 | 
            +
                requirements: 
         | 
| 49 | 
            +
                - - ">="
         | 
| 50 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 51 | 
            +
                    segments: 
         | 
| 52 | 
            +
                    - 0
         | 
| 53 | 
            +
                    version: "0"
         | 
| 54 | 
            +
              type: :runtime
         | 
| 55 | 
            +
              version_requirements: *id003
         | 
| 56 | 
            +
            description: Tiny facebook library. This fork adds video upload support
         | 
| 57 | 
            +
            email: travis@appoxy.com
         | 
| 58 | 
            +
            executables: []
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            extensions: []
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            extra_rdoc_files: 
         | 
| 63 | 
            +
            - LICENSE.txt
         | 
| 64 | 
            +
            - README.markdown
         | 
| 65 | 
            +
            files: 
         | 
| 66 | 
            +
            - lib/mini_fb.rb
         | 
| 67 | 
            +
            - LICENSE.txt
         | 
| 68 | 
            +
            - README.markdown
         | 
| 69 | 
            +
            has_rdoc: true
         | 
| 70 | 
            +
            homepage: http://github.com/jkarlsson/mini_fb
         | 
| 71 | 
            +
            licenses: []
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            post_install_message: 
         | 
| 74 | 
            +
            rdoc_options: []
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            require_paths: 
         | 
| 77 | 
            +
            - lib
         | 
| 78 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 79 | 
            +
              requirements: 
         | 
| 80 | 
            +
              - - ">="
         | 
| 81 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 82 | 
            +
                  segments: 
         | 
| 83 | 
            +
                  - 0
         | 
| 84 | 
            +
                  version: "0"
         | 
| 85 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 86 | 
            +
              requirements: 
         | 
| 87 | 
            +
              - - ">="
         | 
| 88 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 89 | 
            +
                  segments: 
         | 
| 90 | 
            +
                  - 0
         | 
| 91 | 
            +
                  version: "0"
         | 
| 92 | 
            +
            requirements: []
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            rubyforge_project: 
         | 
| 95 | 
            +
            rubygems_version: 1.3.6
         | 
| 96 | 
            +
            signing_key: 
         | 
| 97 | 
            +
            specification_version: 3
         | 
| 98 | 
            +
            summary: Tiny facebook library
         | 
| 99 | 
            +
            test_files: 
         | 
| 100 | 
            +
            - test/test_mini_fb.rb
         |