cartodb-rb-client 0.1.8
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/.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,116 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
class String
|
4
|
+
def self.random(length=10)
|
5
|
+
('a'..'z').sort_by {rand}[0,length].join
|
6
|
+
end
|
7
|
+
|
8
|
+
def normalize
|
9
|
+
str = self.downcase
|
10
|
+
return '' if str.blank?
|
11
|
+
n = str.force_encoding("UTF-8")
|
12
|
+
n.gsub!(/[àáâãäåāă]/, 'a')
|
13
|
+
n.gsub!(/æ/, 'ae')
|
14
|
+
n.gsub!(/[ďđ]/, 'd')
|
15
|
+
n.gsub!(/[çćčĉċ]/, 'c')
|
16
|
+
n.gsub!(/[èéêëēęěĕė]/, 'e')
|
17
|
+
n.gsub!(/ƒ/, 'f')
|
18
|
+
n.gsub!(/[ĝğġģ]/, 'g')
|
19
|
+
n.gsub!(/[ĥħ]/, 'h')
|
20
|
+
n.gsub!(/[ììíîïīĩĭ]/, 'i')
|
21
|
+
n.gsub!(/[įıijĵ]/, 'j')
|
22
|
+
n.gsub!(/[ķĸ]/, 'k')
|
23
|
+
n.gsub!(/[łľĺļŀ]/, 'l')
|
24
|
+
n.gsub!(/[ñńňņʼnŋ]/, 'n')
|
25
|
+
n.gsub!(/[òóôõöøōőŏŏ]/, 'o')
|
26
|
+
n.gsub!(/œ/, 'oe')
|
27
|
+
n.gsub!(/ą/, 'q')
|
28
|
+
n.gsub!(/[ŕřŗ]/, 'r')
|
29
|
+
n.gsub!(/[śšşŝș]/, 's')
|
30
|
+
n.gsub!(/[ťţŧț]/, 't')
|
31
|
+
n.gsub!(/[ùúûüūůűŭũų]/, 'u')
|
32
|
+
n.gsub!(/ŵ/, 'w')
|
33
|
+
n.gsub!(/[ýÿŷ]/, 'y')
|
34
|
+
n.gsub!(/[žżź]/, 'z')
|
35
|
+
n.gsub!(/[ÀÁÂÃÄÅĀĂ]/i, 'A')
|
36
|
+
n.gsub!(/Æ/i, 'AE')
|
37
|
+
n.gsub!(/[ĎĐ]/i, 'D')
|
38
|
+
n.gsub!(/[ÇĆČĈĊ]/i, 'C')
|
39
|
+
n.gsub!(/[ÈÉÊËĒĘĚĔĖ]/i, 'E')
|
40
|
+
n.gsub!(/Ƒ/i, 'F')
|
41
|
+
n.gsub!(/[ĜĞĠĢ]/i, 'G')
|
42
|
+
n.gsub!(/[ĤĦ]/i, 'H')
|
43
|
+
n.gsub!(/[ÌÌÍÎÏĪĨĬ]/i, 'I')
|
44
|
+
n.gsub!(/[IJĴ]/i, 'J')
|
45
|
+
n.gsub!(/[Ķĸ]/i, 'J')
|
46
|
+
n.gsub!(/[ŁĽĹĻĿ]/i, 'L')
|
47
|
+
n.gsub!(/[ÑŃŇŅʼnŊ]/i, 'M')
|
48
|
+
n.gsub!(/[ÒÓÔÕÖØŌŐŎŎ]/i, 'N')
|
49
|
+
n.gsub!(/Œ/i, 'OE')
|
50
|
+
n.gsub!(/Ą/i, 'Q')
|
51
|
+
n.gsub!(/[ŔŘŖ]/i, 'R')
|
52
|
+
n.gsub!(/[ŚŠŞŜȘ]/i, 'S')
|
53
|
+
n.gsub!(/[ŤŢŦȚ]/i, 'T')
|
54
|
+
n.gsub!(/[ÙÚÛÜŪŮŰŬŨŲ]/i, 'U')
|
55
|
+
n.gsub!(/Ŵ/i, 'W')
|
56
|
+
n.gsub!(/[ÝŸŶ]/i, 'Y')
|
57
|
+
n.gsub!(/[ŽŻŹ]/i, 'Z')
|
58
|
+
n
|
59
|
+
end
|
60
|
+
|
61
|
+
def sanitize
|
62
|
+
return if self.blank?
|
63
|
+
self.gsub(/<[^>]+>/m,'').normalize.downcase.gsub(/&.+?;/,'-').
|
64
|
+
gsub(/[^a-z0-9 _-]/,'-').strip.gsub(/\s+/,'-').gsub(/-+/,'-').
|
65
|
+
gsub(/-/,' ').strip.gsub(/ /,'-').gsub(/-/,'_')
|
66
|
+
end
|
67
|
+
|
68
|
+
def strip_tags
|
69
|
+
self.gsub(/<[^>]+>/m,'').strip
|
70
|
+
end
|
71
|
+
|
72
|
+
def convert_to_db_type
|
73
|
+
if CartoDB::TYPES.keys.include?(self.downcase)
|
74
|
+
if self.downcase == "number"
|
75
|
+
"double precision"
|
76
|
+
else
|
77
|
+
CartoDB::TYPES[self.downcase].first
|
78
|
+
end
|
79
|
+
else
|
80
|
+
self.downcase
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# {"integer"=>:number, "real"=>:number, "varchar"=>:string, "text"=>:string, "timestamp"=>:date, "boolean"=>:boolean}
|
85
|
+
def convert_to_cartodb_type
|
86
|
+
inverse_types = CartoDB::TYPES.invert.inject({}){ |h, e| e.first.each{ |k| h[k] = e.last }; h}
|
87
|
+
if inverse_types.keys.include?(self.downcase)
|
88
|
+
inverse_types[self.downcase]
|
89
|
+
else
|
90
|
+
inverse_types.keys.select{ |t| !t.is_a?(String) }.each do |re|
|
91
|
+
if self.downcase.match(re)
|
92
|
+
return inverse_types[re]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
self.downcase
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def sanitize_sql
|
100
|
+
self.gsub(/\\/, '\&\&').gsub(/'/, "''")
|
101
|
+
end
|
102
|
+
|
103
|
+
def host
|
104
|
+
self.split('/')[2]
|
105
|
+
end
|
106
|
+
|
107
|
+
def sanitize_column_name
|
108
|
+
temporal_name = self.sanitize
|
109
|
+
if temporal_name !~ /^[a-zA-Z_]/ || CartoDB::POSTGRESQL_RESERVED_WORDS.include?(self.upcase)
|
110
|
+
return '_' + temporal_name
|
111
|
+
else
|
112
|
+
temporal_name
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
class Base
|
4
|
+
include CartoDB::Model::Getters
|
5
|
+
include CartoDB::Model::Setters
|
6
|
+
include CartoDB::Model::Geo
|
7
|
+
include CartoDB::Model::Schema
|
8
|
+
include CartoDB::Model::Persistence
|
9
|
+
include CartoDB::Model::Query
|
10
|
+
include RGeo::Feature
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
self.class.cartodb_table = nil
|
14
|
+
self.class.send(:field, :the_geom, :type => Point)
|
15
|
+
self.attributes = attributes
|
16
|
+
self.class.send(:update_cartodb_schema) unless schema_synchronized?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Constants
|
4
|
+
include RGeo::Feature
|
5
|
+
|
6
|
+
CARTODB_TYPES = {
|
7
|
+
String => 'text',
|
8
|
+
Integer => 'numeric',
|
9
|
+
Numeric => 'numeric',
|
10
|
+
Date => 'date',
|
11
|
+
DateTime => 'date',
|
12
|
+
TrueClass => 'boolean',
|
13
|
+
FalseClass => 'boolean',
|
14
|
+
Point => 'geometry'
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
INVALID_COLUMNS = [
|
18
|
+
:cartodb_id,
|
19
|
+
:id
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
GEOMETRY_COLUMN = 'the_geom'.freeze
|
23
|
+
|
24
|
+
RGEO_FACTORY = ::RGeo::Geographic.simple_mercator_factory(:srid => 4326)
|
25
|
+
|
26
|
+
DEFAULT_ROWS_PER_PAGE = 10.freeze
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Geo
|
4
|
+
include CartoDB::Model::Constants
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
include CartoDB::Model::Constants
|
12
|
+
|
13
|
+
def setup_geometry_column(geometry_column)
|
14
|
+
return if geometry_column[:geometry_type].nil?
|
15
|
+
|
16
|
+
geometry_name = geometry_column[:name].to_sym
|
17
|
+
|
18
|
+
self.send :define_method, :the_geom do
|
19
|
+
self.attributes[geometry_name]
|
20
|
+
end
|
21
|
+
|
22
|
+
self.send :define_method, :the_geom= do |the_geom|
|
23
|
+
self.attributes[geometry_name] = the_geom
|
24
|
+
end
|
25
|
+
|
26
|
+
case geometry_column[:geometry_type].upcase
|
27
|
+
when 'POINT'
|
28
|
+
setup_point_geometry
|
29
|
+
end
|
30
|
+
end
|
31
|
+
private :setup_geometry_column
|
32
|
+
|
33
|
+
def setup_point_geometry
|
34
|
+
self.send :define_method, :latitude do
|
35
|
+
self.the_geom ? self.the_geom.y : nil
|
36
|
+
end
|
37
|
+
self.send :define_method, :longitude do
|
38
|
+
self.the_geom ? self.the_geom.x : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
self.send :define_method, :latitude= do |latitude|
|
42
|
+
@latitude = latitude
|
43
|
+
if @latitude && @longitude
|
44
|
+
self.the_geom = RGEO_FACTORY.point(@longitude, @latitude)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
self.send :define_method, :longitude= do |longitude|
|
48
|
+
@longitude = longitude
|
49
|
+
if @latitude && @longitude
|
50
|
+
self.the_geom = RGEO_FACTORY.point(@longitude, @latitude)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
private :setup_point_geometry
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def prepare_geo_attributes(attributes)
|
60
|
+
return if attributes.nil?
|
61
|
+
longitude = attributes.delete(:longitude)
|
62
|
+
latitude = attributes.delete(:latitude)
|
63
|
+
if latitude && longitude
|
64
|
+
attributes[:the_geom] = RGEO_FACTORY.point(longitude, latitude)
|
65
|
+
end
|
66
|
+
|
67
|
+
attributes
|
68
|
+
end
|
69
|
+
private :prepare_geo_attributes
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Getters
|
4
|
+
attr_reader :table, :attributes
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def connection
|
13
|
+
CartoDB::Connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def table_name
|
17
|
+
@table_name ||= self.name.tableize
|
18
|
+
end
|
19
|
+
|
20
|
+
def cartodb_table
|
21
|
+
@cartodb_table = begin
|
22
|
+
connection.table table_name
|
23
|
+
rescue CartoDB::Client::Error
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def columns
|
29
|
+
update_cartodb_schema unless schema_synchronized?
|
30
|
+
@columns
|
31
|
+
end
|
32
|
+
|
33
|
+
def data_columns
|
34
|
+
columns.reject{|c| %w(cartodb_id created_at updated_at).include?(c[:name])}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def model_columns
|
38
|
+
@model_columns || []
|
39
|
+
end
|
40
|
+
private :model_columns
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def connection
|
45
|
+
self.class.connection
|
46
|
+
end
|
47
|
+
|
48
|
+
def table_name
|
49
|
+
self.class.table_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def cartodb_table
|
53
|
+
self.class.cartodb_table
|
54
|
+
end
|
55
|
+
|
56
|
+
# def method_missing(name, *args, &block)
|
57
|
+
# if args.empty? && block.nil? && column_names.include?(name.to_s)
|
58
|
+
# attributes[name]
|
59
|
+
# else
|
60
|
+
# super
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
|
64
|
+
def columns
|
65
|
+
self.class.columns
|
66
|
+
end
|
67
|
+
|
68
|
+
def attributes
|
69
|
+
@attributes ||= {}
|
70
|
+
end
|
71
|
+
|
72
|
+
def column_names
|
73
|
+
columns.map{|column| column[:name]}
|
74
|
+
end
|
75
|
+
private :column_names
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Persistence
|
4
|
+
include CartoDB::Model::Constants
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def create(attributes = {})
|
13
|
+
model = self.new attributes
|
14
|
+
model.save
|
15
|
+
model
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
if new_record?
|
22
|
+
create_row
|
23
|
+
else
|
24
|
+
update_row
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy
|
29
|
+
unless new_record?
|
30
|
+
delete_row
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_record?
|
35
|
+
cartodb_id.nil? || cartodb_id <= 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_row
|
39
|
+
inserted_record = connection.insert_row table_name, attributes_for_insert
|
40
|
+
self.cartodb_id = inserted_record.id
|
41
|
+
end
|
42
|
+
private :create_row
|
43
|
+
|
44
|
+
def update_row
|
45
|
+
connection.update_row table_name, cartodb_id, attributes_for_update
|
46
|
+
end
|
47
|
+
private :update_row
|
48
|
+
|
49
|
+
def delete_row
|
50
|
+
connection.delete_row table_name, cartodb_id
|
51
|
+
end
|
52
|
+
private :delete_row
|
53
|
+
|
54
|
+
def attributes_for_insert
|
55
|
+
# only the columns defined in the model are allowed to be inserted
|
56
|
+
row = attributes.symbolize_keys.reject{|key,value| INVALID_COLUMNS.include?(key) || !column_names.include?(key.to_s) }
|
57
|
+
row
|
58
|
+
end
|
59
|
+
private :attributes_for_insert
|
60
|
+
|
61
|
+
def attributes_for_update
|
62
|
+
row = attributes.reject{|key,value| !column_names.include?(key.to_s) }
|
63
|
+
row
|
64
|
+
end
|
65
|
+
private :attributes_for_update
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Query
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def select(*fields)
|
12
|
+
scope = Scope.new(self)
|
13
|
+
scope.select(fields)
|
14
|
+
end
|
15
|
+
|
16
|
+
def all
|
17
|
+
scope = Scope.new(self)
|
18
|
+
scope.all
|
19
|
+
end
|
20
|
+
|
21
|
+
def where(attributes = nil, *rest)
|
22
|
+
scope = Scope.new(self)
|
23
|
+
scope.where(attributes, rest)
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(id)
|
27
|
+
where(id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def count
|
31
|
+
begin
|
32
|
+
results = connection.query "SELECT COUNT(CARTODB_ID) FROM #{table_name}"
|
33
|
+
results.rows.first[:count].try(:to_i)
|
34
|
+
rescue Exception => e
|
35
|
+
0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def page(page_number)
|
40
|
+
scope = Scope.new(self)
|
41
|
+
scope.page(page_number)
|
42
|
+
end
|
43
|
+
|
44
|
+
def per_page(ammount)
|
45
|
+
scope = Scope.new(self)
|
46
|
+
scope.page(page_number)
|
47
|
+
end
|
48
|
+
|
49
|
+
def order(order_clause)
|
50
|
+
scope = Scope.new(self)
|
51
|
+
scope.order(order_clause)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def count
|
57
|
+
self.class.count
|
58
|
+
end
|
59
|
+
|
60
|
+
def count=(ammount)
|
61
|
+
self.class.count= ammount
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module CartoDB
|
2
|
+
module Model
|
3
|
+
module Schema
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
include CartoDB::Model::Constants
|
11
|
+
|
12
|
+
def schema_synchronized?
|
13
|
+
cartodb_table && @columns_synchronized
|
14
|
+
end
|
15
|
+
|
16
|
+
def cartodb_table_exists?
|
17
|
+
begin
|
18
|
+
cartodb_table && cartodb_table.name.eql?(table_name)
|
19
|
+
rescue CartoDB::Client::Error => e
|
20
|
+
e.status_code != 404
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def field(name, options = {:type => String})
|
25
|
+
@columns_synchronized = false
|
26
|
+
@model_columns ||= []
|
27
|
+
column = {
|
28
|
+
:name => name.to_s,
|
29
|
+
:type => CARTODB_TYPES[options[:type]] || options[:type]
|
30
|
+
}
|
31
|
+
column[:geometry_type] = options[:type].name.split('::').last.downcase if column[:type].eql?('geometry')
|
32
|
+
return if model_columns.include?(column)
|
33
|
+
|
34
|
+
model_columns << column
|
35
|
+
update_cartodb_schema
|
36
|
+
end
|
37
|
+
private :field
|
38
|
+
|
39
|
+
def update_cartodb_schema
|
40
|
+
table = nil
|
41
|
+
if cartodb_table_exists?
|
42
|
+
table = cartodb_table
|
43
|
+
else
|
44
|
+
table = connection.create_table table_name, model_columns
|
45
|
+
end
|
46
|
+
|
47
|
+
read_metadata table
|
48
|
+
create_missing_columns
|
49
|
+
create_column_accessors
|
50
|
+
@columns_synchronized = true
|
51
|
+
end
|
52
|
+
private :update_cartodb_schema
|
53
|
+
|
54
|
+
def read_metadata(table)
|
55
|
+
extract_columns table
|
56
|
+
end
|
57
|
+
private :read_metadata
|
58
|
+
|
59
|
+
def extract_columns(table)
|
60
|
+
@columns = table.schema.map{|c| c[0].eql?('the_geom') ? {:name => c[0], :type => c[1], :geometry_type => c[3]} : {:name => c[0], :type => c[1]}}
|
61
|
+
end
|
62
|
+
private :extract_columns
|
63
|
+
|
64
|
+
def create_missing_columns
|
65
|
+
table_column_names = @columns.map{|c| c[:name]}
|
66
|
+
missing_columns = model_columns.reject{|c| table_column_names.include?(c[:name])}
|
67
|
+
return unless missing_columns && missing_columns.any?
|
68
|
+
|
69
|
+
missing_columns.each do |column|
|
70
|
+
connection.add_column table_name, column[:name], column[:type]
|
71
|
+
end
|
72
|
+
|
73
|
+
self.cartodb_table = nil
|
74
|
+
read_metadata self.cartodb_table
|
75
|
+
end
|
76
|
+
private :create_missing_columns
|
77
|
+
|
78
|
+
def create_column_accessors
|
79
|
+
@columns.each do |c|
|
80
|
+
column_name = c[:name]
|
81
|
+
column_type = c[:type]
|
82
|
+
setup_geometry_column(c) and next if column_name.eql?(GEOMETRY_COLUMN) || column_type.eql?('geometry')
|
83
|
+
|
84
|
+
# unless self.methods.include?(column_name)
|
85
|
+
self.send :define_method, column_name do
|
86
|
+
self.attributes[column_name.to_sym]
|
87
|
+
end
|
88
|
+
# end
|
89
|
+
|
90
|
+
# unless self.methods.include?("#{column_name}=")
|
91
|
+
self.send :define_method, "#{column_name}=" do |value|
|
92
|
+
self.attributes[column_name.to_sym] = value
|
93
|
+
end
|
94
|
+
# end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
private :create_column_accessors
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def schema_synchronized?
|
102
|
+
self.class.schema_synchronized? && cartodb_table_exists?
|
103
|
+
end
|
104
|
+
|
105
|
+
def cartodb_table_exists?
|
106
|
+
self.class.cartodb_table_exists?
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|