activerecord-postgis-adapter 1.0.0 → 1.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.
@@ -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