google_drive 0.3.0 → 0.3.1
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/README.rdoc +2 -0
- data/lib/google_drive.rb +7 -1
- data/lib/google_drive/collection.rb +103 -17
- data/lib/google_drive/file.rb +51 -12
- data/lib/google_drive/list.rb +4 -0
- data/lib/google_drive/list_row.rb +4 -0
- data/lib/google_drive/oauth2_fetcher.rb +2 -2
- data/lib/google_drive/session.rb +34 -10
- data/lib/google_drive/spreadsheet.rb +1 -2
- data/lib/google_drive/util.rb +13 -1
- data/lib/google_drive/worksheet.rb +34 -4
- metadata +37 -11
    
        data/README.rdoc
    CHANGED
    
    
    
        data/lib/google_drive.rb
    CHANGED
    
    | @@ -29,7 +29,10 @@ module GoogleDrive | |
| 29 29 | 
             
                #       :authorize_url => "/o/oauth2/auth")
         | 
| 30 30 | 
             
                #   auth_url = client.auth_code.authorize_url(
         | 
| 31 31 | 
             
                #       :redirect_uri => "http://example.com/",
         | 
| 32 | 
            -
                #        | 
| 32 | 
            +
                #       :scope =>
         | 
| 33 | 
            +
                #           "https://docs.google.com/feeds/ " +
         | 
| 34 | 
            +
                #           "https://docs.googleusercontent.com/ " +
         | 
| 35 | 
            +
                #           "https://spreadsheets.google.com/feeds/")
         | 
| 33 36 | 
             
                #   # Redirect the user to auth_url and get authorization code from redirect URL.
         | 
| 34 37 | 
             
                #   auth_token = client.auth_code.get_token(
         | 
| 35 38 | 
             
                #       authorization_code, :redirect_uri => "http://example.com/")
         | 
| @@ -42,6 +45,9 @@ module GoogleDrive | |
| 42 45 | 
             
                #   access_token = access_token.refresh!
         | 
| 43 46 | 
             
                #   session = GoogleDrive.login_with_oauth(access_token)
         | 
| 44 47 | 
             
                #
         | 
| 48 | 
            +
                # If your app is not a Web app, use "urn:ietf:wg:oauth:2.0:oob" as redirect_url. Then
         | 
| 49 | 
            +
                # authorization code is shown after authorization.
         | 
| 50 | 
            +
                #
         | 
| 45 51 | 
             
                # OAuth1 code example:
         | 
| 46 52 | 
             
                #
         | 
| 47 53 | 
             
                # 1) First generate OAuth consumer object with key and secret for your site by registering site
         | 
| @@ -8,21 +8,45 @@ require "google_drive/spreadsheet" | |
| 8 8 |  | 
| 9 9 | 
             
            module GoogleDrive
         | 
| 10 10 |  | 
| 11 | 
            -
                # Use GoogleDrive::Session# | 
| 12 | 
            -
                 | 
| 11 | 
            +
                # Use GoogleDrive::Session#root_collection, GoogleDrive::Collection#subcollections,
         | 
| 12 | 
            +
                # or GoogleDrive::Session#collection_by_url to get GoogleDrive::Collection object.
         | 
| 13 | 
            +
                class Collection < GoogleDrive::File
         | 
| 13 14 |  | 
| 14 15 | 
             
                    include(Util)
         | 
| 15 16 |  | 
| 16 | 
            -
                     | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 17 | 
            +
                    #:nodoc:
         | 
| 18 | 
            +
                    ROOT_URL = "#{DOCS_BASE_URL}/folder%3Aroot"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    alias collection_feed_url document_feed_url
         | 
| 21 | 
            +
                    
         | 
| 22 | 
            +
                    def contents_url
         | 
| 23 | 
            +
                      if self.root?
         | 
| 24 | 
            +
                        # The root collection doesn't have document feed.
         | 
| 25 | 
            +
                        return concat_url(ROOT_URL, "/contents")
         | 
| 26 | 
            +
                      else
         | 
| 27 | 
            +
                        return self.document_feed_entry.css(
         | 
| 28 | 
            +
                            "content[type='application/atom+xml;type=feed']")[0]["src"]
         | 
| 29 | 
            +
                      end
         | 
| 19 30 | 
             
                    end
         | 
| 20 31 |  | 
| 21 | 
            -
                     | 
| 32 | 
            +
                    # Title of the collection.
         | 
| 33 | 
            +
                    #
         | 
| 34 | 
            +
                    # Set <tt>params[:reload]</tt> to true to force reloading the title.
         | 
| 35 | 
            +
                    def title(params = {})
         | 
| 36 | 
            +
                      if self.root?
         | 
| 37 | 
            +
                        # The root collection doesn't have document feed.
         | 
| 38 | 
            +
                        return nil
         | 
| 39 | 
            +
                      else
         | 
| 40 | 
            +
                        return super
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def resource_id
         | 
| 45 | 
            +
                      return self.root? ? nil : super
         | 
