cartodb-rb-client 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +2 -0
- data/Gemfile +12 -0
- data/LICENSE +28 -0
- data/README.markdown +324 -0
- data/Rakefile +10 -0
- data/cartodb-rb-client.gemspec +34 -0
- data/lib/cartodb-rb-client/cartodb/client/authorization.rb +58 -0
- data/lib/cartodb-rb-client/cartodb/client/cache.rb +14 -0
- data/lib/cartodb-rb-client/cartodb/client/connection/base.rb +44 -0
- data/lib/cartodb-rb-client/cartodb/client/connection/cartodb.rb +231 -0
- data/lib/cartodb-rb-client/cartodb/client/connection/postgres.rb +255 -0
- data/lib/cartodb-rb-client/cartodb/client/connection.rb +4 -0
- data/lib/cartodb-rb-client/cartodb/client/error.rb +68 -0
- data/lib/cartodb-rb-client/cartodb/client/utils.rb +20 -0
- data/lib/cartodb-rb-client/cartodb/client.rb +4 -0
- data/lib/cartodb-rb-client/cartodb/helpers/sql_helper.rb +30 -0
- data/lib/cartodb-rb-client/cartodb/helpers.rb +1 -0
- data/lib/cartodb-rb-client/cartodb/init.rb +30 -0
- data/lib/cartodb-rb-client/cartodb/libs/object.rb +15 -0
- data/lib/cartodb-rb-client/cartodb/libs/string.rb +116 -0
- data/lib/cartodb-rb-client/cartodb/libs.rb +2 -0
- data/lib/cartodb-rb-client/cartodb/model/base.rb +20 -0
- data/lib/cartodb-rb-client/cartodb/model/constants.rb +30 -0
- data/lib/cartodb-rb-client/cartodb/model/defaults.rb +15 -0
- data/lib/cartodb-rb-client/cartodb/model/geo.rb +73 -0
- data/lib/cartodb-rb-client/cartodb/model/getters.rb +79 -0
- data/lib/cartodb-rb-client/cartodb/model/persistence.rb +69 -0
- data/lib/cartodb-rb-client/cartodb/model/query.rb +66 -0
- data/lib/cartodb-rb-client/cartodb/model/schema.rb +111 -0
- data/lib/cartodb-rb-client/cartodb/model/scope.rb +163 -0
- data/lib/cartodb-rb-client/cartodb/model/setters.rb +42 -0
- data/lib/cartodb-rb-client/cartodb/model.rb +11 -0
- data/lib/cartodb-rb-client/cartodb/types/metadata.rb +89 -0
- data/lib/cartodb-rb-client/cartodb/types/pg_result.rb +17 -0
- data/lib/cartodb-rb-client/cartodb/types.rb +2 -0
- data/lib/cartodb-rb-client/cartodb.rb +6 -0
- data/lib/cartodb-rb-client/install_utils.rb +19 -0
- data/lib/cartodb-rb-client/version.rb +7 -0
- data/lib/cartodb-rb-client.rb +17 -0
- data/run_tests.sh +6 -0
- data/spec/client_spec.rb +278 -0
- data/spec/model/data_spec.rb +130 -0
- data/spec/model/metadata_spec.rb +116 -0
- data/spec/model/scopes_spec.rb +171 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/cartodb_config.yml +15 -0
- data/spec/support/cartodb_factories.rb +33 -0
- data/spec/support/cartodb_helpers.rb +14 -0
- data/spec/support/cartodb_models.rb +22 -0
- data/spec/support/database.yml +5 -0
- data/spec/support/shp/cereal.dbf +0 -0
- data/spec/support/shp/cereal.shp +0 -0
- data/spec/support/shp/cereal.shx +0 -0
- data/spec/support/shp/cereal.zip +0 -0
- data/spec/support/shp/parcelas.dbf +0 -0
- data/spec/support/shp/parcelas.shp +0 -0
- data/spec/support/shp/parcelas.shx +0 -0
- data/spec/support/shp/parcelas.zip +0 -0
- data/spec/support/shp/zonas.dbf +0 -0
- data/spec/support/shp/zonas.shp +0 -0
- data/spec/support/shp/zonas.shx +0 -0
- data/spec/support/shp/zonas.zip +0 -0
- data/spec/support/whs_features.csv +33425 -0
- metadata +311 -0
@@ -0,0 +1,231 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Client
|
3
|
+
module Connection
|
4
|
+
class CartoDBConnection
|
5
|
+
include OAuth::RequestProxy::Typhoeus
|
6
|
+
include CartoDB::Client::Authorization
|
7
|
+
include CartoDB::Client::Utils
|
8
|
+
include CartoDB::Helpers::SqlHelper
|
9
|
+
|
10
|
+
VERSION = 'v1'.freeze
|
11
|
+
|
12
|
+
def initialize(connection_settings)
|
13
|
+
@hydra = Typhoeus::Hydra.new(:max_concurrency => 200)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_table(table_name = nil, schema_or_file = nil, the_geom_type = 'Point')
|
17
|
+
schema = schema_or_file if schema_or_file && schema_or_file.is_a?(Array)
|
18
|
+
file = schema_or_file if schema_or_file && schema_or_file.is_a?(File)
|
19
|
+
|
20
|
+
params = {:name => table_name}
|
21
|
+
params[:file] = file if file
|
22
|
+
params[:schema] = schema.map{|s| "#{s[:name]} #{s[:type]}"}.join(', ') if schema
|
23
|
+
|
24
|
+
request = cartodb_request 'tables', :post, :params => params, :the_geom_type => the_geom_type do |response|
|
25
|
+
return Utils.parse_json(response)
|
26
|
+
end
|
27
|
+
|
28
|
+
execute_queue
|
29
|
+
|
30
|
+
request.handled_response
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_column(table_name, column_name, column_type)
|
34
|
+
cartodb_request "tables/#{table_name}/columns",
|
35
|
+
:post,
|
36
|
+
:params => {
|
37
|
+
:name => column_name,
|
38
|
+
:type => column_type
|
39
|
+
}
|
40
|
+
|
41
|
+
execute_queue
|
42
|
+
end
|
43
|
+
|
44
|
+
def drop_column(table_name, column_name)
|
45
|
+
cartodb_request "tables/#{table_name}/columns/#{column_name}",
|
46
|
+
:delete
|
47
|
+
|
48
|
+
execute_queue
|
49
|
+
end
|
50
|
+
|
51
|
+
def change_column(table_name, old_column_name, new_column_name, column_type)
|
52
|
+
cartodb_request "tables/#{table_name}/columns/#{old_column_name}",
|
53
|
+
:put,
|
54
|
+
:params => {
|
55
|
+
:new_name => new_column_name,
|
56
|
+
:type => column_type
|
57
|
+
}
|
58
|
+
|
59
|
+
execute_queue
|
60
|
+
end
|
61
|
+
|
62
|
+
def tables
|
63
|
+
request = cartodb_request 'tables' do |response|
|
64
|
+
return Utils.parse_json(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
execute_queue
|
68
|
+
|
69
|
+
request.handled_response
|
70
|
+
end
|
71
|
+
|
72
|
+
def table(table_name)
|
73
|
+
request = cartodb_request "tables/#{table_name}" do |response|
|
74
|
+
return Utils.parse_json(response)
|
75
|
+
end
|
76
|
+
|
77
|
+
execute_queue
|
78
|
+
|
79
|
+
request.handled_response
|
80
|
+
end
|
81
|
+
|
82
|
+
def drop_table(table_name)
|
83
|
+
cartodb_request "tables/#{table_name}", :delete
|
84
|
+
|
85
|
+
execute_queue
|
86
|
+
end
|
87
|
+
|
88
|
+
def row(table_name, row_id)
|
89
|
+
cartodb_request "tables/#{table_name}/records/#{row_id}" do |response|
|
90
|
+
return Utils.parse_json(response)
|
91
|
+
end
|
92
|
+
|
93
|
+
execute_queue
|
94
|
+
|
95
|
+
request.handled_response
|
96
|
+
end
|
97
|
+
|
98
|
+
def insert_row(table_name, row)
|
99
|
+
row = prepare_data(row)
|
100
|
+
|
101
|
+
results = query(<<-SQL
|
102
|
+
INSERT INTO #{table_name}
|
103
|
+
(#{row.keys.join(',')})
|
104
|
+
VALUES (#{row.values.join(',')});
|
105
|
+
|
106
|
+
SELECT #{table_name}.cartodb_id as id, #{table_name}.*
|
107
|
+
FROM #{table_name}
|
108
|
+
WHERE cartodb_id = currval('public.#{table_name}_cartodb_id_seq');
|
109
|
+
SQL
|
110
|
+
)
|
111
|
+
|
112
|
+
results.rows.first
|
113
|
+
end
|
114
|
+
|
115
|
+
def update_row(table_name, row_id, row)
|
116
|
+
row = prepare_data(row)
|
117
|
+
|
118
|
+
results = query(<<-SQL
|
119
|
+
UPDATE #{table_name}
|
120
|
+
SET (#{row.keys.join(',')})
|
121
|
+
= (#{row.values.join(',')})
|
122
|
+
WHERE cartodb_id = #{row_id};
|
123
|
+
SELECT #{table_name}.cartodb_id as id, #{table_name}.*
|
124
|
+
FROM #{table_name}
|
125
|
+
WHERE cartodb_id = currval('public.#{table_name}_cartodb_id_seq');
|
126
|
+
SQL
|
127
|
+
)
|
128
|
+
|
129
|
+
results.rows.first
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def delete_row(table_name, row_id)
|
134
|
+
query(<<-SQL
|
135
|
+
DELETE FROM #{table_name}
|
136
|
+
WHERE cartodb_id = #{row_id}
|
137
|
+
SQL
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def records(table_name, options = {})
|
142
|
+
request = cartodb_request "tables/#{table_name}/records", :params => options.slice(:rows_per_page, :page) do |response|
|
143
|
+
return Utils.parse_json(response)
|
144
|
+
end
|
145
|
+
|
146
|
+
execute_queue
|
147
|
+
|
148
|
+
request.handled_response
|
149
|
+
end
|
150
|
+
|
151
|
+
def query(sql, options = {})
|
152
|
+
params = {:q => sql}
|
153
|
+
|
154
|
+
uri = 'sql'
|
155
|
+
uri_params = []
|
156
|
+
|
157
|
+
if options && options.any?
|
158
|
+
uri_params << "page=#{options[:page]}" if options[:page]
|
159
|
+
uri_params << "rows_per_page=#{options[:rows_per_page]}" if options[:rows_per_page]
|
160
|
+
uri << "?#{uri_params.join('&')}" if uri_params.any?
|
161
|
+
end
|
162
|
+
|
163
|
+
request = cartodb_request uri, :post, :params => params do |response|
|
164
|
+
return Utils.parse_json(response)
|
165
|
+
end
|
166
|
+
|
167
|
+
execute_queue
|
168
|
+
|
169
|
+
request.handled_response
|
170
|
+
end
|
171
|
+
|
172
|
+
def cartodb_request(uri, method = :get, arguments = {:params => {}}, &block)
|
173
|
+
params = arguments[:params]
|
174
|
+
if method.is_a? Hash
|
175
|
+
params = method[:params]
|
176
|
+
method = :get
|
177
|
+
end
|
178
|
+
|
179
|
+
uri = "/api/#{VERSION}/#{uri}"
|
180
|
+
url = generate_url uri
|
181
|
+
|
182
|
+
headers = {}
|
183
|
+
headers['Accept'] = MIME::Types['application/json']
|
184
|
+
headers.merge!(arguments[:headers]) if arguments[:headers]
|
185
|
+
|
186
|
+
request = signed_request(url,
|
187
|
+
:method => method,
|
188
|
+
:headers => headers,
|
189
|
+
:params => params,
|
190
|
+
:cache_timeout => settings['cache_timeout'],
|
191
|
+
:verbose => settings['debug']
|
192
|
+
)
|
193
|
+
|
194
|
+
request.on_complete do |response|
|
195
|
+
if response.success?
|
196
|
+
yield(response) if block_given?
|
197
|
+
else
|
198
|
+
raise CartoDB::Client::Error.new url, method, response
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
enqueue request
|
203
|
+
end
|
204
|
+
private :cartodb_request
|
205
|
+
|
206
|
+
def enqueue(request)
|
207
|
+
@hydra.queue request
|
208
|
+
request
|
209
|
+
end
|
210
|
+
private :enqueue
|
211
|
+
|
212
|
+
def execute_queue
|
213
|
+
@hydra.run
|
214
|
+
end
|
215
|
+
private :execute_queue
|
216
|
+
|
217
|
+
def generate_url(uri)
|
218
|
+
uri = URI.parse("#{settings['host']}#{uri}")
|
219
|
+
"#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}"
|
220
|
+
end
|
221
|
+
private :generate_url
|
222
|
+
|
223
|
+
def settings
|
224
|
+
CartoDB::Settings || {}
|
225
|
+
end
|
226
|
+
private :settings
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Client
|
3
|
+
module Connection
|
4
|
+
class PostgreSQL
|
5
|
+
include CartoDB::Helpers::SqlHelper
|
6
|
+
|
7
|
+
DATA_TYPES_TRANSLATION_TABLE = {
|
8
|
+
'int4' => 'number',
|
9
|
+
'numeric' => 'number',
|
10
|
+
'text' => 'string',
|
11
|
+
'varchar' => 'string',
|
12
|
+
'date' => 'date',
|
13
|
+
'timestamp' => 'date',
|
14
|
+
'bool' => 'boolean',
|
15
|
+
'geometry' => 'geometry'
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(connection_settings)
|
19
|
+
@pg = connect_to_postgres(connection_settings)
|
20
|
+
end
|
21
|
+
|
22
|
+
def connect_to_postgres(settings)
|
23
|
+
pg_connection = PGconn.open(
|
24
|
+
:host => settings['host'],
|
25
|
+
:port => settings['port'],
|
26
|
+
:user => settings['user'],
|
27
|
+
:password => settings['password'],
|
28
|
+
:dbname => settings['database']
|
29
|
+
)
|
30
|
+
pg_connection.set_notice_processor{|message| nil}
|
31
|
+
pg_connection
|
32
|
+
end
|
33
|
+
private :connect_to_postgres
|
34
|
+
|
35
|
+
def create_table(table_name = nil, schema_or_file = nil, the_geom_type = 'Point')
|
36
|
+
schema = schema_or_file if schema_or_file && schema_or_file.is_a?(Array)
|
37
|
+
file = schema_or_file if schema_or_file && schema_or_file.is_a?(File)
|
38
|
+
|
39
|
+
params = {:name => table_name}
|
40
|
+
params[:file] = file if file
|
41
|
+
|
42
|
+
table_name = table_name.sanitize
|
43
|
+
|
44
|
+
if schema.nil? || schema.empty?
|
45
|
+
schema = []
|
46
|
+
schema << {:name => 'name', :type => 'text'} if schema.select{|c| c[:name].eql?('name')}.empty?
|
47
|
+
schema << {:name => 'description', :type => 'text'} if schema.select{|c| c[:name].eql?('description')}.empty?
|
48
|
+
create_the_geom = "SELECT AddGeometryColumn('public', '#{table_name}', 'the_geom', 4326, 'POINT', 2)"
|
49
|
+
end
|
50
|
+
|
51
|
+
schema << {:name => 'cartodb_id', :type => 'serial', :extra => 'NOT NULL'} if schema.select{|c| c[:name].eql?('cartodb_id')}.empty?
|
52
|
+
schema << {:name => 'created_at', :type => 'timestamp', :extra => 'without time zone DEFAULT current_timestamp::timestamp without time zone'} if schema.select{|c| c[:name].eql?('created_at')}.empty?
|
53
|
+
schema << {:name => 'updated_at', :type => 'timestamp', :extra => 'without time zone DEFAULT current_timestamp::timestamp without time zone'} if schema.select{|c| c[:name].eql?('updated_at')}.empty?
|
54
|
+
if schema.any? && schema.select{|c| c[:name].downcase.match(/geo/)}.any?
|
55
|
+
the_geom_field = schema.select{|c| c[:name].downcase.match(/geo/)}.first
|
56
|
+
create_the_geom = "SELECT AddGeometryColumn('public', '#{table_name}', 'the_geom', 4326, '#{the_geom_field[:geometry_type].upcase}', 2)"
|
57
|
+
schema.reject!{|c| c[:name].downcase.match(/geo/)}
|
58
|
+
end
|
59
|
+
|
60
|
+
@pg.query(<<-SQL
|
61
|
+
CREATE TABLE #{table_name}
|
62
|
+
(
|
63
|
+
#{schema.map{|s| "#{s[:name]} #{s[:type]} #{s[:extra]}"}.join(', ')}
|
64
|
+
)
|
65
|
+
WITH (
|
66
|
+
OIDS=FALSE
|
67
|
+
);
|
68
|
+
ALTER TABLE #{table_name} OWNER TO #{@pg.user};
|
69
|
+
SQL
|
70
|
+
)
|
71
|
+
|
72
|
+
@pg.query(create_the_geom) if create_the_geom
|
73
|
+
|
74
|
+
table table_name
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_column(table_name, column_name, column_type)
|
78
|
+
@pg.query(<<-SQL
|
79
|
+
ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{column_type};
|
80
|
+
SQL
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def drop_column(table_name, column_name)
|
85
|
+
@pg.query(<<-SQL
|
86
|
+
ALTER TABLE #{table_name} DROP COLUMN #{column_name};
|
87
|
+
SQL
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def change_column(table_name, old_column_name, new_column_name, column_type)
|
92
|
+
add_column table_name, new_column_name, column_type
|
93
|
+
@pg.query(<<-SQL
|
94
|
+
UPDATE #{table_name} SET #{new_column_name}=cast(#{old_column_name} as #{column_type})
|
95
|
+
SQL
|
96
|
+
)
|
97
|
+
drop_column table_name, old_column_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def tables
|
101
|
+
pg_result = @pg.query(<<-SQL
|
102
|
+
SELECT columns.table_name, columns.column_name, columns.udt_name as data_type
|
103
|
+
FROM information_schema.tables tables
|
104
|
+
INNER JOIN information_schema.columns columns ON columns.table_name = tables.table_name
|
105
|
+
WHERE tables.table_schema = 'public' AND tables.table_name not IN ('spatial_ref_sys', 'geometry_columns', 'geography_columns')
|
106
|
+
SQL
|
107
|
+
)
|
108
|
+
|
109
|
+
result = CartoDB::Types::Metadata.new
|
110
|
+
tables = {}
|
111
|
+
pg_result.each do |column|
|
112
|
+
tables[column['table_name']] = CartoDB::Types::Metadata.from_hash({:name => column['table_name'], :schema => []}) unless tables[column['table_name']]
|
113
|
+
tables[column['table_name']].schema << %W(#{column['column_name']} #{CartoDB::Client::Connection::PostgreSQL::DATA_TYPES_TRANSLATION_TABLE[column['data_type']]})
|
114
|
+
end
|
115
|
+
|
116
|
+
result.total_entries = tables.to_a.length
|
117
|
+
result.tables = tables.to_a.map(&:last)
|
118
|
+
result
|
119
|
+
end
|
120
|
+
|
121
|
+
def table(table_name)
|
122
|
+
pg_result = @pg.query(<<-SQL
|
123
|
+
SELECT columns.table_name, columns.column_name, columns.udt_name as data_type, geo_cols.type as geometry_type
|
124
|
+
FROM information_schema.tables tables
|
125
|
+
INNER JOIN information_schema.columns columns ON columns.table_name = tables.table_name
|
126
|
+
LEFT OUTER JOIN public.geometry_columns geo_cols ON geo_cols.f_table_schema = columns.table_schema AND geo_cols.f_table_name = columns.table_name AND geo_cols.f_geometry_column = columns.column_name
|
127
|
+
WHERE tables.table_schema = 'public' AND tables.table_name not IN ('spatial_ref_sys', 'geometry_columns', 'geography_columns') AND tables.table_name ilike '#{table_name}'
|
128
|
+
SQL
|
129
|
+
)
|
130
|
+
|
131
|
+
if pg_result.to_a.empty?
|
132
|
+
non_existing_table = @pg.query(<<-SQL
|
133
|
+
SELECT tables.table_name
|
134
|
+
FROM information_schema.tables tables
|
135
|
+
WHERE tables.table_schema = 'public' AND tables.table_name not IN ('spatial_ref_sys', 'geometry_columns', 'geography_columns') AND tables.table_name ilike '#{table_name}'
|
136
|
+
SQL
|
137
|
+
)
|
138
|
+
raise CartoDB::Client::Error.new if non_existing_table.to_a.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
table_result = nil
|
142
|
+
pg_result.each do |column|
|
143
|
+
table_result = CartoDB::Types::Metadata.from_hash({:name => column['table_name'], :schema => []}) unless table_result
|
144
|
+
table_result.schema << %W(#{column['column_name']} #{CartoDB::Client::Connection::PostgreSQL::DATA_TYPES_TRANSLATION_TABLE[column['data_type']]})
|
145
|
+
table_result.schema.last << CartoDB::Client::Connection::PostgreSQL::DATA_TYPES_TRANSLATION_TABLE[column['data_type']] if column['geometry_type']
|
146
|
+
table_result.schema.last << column['geometry_type'].downcase if column['geometry_type']
|
147
|
+
end
|
148
|
+
table_result
|
149
|
+
end
|
150
|
+
|
151
|
+
def drop_table(table_name)
|
152
|
+
@pg.query(<<-SQL
|
153
|
+
DROP TABLE #{table_name}
|
154
|
+
SQL
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
def row(table_name, row_id)
|
159
|
+
results = query(<<-SQL
|
160
|
+
SELECT #{table_name}.cartodb_id as id, #{table_name}.*
|
161
|
+
FROM #{table_name}
|
162
|
+
WHERE cartodb_id = #{row_id};
|
163
|
+
SQL
|
164
|
+
)
|
165
|
+
|
166
|
+
results.rows.first
|
167
|
+
end
|
168
|
+
|
169
|
+
def insert_row(table_name, row)
|
170
|
+
row = prepare_data(row)
|
171
|
+
|
172
|
+
results = query(<<-SQL
|
173
|
+
INSERT INTO #{table_name}
|
174
|
+
(#{row.keys.join(',')})
|
175
|
+
VALUES (#{row.values.join(',')});
|
176
|
+
|
177
|
+
SELECT #{table_name}.cartodb_id as id, #{table_name}.*
|
178
|
+
FROM #{table_name}
|
179
|
+
WHERE cartodb_id = currval('public.#{table_name}_cartodb_id_seq');
|
180
|
+
SQL
|
181
|
+
)
|
182
|
+
|
183
|
+
results.rows.first
|
184
|
+
end
|
185
|
+
|
186
|
+
def update_row(table_name, row_id, row)
|
187
|
+
row = prepare_data(row)
|
188
|
+
|
189
|
+
results = query(<<-SQL
|
190
|
+
UPDATE #{table_name}
|
191
|
+
SET (#{row.keys.join(',')})
|
192
|
+
= (#{row.values.join(',')})
|
193
|
+
WHERE cartodb_id = #{row_id};
|
194
|
+
SELECT #{table_name}.cartodb_id as id, #{table_name}.*
|
195
|
+
FROM #{table_name}
|
196
|
+
WHERE cartodb_id = currval('public.#{table_name}_cartodb_id_seq');
|
197
|
+
SQL
|
198
|
+
)
|
199
|
+
|
200
|
+
results.rows.first
|
201
|
+
end
|
202
|
+
|
203
|
+
def delete_row(table_name, row_id)
|
204
|
+
@pg.query(<<-SQL
|
205
|
+
DELETE FROM #{table_name}
|
206
|
+
WHERE cartodb_id = #{row_id}
|
207
|
+
SQL
|
208
|
+
)
|
209
|
+
end
|
210
|
+
|
211
|
+
def records(table_name, options = {})
|
212
|
+
sql = <<-SQL
|
213
|
+
SELECT #{table_name}.cartodb_id AS id, #{table_name}.*
|
214
|
+
FROM #{table_name}
|
215
|
+
SQL
|
216
|
+
|
217
|
+
results = query(sql, options)
|
218
|
+
|
219
|
+
results[:name] = table_name
|
220
|
+
results
|
221
|
+
end
|
222
|
+
|
223
|
+
def query(sql, options = {})
|
224
|
+
sql = sql.strip if sql
|
225
|
+
|
226
|
+
if sql.include?('*')
|
227
|
+
table_name = sql.match(/select(.*)\s((\w+\.)?\*)(.*)from\s+(\w*)[^;]*;?/im)[5]
|
228
|
+
schema = table(table_name).schema if table_name
|
229
|
+
|
230
|
+
sql.gsub!(/^select(.*)\s((\w+\.)?\*)(.*)from/im) do |matches|
|
231
|
+
%Q{SELECT #{$1.strip} #{schema.map{|c| "#{$3}#{c[0]}"}.join(', ')} #{$4.strip} FROM}
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
crono = Time.now
|
236
|
+
begin
|
237
|
+
result = @pg.query(<<-SQL
|
238
|
+
#{sql}
|
239
|
+
SQL
|
240
|
+
)
|
241
|
+
rescue PGError => e
|
242
|
+
raise CartoDB::Client::Error.new(nil, nil, nil, e.message)
|
243
|
+
end
|
244
|
+
|
245
|
+
CartoDB::Types::Metadata.from_hash({
|
246
|
+
:time => Time.now - crono,
|
247
|
+
:total_rows => result.cmd_tuples,
|
248
|
+
:rows => result.map{|row| CartoDB::Types::Metadata.from_hash(row)}
|
249
|
+
})
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Client
|
3
|
+
class Error < Exception
|
4
|
+
attr_accessor :status_code
|
5
|
+
|
6
|
+
HTTP_MESSAGES = {
|
7
|
+
401 => 'Unauthorized request',
|
8
|
+
404 => 'Not found',
|
9
|
+
500 => 'Server error'
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(uri = nil, method = nil, http_response = nil, error_message = nil)
|
13
|
+
@uri = uri
|
14
|
+
@method = method
|
15
|
+
@error_messages = ['undefined CartoDB error']
|
16
|
+
@status_code = 400
|
17
|
+
@error_messages = [uri] if method == nil && http_response == nil && error_message == nil
|
18
|
+
@error_messages = [error_message] if error_message
|
19
|
+
|
20
|
+
if http_response
|
21
|
+
@status_code = http_response.code
|
22
|
+
@error_messages = custom_error(http_response) || standard_error
|
23
|
+
@body = http_response.body
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
message = <<-EOF
|
30
|
+
#{http_error_message_header}
|
31
|
+
#{format_error_messages}
|
32
|
+
#{@body}
|
33
|
+
EOF
|
34
|
+
message.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
def http_error_message_header
|
38
|
+
if @method && @uri
|
39
|
+
%{There were errors running the #{@method.to_s.upcase} request "#{@uri}":}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
private :http_error_message_header
|
43
|
+
|
44
|
+
def custom_error(http_response)
|
45
|
+
json = Utils.parse_json(http_response)
|
46
|
+
json[:error] if json
|
47
|
+
end
|
48
|
+
|
49
|
+
def standard_error
|
50
|
+
"#{status_code} - #{HTTP_MESSAGES[status_code.to_i]}"
|
51
|
+
end
|
52
|
+
private :standard_error
|
53
|
+
|
54
|
+
def format_error_messages
|
55
|
+
return '' unless @error_messages
|
56
|
+
if @error_messages.is_a?(String)
|
57
|
+
@error_messages
|
58
|
+
elsif @error_messages.is_a?(Array) && @error_messages.count == 1
|
59
|
+
@error_messages.first
|
60
|
+
else
|
61
|
+
@error_messages.map{|e| "- #{e}"}.join("\n")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
private :format_error_messages
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Client
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
def self.parse_json(response)
|
6
|
+
json = nil
|
7
|
+
|
8
|
+
unless response.nil? || response.body.nil? || response.body.strip == ''
|
9
|
+
begin
|
10
|
+
json = ::JSON.parse(response.body, :object_class => CartoDB::Types::Metadata, :symbolize_names => true)
|
11
|
+
rescue JSON::ParserError => e
|
12
|
+
json = CartoDB::Types::Metadata.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
json
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Helpers
|
3
|
+
module SqlHelper
|
4
|
+
|
5
|
+
def prepare_data(hash)
|
6
|
+
hash.each do |key, value|
|
7
|
+
hash[key] = format_value(value)
|
8
|
+
end
|
9
|
+
hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def format_value(value)
|
13
|
+
case value
|
14
|
+
when ::String
|
15
|
+
"'#{value}'"
|
16
|
+
when ::Date, ::DateTime, ::Time
|
17
|
+
"'#{value}'"
|
18
|
+
when RGeo::Feature::Geometry
|
19
|
+
"'#{RGeo::WKRep::WKBGenerator.new(:type_format => :ewkb, :emit_ewkb_srid => true, :hex_format => true).generate(value)}'"
|
20
|
+
when NilClass
|
21
|
+
'NULL'
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
private :format_value
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'cartodb-rb-client/cartodb/helpers/sql_helper'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CartoDB
|
2
|
+
class Init
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def start(cartodb_settings = nil)
|
7
|
+
if cartodb_settings.blank?
|
8
|
+
config_path = Rails.root.join('config/cartodb_config.yml')
|
9
|
+
cartodb_settings = YAML.load_file(config_path)[Rails.env.to_s] if File.exists?(config_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
return if cartodb_settings.blank?
|
13
|
+
|
14
|
+
if CartoDB.const_defined?('Settings')
|
15
|
+
CartoDB::Settings.merge!(cartodb_settings)
|
16
|
+
else
|
17
|
+
CartoDB.const_set('Settings', cartodb_settings)
|
18
|
+
end
|
19
|
+
|
20
|
+
CartoDB.const_set('Connection', CartoDB::Client::Connection::Base.new) unless CartoDB.const_defined?('Connection')
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|