oozou-fusion_tables 0.2.3.dev.20110408163600

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.
@@ -0,0 +1,4 @@
1
+ twitter_username: your_twitter_username
2
+ twitter_password: your_twitter_password
3
+ google_username: your_google_username
4
+ google_password: your_google_password
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{fusion_tables}
8
+ s.version = "0.2.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Simon Tokumine", "Tom Verbeure"]
12
+ s.date = %q{2010-09-02}
13
+ s.description = %q{A simple Google Fusion Tables API wrapper. Supports bulk inserts and most API functions}
14
+ s.email = %q{simon@tinypla.net}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.textile",
25
+ "Rakefile",
26
+ "TODO",
27
+ "VERSION",
28
+ "examples/compare_tweets.rb",
29
+ "examples/credentials.example.yml",
30
+ "fusion_tables.gemspec",
31
+ "lib/fusion_tables.rb",
32
+ "lib/fusion_tables/client/fusion_tables.rb",
33
+ "lib/fusion_tables/data/data.rb",
34
+ "lib/fusion_tables/data/table.rb",
35
+ "lib/fusion_tables/ext/fusion_tables.rb",
36
+ "pkg/fusion_tables-0.1.0.gem",
37
+ "pkg/fusion_tables-0.1.1.gem",
38
+ "pkg/fusion_tables-0.1.2.gem",
39
+ "pkg/fusion_tables-0.2.0.gem",
40
+ "pkg/fusion_tables-0.2.1.gem",
41
+ "pkg/fusion_tables-0.2.2.gem",
42
+ "test/README",
43
+ "test/helper.rb",
44
+ "test/test_client.rb",
45
+ "test/test_config.yml.sample",
46
+ "test/test_ext.rb",
47
+ "test/test_table.rb"
48
+ ]
49
+ s.homepage = %q{http://github.com/tokumine/fusion-tables}
50
+ s.rdoc_options = ["--charset=UTF-8"]
51
+ s.require_paths = ["lib"]
52
+ s.rubygems_version = %q{1.3.7}
53
+ s.summary = %q{Google Fusion Tables API wrapper}
54
+ s.test_files = [
55
+ "test/helper.rb",
56
+ "test/test_client.rb",
57
+ "test/test_ext.rb",
58
+ "test/test_table.rb",
59
+ "examples/compare_tweets.rb"
60
+ ]
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
68
+ s.add_runtime_dependency(%q<gdata_19>, [">= 1.1.2"])
69
+ else
70
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
71
+ s.add_dependency(%q<gdata_19>, [">= 1.1.2"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
75
+ s.add_dependency(%q<gdata_19>, [">= 1.1.2"])
76
+ end
77
+ end
78
+
@@ -0,0 +1,6 @@
1
+ require 'csv'
2
+ require 'gdata'
3
+ require 'fusion_tables/client/fusion_tables'
4
+ require 'fusion_tables/ext/fusion_tables'
5
+ require 'fusion_tables/data/data'
6
+ require 'fusion_tables/data/table'
@@ -0,0 +1,77 @@
1
+ # Copyright (C) 2010 Tom Verbeure, Simon Tokumine
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module GData
16
+ module Client
17
+ class FusionTables < Base
18
+
19
+ SERVICE_URL = "http://tables.googlelabs.com/api/query"
20
+ DATATYPES = %w(number string location datetime)
21
+
22
+ def initialize(options = {})
23
+ options[:clientlogin_service] ||= 'fusiontables'
24
+ options[:headers] = { 'Content-Type' => 'application/x-www-form-urlencoded' }
25
+ super(options)
26
+ end
27
+
28
+ def sql_encode(sql)
29
+ "sql=" + CGI::escape(sql)
30
+ end
31
+
32
+ def sql_get(sql)
33
+ resp = self.get(SERVICE_URL + "?" + sql_encode(sql))
34
+ end
35
+
36
+ def sql_post(sql)
37
+ resp = self.post(SERVICE_URL, sql_encode(sql))
38
+ end
39
+
40
+ def sql_put(sql)
41
+ resp = self.put(SERVICE_URL, sql_encode(sql))
42
+ end
43
+
44
+
45
+ # Overrides auth_handler= so if the authentication changes,
46
+ # the session cookie is cleared.
47
+ def auth_handler=(handler)
48
+ @session_cookie = nil
49
+ return super(handler)
50
+ end
51
+
52
+ # Overrides make_request to handle 500 redirects with a session cookie.
53
+ def make_request(method, url, body = '', retries = 10)
54
+ begin
55
+ response = super(method, url, body)
56
+ rescue GData::Client::ServerError => e
57
+ if e.response.status_code == 500 and retries > 0
58
+ sleep_time = 11 - retries
59
+ sleep sleep_time # <= Fusion tables has rate of 5 calls per second. Be nice, get longer
60
+ @session_cookie = e.response.headers['set-cookie']
61
+ return self.make_request(method, url, body, retries - 1)
62
+ else
63
+ return e.response
64
+ end
65
+ end
66
+ end
67
+
68
+ # Custom prepare_headers to include the session cookie if it exists
69
+ def prepare_headers
70
+ if @session_cookie
71
+ @headers['cookie'] = @session_cookie
72
+ end
73
+ super
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,72 @@
1
+ # Copyright (C) 2010 Simon Tokumine
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module GData
16
+ module Client
17
+ class FusionTables < Base
18
+ class Data
19
+ include Enumerable
20
+
21
+ attr_reader :headers, :body, :live
22
+ alias :to_h :body
23
+
24
+ # configures headers hash and sets up
25
+ def initialize options
26
+ @headers = options[:headers]
27
+ @body = options[:body]
28
+ end
29
+
30
+ # Reads in CSV
31
+ def self.parse response
32
+ body = []
33
+ headers = []
34
+
35
+ first = true
36
+ if CSV.const_defined? :Reader
37
+ CSV::Reader.parse(response.body) do |row|
38
+ if first
39
+ first = false
40
+ headers = row.map { |x|x.strip.downcase.gsub(" ","_").to_sym }
41
+ next
42
+ end
43
+ body << Hash[*headers.zip(row).flatten]
44
+ end
45
+ else
46
+ CSV.parse(response.body) do |row|
47
+ if first
48
+ first = false
49
+ headers = row.map { |x|x.strip.downcase.gsub(" ","_").to_sym }
50
+ next
51
+ end
52
+ body << Hash[*headers.zip(row).flatten]
53
+ end
54
+ end
55
+ self.new :headers => headers, :body => body
56
+ end
57
+
58
+ # Implement enumerable
59
+ def each
60
+ @body.each { |i| yield i }
61
+ end
62
+
63
+ private
64
+ # Encodes row according to type
65
+ def encode
66
+
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,144 @@
1
+ # Copyright (C) 2010 Simon Tokumine
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module GData
16
+ module Client
17
+ class FusionTables < Base
18
+ class Table
19
+ attr_reader :headers, :id, :name
20
+
21
+ # configures headers hash and sets up
22
+ #
23
+ # eg options: {:table_id => "x", :name => "y"}
24
+ #
25
+ def initialize client, options
26
+ raise ArgumentError, "need ft client" if client.class != GData::Client::FusionTables
27
+ raise ArgumentError, "need table_id and name hash" if !options.has_key?(:name) || !options.has_key?(:table_id)
28
+ @client = client
29
+ @id = options[:table_id]
30
+ @name = options[:name]
31
+ end
32
+
33
+
34
+ # Sets up data types from google
35
+ #
36
+ def describe
37
+ GData::Client::FusionTables::Data.parse(@client.sql_get("DESCRIBE #{@id}")).body
38
+ end
39
+
40
+ # Runs select and returns data obj
41
+ #
42
+ # Define columns and SQL conditions separatly
43
+ #
44
+ # See http://code.google.com/apis/fusiontables/docs/developers_reference.html#Select
45
+ #
46
+ # use columns=ROWID to select row ids
47
+ #
48
+ def select columns="*", conditions=nil
49
+ sql = "SELECT #{columns} FROM #{@id} #{conditions}"
50
+ GData::Client::FusionTables::Data.parse(@client.sql_get(sql)).body
51
+ end
52
+
53
+ # Returns a count of rows. SQL conditions optional
54
+ #
55
+ def count conditions=nil
56
+ select("count()", conditions).first.values.first.to_i
57
+ end
58
+
59
+
60
+ # Outputs data to an array of concatenated INSERT SQL statements
61
+ #
62
+ # format should be:
63
+ #
64
+ # [{:col_1 => data, :col2 => data}, {:col_1 => data, :col2 => data}]
65
+ #
66
+ # Fields are escaped and formatted for FT based on type
67
+ #
68
+ def insert data
69
+ data = [data] unless data.respond_to?(:to_ary)
70
+
71
+ # encode values to insert
72
+ data = encode data
73
+
74
+ # Chunk up the data and send
75
+ chunk = ""
76
+ data.each_with_index do |d,i|
77
+ chunk << "INSERT INTO #{@id} (#{ d.keys.join(",") }) VALUES (#{ d.values.join(",") });"
78
+ if (i+1) % 500 == 0 || (i+1) == data.size
79
+ begin
80
+ @client.sql_post(chunk)
81
+ chunk = ""
82
+ rescue => e
83
+ raise "INSERT to table:#{@id} failed on row #{i} with #{e}"
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ # Runs update on rows and return data obj
90
+ # No bulk update, so may aswell drop table and start again
91
+ def update row_id, data
92
+ data = encode([data]).first
93
+ data = data.to_a.map{|x| x.join("=")}.join(", ")
94
+
95
+ sql = "UPDATE #{@id} SET #{data} WHERE ROWID = #{row_id}"
96
+ GData::Client::FusionTables::Data.parse(@client.sql_post(sql)).body
97
+ end
98
+
99
+ # delete row
100
+ # no bulk delete so may aswell drop table and start again
101
+ def delete row_id
102
+ sql = "DELETE FROM #{@id} WHERE rowid='#{row_id}'"
103
+ GData::Client::FusionTables::Data.parse(@client.sql_post(sql)).body
104
+ end
105
+
106
+ # Delete all the data from one table
107
+ def truncate!
108
+ GData::Client::FusionTables::Data.parse(@client.sql_post("DELETE FROM #{@id}")).body
109
+ end
110
+
111
+ def get_headers
112
+ @headers ||= describe
113
+ end
114
+
115
+ def encode data
116
+ data.inject([]) do |ar,h|
117
+ ret = {}
118
+ h.each do |key, value|
119
+ ret["'#{key.to_s}'"] = case get_datatype(key)
120
+ when "number" then "#{value}"
121
+ when "datetime" then "'#{value.strftime("%m-%d-%Y %H:%M:%S")}'"
122
+ else "'#{value.gsub(/\\/, '\&\&').gsub(/'/, "''")}'"
123
+ end
124
+ end
125
+ ar << ret
126
+ ar
127
+ end
128
+ end
129
+
130
+ #
131
+ # Returns datatype of given column name
132
+ #
133
+ def get_datatype column_name
134
+ get_headers
135
+
136
+ @headers.each do |h|
137
+ return h[:type] if h[:name] == column_name.to_s
138
+ end
139
+ raise ArgumentError "The column doesn't exist"
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,96 @@
1
+ # Copyright (C) 2010 Tom Verbeure, Simon Tokumine
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module GData
16
+ module Client
17
+ class FusionTables < Base
18
+
19
+ # Show a list of fusion tables
20
+ def show_tables
21
+ data = GData::Client::FusionTables::Data.parse(self.sql_get("SHOW TABLES"))
22
+ data.inject([]) do |x, row|
23
+ x << GData::Client::FusionTables::Table.new(self, row)
24
+ x
25
+ end
26
+ end
27
+
28
+
29
+ # Create a new table. Return the corresponding table
30
+ #
31
+ # Columns specified as [{:name => 'my_col_name', :type => 'my_type'}]
32
+ #
33
+ # Type must be one of:
34
+ #
35
+ # * number
36
+ # * string
37
+ # * location
38
+ # * datetime
39
+ #
40
+ def create_table(table_name, columns)
41
+
42
+ # Sanity check name
43
+ table_name = table_name.strip.gsub(/ /,'_')
44
+
45
+ # ensure all column types are valid
46
+ columns.each do |col|
47
+ if !DATATYPES.include? col[:type].downcase
48
+ raise ArgumentError, "Ensure input types are: 'number', 'string', 'location' or 'datetime'"
49
+ end
50
+ end
51
+
52
+ # generate sql
53
+ fields = columns.map{ |col| "'#{col[:name]}': #{col[:type].upcase}" }.join(", ")
54
+ sql = "CREATE TABLE #{table_name} (#{fields})"
55
+
56
+ # create table
57
+ resp = self.sql_post(sql)
58
+ raise "unknown column type" if resp.body == "Unknown column type."
59
+
60
+ # construct table object and return
61
+ table_id = resp.body.split("\n")[1].chomp.to_i
62
+ table = GData::Client::FusionTables::Table.new(self, :table_id => table_id, :name => table_name)
63
+ table.get_headers
64
+ table
65
+ end
66
+
67
+ # Drops Fusion Tables
68
+ #
69
+ # options can be:
70
+ #
71
+ # * an integer for single drop
72
+ # * array of integers for multi drop
73
+ # * a regex against table_name for flexible multi_drop
74
+ #
75
+ def drop(options)
76
+ # collect ids
77
+ ids = []
78
+ ids << options if options.class == Integer || options.class == String || Fixnum
79
+ ids = options if options.class == Array
80
+
81
+ if options.class == Regexp
82
+ tables = show_tables
83
+ ids = tables.map { |table| table.id if options =~ table.name }.compact
84
+ end
85
+
86
+ # drop tables
87
+ delete_count = 0
88
+ ids.each do |id|
89
+ resp = self.sql_post("DROP TABLE #{id}")
90
+ delete_count += 1 if resp.body.strip.downcase == 'ok'
91
+ end
92
+ delete_count
93
+ end
94
+ end
95
+ end
96
+ end