activerecord-postgis-adapter 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,13 @@
1
1
  module ActiveRecord # :nodoc:
2
-
3
2
  module ConnectionAdapters # :nodoc:
4
-
5
3
  module PostGISAdapter # :nodoc:
6
-
7
4
  class PostGISDatabaseTasks < ::ActiveRecord::Tasks::PostgreSQLDatabaseTasks # :nodoc:
8
5
 
9
-
10
- def initialize(config_)
6
+ def initialize(config)
11
7
  super
12
8
  ensure_installation_configs
13
9
  end
14
10
 
15
-
16
11
  def setup_gis
17
12
  establish_su_connection
18
13
  setup_gis_schemas
@@ -27,43 +22,36 @@ module ActiveRecord # :nodoc:
27
22
  establish_connection(configuration)
28
23
  end
29
24
 
30
-
31
25
  # Overridden to set the database owner and call setup_gis
32
-
33
- def create(master_established_=false)
34
- establish_master_connection unless master_established_
35
- extra_configs_ = {'encoding' => encoding}
36
- extra_configs_['owner'] = username if has_su?
37
- connection.create_database(configuration['database'], configuration.merge(extra_configs_))
26
+ def create(master_established = false)
27
+ establish_master_connection unless master_established
28
+ extra_configs = {'encoding' => encoding}
29
+ extra_configs['owner'] = username if has_su?
30
+ connection.create_database(configuration['database'], configuration.merge(extra_configs))
38
31
  setup_gis
39
- rescue ::ActiveRecord::StatementInvalid => error_
40
- if /database .* already exists/ === error_.message
32
+ rescue ::ActiveRecord::StatementInvalid => error
33
+ if /database .* already exists/ === error.message
41
34
  raise ::ActiveRecord::Tasks::DatabaseAlreadyExists
42
35
  else
43
36
  raise
44
37
  end
45
38
  end
46
39
 
47
-
48
40
  # Overridden to remove postgis schema
49
-
50
- def structure_dump(filename_)
41
+ def structure_dump(filename)
51
42
  set_psql_env
52
- search_path_ = search_path.dup
53
- search_path_.delete('postgis')
54
- search_path_ = ['public'] if search_path_.length == 0
55
- search_path_clause_ = search_path_.map{ |part_| "--schema=#{::Shellwords.escape(part_)}" }.join(' ')
56
- command_ = "pg_dump -i -s -x -O -f #{::Shellwords.escape(filename_)} #{search_path_clause_} #{::Shellwords.escape(configuration['database'])}"
57
- raise 'Error dumping database' unless ::Kernel.system(command_)
58
- ::File.open(filename_, "a") { |f_| f_ << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
43
+ _search_path = search_path.dup
44
+ _search_path.delete('postgis')
45
+ _search_path = ['public'] if _search_path.length == 0
46
+ search_path_clause_ = _search_path.map{ |part_| "--schema=#{::Shellwords.escape(part_)}" }.join(' ')
47
+ command = "pg_dump -i -s -x -O -f #{::Shellwords.escape(filename)} #{search_path_clause_} #{::Shellwords.escape(configuration['database'])}"
48
+ raise 'Error dumping database' unless ::Kernel.system(command)
49
+ ::File.open(filename, "a") { |f_| f_ << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
59
50
  end
60
51
 
61
-
62
52
  private
63
53
 
64
-
65
54
  # Overridden to use su_username and su_password
66
-
67
55
  def establish_master_connection
68
56
  establish_connection(configuration.merge(
69
57
  'database' => 'postgres',
@@ -72,7 +60,6 @@ module ActiveRecord # :nodoc:
72
60
  'password' => su_password))
73
61
  end
74
62
 
75
-
76
63
  def establish_su_connection
77
64
  establish_connection(configuration.merge(
78
65
  'schema_search_path' => 'public',
@@ -80,7 +67,6 @@ module ActiveRecord # :nodoc:
80
67
  'password' => su_password))
81
68
  end
82
69
 
83
-
84
70
  def username
85
71
  @username ||= configuration['username']
86
72
  end
@@ -133,7 +119,6 @@ module ActiveRecord # :nodoc:
133
119
  end
134
120
  end
135
121
 
136
-
137
122
  def ensure_installation_configs
138
123
  if configuration['setup'] == 'default' && !configuration['script_dir'] && !configuration['postgis_extension']
139
124
  share_dir_ = `pg_config --sharedir`.strip rescue '/usr/share'
@@ -147,7 +132,6 @@ module ActiveRecord # :nodoc:
147
132
  end
148
133
  end
149
134
 
150
-
151
135
  def setup_gis_schemas
152
136
  auth_ = has_su? ? " AUTHORIZATION #{quoted_username}" : ''
153
137
  search_path.each do |schema_|
@@ -157,26 +141,23 @@ module ActiveRecord # :nodoc:
157
141
  end
158
142
  end
159
143
 
160
-
161
144
  def setup_gis_from_extension
162
- extension_names.each do |extname_|
163
- if extname_ == 'postgis_topology'
145
+ extension_names.each do |extname|
146
+ if extname == 'postgis_topology'
164
147
  raise ::ArgumentError, "'topology' must be in schema_search_path for postgis_topology" unless search_path.include?('topology')
165
- connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname_} SCHEMA topology")
148
+ connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname} SCHEMA topology")
166
149
  else
