fusion_tables 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::TEST
22
+ test/test_config.yml
23
+
24
+ ## PROJECT::SPECIFIC
25
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Simon Tokumine
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.textile ADDED
@@ -0,0 +1,122 @@
1
+ h1. fusion-tables
2
+
3
+ This API lets you easily interact with Google Fusion Tables from your Ruby application.
4
+
5
+ h2. Installation
6
+
7
+ gem install fusion-tables
8
+
9
+ h2. API examples
10
+
11
+ The best place to see what's going on is in the tests, but this is a good starting point.
12
+
13
+ <pre><code>
14
+ # Connect to service
15
+ @ft = GData::Client::FusionTables.new
16
+ @ft.clientlogin(username, password)
17
+
18
+ # Create a new table
19
+ # Configure the columns you want
20
+ cols = [{:name => "friend name", :datatype => 'string' },
21
+ {:name => "age", :datatype => 'number' },
22
+ {:name => "meeting time", :datatype => 'datetime' },
23
+ {:name => "where", :datatype => 'location' }]
24
+
25
+ # Create the table
26
+ @table = @ft.create_table "My upcoming meetings", cols
27
+ @table.id #=> 42342 (the table's google id)
28
+
29
+ # Insert data
30
+ data = [{"friend name" => "Eric Wimp",
31
+ "age" => 25,
32
+ "meeting time" => Time.utc(2010,"aug",10,20,15,1),
33
+ "where" => "29 Acacia Road, Nuttytown"]
34
+
35
+ @table.insert data
36
+
37
+ # Note - the insert method chunks the data array into 500 row chunks
38
+
39
+ # Count data
40
+ @table.count #=> 1
41
+
42
+ # Select data
43
+ @table.select #=> data
44
+ row_ids = @table.select "ROWID"
45
+
46
+ # Update data (not working)
47
+ # @table.update row_id, {:age => 26}
48
+
49
+ # Delete data
50
+ @table.delete row_id
51
+
52
+ # Drop tables
53
+ @ft.drop @table.id # table id
54
+ @ft.drop [@table1.id, @table2.id] # arrays of table ids
55
+ @ft.drop /yacht/ # regex
56
+ </code></pre>
57
+
58
+ h2. Fusion Tables secret Geospatial Sauce
59
+
60
+ Fusion Tables is a labs product from Google. You can "read more here":http://tables.googlelabs.com/, but the key thing is that it gives you *access to the google tile mill for ultra-fast generating of custom google map layers across massive datasets*
61
+
62
+ Fusion Tables supports the following geometry types:
63
+
64
+ * lat/long
65
+ * addresses (automatically geocodes them for you)
66
+ * KML (point, polyline, polygon, multipolygon)
67
+
68
+ h2. Thousands of geometries rendered in near real time, integrated with google maps v3
69
+
70
+ Adding a fusion tables datalayer with many points/polygons to your v3 map is as simple as:
71
+
72
+ <pre><code>
73
+ layer = new google.maps.FusionTablesLayer(139529);
74
+ </code></pre>
75
+
76
+ That's it
77
+
78
+ You can also refine the tiles by SQL, and can even do so dynamically:
79
+
80
+ <pre><code>
81
+ layer = new google.maps.FusionTablesLayer(198945, {
82
+ query: "SELECT address FROM 198945 WHERE ridership > 5000"}
83
+ );
84
+ </code></pre>
85
+
86
+ Finally, fusion tables also lets you make Heatmaps
87
+
88
+ <pre><code>
89
+ layer = new google.maps.FusionTablesLayer(136705, {
90
+ heatmap: true
91
+ });
92
+ </code></pre>
93
+
94
+ You can also export your data (filtered and geocoded) to KML. As an example, here are "all the Gasoline filling stations in the UK":http://tables.googlelabs.com/exporttable?query=select+col0%2Ccol1%2Ccol2%2Ccol3%2Ccol4%2Ccol5%2Ccol6%2Ccol12%2Ccol13%2Ccol14%2Ccol15%2Ccol16%2Ccol17%2Ccol18%2Ccol19%2Ccol20%2Ccol21+from+214045+&o=kmllink&g=col0
95
+
96
+ read "more here":http://code.google.com/apis/maps/documentation/javascript/overlays.html#FusionTables
97
+
98
+ h2. A word of warning...
99
+
100
+ # Google has a recent habit of deleting projects. This is still a labs project, and may disappear at any time
101
+ # The API is still very young and *will change*
102
+ # Currently you have to make a table public before you can display it on a map, unfortunately, this can only be done on the web interface. A suggested workaround is to put all your data in 1 big public table, and then query for the data you want to display based off a key/flag column
103
+
104
+ h2. Note on Patches/Pull Requests
105
+
106
+ * Fork the project.
107
+ * Make your feature addition or bug fix.
108
+ * Add tests for it. This is important so I don't break it in a
109
+ future version unintentionally.
110
+ * Commit, do not mess with rakefile, version, or history.
111
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
112
+ * Send me a pull request. Bonus points for topic branches.
113
+
114
+ h2. Copyright
115
+
116
+ Largely based on Tom Verbeure's work for MTBGuru: http://code.google.com/p/mtbguru-fusiontables/
117
+
118
+ Copyright (c) 2010 Simon Tokumine. See LICENSE for details.
119
+
120
+
121
+
122
+
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "fusion_tables"
8
+ gem.summary = %Q{Google Fusion Tables API wrapper}
9
+ gem.description = %Q{A simple Google Fusion Tables API wrapper. Supports bulk inserts and most API functions}
10
+ gem.email = "simon@tinypla.net"
11
+ gem.homepage = "http://github.com/tokumine/fusion-tables"
12
+ gem.authors = ["Simon Tokumine", "Tom Verbeure"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_dependency "gdata", ">= 1.1.1"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "fusion_tables #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ Areas of improvement:
2
+
3
+ * response format json/xml
4
+ * "create table" should return name
5
+ * describe table should include name
6
+ * multiple batch statements for all actions (only insert now)
7
+ * docs need updating, esp on response codes
8
+ * response codes need to be more standardised
9
+ * irritating can't include ROWID with * in selects
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,69 @@
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.1.0"
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-08-10}
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
+ "fusion_tables.gemspec",
29
+ "lib/fusion_tables.rb",
30
+ "lib/fusion_tables/client/fusion_tables.rb",
31
+ "lib/fusion_tables/data/data.rb",
32
+ "lib/fusion_tables/data/table.rb",
33
+ "lib/fusion_tables/ext/fusion_tables.rb",
34
+ "test/README",
35
+ "test/helper.rb",
36
+ "test/test_client.rb",
37
+ "test/test_config.yml.sample",
38
+ "test/test_ext.rb",
39
+ "test/test_table.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/tokumine/fusion-tables}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.7}
45
+ s.summary = %q{Google Fusion Tables API wrapper}
46
+ s.test_files = [
47
+ "test/helper.rb",
48
+ "test/test_client.rb",
49
+ "test/test_ext.rb",
50
+ "test/test_table.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
59
+ s.add_runtime_dependency(%q<gdata>, [">= 1.1.1"])
60
+ else
61
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
62
+ s.add_dependency(%q<gdata>, [">= 1.1.1"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
66
+ s.add_dependency(%q<gdata>, [">= 1.1.1"])
67
+ end
68
+ end
69
+
@@ -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,61 @@
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
+ CSV::Reader.parse(response.body) do |row|
37
+ if first
38
+ first = false
39
+ headers = row.map { |x|x.strip.downcase.gsub(" ","_").to_sym }
40
+ next
41
+ end
42
+ body << Hash[*headers.zip(row).flatten]
43
+ end
44
+ self.new :headers => headers, :body => body
45
+ end
46
+
47
+ # Implement enumerable
48
+ def each
49
+ @body.each { |i| yield i }
50
+ end
51
+
52
+ private
53
+ # Encodes row according to type
54
+ def encode
55
+
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,142 @@
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
+
70
+ # encode values to insert
71
+ data = encode data
72
+
73
+ # Chunk up the data and send
74
+ chunk = ""
75
+ data.each_with_index do |d,i|
76
+ chunk << "INSERT INTO #{@id} (#{ d.keys.join(",") }) VALUES (#{ d.values.join(",") });"
77
+ if (i+1) % 500 == 0 || (i+1) == data.size
78
+ begin
79
+ @client.sql_post(chunk)
80
+ chunk = ""
81
+ rescue => e
82
+ raise "INSERT to table:#{@id} failed on row #{i} with #{e}"
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # Runs update on rows and return data obj
89
+ # No bulk update, so may aswell drop table and start again
90
+ #
91
+ # TODO: FIXME
92
+ #
93
+ #def update row_id, data
94
+ # data = encode([data]).first
95
+ # data = data.to_a.map{|x| x.join("=")}.join(", ")
96
+ #
97
+ # sql = "UPDATE #{@id} SET #{data} WHERE ROWID = #{row_id}"
98
+ # GData::Client::FusionTables::Data.parse(@client.sql_post(sql)).body
99
+ #end
100
+
101
+ # delete row
102
+ # no bulk delete so may aswell drop table and start again
103
+ def delete row_id
104
+ sql = "DELETE FROM #{@id} WHERE rowid='#{row_id}'"
105
+ GData::Client::FusionTables::Data.parse(@client.sql_post(sql)).body
106
+ end
107
+
108
+
109
+ def get_headers
110
+ @headers ||= describe
111
+ end
112
+
113
+ def encode data
114
+ data.inject([]) do |ar,h|
115
+ ret = {}
116
+ h.each do |key, value|
117
+ ret[key] = case get_datatype(key)
118
+ when "number" then "#{value}"
119
+ when "datetime" then "'#{value.strftime("%m-%d-%Y")}'"
120
+ else "'#{value.gsub(/\\/, '\&\&').gsub(/'/, "''")}'"
121
+ end
122
+ end
123
+ ar << ret
124
+ ar
125
+ end
126
+ end
127
+
128
+ #
129
+ # Returns datatype of given column name
130
+ #
131
+ def get_datatype column_name
132
+ get_headers
133
+
134
+ @headers.each do |h|
135
+ return h[:type] if h[:name] == column_name.to_s
136
+ end
137
+ raise ArgumentError "The column doesn't exist"
138
+ end
139
+ end
140
+ end
141
+ end
142
+ 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
data/test/README ADDED
@@ -0,0 +1,3 @@
1
+ To run the tests, make a test_config.yml file using test/test_config.yml.sample as a base
2
+
3
+ If you get GData::Client::CaptchaError's, that usually means your credentials are not correct
data/test/helper.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'fusion_tables'
8
+
9
+ class Test::Unit::TestCase
10
+
11
+ def init_config
12
+ if not defined? @config_file
13
+ begin
14
+ @config_file = YAML::load_file(File.join(File.dirname(__FILE__), 'test_config.yml'))
15
+ rescue
16
+ puts "Please configure your test_config.yml file using test_config.yml.sample as base"
17
+ end
18
+ end
19
+ @config_file
20
+ end
21
+
22
+ def username
23
+ @config_file['username']
24
+ end
25
+
26
+ def password
27
+ @config_file['password']
28
+ end
29
+
30
+ def table_name
31
+ @config_file['table_name']
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestClient < Test::Unit::TestCase
4
+
5
+ context "The fusion_tables client library" do
6
+ setup do
7
+ init_config
8
+ @ft = GData::Client::FusionTables.new
9
+ @ft.clientlogin(username, password)
10
+ end
11
+
12
+ should "be properly setup" do
13
+ assert_equal @ft.clientlogin_service, "fusiontables"
14
+ assert_equal @ft.headers["Content-Type"], "application/x-www-form-urlencoded"
15
+ end
16
+
17
+ should "be able to authenticate with the google services" do
18
+ assert_equal @ft.auth_handler.service, "fusiontables"
19
+ assert @ft.auth_handler.token
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ username: test
2
+ password: test
3
+ table_name: "test_table"
data/test/test_ext.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'helper'
2
+
3
+ class TestExt < Test::Unit::TestCase
4
+
5
+ context "The Fusion Tables helper functions" do
6
+ setup do
7
+ init_config
8
+ @ft = GData::Client::FusionTables.new
9
+ @ft.clientlogin(username, password)
10
+ end
11
+
12
+
13
+ should "raise ArgumentError if supply unknown types to it" do
14
+ assert_raise ArgumentError do
15
+ @ft.create_table "test table", [{:name => "test_col", :type => "billys birthday" }]
16
+ end
17
+ end
18
+
19
+ should "let you create a table if you get everything right" do
20
+ table = @ft.create_table "test_table", [{:name => "test_col", :type => "string" }]
21
+ assert_equal table.class, GData::Client::FusionTables::Table
22
+ @ft.drop(table.id)
23
+ end
24
+
25
+ should "correct your table name to a certain degree on create" do
26
+ table = @ft.create_table "test table", [{:name => "test col", :type => "string" }]
27
+ assert_equal table.name, "test_table"
28
+ @ft.drop(table.id)
29
+ end
30
+
31
+ should "return you a list of your fusion tables" do
32
+ resp = @ft.show_tables
33
+ assert_equal resp.first.class, GData::Client::FusionTables::Table if resp.first
34
+ end
35
+
36
+ should "be possible to delete a table with an id" do
37
+ table = @ft.create_table "test_table", [{:name => "test col", :type => "string" }]
38
+ assert_equal @ft.drop(table.id), 1
39
+ end
40
+
41
+ should "be possible to delete tables with an array of ids" do
42
+ table1 = @ft.create_table "test_table", [{:name => "test col", :type => "string" }]
43
+ table2 = @ft.create_table "test_table", [{:name => "test col", :type => "string" }]
44
+ assert_equal @ft.drop([table1.id, table2.id]), 2
45
+ end
46
+
47
+ should "be possible to delete multiple tables with a regex" do
48
+ table1 = @ft.create_table "test_table", [{:name => "test col", :type => "string" }]
49
+ table2 = @ft.create_table "test_table", [{:name => "test col", :type => "string" }]
50
+ assert_equal @ft.drop(/^test_/), 2
51
+ end
52
+
53
+ should "return zero if passed a silly id" do
54
+ assert_equal @ft.drop(235243875629384756), 0
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ class TestTable < Test::Unit::TestCase
4
+
5
+ context "uploading data to FT" do
6
+ setup do
7
+ init_config
8
+ @ft = GData::Client::FusionTables.new
9
+ @ft.clientlogin(username, password)
10
+ @table = @ft.create_table "test", [{:name => 'firstname', :type => 'string'},
11
+ {:name => 'phone', :type => 'number'},
12
+ {:name => 'dob', :type => 'datetime'},
13
+ {:name => 'house', :type => 'location'}]
14
+ end
15
+
16
+ should "format data and prep for upload" do
17
+ data = @table.encode [{:firstname => "\\bob's piz\za",
18
+ :phone => 12,
19
+ :dob => Time.utc(2010,"aug",10,20,15,1),
20
+ :house => "POINT(1,1)"}]
21
+
22
+ row = data.first
23
+ assert_equal row[:firstname], "'\\\\bob''s pizza'"
24
+ assert_equal row[:phone], "#{12}"
25
+ assert_equal row[:dob], "'08-10-2010'"
26
+ assert_equal row[:house], "'POINT(1,1)'"
27
+ end
28
+
29
+ should "be able to insert 1 row of data" do
30
+ data = 1.times.inject([]) { |a,i|
31
+ a << {:firstname => "\\bob's piz\za-#{i}",
32
+ :phone => 12,
33
+ :dob => Time.utc(2010,"aug",10,20,15,1),
34
+ :house => '<Point><coordinates>-74.006393,40.714172,0</coordinates></Point>'}
35
+ }
36
+
37
+ @table.insert data
38
+ end
39
+
40
+ should "be able to insert 501 rows of data" do
41
+ data = 501.times.inject([]) { |a,i|
42
+ a << {:firstname => "Person-#{i}",
43
+ :phone => 12,
44
+ :dob => Time.utc(2010,"aug",10,20,15,1),
45
+ :house => "<Point><coordinates>#{180-rand(360)},#{90-rand(180)},0</coordinates></Point>"}
46
+ }
47
+
48
+ @table.insert data
49
+ end
50
+
51
+
52
+ should "be able to count the number of rows" do
53
+ data = 2.times.inject([]) { |a,i|
54
+ a << {:firstname => "Person-#{i}",
55
+ :phone => 12,
56
+ :dob => Time.utc(2010,"aug",10,20,15,1),
57
+ :house => "<Point><coordinates>#{180-rand(360)},#{90-rand(180)},0</coordinates></Point>"}
58
+ }
59
+
60
+ @table.insert data
61
+ assert_equal @table.count, 2
62
+ end
63
+
64
+ should "be able to select the rows" do
65
+ data = 2.times.inject([]) { |a,i|
66
+ a << {:firstname => "Person-#{i}",
67
+ :phone => 12,
68
+ :dob => Time.utc(2010,"aug",10,20,15,1),
69
+ :house => "<Point><coordinates>1,1,0</coordinates></Point>"}
70
+ }
71
+
72
+ @table.insert data
73
+ assert_equal @table.select, [{:firstname=>"Person-0", :phone=>"12", :dob=>"08-10-2010", :house=>"<Point><coordinates>1,1,0</coordinates></Point>"}, {:firstname=>"Person-1", :phone=>"12", :dob=>"08-10-2010", :house=>"<Point><coordinates>1,1,0</coordinates></Point>"}]
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fusion_tables
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Simon Tokumine
14
+ - Tom Verbeure
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-10 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: thoughtbot-shoulda
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: gdata
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 17
45
+ segments:
46
+ - 1
47
+ - 1
48
+ - 1
49
+ version: 1.1.1
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: A simple Google Fusion Tables API wrapper. Supports bulk inserts and most API functions
53
+ email: simon@tinypla.net
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files:
59
+ - LICENSE
60
+ - README.textile
61
+ - TODO
62
+ files:
63
+ - .document
64
+ - .gitignore
65
+ - LICENSE
66
+ - README.textile
67
+ - Rakefile
68
+ - TODO
69
+ - VERSION
70
+ - fusion_tables.gemspec
71
+ - lib/fusion_tables.rb
72
+ - lib/fusion_tables/client/fusion_tables.rb
73
+ - lib/fusion_tables/data/data.rb
74
+ - lib/fusion_tables/data/table.rb
75
+ - lib/fusion_tables/ext/fusion_tables.rb
76
+ - test/README
77
+ - test/helper.rb
78
+ - test/test_client.rb
79
+ - test/test_config.yml.sample
80
+ - test/test_ext.rb
81
+ - test/test_table.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/tokumine/fusion-tables
84
+ licenses: []
85
+
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --charset=UTF-8
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ requirements: []
110
+
111
+ rubyforge_project:
112
+ rubygems_version: 1.3.7
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Google Fusion Tables API wrapper
116
+ test_files:
117
+ - test/helper.rb
118
+ - test/test_client.rb
119
+ - test/test_ext.rb
120
+ - test/test_table.rb