| 46 | 
            +
                    end
         | 
| 22 47 |  | 
| 23 48 | 
             
                    # Adds the given GoogleDrive::File to the collection.
         | 
| 24 49 | 
             
                    def add(file)
         | 
| 25 | 
            -
                      contents_url = concat_url(@collection_feed_url, "/contents")
         | 
| 26 50 | 
             
                      header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
         | 
| 27 51 | 
             
                      xml = <<-"EOS"
         | 
| 28 52 | 
             
                        <entry xmlns="http://www.w3.org/2005/Atom">
         | 
| @@ -30,25 +54,87 @@ module GoogleDrive | |
| 30 54 | 
             
                        </entry>
         | 
| 31 55 | 
             
                      EOS
         | 
| 32 56 | 
             
                      @session.request(
         | 
| 33 | 
            -
                          :post, contents_url, :data => xml, :header => header, :auth => :writely)
         | 
| 57 | 
            +
                          :post, self.contents_url, :data => xml, :header => header, :auth => :writely)
         | 
| 34 58 | 
             
                      return nil
         | 
| 35 59 | 
             
                    end
         | 
| 36 60 |  | 
| 37 | 
            -
                    #  | 
| 38 | 
            -
                    def  | 
| 39 | 
            -
                      contents_url = concat_url(@collection_feed_url, "/contents")
         | 
| 61 | 
            +
                    # Creates a sub-collection with given title. Returns GoogleDrive::Collection object.
         | 
| 62 | 
            +
                    def create_subcollection(title)
         | 
| 40 63 | 
             
                      header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
         | 
| 41 | 
            -
                       | 
| 42 | 
            -
             | 
| 64 | 
            +
                      xml = <<-EOS
         | 
| 65 | 
            +
                        <entry xmlns="http://www.w3.org/2005/Atom">
         | 
| 66 | 
            +
                          <category scheme="http://schemas.google.com/g/2005#kind"
         | 
| 67 | 
            +
                            term="http://schemas.google.com/docs/2007#folder"/>
         | 
| 68 | 
            +
                          <title>#{h(title)}</title>
         | 
| 69 | 
            +
                        </entry>
         | 
| 70 | 
            +
                      EOS
         | 
| 71 | 
            +
                      doc = @session.request(
         | 
| 72 | 
            +
                          :post, contents_url, :data => xml, :header => header, :auth => :writely)
         | 
| 73 | 
            +
                      return @session.entry_element_to_file(doc)
         | 
| 43 74 | 
             
                    end
         | 
| 44 | 
            -
             | 
| 75 | 
            +
             | 
| 76 | 
            +
                    # Removes the given GoogleDrive::File from the collection.
         | 
| 77 | 
            +
                    def remove(file)
         | 
| 78 | 
            +
                      url = to_v3_url("#{contents_url}/#{file.resource_id}")
         | 
| 79 | 
            +
                      @session.request(:delete, url, :auth => :writely, :header => {"If-Match" => "*"})
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    # Returns true if this is a root collection
         | 
| 83 | 
            +
                    def root?
         | 
| 84 | 
            +
                      self.document_feed_url == ROOT_URL
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    # Returns all the files (including spreadsheets, documents, subcollections) in the collection.
         | 
| 88 | 
            +
                    #
         | 
| 89 | 
            +
                    # You can specify query parameters described at
         | 
| 90 | 
            +
                    # https://developers.google.com/google-apps/documents-list/#getting_a_list_of_documents_and_files
         | 
| 91 | 
            +
                    #
         | 
| 92 | 
            +
                    # e.g.
         | 
| 93 | 
            +
                    #
         | 
| 94 | 
            +
                    #   # Gets all the files in collection, including subcollections.
         | 
| 95 | 
            +
                    #   collection.files
         | 
| 96 | 
            +
                    #   
         | 
| 97 | 
            +
                    #   # Gets only files with title "hoge".
         | 
| 98 | 
            +
                    #   collection.files("title" => "hoge", "title-exact" => "true")
         | 
| 99 | 
            +
                    def files(params = {})
         | 
| 100 | 
            +
                      return files_with_type(nil, params)
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    alias contents files
         | 
| 104 | 
            +
             | 
| 45 105 | 
             
                    # Returns all the spreadsheets in the collection.
         | 
| 46 | 
            -
                    def spreadsheets
         | 
| 47 | 
            -
                      return  | 
| 106 | 
            +
                    def spreadsheets(params = {})
         | 
| 107 | 
            +
                      return files_with_type("spreadsheet", params)
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
                    
         | 
| 110 | 
            +
                    # Returns all the Google Docs documents in the collection.
         | 
| 111 | 
            +
                    def documents(params = {})
         | 
| 112 | 
            +
                      return files_with_type("document", params)
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
                    
         | 
| 115 | 
            +
                    # Returns all its subcollections.
         | 
| 116 | 
            +
                    def subcollections(params = {})
         | 
