fusion_tables 0.1.0
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/.document +5 -0
- data/.gitignore +25 -0
- data/LICENSE +20 -0
- data/README.textile +122 -0
- data/Rakefile +54 -0
- data/TODO +9 -0
- data/VERSION +1 -0
- data/fusion_tables.gemspec +69 -0
- data/lib/fusion_tables.rb +6 -0
- data/lib/fusion_tables/client/fusion_tables.rb +77 -0
- data/lib/fusion_tables/data/data.rb +61 -0
- data/lib/fusion_tables/data/table.rb +142 -0
- data/lib/fusion_tables/ext/fusion_tables.rb +96 -0
- data/test/README +3 -0
- data/test/helper.rb +33 -0
- data/test/test_client.rb +22 -0
- data/test/test_config.yml.sample +3 -0
- data/test/test_ext.rb +58 -0
- data/test/test_table.rb +76 -0
- metadata +120 -0
data/.document
ADDED
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,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
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
|
data/test/test_client.rb
ADDED
|
@@ -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
|
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
|
data/test/test_table.rb
ADDED
|
@@ -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
|