167
- connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname_} SCHEMA #{postgis_schema}")
150
+ connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname} SCHEMA #{postgis_schema}")
168
151
  end
169
152
  end
170
153
  end
171
154
 
172
-
173
155
  def setup_gis_from_script_dir
174
156
  connection.execute("SET search_path TO #{postgis_schema}")
175
157
  connection.execute(::File.read(::File.expand_path('postgis.sql', script_dir)))
176
158
  connection.execute(::File.read(::File.expand_path('spatial_ref_sys.sql', script_dir)))
177
159
  end
178
160
 
179
-
180
161
  def setup_gis_grant_privileges
181
162
  connection.execute("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA #{postgis_schema} TO #{quoted_username}")
182
163
  connection.execute("GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA #{postgis_schema} TO #{quoted_username}")
@@ -190,15 +171,10 @@ module ActiveRecord # :nodoc:
190
171
  connection.execute("ALTER TABLE #{postgis_schema}.spatial_ref_sys OWNER TO #{quoted_username}")
191
172
  end
192
173
 
193
-
194
174
  end
195
175
 
196
-
197
176
  ::ActiveRecord::Tasks::DatabaseTasks.register_task(/postgis/, PostGISDatabaseTasks)
198
177
 
199
-
200
178
  end
201
-
202
179
  end
203
-
204
180
  end
@@ -1,42 +1,37 @@
1
1
  module ActiveRecord # :nodoc:
2
-
3
2
  module ConnectionAdapters # :nodoc:
4
-
5
3
  module PostGISAdapter # :nodoc:
6
-
7
-
8
4
  class SpatialColumn < ConnectionAdapters::PostgreSQLColumn # :nodoc:
9
5
 
10
-
11
- def initialize(factory_settings_, table_name_, name_, default_, oid_type_, sql_type_=nil, null_=true, opts_=nil)
12
- @factory_settings = factory_settings_
13
- @table_name = table_name_
14
- @geographic = !!(sql_type_ =~ /geography/i)
15
- if opts_
6
+ def initialize(factory_settings, table_name, name, default, oid_type, sql_type=nil, null=true, opts=nil)
7
+ @factory_settings = factory_settings
8
+ @table_name = table_name
9
+ @geographic = !!(sql_type =~ /geography/i)
10
+ if opts
16
11
  # This case comes from an entry in the geometry_columns table
17
- @geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(opts_[:type]) || ::RGeo::Feature::Geometry
18
- @srid = opts_[:srid].to_i
19
- @has_z = !!opts_[:has_z]
20
- @has_m = !!opts_[:has_m]
12
+ @geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(opts[:type]) || ::RGeo::Feature::Geometry
13
+ @srid = opts[:srid].to_i
14
+ @has_z = !!opts[:has_z]
15
+ @has_m = !!opts[:has_m]
21
16
  elsif @geographic
22
17
  # Geographic type information is embedded in the SQL type
23
18
  @geometric_type = ::RGeo::Feature::Geometry
24
19
  @srid = 4326
25
20
  @has_z = @has_m = false
26
- if sql_type_ =~ /geography\((.*)\)$/i
27
- params_ = $1.split(',')
28
- if params_.size >= 2
29
- if params_.first =~ /([a-z]+[^zm])(z?)(m?)/i
21
+ if sql_type =~ /geography\((.*)\)$/i
22
+ params = $1.split(',')
23
+ if params.size >= 2
24
+ if params.first =~ /([a-z]+[^zm])(z?)(m?)/i
30
25
  @has_z = $2.length > 0
31
26
  @has_m = $3.length > 0
32
27
  @geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name($1)
33
28
  end
34
- if params_.last =~ /(\d+)/
29
+ if params.last =~ /(\d+)/
35
30
  @srid = $1.to_i
36
31
  end