| 117 | 
            +
                      return files_with_type("folder", params)
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
                    
         | 
| 120 | 
            +
                    # Returns its subcollection whose title exactly matches +title+ as GoogleDrive::Collection.
         | 
| 121 | 
            +
                    # Returns nil if not found. If multiple collections with the +title+ are found, returns
         | 
| 122 | 
            +
                    # one of them.
         | 
| 123 | 
            +
                    def subcollection_by_title(title)
         | 
| 124 | 
            +
                      return subcollections("title" => title, "title-exact" => "true")[0]
         | 
| 48 125 | 
             
                    end
         | 
| 49 126 |  | 
| 50 | 
            -
             | 
| 127 | 
            +
                  private
         | 
| 51 128 |  | 
| 129 | 
            +
                    def files_with_type(type, params = {})
         | 
| 130 | 
            +
                      contents_url = self.contents_url
         | 
| 131 | 
            +
                      contents_url = concat_url(contents_url, "/-/#{type}") if type
         | 
| 132 | 
            +
                      contents_url = concat_url(contents_url, "?" + encode_query(params))
         | 
| 133 | 
            +
                      header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
         | 
| 134 | 
            +
                      doc = @session.request(:get, contents_url, :header => header, :auth => :writely)
         | 
| 135 | 
            +
                      return doc.css("feed > entry").map(){ |e| @session.entry_element_to_file(e) }
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                    
         | 
| 52 138 | 
             
                end
         | 
| 53 139 |  | 
| 54 140 | 
             
            end
         | 
    
        data/lib/google_drive/file.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            # Author: Hiroshi Ichikawa <http://gimite.net/>
         | 
| 2 2 | 
             
            # The license of this source is "New BSD Licence"
         | 
| 3 3 |  | 
| 4 | 
            +
            require "cgi"
         | 
| 4 5 | 
             
            require "stringio"
         | 
| 5 6 |  | 
| 6 7 | 
             
            require "google_drive/util"
         | 
| @@ -17,11 +18,24 @@ module GoogleDrive | |
| 17 18 |  | 
| 18 19 | 
             
                    include(Util)
         | 
