ft 0.0.1 → 0.0.2
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/Rakefile +15 -0
- data/ft.gemspec +2 -1
- data/lib/ft.rb +72 -4
- data/test/authenticated.rb +48 -0
- data/test/ft.rb +10 -0
- data/test/private.example.rb +8 -0
- data/test/private.rb +3 -0
- metadata +6 -2
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
desc "Run all tests"
|
4
|
+
task :test do
|
5
|
+
require "cutest"
|
6
|
+
|
7
|
+
files = Dir["./test/*.rb"]
|
8
|
+
|
9
|
+
if !files.include?("./test/private.rb")
|
10
|
+
$stderr.puts("Couldn't find a test/private.rb file, so skipping tests that require authentication.\nIf you want to run them, take a look at test/private.example.rb")
|
11
|
+
files.delete("./test/authenticated.rb")
|
12
|
+
end
|
13
|
+
|
14
|
+
Cutest.run(files)
|
15
|
+
end
|
data/ft.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "ft"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.2"
|
4
4
|
s.summary = "Low-level interface to Google's Fusion Tables + CLI tool"
|
5
5
|
s.authors = ["Damian Janowski"]
|
6
6
|
s.email = ["djanowski@dimaion.com"]
|
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.files = Dir[
|
16
16
|
"*.gemspec",
|
17
|
+
"CHANGELOG.*",
|
17
18
|
"LICENSE",
|
18
19
|
"README*",
|
19
20
|
"Rakefile",
|
data/lib/ft.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "csv"
|
2
2
|
require "net/http/persistent"
|
3
|
+
require "net/https"
|
3
4
|
|
4
5
|
class FusionTables
|
5
6
|
Error = Class.new(RuntimeError)
|
@@ -13,15 +14,16 @@ class FusionTables
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
17
|
+
# Queries the Fusion Tables API with the given SQL and returns an
|
18
|
+
# array of arrays for rows and columns.
|
16
19
|
def query(sql)
|
17
|
-
|
18
|
-
|
19
|
-
url.query = "sql=#{URI.escape(sql)}"
|
20
|
-
res = http.request(url)
|
20
|
+
res = process_sql(sql)
|
21
21
|
|
22
22
|
case res
|
23
23
|
when Net::HTTPOK
|
24
24
|
CSV.parse(res.body.force_encoding(Encoding::UTF_8))
|
25
|
+
when Net::HTTPFound
|
26
|
+
raise Error.new("Authentication required. See #{self.class}#authenticate")
|
25
27
|
when Net::HTTPBadRequest
|
26
28
|
message = CGI.unescapeHTML(res.body[%r[<title>(.*)</title>]i, 1])
|
27
29
|
raise Error.new("#{message}. SQL was: #{sql}")
|
@@ -29,5 +31,71 @@ class FusionTables
|
|
29
31
|
raise "Got #{res.class}: #{res.body}"
|
30
32
|
end
|
31
33
|
end
|
34
|
+
|
35
|
+
# Authenticates against Google using your email and password.
|
36
|
+
#
|
37
|
+
# Note that this method uses the ClientLogin mechanism and it
|
38
|
+
# only stores the resulting token as an instance variable. Your
|
39
|
+
# credentials are discarded after the authentication process.
|
40
|
+
def authenticate(email, password)
|
41
|
+
uri = URI.parse("https://www.google.com/accounts/ClientLogin")
|
42
|
+
|
43
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
44
|
+
http.use_ssl = true
|
45
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
46
|
+
|
47
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
48
|
+
|
49
|
+
request.set_form_data({
|
50
|
+
"accountType" => "GOOGLE",
|
51
|
+
"Email" => email,
|
52
|
+
"Passwd" => password,
|
53
|
+
"service" => "fusiontables"
|
54
|
+
})
|
55
|
+
|
56
|
+
response = http.request(request)
|
57
|
+
|
58
|
+
case response
|
59
|
+
when Net::HTTPOK
|
60
|
+
@token = response.body[/^Auth=(.*)$/, 1]
|
61
|
+
return true
|
62
|
+
else
|
63
|
+
@token = nil
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Prevents any authorization tokens from being exposed in error logs
|
69
|
+
# and the like.
|
70
|
+
def inspect
|
71
|
+
"#<#{self.class}>"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Safely quotes a value.
|
75
|
+
def self.quote(value)
|
76
|
+
"'#{value.to_s.gsub("'", "\\\\'")}'"
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
# Takes SQL and queries the Fusion Tables API.
|
82
|
+
def process_sql(sql)
|
83
|
+
url = URL.dup
|
84
|
+
|
85
|
+
if @token
|
86
|
+
http.headers["Authorization"] = "GoogleLogin auth=#{@token}"
|
87
|
+
end
|
88
|
+
|
89
|
+
if sql =~ /^select/i
|
90
|
+
url.query = "sql=#{URI.escape(sql)}"
|
91
|
+
res = http.request(url)
|
92
|
+
else
|
93
|
+
req = Net::HTTP::Post.new(url.path)
|
94
|
+
req.set_form_data(sql: sql)
|
95
|
+
res = http.request(url, req)
|
96
|
+
end
|
97
|
+
|
98
|
+
res
|
99
|
+
end
|
32
100
|
end
|
33
101
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require "./lib/ft"
|
3
|
+
require "./test/private"
|
4
|
+
|
5
|
+
setup do
|
6
|
+
[FusionTables::Connection.new, ENV["EMAIL"], ENV["PASSWORD"], ENV["PRIVATE_TABLE_ID"]]
|
7
|
+
end
|
8
|
+
|
9
|
+
test "authenticated SELECTs" do |conn, email, password, table_id|
|
10
|
+
conn.authenticate(email, password)
|
11
|
+
|
12
|
+
result = conn.query("SELECT Client, Invoice FROM #{table_id}")
|
13
|
+
|
14
|
+
assert_equal result, [
|
15
|
+
["Client", "Invoice"],
|
16
|
+
["Madalyn Streich", "1"],
|
17
|
+
["Mr. Vincenza Bailey", "2"]
|
18
|
+
]
|
19
|
+
|
20
|
+
assert conn.inspect !~ /token/
|
21
|
+
end
|
22
|
+
|
23
|
+
test "INSERT" do |conn, email, password, table_id|
|
24
|
+
conn.authenticate(email, password)
|
25
|
+
|
26
|
+
begin
|
27
|
+
result = conn.query("INSERT INTO #{table_id} (Client) VALUES ('Foo')")
|
28
|
+
|
29
|
+
assert_equal result[0][0], "rowid"
|
30
|
+
assert result[1][0] =~ /^\d+$/
|
31
|
+
ensure
|
32
|
+
conn.query("DELETE FROM #{table_id} WHERE ROWID = '#{result[1][0]}'") if result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
test "DELETE" do |conn, email, password, table_id|
|
37
|
+
conn.authenticate(email, password)
|
38
|
+
|
39
|
+
result = conn.query("INSERT INTO #{table_id} (Client) VALUES ('Foo')")
|
40
|
+
|
41
|
+
rowid = result[1][0]
|
42
|
+
|
43
|
+
conn.query("DELETE FROM #{table_id} WHERE ROWID = '#{rowid}'")
|
44
|
+
|
45
|
+
result = conn.query("SELECT ROWID FROM #{table_id} WHERE ROWID = '#{rowid}'")
|
46
|
+
|
47
|
+
assert_equal result[1], nil
|
48
|
+
end
|
data/test/ft.rb
CHANGED
@@ -16,3 +16,13 @@ test "raises errors" do |conn|
|
|
16
16
|
conn.query("SELECT foo FROM 1310767")
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
test "raises on authentication errors" do |conn|
|
21
|
+
assert_raise FusionTables::Error do
|
22
|
+
conn.query("INSERT INTO 1310767 (Name) VALUES ('Foo')")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
test "quoting" do |conn|
|
27
|
+
assert_equal FusionTables::Connection.quote("C'mon"), %q{'C\'mon'}
|
28
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# This example works with OS X's keychain, but you can set the variables
|
2
|
+
# as you please.
|
3
|
+
#
|
4
|
+
|
5
|
+
# ENV["EMAIL"] = "<your google account email>" # If not already in your shell.
|
6
|
+
ENV["PASSWORD"] = `security 2>&1 find-internet-password -g -s www.google.com -a #{ENV["EMAIL"]}`[/^password: "(.*)"/, 1]
|
7
|
+
|
8
|
+
ENV["PRIVATE_TABLE_ID"] = "<your private table id>"
|
data/test/private.rb
ADDED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Damian Janowski
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-08-
|
13
|
+
date: 2011-08-26 00:00:00 -03:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -57,10 +57,14 @@ extra_rdoc_files: []
|
|
57
57
|
|
58
58
|
files:
|
59
59
|
- ft.gemspec
|
60
|
+
- Rakefile
|
60
61
|
- bin/ft
|
61
62
|
- lib/ft.rb
|
63
|
+
- test/authenticated.rb
|
62
64
|
- test/cli.rb
|
63
65
|
- test/ft.rb
|
66
|
+
- test/private.example.rb
|
67
|
+
- test/private.rb
|
64
68
|
has_rdoc: true
|
65
69
|
homepage: http://github.com/djanowski/ft
|
66
70
|
licenses: []
|