37
32
  end
38
33
  end
39
- elsif sql_type_ =~ /geography|geometry|point|linestring|polygon/i
34
+ elsif sql_type =~ /geography|geometry|point|linestring|polygon/i
40
35
  # Just in case there is a geometry column with no geometry_columns entry.
41
36
  @geometric_type = ::RGeo::Feature::Geometry
42
37
  @srid = @has_z = @has_m = nil
@@ -44,7 +39,7 @@ module ActiveRecord # :nodoc:
44
39
  # Non-spatial column
45
40
  @geometric_type = @has_z = @has_m = @srid = nil
46
41
  end
47
- super(name_, default_, oid_type_, sql_type_, null_)
42
+ super(name, default, oid_type, sql_type, null)
48
43
  if spatial?
49
44
  if @srid
50
45
  @limit = {:srid => @srid, :type => @geometric_type.type_name.underscore}
@@ -57,7 +52,6 @@ module ActiveRecord # :nodoc:
57
52
  end
58
53
  end
59
54
 
60
-
61
55
  attr_reader :geographic
62
56
  attr_reader :srid
63
57
  attr_reader :geometric_type
@@ -68,61 +62,54 @@ module ActiveRecord # :nodoc:
68
62
  alias_method :has_z?, :has_z
69
63
  alias_method :has_m?, :has_m
70
64
 
71
-
72
65
  def spatial?
73
66
  type == :spatial || type == :geography
74
67
  end
75
68
 
76
-
77
69
  def has_spatial_constraints?
78
70
  !@srid.nil?
79
71
  end
80
72
 
81
-
82
73
  def klass
83
74
  spatial? ? ::RGeo::Feature::Geometry : super
84
75
  end
85
76
 
86
-
87
- def type_cast(value_)
77
+ def type_cast(value)
88
78
  if spatial?