| 19 20 |  | 
| 20 | 
            -
                    def initialize(session,  | 
| 21 | 
            +
                    def initialize(session, entry_or_url) #:nodoc:
         | 
| 21 22 | 
             
                      @session = session
         | 
| 22 | 
            -
                       | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 23 | 
            +
                      if !entry_or_url
         | 
| 24 | 
            +
                        # TODO Delete this after editing spreadsheet.rb.
         | 
| 25 | 
            +
                        @document_feed_entry = nil
         | 
| 26 | 
            +
                        @document_feed_url = entry_or_url
         | 
| 27 | 
            +
                      elsif entry_or_url.is_a?(String)
         | 
| 28 | 
            +
                        @document_feed_entry = nil
         | 
| 29 | 
            +
                        @document_feed_url = entry_or_url
         | 
| 30 | 
            +
                      else
         | 
| 31 | 
            +
                        @document_feed_entry = entry_or_url
         | 
| 32 | 
            +
                        # This is usually equal to the URL in <link rel="self">. But the URL in
         | 
| 33 | 
            +
                        # <link rel="self"> in collection feed is e.g.
         | 
| 34 | 
            +
                        # https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/folder%3Axxx
         | 
| 35 | 
            +
                        # and deletion of the URL doesn't delete the file itself.
         | 
| 36 | 
            +
                        # So we construct the URL here using resource ID instead.
         | 
| 37 | 
            +
                        @document_feed_url = "%s/%s" % [DOCS_BASE_URL, CGI.escape(self.resource_id)]
         | 
| 38 | 
            +
                      end
         | 
| 25 39 | 
             
                      @acl = nil
         | 
| 26 40 | 
             
                    end
         | 
| 27 41 |  | 
| @@ -29,10 +43,32 @@ module GoogleDrive | |
| 29 43 | 
             
                    attr_reader(:document_feed_url)
         | 
| 30 44 |  | 
| 31 45 | 
             
                    # <entry> element of document list feed as Nokogiri::XML::Element.
         | 
| 32 | 
            -
                     | 
| 33 | 
            -
                    
         | 
| 46 | 
            +
                    #
         | 
| 47 | 
            +
                    # Set <tt>params[:reload]</tt> to true to force reloading the feed.
         | 
| 48 | 
            +
                    def document_feed_entry(params = {})
         | 
| 49 | 
            +
                      if !@document_feed_entry || params[:reload]
         | 
| 50 | 
            +
                        @document_feed_entry =
         | 
| 51 | 
            +
                            @session.request(:get, self.document_feed_url, :auth => :writely).css("entry")[0]
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
                      return @document_feed_entry
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    # Resource ID.
         | 
| 57 | 
            +
                    def resource_id
         | 
| 58 | 
            +
                      return self.document_feed_entry.css("gd|resourceId").text
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    # The type of resourse. e.g. "document", "spreadsheet", "folder"
         | 
| 62 | 
            +
                    def resource_type
         | 
| 63 | 
            +
                      return self.resource_id.split(/:/)[0]
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 34 66 | 
             
                    # Title of the file.
         | 
| 35 | 
            -
                     | 
| 67 | 
            +
                    #
         | 
| 68 | 
            +
                    # Set <tt>params[:reload]</tt> to true to force reloading the title.
         | 
| 69 | 
            +
                    def title(params = {})
         | 
| 70 | 
            +
                      return document_feed_entry(params).css("title").text
         | 
| 71 | 
            +
                    end
         | 
| 36 72 |  | 
| 37 73 | 
             
                    # URL to view/edit the file in a Web browser.
         | 
| 38 74 | 
             
                    #
         | 
| @@ -50,7 +86,7 @@ module GoogleDrive | |
| 50 86 | 
             
                          return orig_acl_feed_url
         | 
| 51 87 | 
             
                        when %r{^https?://docs.google.com/feeds/acl/private/full/([^\?]*)(\?.*)?$}
         | 
| 52 88 | 
             
                          # URL of old API version. Converts to v3 URL.
         | 
| 53 | 
            -
                          return " | 
| 89 | 
            +
                          return "#{DOCS_BASE_URL}/#{$1}/acl"
         | 
| 54 90 | 
             
                        else
         | 
| 55 91 | 
             
                          raise(GoogleDrive::Error,
         | 
| 56 92 | 
             
                            "ACL feed URL is in unknown format: #{orig_acl_feed_url}")
         | 
| @@ -149,8 +185,9 @@ module GoogleDrive | |
| 149 185 | 
             
                    # If +permanent+ is +false+, moves the file to the trash.
         | 
| 150 186 | 
             
                    # If +permanent+ is +true+, deletes the file permanently.
         | 
| 151 187 | 
             
                    def delete(permanent = false)
         | 
| 152 | 
            -
                       | 
| 153 | 
            -
             | 
| 188 | 
            +
                      url = to_v3_url(self.document_feed_url)
         | 
| 189 | 
            +
                      url = concat_url(url, "?delete=true") if permanent
         | 
| 190 | 
            +
                      @session.request(:delete, url,
         | 
| 154 191 | 
             
                        :auth => :writely, :header => {"If-Match" => "*"})
         | 
| 155 192 | 
             
                    end
         | 
| 156 193 |  | 
| @@ -180,7 +217,7 @@ module GoogleDrive | |
| 180 217 | 
             
                    # With the object, you can see and modify people who can access the file.
         | 
| 181 218 | 
             
                    # Modifications take effect immediately.
         | 
| 182 219 | 
             
                    #
         | 
| 183 | 
            -
                    # Set <tt>params[:reload]</tt> to true to force reloading the  | 
| 220 | 
            +
                    # Set <tt>params[:reload]</tt> to true to force reloading the data.
         | 
| 184 221 | 
             
                    #
         | 
| 185 222 | 
             
                    # e.g.
         | 
| 186 223 | 
             
                    #   # Dumps people who have access:
         | 
| @@ -209,7 +246,9 @@ module GoogleDrive | |
| 209 246 | 
             
                    end
         | 
| 210 247 |  | 
| 211 248 | 
             
                    def inspect
         | 
| 212 | 
            -
                       | 
| 249 | 
            +
                      fields = {:document_feed_url => self.document_feed_url}
         | 
| 250 | 
            +
                      fields[:title] = self.title if @document_feed_entry
         | 
| 251 | 
            +
                      return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
         | 
| 213 252 | 
             
                    end
         | 
| 214 253 |  | 
| 215 254 | 
             
                end
         | 
    
        data/lib/google_drive/list.rb
    CHANGED
    
    | @@ -97,6 +97,10 @@ module GoogleDrive | |
| 97 97 | 
             
                      return @worksheet[index + 2, key_to_col(key)]
         | 
| 98 98 | 
             
                    end
         | 
| 99 99 |  | 
| 100 | 
            +
                    def numeric_value(index, key) #:nodoc:
         | 
| 101 | 
            +
                      return @worksheet.numeric_value(index + 2, key_to_col(key))
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                    
         | 
| 100 104 | 
             
                    def set(index, key, value) #:nodoc:
         | 
| 101 105 | 
             
                      @worksheet[index + 2, key_to_col(key)] = value
         | 
| 102 106 | 
             
                    end
         | 
| @@ -35,9 +35,9 @@ module GoogleDrive | |
| 35 35 |  | 
| 36 36 | 
             
                    def request_raw(method, url, data, extra_header, auth)
         | 
| 37 37 | 
             
                      if method == :delete || method == :get
         | 
| 38 | 
            -
                        raw_res = @oauth2_token.request(method, url, {: | 
| 38 | 
            +
                        raw_res = @oauth2_token.request(method, url, {:headers => extra_header})
         | 
| 39 39 | 
             
                      else
         | 
| 40 | 
            -
                        raw_res = @oauth2_token.request(method, url, {: | 
| 40 | 
            +
                        raw_res = @oauth2_token.request(method, url, {:headers => extra_header, :body => data})
         | 
| 41 41 | 
             
                      end
         | 
| 42 42 | 
             
                      return Response.new(raw_res)
         | 
| 43 43 | 
             
                    end
         | 
    
        data/lib/google_drive/session.rb
    CHANGED
    
    | @@ -112,12 +112,14 @@ module GoogleDrive | |
| 112 112 | 
             
                    # You can specify query parameters described at
         | 
| 113 113 | 
             
                    # https://developers.google.com/google-apps/documents-list/#getting_a_list_of_documents_and_files
         | 
| 114 114 | 
             
                    #
         | 
| 115 | 
            +
                    # files doesn't return collections unless "showfolders" => true is specified.
         | 
| 116 | 
            +
                    #
         | 
| 115 117 | 
             
                    # e.g.
         | 
| 116 118 | 
             
                    #   session.files
         | 
| 117 119 | 
             
                    #   session.files("title" => "hoge", "title-exact" => "true")
         | 
| 118 120 | 
             
                    def files(params = {})
         | 
| 119 121 | 
             
                      url = concat_url(
         | 
| 120 | 
            -
                          " | 
| 122 | 
            +
                          "#{DOCS_BASE_URL}?v=3", "?" + encode_query(params))
         | 
| 121 123 | 
             
                      doc = request(:get, url, :auth => :writely)
         | 
| 122 124 | 
             
                      return doc.css("feed > entry").map(){ |e| entry_element_to_file(e) }
         | 
| 123 125 | 
             
                    end
         | 
| @@ -200,7 +202,25 @@ module GoogleDrive | |
| 200 202 | 
             
                    def worksheet_by_url(url)
         | 
| 201 203 | 
             
                      return Worksheet.new(self, nil, url)
         | 
| 202 204 | 
             
                    end
         | 
| 203 | 
            -
             | 
| 205 | 
            +
                    
         | 
| 206 | 
            +
                    # Returns the root collection.
         | 
| 207 | 
            +
                    def root_collection
         | 
| 208 | 
            +
                      return Collection.new(self, Collection::ROOT_URL)
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                    
         | 
| 211 | 
            +
                    # Returns the top-level collections (direct children of the root collection).
         | 
| 212 | 
            +
                    def collections
         | 
| 213 | 
            +
                      return self.root_collection.subcollections
         | 
| 214 | 
            +
                    end
         | 
| 215 | 
            +
                    
         | 
| 216 | 
            +
                    # Returns a top-level collection whose title exactly matches +title+ as
         | 
| 217 | 
            +
                    # GoogleDrive::Collection.
         | 
| 218 | 
            +
                    # Returns nil if not found. If multiple collections with the +title+ are found, returns
         | 
| 219 | 
            +
                    # one of them.
         | 
| 220 | 
            +
                    def collection_by_title(title)
         | 
| 221 | 
            +
                      return self.root_collection.subcollection_by_title(title)
         | 
| 222 | 
            +
                    end
         | 
| 223 | 
            +
                    
         | 
| 204 224 | 
             
                    # Returns GoogleDrive::Collection with given +url+.
         | 
| 205 225 | 
             
                    # You must specify either of:
         | 
| 206 226 | 
             
                    # - URL of the page you get when you go to https://docs.google.com/ with your browser and
         | 
| @@ -219,7 +239,7 @@ module GoogleDrive | |
| 219 239 | 
             
                      if ["docs.google.com", "drive.google.com"].include?(uri.host) &&
         | 
| 220 240 | 
             
                          uri.fragment =~ /^folders\/(.+)$/
         | 
| 221 241 | 
             
                        # Looks like a URL of human-readable collection page. Converts to collection feed URL.
         | 
| 222 | 
            -
                        url = " | 
| 242 | 
            +
                        url = "#{DOCS_BASE_URL}/folder%3A#{$1}"
         | 
| 223 243 | 
             
                      end
         | 
| 224 244 | 
             
                      return Collection.new(self, url)
         | 
| 225 245 | 
             
                    end
         | 
| @@ -289,7 +309,7 @@ module GoogleDrive | |
| 289 309 | 
             
                    # Uploads a file. Reads content from +io+.
         | 
| 290 310 | 
             
                    # Returns a GoogleSpreadsheet::File object.
         | 
| 291 311 | 
             
                    def upload_from_io(io, title = "Untitled", params = {})
         | 
| 292 | 
            -
                      doc = request(:get, " | 
| 312 | 
            +
                      doc = request(:get, "#{DOCS_BASE_URL}?v=3",
         | 
| 293 313 | 
             
                          :auth => :writely)
         | 
| 294 314 | 
             
                      initial_url = doc.css(
         | 
| 295 315 | 
             
                          "link[rel='http://schemas.google.com/g/2005#resumable-create-media']")[0]["href"]
         | 
| @@ -352,13 +372,17 @@ module GoogleDrive | |
| 352 372 | 
             
                    end
         | 
| 353 373 |  | 
| 354 374 | 
             
                    def entry_element_to_file(entry) #:nodoc:
         | 
| 375 | 
            +
                      type, resource_id = entry.css("gd|resourceId").text.split(/:/)
         | 
| 355 376 | 
             
                      title = entry.css("title").text
         | 
| 356 | 
            -
                       | 
| 357 | 
            -
                        " | 
| 358 | 
            -
             | 
| 359 | 
            -
                         | 
| 360 | 
            -
             | 
| 361 | 
            -
             | 
| 377 | 
            +
                      case type
         | 
| 378 | 
            +
                        when "folder"
         | 
| 379 | 
            +
                          return Collection.new(self, entry)
         | 
| 380 | 
            +
                        when "spreadsheet"
         | 
| 381 | 
            +
                          worksheets_feed_link = entry.css(
         | 
| 382 | 
            +
                            "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]
         | 
| 383 | 
            +
                          return Spreadsheet.new(self, worksheets_feed_link["href"], title)
         | 
| 384 | 
            +
                        else
         | 
| 385 | 
            +
                          return GoogleDrive::File.new(self, entry)
         | 
| 362 386 | 
             
                      end
         | 
| 363 387 | 
             
                    end
         | 
| 364 388 |  | 
| @@ -103,7 +103,6 @@ module GoogleDrive | |
| 103 103 | 
             
                    # Creates copy of this spreadsheet with the given title.
         | 
| 104 104 | 
             
                    def duplicate(new_title = nil)
         | 
| 105 105 | 
             
                      new_title ||= (self.title ? "Copy of " + self.title : "Untitled")
         | 
| 106 | 
            -
                      post_url = "https://docs.google.com/feeds/default/private/full/"
         | 
| 107 106 | 
             
                      header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
         | 
| 108 107 | 
             
                      xml = <<-"EOS"
         | 
| 109 108 | 
             
                        <entry xmlns='http://www.w3.org/2005/Atom'>
         | 
| @@ -112,7 +111,7 @@ module GoogleDrive | |
| 112 111 | 
             
                        </entry>
         | 
| 113 112 | 
             
                      EOS
         | 
| 114 113 | 
             
                      doc = @session.request(
         | 
| 115 | 
            -
                          :post,  | 
| 114 | 
            +
                          :post, DOCS_BASE_URL, :data => xml, :header => header, :auth => :writely)
         | 
| 116 115 | 
             
                      ss_url = doc.css(
         | 
| 117 116 | 
             
                          "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
         | 
| 118 117 | 
             
                      return Spreadsheet.new(@session, ss_url, new_title)
         | 
    
        data/lib/google_drive/util.rb
    CHANGED
    
    | @@ -7,6 +7,9 @@ require "cgi" | |
| 7 7 | 
             
            module GoogleDrive
         | 
| 8 8 |  | 
| 9 9 | 
             
                module Util #:nodoc:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    # The beginning of Doc List API URL that is used in all requests (version 3).
         | 
| 12 | 
            +
                    DOCS_BASE_URL = "https://docs.google.com/feeds/default/private/full"
         | 
| 10 13 |  | 
| 11 14 | 
             
                    EXT_TO_CONTENT_TYPE = {
         | 
| 12 15 | 
             
                        ".csv" =>"text/csv",
         | 
| @@ -45,7 +48,16 @@ module GoogleDrive | |
| 45 48 | 
             
                          (piece_base || "") +
         | 
| 46 49 | 
             
                          (result_query.empty? ? "" : "?#{result_query}")
         | 
| 47 50 | 
             
                    end
         | 
| 48 | 
            -
             | 
| 51 | 
            +
             | 
| 52 | 
            +
                    # Returns a URL with added version parameter ("?v=3") if needed.
         | 
| 53 | 
            +
                    def to_v3_url(url)
         | 
| 54 | 
            +
                      if url =~ %r{docs.google.com/feeds/default/private/} && !(url =~ /[?&]v=3/)
         | 
| 55 | 
            +
                        return concat_url(url, "?v=3")
         | 
| 56 | 
            +
                      else
         | 
| 57 | 
            +
                        return url
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 49 61 | 
             
                    def h(str)
         | 
| 50 62 | 
             
                      return CGI.escapeHTML(str.to_s())
         | 
| 51 63 | 
             
                    end
         | 
| @@ -26,6 +26,7 @@ module GoogleDrive | |
| 26 26 |  | 
| 27 27 | 
             
                      @cells = nil
         | 
| 28 28 | 
             
                      @input_values = nil
         | 
| 29 | 
            +
                      @numeric_values = nil
         | 
| 29 30 | 
             
                      @modified = Set.new()
         | 
| 30 31 | 
             
                      @list = nil
         | 
| 31 32 |  | 
| @@ -86,6 +87,7 @@ module GoogleDrive | |
| 86 87 | 
             
                      reload() if !@cells
         | 
| 87 88 | 
             
                      @cells[[row, col]] = value
         | 
| 88 89 | 
             
                      @input_values[[row, col]] = value
         | 
| 90 | 
            +
                      @numeric_values[[row, col]] = nil
         | 
| 89 91 | 
             
                      @modified.add([row, col])
         | 
| 90 92 | 
             
                      self.max_rows = row if row > @max_rows
         | 
| 91 93 | 
             
                      self.max_cols = col if col > @max_cols
         | 
| @@ -116,6 +118,25 @@ module GoogleDrive | |
| 116 118 | 
             
                      return @input_values[[row, col]] || ""
         | 
| 117 119 | 
             
                    end
         | 
| 118 120 |  | 
| 121 | 
            +
                    # Returns the numeric value of the cell. Arguments must be either
         | 
| 122 | 
            +
                    # (row number, column number) or cell name. Top-left cell is [1, 1].
         | 
| 123 | 
            +
                    #
         | 
| 124 | 
            +
                    # e.g.
         | 
| 125 | 
            +
                    #   worksheet[1, 3]                #=> "3,0" # it depends on locale, currency...
         | 
| 126 | 
            +
                    #   worksheet.numeric_value(1, 3)  #=> 3.0
         | 
| 127 | 
            +
                    #
         | 
| 128 | 
            +
                    # Returns nil if the cell is empty or contains non-number.
         | 
| 129 | 
            +
                    #
         | 
| 130 | 
            +
                    # If you modify the cell, its numeric_value is nil until you call save() and reload().
         | 
| 131 | 
            +
                    #
         | 
| 132 | 
            +
                    # For details, see:
         | 
| 133 | 
            +
                    # https://developers.google.com/google-apps/spreadsheets/#working_with_cell-based_feeds
         | 
| 134 | 
            +
                    def numeric_value(*args)
         | 
| 135 | 
            +
                      (row, col) = parse_cell_args(args)
         | 
| 136 | 
            +
                      reload() if !@cells
         | 
| 137 | 
            +
                      return @numeric_values[[row, col]]
         | 
| 138 | 
            +
                    end
         | 
| 139 | 
            +
                    
         | 
| 119 140 | 
             
                    # Row number of the bottom-most non-empty row.
         | 
| 120 141 | 
             
                    def num_rows
         | 
| 121 142 | 
             
                      reload() if !@cells
         | 
| @@ -176,8 +197,9 @@ module GoogleDrive | |
| 176 197 | 
             
                    end
         | 
| 177 198 |  | 
| 178 199 | 
             
                    # An array of spreadsheet rows. Each row contains an array of
         | 
| 179 | 
            -
                    # columns. Note that resulting array is 0-origin so
         | 
| 180 | 
            -
                    # | 
| 200 | 
            +
                    # columns. Note that resulting array is 0-origin so:
         | 
| 201 | 
            +
                    #
         | 
| 202 | 
            +
                    #   worksheet.rows[0][0] == worksheet[1, 1]
         | 
| 181 203 | 
             
                    def rows(skip = 0)
         | 
| 182 204 | 
             
                      nc = self.num_cols
         | 
| 183 205 | 
             
                      result = ((1 + skip)..self.num_rows).map() do |row|
         | 
| @@ -197,12 +219,15 @@ module GoogleDrive | |
| 197 219 |  | 
| 198 220 | 
             
                      @cells = {}
         | 
| 199 221 | 
             
                      @input_values = {}
         | 
| 222 | 
            +
                      @numeric_values = {}
         | 
| 200 223 | 
             
                      doc.css("feed > entry").each() do |entry|
         | 
| 201 224 | 
             
                        cell = entry.css("gs|cell")[0]
         | 
| 202 225 | 
             
                        row = cell["row"].to_i()
         | 
| 203 226 | 
             
                        col = cell["col"].to_i()
         | 
| 204 227 | 
             
                        @cells[[row, col]] = cell.inner_text
         | 
| 205 228 | 
             
                        @input_values[[row, col]] = cell["inputValue"]
         | 
| 229 | 
            +
                        numeric_value = cell["numericValue"]
         | 
| 230 | 
            +
                        @numeric_values[[row, col]] = numeric_value ? numeric_value.to_f() : nil
         | 
| 206 231 | 
             
                      end
         | 
| 207 232 | 
             
                      @modified.clear()
         | 
| 208 233 | 
             
                      @meta_modified = false
         | 
| @@ -433,10 +458,15 @@ module GoogleDrive | |
| 433 458 | 
             
                      if args.size == 1 && args[0].is_a?(String)
         | 
| 434 459 | 
             
                        return cell_name_to_row_col(args[0])
         | 
| 435 460 | 
             
                      elsif args.size == 2 && args[0].is_a?(Integer) && args[1].is_a?(Integer)
         | 
| 436 | 
            -
                         | 
| 461 | 
            +
                        if args[0] >= 1 && args[1] >= 1
         | 
| 462 | 
            +
                          return args
         | 
| 463 | 
            +
                        else
         | 
| 464 | 
            +
                          raise(ArgumentError,
         | 
| 465 | 
            +
                              "Row/col must be >= 1 (1-origin), but are %d/%d" % [args[0], args[1]])
         | 
| 466 | 
            +
                        end
         | 
| 437 467 | 
             
                      else
         | 
| 438 468 | 
             
                        raise(ArgumentError,
         | 
| 439 | 
            -
                            "Arguments must be either one String or two Integer's, but are %p" % args)
         | 
| 469 | 
            +
                            "Arguments must be either one String or two Integer's, but are %p" % [args])
         | 
| 440 470 | 
             
                      end
         | 
| 441 471 | 
             
                    end
         | 
| 442 472 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: google_drive
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3. | 
| 4 | 
            +
              version: 0.3.1
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-07-01 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: nokogiri
         | 
| 16 | 
            -
              requirement:  | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| @@ -27,10 +27,21 @@ dependencies: | |
| 27 27 | 
             
                    version: 1.5.2
         | 
| 28 28 | 
             
              type: :runtime
         | 
| 29 29 | 
             
              prerelease: false
         | 
| 30 | 
            -
              version_requirements:  | 
| 30 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 31 | 
            +
                none: false
         | 
| 32 | 
            +
                requirements:
         | 
| 33 | 
            +
                - - ! '>='
         | 
| 34 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 35 | 
            +
                    version: 1.4.4
         | 
| 36 | 
            +
                - - ! '!='
         | 
| 37 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 38 | 
            +
                    version: 1.5.1
         | 
| 39 | 
            +
                - - ! '!='
         | 
| 40 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 41 | 
            +
                    version: 1.5.2
         | 
| 31 42 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 32 43 | 
             
              name: oauth
         | 
| 33 | 
            -
              requirement:  | 
| 44 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 34 45 | 
             
                none: false
         | 
| 35 46 | 
             
                requirements:
         | 
| 36 47 | 
             
                - - ! '>='
         | 
| @@ -38,10 +49,15 @@ dependencies: | |
| 38 49 | 
             
                    version: 0.3.6
         | 
| 39 50 | 
             
              type: :runtime
         | 
| 40 51 | 
             
              prerelease: false
         | 
| 41 | 
            -
              version_requirements:  | 
| 52 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 53 | 
            +
                none: false
         | 
| 54 | 
            +
                requirements:
         | 
| 55 | 
            +
                - - ! '>='
         | 
| 56 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 57 | 
            +
                    version: 0.3.6
         | 
| 42 58 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 43 59 | 
             
              name: oauth2
         | 
| 44 | 
            -
              requirement:  | 
| 60 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 45 61 | 
             
                none: false
         | 
| 46 62 | 
             
                requirements:
         | 
| 47 63 | 
             
                - - ! '>='
         | 
| @@ -49,10 +65,15 @@ dependencies: | |
| 49 65 | 
             
                    version: 0.5.0
         | 
| 50 66 | 
             
              type: :runtime
         | 
| 51 67 | 
             
              prerelease: false
         | 
| 52 | 
            -
              version_requirements:  | 
| 68 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 69 | 
            +
                none: false
         | 
| 70 | 
            +
                requirements:
         | 
| 71 | 
            +
                - - ! '>='
         | 
| 72 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 73 | 
            +
                    version: 0.5.0
         | 
| 53 74 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 54 75 | 
             
              name: rake
         | 
| 55 | 
            -
              requirement:  | 
| 76 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 56 77 | 
             
                none: false
         | 
| 57 78 | 
             
                requirements:
         | 
| 58 79 | 
             
                - - ! '>='
         | 
| @@ -60,7 +81,12 @@ dependencies: | |
| 60 81 | 
             
                    version: 0.8.0
         | 
| 61 82 | 
             
              type: :development
         | 
| 62 83 | 
             
              prerelease: false
         | 
| 63 | 
            -
              version_requirements:  | 
| 84 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 85 | 
            +
                none: false
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - ! '>='
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: 0.8.0
         | 
| 64 90 | 
             
            description: A library to read/write files/spreadsheets in Google Drive/Docs.
         | 
| 65 91 | 
             
            email:
         | 
| 66 92 | 
             
            - gimite+github@gmail.com
         | 
| @@ -114,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 114 140 | 
             
                  version: '0'
         | 
| 115 141 | 
             
            requirements: []
         | 
| 116 142 | 
             
            rubyforge_project: 
         | 
| 117 | 
            -
            rubygems_version: 1.8. | 
| 143 | 
            +
            rubygems_version: 1.8.23
         | 
| 118 144 | 
             
            signing_key: 
         | 
| 119 145 | 
             
            specification_version: 3
         | 
| 120 146 | 
             
            summary: A library to read/write files/spreadsheets in Google Drive/Docs.
         |