89
- SpatialColumn.convert_to_geometry(value_, @factory_settings, @table_name, name,
79
+ SpatialColumn.convert_to_geometry(value, @factory_settings, @table_name, name,
90
80
  @geographic, @srid, @has_z, @has_m)
91
81
  else
92
82
  super
93
83
  end
94
84
  end
95
85
 
96
-
97
86
  private
98
87
 
99
-
100
- def simplified_type(sql_type_)
101
- sql_type_ =~ /geography|geometry|point|linestring|polygon/i ? :spatial : super
88
+ def simplified_type(sql_type)
89
+ sql_type =~ /geography|geometry|point|linestring|polygon/i ? :spatial : super
102
90
  end
103
91
 
104
-
105
- def self.convert_to_geometry(input_, factory_settings_, table_name_, column_, geographic_, srid_, has_z_, has_m_)
106
- if srid_
107
- constraints_ = {:geographic => geographic_, :has_z_coordinate => has_z_,
108
- :has_m_coordinate => has_m_, :srid => srid_}
92
+ def self.convert_to_geometry(input, factory_settings, table_name, column, geographic, srid, has_z, has_m)
93
+ if srid
94
+ constraints = {:geographic => geographic, :has_z_coordinate => has_z,
95
+ :has_m_coordinate => has_m, :srid => srid}
109
96
  else
110
- constraints_ = nil
97
+ constraints = nil
111
98
  end
112
- if ::RGeo::Feature::Geometry === input_
113
- factory_ = factory_settings_.get_column_factory(table_name_, column_, constraints_)
114
- ::RGeo::Feature.cast(input_, factory_) rescue nil
115
- elsif input_.respond_to?(:to_str)
116
- input_ = input_.to_str
117
- if input_.length == 0
99
+ if ::RGeo::Feature::Geometry === input
100
+ factory = factory_settings.get_column_factory(table_name, column, constraints)
101
+ ::RGeo::Feature.cast(input, factory) rescue nil
102
+ elsif input.respond_to?(:to_str)
103
+ input = input.to_str
104
+ if input.length == 0
118
105
  nil
119
106
  else
120
- factory_ = factory_settings_.get_column_factory(table_name_, column_, constraints_)
121
- marker_ = input_[0,1]
122
- if marker_ == "\x00" || marker_ == "\x01" || input_[0,4] =~ /[0-9a-fA-F]{4}/
123
- ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(input_) rescue nil
107
+ factory = factory_settings.get_column_factory(table_name, column, constraints)
108
+ marker = input[0,1]
109
+ if marker == "\x00" || marker == "\x01" || input[0,4] =~ /[0-9a-fA-F]{4}/
110
+ ::RGeo::WKRep::WKBParser.new(factory, :support_ewkb => true).parse(input) rescue nil
124
111
  else
125
- ::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(input_) rescue nil
112
+ ::RGeo::WKRep::WKTParser.new(factory, :support_ewkt => true).parse(input) rescue nil
126
113
  end
127
114
  end
128
115
  else
@@ -130,22 +117,19 @@ module ActiveRecord # :nodoc:
130
117
  end
131
118
  end
132
119
 
133
-
134
120
  end
135
121
 
136
-
137
122
  # Register spatial types with the postgres OID mechanism
138
123
  # so we can recognize custom columns coming from the database.
139
-
140
124
  class SpatialOID < PostgreSQLAdapter::OID::Type # :nodoc:
141
125
 
142
- def initialize(factory_generator_)
143
- @factory_generator = factory_generator_
126
+ def initialize(factory_generator)
127
+ @factory_generator = factory_generator
144
128
  end
145
129
 
146
- def type_cast(value_)
147
- return if value_.nil?
148
- ::RGeo::WKRep::WKBParser.new(@factory_generator, :support_ewkb => true).parse(value_) rescue nil
130
+ def type_cast(value)
131
+ return if value.nil?
132
+ ::RGeo::WKRep::WKBParser.new(@factory_generator, :support_ewkb => true).parse(value) rescue nil
149
133
  end
150
134
 
151
135
  end
@@ -153,31 +137,26 @@ module ActiveRecord # :nodoc:
153
137
  PostgreSQLAdapter::OID.register_type('geometry', SpatialOID.new(nil))
154
138
  PostgreSQLAdapter::OID.register_type('geography', SpatialOID.new(::RGeo::Geographic.method(:spherical_factory)))
155
139
 
156
-
157
140
  # This is a hack to ActiveRecord::ModelSchema. We have to "decorate" the decorate_columns
158
141
  # method to apply class-specific customizations to spatial type casting.
159
-
160
142
  module DecorateColumnsModification # :nodoc:
161
143
 
162
- def decorate_columns(columns_hash_)
163
- columns_hash_ = super(columns_hash_)
164
- return unless columns_hash_
144
+ def decorate_columns(columns_hash)
145
+ columns_hash = super(columns_hash)
146
+ return unless columns_hash
165
147
  canonical_columns_ = self.columns_hash
166
- columns_hash_.each do |name_, col_|
167
- if col_.is_a?(SpatialOID) && (canonical_ = canonical_columns_[name_]) && canonical_.spatial?
168
- columns_hash_[name_] = canonical_
148
+ columns_hash.each do |name, col|
149
+ if col.is_a?(SpatialOID) && (canonical = canonical_columns_[name]) && canonical.spatial?
150
+ columns_hash[name] = canonical
169
151
  end
170
152
  end
171
- columns_hash_
153
+ columns_hash
172
154
  end
173
155
 
174
156
  end
175
157
 
176
158
  ::ActiveRecord::Base.extend(DecorateColumnsModification)
177
159
 
178
-
179
160
  end
180
-
181
161
  end
182
-
183
162
  end
@@ -1,62 +1,63 @@
1
1
  module ActiveRecord # :nodoc:
2
-
3
2
  module ConnectionAdapters # :nodoc:
4
-
5
3
  module PostGISAdapter # :nodoc:
6
-
7
-
8
4
  class TableDefinition < ConnectionAdapters::PostgreSQLAdapter::TableDefinition # :nodoc:
9
5
 
10
- def initialize(types_, name_, temporary_, options_, as_, base_)
11
- @base = base_
12
- @spatial_columns_hash = {}
13
- super(types_, name_, temporary_, options_, as_)
6
+ if ActiveRecord::VERSION::STRING > '4.1'
7
+ def initialize(types, name, temporary, options, as, base)
8
+ @base = base
9
+ @spatial_columns_hash = {}
10
+ super(types, name, temporary, options, as)
11
+ end
12
+ else
13
+ def initialize(types, name, temporary, options, base)
14
+ @base = base
15
+ @spatial_columns_hash = {}
16
+ super(types, name, temporary, options)
17
+ end
14
18
  end
15
19
 
16
- def column(name_, type_, options_={})
17
- if (info_ = @base.spatial_column_constructor(type_.to_sym))
18
- type_ = options_[:type] || info_[:type] || type_
19
- if type_.to_s == 'geometry' &&
20
- (options_[:no_constraints] ||
21
- options_[:limit].is_a?(::Hash) && options_[:limit][:no_constraints])
22
- then
23
- options_.delete(:limit)
20
+ def column(name, type, options={})
21
+ if (info = @base.spatial_column_constructor(type.to_sym))
22
+ type = options[:type] || info[:type] || type
23
+ if type.to_s == 'geometry' && (options[:no_constraints] || options[:limit].is_a?(::Hash) && options[:limit][:no_constraints])
24
+ options.delete(:limit)
24
25
  else
25
- options_[:type] = type_
26
- type_ = :spatial
26
+ options[:type] = type
27
+ type = :spatial
27
28
  end
28
29
  end
29
- if type_ == :spatial
30
- if (limit_ = options_.delete(:limit))
31
- options_.merge!(limit_) if limit_.is_a?(::Hash)
30
+ if type == :spatial
31
+ if (limit = options.delete(:limit))
32
+ options.merge!(limit) if limit.is_a?(::Hash)
32
33
  end
33
- if options_[:geographic]
34
- type_ = :geography
35
- spatial_type_ = (options_[:type] || 'geometry').to_s.upcase.gsub('_', '')
36
- spatial_type_ << 'Z' if options_[:has_z]
37
- spatial_type_ << 'M' if options_[:has_m]
38
- options_[:limit] = "#{spatial_type_},#{options_[:srid] || 4326}"
34
+ if options[:geographic]
35
+ type = :geography
36
+ spatial_type = (options[:type] || 'geometry').to_s.upcase.gsub('_', '')
37
+ spatial_type << 'Z' if options[:has_z]
38
+ spatial_type << 'M' if options[:has_m]
39
+ options[:limit] = "#{spatial_type},#{options[:srid] || 4326}"
39
40
  end
40
- name_ = name_.to_s
41
- if primary_key_column_name == name_
42
- raise ArgumentError, "you can't redefine the primary key column '#{name_}'. To define a custom primary key, pass { id: false } to create_table."
41
+ name = name.to_s
42
+ if primary_key_column_name == name
43
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
43
44
  end
44
- col_ = new_column_definition(name_, type_, options_)
45
- col_.set_spatial_type(options_[:type])
46
- col_.set_geographic(options_[:geographic])
47
- col_.set_srid(options_[:srid])
48
- col_.set_has_z(options_[:has_z])
49
- col_.set_has_m(options_[:has_m])
50
- (col_.geographic? ? @columns_hash : @spatial_columns_hash)[name_] = col_
45
+ column = new_column_definition(name, type, options)
46
+ column.set_spatial_type(options[:type])
47
+ column.set_geographic(options[:geographic])
48
+ column.set_srid(options[:srid])
49
+ column.set_has_z(options[:has_z])
50
+ column.set_has_m(options[:has_m])
51
+ (column.geographic? ? @columns_hash : @spatial_columns_hash)[name] = column
51
52
  else
52
- super(name_, type_, options_)
53
+ super(name, type, options)
53
54
  end
54
55
  self
55
56
  end
56
57
 
57
- def create_column_definition(name_, type_)
58
- if type_ == :spatial || type_ == :geography
59
- PostGISAdapter::ColumnDefinition.new(name_, type_)
58
+ def create_column_definition(name, type)
59
+ if type == :spatial || type == :geography
60
+ PostGISAdapter::ColumnDefinition.new(name, type)
60
61
  else
61
62
  super
62
63
  end
@@ -68,7 +69,6 @@ module ActiveRecord # :nodoc:
68
69
 
69
70
  end
70
71
 
71
-
72
72
  class ColumnDefinition < ConnectionAdapters::ColumnDefinition # :nodoc:
73
73
 
74
74
  def spatial_type
@@ -95,31 +95,28 @@ module ActiveRecord # :nodoc:
95
95
  @has_m
96
96
  end
97
97
 
98
- def set_geographic(value_)
99
- @geographic = value_ ? true : false
98
+ def set_geographic(value)
99
+ @geographic = !!value
100
100
  end
101
101
 
102
- def set_spatial_type(value_)
103
- @spatial_type = value_.to_s
102
+ def set_spatial_type(value)
103
+ @spatial_type = value.to_s
104
104
  end
105
105
 
106
- def set_srid(value_)
107
- @srid = value_
106
+ def set_srid(value)
107
+ @srid = value
108
108
  end
109
109
 
110
- def set_has_z(value_)
111
- @has_z = value_ ? true : false
110
+ def set_has_z(value)
111
+ @has_z = !!value
112
112
  end
113
113
 
114
- def set_has_m(value_)
115
- @has_m = value_ ? true : false
114
+ def set_has_m(value)
115
+ @has_m = !!value
116
116
  end
117
117
 
118
118
  end
119
119
 
120
-
121
120
  end
122
-
123
121
  end
124
-
125
122
  end