rgeo-activerecord 0.2.4 → 0.3.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.
data/History.rdoc CHANGED
@@ -1,3 +1,15 @@
1
+ === 0.3.0 / 2011-01-26
2
+
3
+ * Experimental support for complex spatial queries. (Requires Arel 2.1, which is expected to be released with Rails 3.1.) Currently, only a low-level Arel-based interface is supported.
4
+ * Better support for geography types in PostGIS.
5
+ * Adapters can now define additional column constructors.
6
+ * Support for spatial column constructors on change_table.
7
+ * Fixed column type inference for some cases where the column included Z and/or M.
8
+ * IS NULL predicates now work properly with spatial types.
9
+ * Preferred attribute type is now :spatial rather than :geometry.
10
+ * The gem version is now accessible via an api.
11
+ * Some code reorganization.
12
+
1
13
  === 0.2.4 / 2011-01-13
2
14
 
3
15
  * Fixed a problem that caused a hang during rake db:rollback, as well as probably certain other functions that use ActiveRecord::Base directly rather than a subclass. (Reported by Alexander Graefe.)
data/README.rdoc CHANGED
@@ -31,8 +31,8 @@ Generally, \ActiveRecord adapters which depend on this module should be
31
31
  installed as gems, and they will install this module automatically as
32
32
  a dependency. However, you can also install it manually as a gem:
33
33
 
34
- gem install rgeo
35
- gem install rgeo-activerecord
34
+ gem install rgeo
35
+ gem install rgeo-activerecord
36
36
 
37
37
  See the README for the "rgeo" gem, a required dependency, for further
38
38
  installation information.
data/Version CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
@@ -60,7 +60,23 @@ module RGeo
60
60
  end
61
61
 
62
62
 
63
+ # The rgeo-activerecord gem installs several patches to Arel to provide
64
+ # support for spatial queries.
65
+
66
+ module Arel
67
+ end
68
+
69
+
70
+ # The rgeo-activerecord gem installs several patches to ActiveRecord
71
+ # to support services needed by spatial adapters.
72
+
73
+ module ActiveRecord
74
+ end
75
+
76
+
63
77
  # Implementation files
64
- require 'rgeo/active_record/common.rb'
65
- require 'rgeo/active_record/arel_modifications'
66
- require 'rgeo/active_record/ar_modifications'
78
+ require 'rgeo/active_record/version.rb'
79
+ require 'rgeo/active_record/spatial_expressions.rb'
80
+ require 'rgeo/active_record/arel_spatial_queries'
81
+ require 'rgeo/active_record/common_adapter_elements.rb'
82
+ require 'rgeo/active_record/ar_factory_settings'
@@ -37,10 +37,6 @@
37
37
  require 'active_record'
38
38
 
39
39
 
40
- # RGeo extensions to ActiveRecord are installed when one of the spatial
41
- # connection adapters is needed. These modifications require ActiveRecord
42
- # 3.0.3 or later.
43
-
44
40
  module ActiveRecord
45
41
 
46
42
 
@@ -64,9 +60,6 @@ module ActiveRecord
64
60
  class Base
65
61
 
66
62
 
67
- self.attribute_types_cached_by_default << :geometry
68
-
69
-
70
63
  class_attribute :rgeo_factory_generator, :instance_writer => false
71
64
  self.rgeo_factory_generator = nil
72
65
 
@@ -110,37 +103,6 @@ module ActiveRecord
110
103
  end
111
104
 
112
105
 
113
- # :stopdoc:
114
- alias_method :columns_without_rgeo_modification, :columns
115
- def columns
116
- unless defined?(@columns) && @columns
117
- columns_without_rgeo_modification.each do |column_|
118
- column_.set_ar_class(self) if column_.respond_to?(:set_ar_class)
119
- end
120
- end
121
- @columns
122
- end
123
- # :startdoc:
124
-
125
- end
126
-
127
- end
128
-
129
-
130
- module ConnectionAdapters # :nodoc:
131
-
132
- class TableDefinition # :nodoc:
133
-
134
- ::RGeo::ActiveRecord::GEOMETRY_TYPES.each do |type_|
135
- method_ = <<-END_METHOD
136
- def #{type_}(*args_)
137
- opts_ = args_.extract_options!
138
- args_.each{ |name_| column(name_, '#{type_}', opts_) }
139
- end
140
- END_METHOD
141
- class_eval(method_, __FILE__, __LINE__-5)
142
- end
143
-
144
106
  end
145
107
 
146
108
  end
@@ -0,0 +1,250 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Mysqlgeo adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # A set of common Arel visitor hacks for spatial ToSql visitors.
43
+ # Generally, a spatial ActiveRecord adapter should provide a custom
44
+ # ToSql Arel visitor that includes and customizes this module.
45
+ # See the existing spatial adapters (i.e. postgis, spatialite,
46
+ # mysqlspatial, and mysql2spatial) for usage examples.
47
+
48
+ module SpatialToSql
49
+
50
+
51
+ # Map a standard OGC SQL function name to the actual name used by
52
+ # a particular database. This method should take a name and
53
+ # return either the changed name or the original name.
54
+
55
+ def st_func(standard_name_)
56
+ standard_name_
57
+ end
58
+
59
+
60
+ # Visit the SpatialNamedFunction node. This operates similarly to
61
+ # the standard NamedFunction node, but it performs function name
62
+ # mapping for the database, and it also uses the type information
63
+ # in the node to determine when to cast string arguments to WKT,
64
+
65
+ def visit_RGeo_ActiveRecord_SpatialNamedFunction(node_)
66
+ name_ = st_func(node_.name)
67
+ exprs_ = []
68
+ node_.expressions.each_with_index do |expr_, index_|
69
+ exprs_ << (node_.spatial_argument?(index_) ? visit_in_spatial_context(expr_) : visit(expr_))
70
+ end
71
+ "#{name_}(#{node_.distinct ? 'DISTINCT ' : ''}#{exprs_.join(', ')})#{node_.alias ? " AS #{visit node_.alias}" : ''}"
72
+ end
73
+
74
+
75
+ # Returns a true value if the given node is of spatial type-- that
76
+ # is, if it is a spatial literal or a reference to a spatial
77
+ # attribute.
78
+
79
+ def node_has_spatial_type?(node_)
80
+ case node_
81
+ when ::Arel::Attribute
82
+ @connection.instance_variable_set(:@_getting_columns, true)
83
+ begin
84
+ col_ = @engine.columns_hash[node_.name.to_s] unless @engine == ::ActiveRecord::Base
85
+ col_ && col_.respond_to?(:spatial?) && col_.spatial? ? true : false
86
+ ensure
87
+ @connection.instance_variable_set(:@_getting_columns, false)
88
+ end
89
+ when ::RGeo::ActiveRecord::SpatialNamedFunction
90
+ node_.spatial_result?
91
+ when ::RGeo::ActiveRecord::SpatialConstantNode, ::RGeo::Feature::Instance
92
+ true
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+
99
+ # Generates SQL for a spatial node.
100
+ # The node must be a string (in which case it is treated as WKT),
101
+ # an RGeo feature, or a spatial attribute.
102
+
103
+ def visit_in_spatial_context(node_)
104
+ case node_
105
+ when ::String
106
+ "#{st_func('ST_WKTToSQL')}(#{visit_String(node_)})"
107
+ when ::RGeo::Feature::Instance
108
+ visit_RGeo_Feature_Instance(node_)
109
+ else
110
+ visit(node_)
111
+ end
112
+ end
113
+
114
+
115
+ def _check_equality_for_rgeo(node_, negate_) # :nodoc:
116
+ left_ = node_.left
117
+ right_ = node_.right
118
+ if !@connection.instance_variable_get(:@_getting_columns) && !right_.nil? && (node_has_spatial_type?(left_) || node_has_spatial_type?(right_))
119
+ "#{negate_ ? 'NOT ' : ''}#{st_func('ST_Equals')}(#{visit_in_spatial_context(left_)}, #{visit_in_spatial_context(right_)})"
120
+ else
121
+ false
122
+ end
123
+ end
124
+
125
+
126
+ # Override equality nodes to use the ST_Equals function if at least
127
+ # one of the operands is a spatial node.
128
+
129
+ def visit_Arel_Nodes_Equality(node_)
130
+ _check_equality_for_rgeo(node_, false) || super
131
+ end
132
+
133
+
134
+ # Override equality nodes to use the ST_Equals function if at least
135
+ # one of the operands is a spatial node.
136
+
137
+ def visit_Arel_Nodes_NotEqual(node_)
138
+ _check_equality_for_rgeo(node_, true) || super
139
+ end
140
+
141
+ end
142
+
143
+
144
+ end
145
+
146
+ end
147
+
148
+
149
+ # :stopdoc:
150
+
151
+
152
+ # This node wraps an RGeo feature and gives it spatial expression
153
+ # constructors.
154
+
155
+ module RGeo
156
+ module ActiveRecord
157
+
158
+ class SpatialConstantNode
159
+
160
+ include ::RGeo::ActiveRecord::SpatialExpressions
161
+
162
+ def initialize(delegate_)
163
+ @delegate = delegate_
164
+ end
165
+
166
+ attr_reader :delegate
167
+
168
+ end
169
+
170
+ end
171
+ end
172
+
173
+
174
+ # Make sure the standard Arel visitors can handle RGeo feature objects
175
+ # by default.
176
+
177
+ module Arel
178
+ module Visitors
179
+ class Visitor
180
+ def visit_RGeo_ActiveRecord_SpatialConstantNode(node_)
181
+ if respond_to?(:visit_in_spatial_context)
182
+ visit_in_spatial_context(node_.delegate)
183
+ else
184
+ visit(node_.delegate)
185
+ end
186
+ end
187
+ end
188
+ class Dot
189
+ alias :visit_RGeo_Feature_Instance :visit_String
190
+ end
191
+ class DepthFirst
192
+ alias :visit_RGeo_Feature_Instance :terminal
193
+ end
194
+ class ToSql
195
+ alias :visit_RGeo_Feature_Instance :visit_String
196
+ end
197
+ end
198
+ end
199
+
200
+
201
+ # Add tools to build spatial structures in the AST.
202
+ # This stuff requires Arel 2.1 or later.
203
+
204
+ if defined?(::Arel::Nodes::NamedFunction)
205
+
206
+ # Allow chaining of predications from named functions
207
+ # (Hack because Arel doesn't do this but should.)
208
+ ::Arel::Nodes::NamedFunction.class_eval do
209
+ include ::Arel::Predications unless include?(::Arel::Predications)
210
+ end
211
+
212
+ # Allow chaining of spatial expressions from attributes
213
+ ::Arel::Attribute.class_eval do
214
+ include ::RGeo::ActiveRecord::SpatialExpressions
215
+ end
216
+
217
+
218
+ # A NamedFunction subclass that keeps track of the spatial-ness of
219
+ # the arguments and return values, so that it can provide context to
220
+ # visitors that want to interpret syntax differently when dealing with
221
+ # spatial elements.
222
+
223
+ class ::RGeo::ActiveRecord::SpatialNamedFunction < ::Arel::Nodes::NamedFunction
224
+
225
+ include ::RGeo::ActiveRecord::SpatialExpressions
226
+
227
+ def initialize(name_, expr_, spatial_flags_=[], aliaz_=nil)
228
+ super(name_, expr_, aliaz_)
229
+ @spatial_flags = spatial_flags_
230
+ end
231
+
232
+ def spatial_result?
233
+ @spatial_flags.first
234
+ end
235
+
236
+ def spatial_argument?(index_)
237
+ @spatial_flags[index_+1]
238
+ end
239
+
240
+ end
241
+
242
+ else
243
+
244
+ # A dummy SpatialNamedFunction for pre-2.1 versions of Arel.
245
+ class ::RGeo::ActiveRecord::SpatialNamedFunction; end
246
+
247
+ end
248
+
249
+
250
+ # :startdoc:
@@ -0,0 +1,226 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Common tools for spatial adapters for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # Some default column constructors specifications for most spatial
43
+ # databases. Individual adapters may add to or override this list.
44
+
45
+ DEFAULT_SPATIAL_COLUMN_CONSTRUCTORS = {
46
+ :spatial => {:type => 'geometry'},
47
+ :geometry => {},
48
+ :point => {},
49
+ :line_string => {},
50
+ :polygon => {},
51
+ :geometry_collection => {},
52
+ :multi_line_string => {},
53
+ :multi_point => {},
54
+ :multi_polygon => {},
55
+ }.freeze
56
+
57
+
58
+ # The default factory generator for ActiveRecord::Base.
59
+
60
+ DEFAULT_FACTORY_GENERATOR = ::Proc.new do |config_|
61
+ if config_.delete(:geographic)
62
+ ::RGeo::Geographic.spherical_factory(config_)
63
+ else
64
+ ::RGeo::Cartesian.preferred_factory(config_)
65
+ end
66
+ end
67
+
68
+
69
+ # Index definition struct with a spatial flag field.
70
+
71
+ class SpatialIndexDefinition < ::Struct.new(:table, :name, :unique, :columns, :lengths, :spatial)
72
+ end
73
+
74
+
75
+ # Returns a feature type module given a string type.
76
+
77
+ def self.geometric_type_from_name(name_)
78
+ case name_.to_s
79
+ when /^geometry/i then ::RGeo::Feature::Geometry
80
+ when /^point/i then ::RGeo::Feature::Point
81
+ when /^linestring/i then ::RGeo::Feature::LineString
82
+ when /^polygon/i then ::RGeo::Feature::Polygon
83
+ when /^geometrycollection/i then ::RGeo::Feature::GeometryCollection
84
+ when /^multipoint/i then ::RGeo::Feature::MultiPoint
85
+ when /^multilinestring/i then ::RGeo::Feature::MultiLineString
86
+ when /^multipolygon/i then ::RGeo::Feature::MultiPolygon
87
+ else nil
88
+ end
89
+ end
90
+
91
+
92
+ end
93
+
94
+ end
95
+
96
+
97
+ # :stopdoc:
98
+
99
+
100
+ # Make sure a few things are autoloaded before we modify them.
101
+ ::Arel::Attributes
102
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter
103
+ ::ActiveRecord::ConnectionAdapters::TableDefinition
104
+ ::ActiveRecord::ConnectionAdapters::Table
105
+ ::ActiveRecord::Base
106
+ ::ActiveRecord::SchemaDumper
107
+
108
+
109
+ # Hack Arel Attributes dispatcher to recognize geometry columns.
110
+ # This is deprecated but necessary to support legacy Arel versions.
111
+
112
+ module Arel
113
+ module Attributes
114
+ class << self
115
+ if method_defined?(:for)
116
+ alias_method :for_without_rgeo_modification, :for
117
+ def for(column_)
118
+ column_.type == :spatial ? Attribute : for_without_rgeo_modification(column_)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+
126
+ # Provide methods for each geometric subtype during table definitions.
127
+
128
+ module ActiveRecord
129
+ module ConnectionAdapters
130
+ class TableDefinition
131
+
132
+ alias_method :method_missing_without_rgeo_modification, :method_missing
133
+ def method_missing(method_name_, *args_, &block_)
134
+ if @base.respond_to?(:spatial_column_constructor) && (info_ = @base.spatial_column_constructor(method_name_))
135
+ type_ = (info_.delete(:type) || method_name_).to_s
136
+ opts_ = args_.extract_options!.merge(info_)
137
+ args_.each do |name_|
138
+ column(name_, type_, opts_)
139
+ end
140
+ else
141
+ method_missing_without_rgeo_modification(method_name_, *args_, &block_)
142
+ end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+
149
+
150
+ # Provide methods for each geometric subtype during table changes.
151
+
152
+ module ActiveRecord
153
+ module ConnectionAdapters
154
+ class Table
155
+
156
+ alias_method :method_missing_without_rgeo_modification, :method_missing
157
+ def method_missing(method_name_, *args_, &block_)
158
+ if @base.respond_to?(:spatial_column_constructor) && (info_ = @base.spatial_column_constructor(method_name_))
159
+ type_ = (info_.delete(:type) || method_name_).to_s
160
+ opts_ = args_.extract_options!.merge(info_)
161
+ args_.each do |name_|
162
+ @base.add_column(@table_name, name_, type_, opts_)
163
+ end
164
+ else
165
+ method_missing_without_rgeo_modification(method_name_, *args_, &block_)
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+ end
172
+
173
+
174
+ # When creating column objects, cause the enclosing ActiveRecord class
175
+ # to be set on any column that recognizes it. This is commonly used by
176
+ # spatial column subclasses.
177
+
178
+ module ActiveRecord
179
+ class Base
180
+ class << self
181
+ alias_method :columns_without_rgeo_modification, :columns
182
+ def columns
183
+ unless defined?(@columns) && @columns
184
+ columns_without_rgeo_modification.each do |column_|
185
+ column_.set_ar_class(self) if column_.respond_to?(:set_ar_class)
186
+ end
187
+ end
188
+ @columns
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ # Hack schema dumper to output spatial index flag
196
+
197
+ module ActiveRecord
198
+ class SchemaDumper
199
+ private
200
+ def indexes(table_, stream_)
201
+ if (indexes_ = @connection.indexes(table_)).any?
202
+ add_index_statements_ = indexes_.map do |index_|
203
+ statement_parts_ = [ ('add_index ' + index_.table.inspect) ]
204
+ statement_parts_ << index_.columns.inspect
205
+ statement_parts_ << (':name => ' + index_.name.inspect)
206
+ statement_parts_ << ':unique => true' if index_.unique
207
+ statement_parts_ << ':spatial => true' if index_.respond_to?(:spatial) && index_.spatial
208
+ index_lengths_ = index_.lengths.compact if index_.lengths.is_a?(::Array)
209
+ statement_parts_ << (':length => ' + ::Hash[*index_.columns.zip(index_.lengths).flatten].inspect) if index_lengths_.present?
210
+ ' ' + statement_parts_.join(', ')
211
+ end
212
+ stream_.puts add_index_statements_.sort.join("\n")
213
+ stream_.puts
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+
220
+ # Tell ActiveRecord to cache spatial attribute values so they don't get
221
+ # re-parsed on every access.
222
+
223
+ ::ActiveRecord::Base.attribute_types_cached_by_default << :spatial
224
+
225
+
226
+ # :startdoc:
@@ -0,0 +1,314 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Common tools for spatial adapters for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # Returns true if spatial expressions (i.e. the methods in the
43
+ # SpatialExpressions module) are supported. Generally, this is true
44
+ # if Arel is at version 2.1 or later.
45
+
46
+ def self.spatial_expressions_supported?
47
+ defined?(::Arel::Nodes::NamedFunction)
48
+ end
49
+
50
+
51
+ # A set of spatial expression builders.
52
+ # These methods can be chained off other spatial expressions to form
53
+ # complex expressions.
54
+ #
55
+ # These functions require Arel 2.1 or later.
56
+
57
+ module SpatialExpressions
58
+
59
+
60
+ #--
61
+ # Generic functions
62
+ #++
63
+
64
+ def st_function(function_, *args_)
65
+ spatial_info_ = args_.last.is_a?(::Array) ? args_.pop : []
66
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new(function_, [self] + args_, spatial_info_)
67
+ end
68
+
69
+
70
+ #--
71
+ # Geometry functions
72
+ #++
73
+
74
+ def st_dimension
75
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Dimension', [self], [false, true])
76
+ end
77
+
78
+ def st_geometrytype
79
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_GeometryType', [self], [false, true])
80
+ end
81
+
82
+ def st_astext
83
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_AsText', [self], [false, true])
84
+ end
85
+
86
+ def st_asbinary
87
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_AsBinary', [self], [false, true])
88
+ end
89
+
90
+ def st_srid
91
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_SRID', [self], [false, true])
92
+ end
93
+
94
+ def st_isempty
95
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsEmpty', [self], [false, true])
96
+ end
97
+
98
+ def st_issimple
99
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsSimple', [self], [false, true])
100
+ end
101
+
102
+ def st_boundary
103
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Boundary', [self], [true, true])
104
+ end
105
+
106
+ def st_envelope
107
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Envelope', [self], [true, true])
108
+ end
109
+
110
+ def st_equals(rhs_)
111
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Equals', [self, rhs_], [false, true, true])
112
+ end
113
+
114
+ def st_disjoint(rhs_)
115
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Disjoint', [self, rhs_], [false, true, true])
116
+ end
117
+
118
+ def st_intersects(rhs_)
119
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Intersects', [self, rhs_], [false, true, true])
120
+ end
121
+
122
+ def st_touches(rhs_)
123
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Touches', [self, rhs_], [false, true, true])
124
+ end
125
+
126
+ def st_crosses(rhs_)
127
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Crosses', [self, rhs_], [false, true, true])
128
+ end
129
+
130
+ def st_within(rhs_)
131
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Within', [self, rhs_], [false, true, true])
132
+ end
133
+
134
+ def st_contains(rhs_)
135
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Contains', [self, rhs_], [false, true, true])
136
+ end
137
+
138
+ def st_overlaps(rhs_)
139
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Overlaps', [self, rhs_], [false, true, true])
140
+ end
141
+
142
+ def st_relate(rhs_, matrix_=nil)
143
+ args_ = [self, rhs_]
144
+ args_ << matrix.to_s if matrix_
145
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Relate', args_, [false, true, true, false])
146
+ end
147
+
148
+ def st_distance(rhs_, units_=nil)
149
+ args_ = [self, rhs_]
150
+ args_ << units.to_s if units_
151
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Distance', args_, [false, true, true, false])
152
+ end
153
+
154
+ def st_intersection(rhs_)
155
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Intersection', [self, rhs_], [true, true, true])
156
+ end
157
+
158
+ def st_difference(rhs_)
159
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Difference', [self, rhs_], [true, true, true])
160
+ end
161
+
162
+ def st_union(rhs_)
163
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Union', [self, rhs_], [true, true, true])
164
+ end
165
+
166
+ def st_symdifference(rhs_)
167
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_SymDifference', [self, rhs_], [true, true, true])
168
+ end
169
+
170
+ def st_buffer(distance_, units_=nil)
171
+ args_ = [self, distance_.to_f]
172
+ args_ << units.to_s if units_
173
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Buffer', args_, [true, true, false])
174
+ end
175
+
176
+ def st_convexhull
177
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_ConvexHull', [self], [true, true])
178
+ end
179
+
180
+
181
+ #--
182
+ # Point functions
183
+ #++
184
+
185
+ def st_x
186
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_X', [self], [false, true])
187
+ end
188
+
189
+ def st_y
190
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Y', [self], [false, true])
191
+ end
192
+
193
+ def st_z
194
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Z', [self], [false, true])
195
+ end
196
+
197
+ def st_m
198
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_M', [self], [false, true])
199
+ end
200
+
201
+
202
+ #--
203
+ # Curve functions
204
+ #++
205
+
206
+ def st_startpoint
207
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_StartPoint', [self], [true, true])
208
+ end
209
+
210
+ def st_endpoint
211
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_EndPoint', [self], [true, true])
212
+ end
213
+
214
+ def st_isclosed
215
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsClosed', [self], [false, true])
216
+ end
217
+
218
+ def st_isring
219
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsRing', [self], [false, true])
220
+ end
221
+
222
+ def st_length(units_=nil)
223
+ args_ = [self]
224
+ args_ << units.to_s if units_
225
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Length', args_, [false, true, false])
226
+ end
227
+
228
+
229
+ #--
230
+ # LineString functions
231
+ #++
232
+
233
+ def st_numpoints
234
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumPoints', [self], [false, true])
235
+ end
236
+
237
+ def st_pointn(n_)
238
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_PointN', [self, n_.to_i], [true, true, false])
239
+ end
240
+
241
+
242
+ #--
243
+ # Surface functions
244
+ #++
245
+
246
+ def st_area(units_=nil)
247
+ args_ = [self]
248
+ args_ << units.to_s if units_
249
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_StartPoint', args_, [false, true, false])
250
+ end
251
+
252
+ def st_centroid
253
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Centroid', [self], [true, true])
254
+ end
255
+
256
+ def st_pointonsurface
257
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_PointOnSurface', [self], [true, true])
258
+ end
259
+
260
+
261
+ #--
262
+ # Polygon functions
263
+ #++
264
+
265
+ def st_exteriorring
266
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_ExteriorRing', [self], [true, true])
267
+ end
268
+
269
+ def st_numinteriorrings
270
+ # Note: the name difference is intentional. The standard
271
+ # names this function incorrectly.
272
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumInteriorRing', [self], [false, true])
273
+ end
274
+
275
+ def st_interiorringn(n_)
276
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_InteriorRingN', [self, n_.to_i], [true, true, false])
277
+ end
278
+
279
+
280
+ #--
281
+ # GeometryCollection functions
282
+ #++
283
+
284
+ def st_numgeometries
285
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumGeometries', [self], [false, true])
286
+ end
287
+
288
+ def st_geometryn(n_)
289
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_GeometryN', [self, n_.to_i], [true, true, false])
290
+ end
291
+
292
+
293
+ end
294
+
295
+
296
+ end
297
+
298
+ end
299
+
300
+
301
+ module Arel
302
+
303
+ # Create a spatial constant node.
304
+ # This node wraps a spatial value (such as an RGeo feature or a text
305
+ # string in WKT format). It supports chaining with the functions
306
+ # defined by RGeo::ActiveRecord::SpatialExpressions.
307
+ #
308
+ # Requires Arel 2.1 or later.
309
+
310
+ def self.spatial(arg_)
311
+ ::RGeo::ActiveRecord::SpatialConstantNode.new(arg_)
312
+ end
313
+
314
+ end
@@ -53,10 +53,15 @@ module RGeo
53
53
  end
54
54
 
55
55
  def call(task_)
56
- config_ = ::ActiveRecord::Base.configurations[@env || ::Rails.env || 'development']
57
- if @pattern === config_['adapter']
58
- task_.actions.delete_if{ |a_| a_ != self }
59
- @proc.call(config_)
56
+ env_ = @env || ::Rails.env || 'development'
57
+ config_ = ::ActiveRecord::Base.configurations[env_]
58
+ if config_
59
+ if @pattern === config_['adapter']
60
+ task_.actions.delete_if{ |a_| a_ != self }
61
+ @proc.call(config_)
62
+ end
63
+ else
64
+ puts "WARNING: Could not find environment #{env_.inspect} in your database.yml"
60
65
  end
61
66
  end
62
67
 
@@ -1,6 +1,6 @@
1
1
  # -----------------------------------------------------------------------------
2
2
  #
3
- # Mysqlgeo adapter for ActiveRecord
3
+ # Version of rgeo-activerecord
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
6
  # Copyright 2010 Daniel Azuma
@@ -34,44 +34,22 @@
34
34
  ;
35
35
 
36
36
 
37
- require 'arel'
37
+ begin
38
+ require 'versionomy'
39
+ rescue ::LoadError
40
+ end
38
41
 
39
42
 
40
- # The rgeo-activerecord gem installs several minor hacks into Arel to
41
- # support geometry values in the AST.
42
- module Arel
43
-
44
- # Hack Attributes dispatcher to recognize geometry columns.
45
- # This is deprecated but necessary to support legacy Arel versions.
46
- module Attributes # :nodoc:
47
- class << self
48
- if method_defined?(:for)
49
- alias_method :for_without_geometry, :for
50
- def for(column_)
51
- column_.type == :geometry ? Attribute : for_without_geometry(column_)
52
- end
53
- end
54
- end
55
- end
43
+ module RGeo
56
44
 
57
- # Visitors are modified to handle RGeo::Feature::Instance objects in
58
- # the AST.
59
- module Visitors
60
-
61
- # RGeo adds visit_RGeo_Feature_Instance to the Dot visitor.
62
- class Dot
63
- alias :visit_RGeo_Feature_Instance :visit_String
64
- end
45
+ module ActiveRecord
65
46
 
66
- # RGeo adds visit_RGeo_Feature_Instance to the DepthFirst visitor.
67
- class DepthFirst
68
- alias :visit_RGeo_Feature_Instance :terminal
69
- end
47
+ # Current version of RGeo::ActiveRecord as a frozen string
48
+ VERSION_STRING = ::File.read(::File.dirname(__FILE__)+'/../../../Version').strip.freeze
70
49
 
71
- # RGeo adds visit_RGeo_Feature_Instance to the ToSql visitor.
72
- class ToSql
73
- alias :visit_RGeo_Feature_Instance :visit_String
74
- end
50
+ # Current version of RGeo::ActiveRecord as a Versionomy object, if the
51
+ # Versionomy gem is available; otherwise equal to VERSION_STRING.
52
+ VERSION = defined?(::Versionomy) ? ::Versionomy.parse(VERSION_STRING) : VERSION_STRING
75
53
 
76
54
  end
77
55
 
data/test/tc_basic.rb CHANGED
@@ -45,6 +45,11 @@ module RGeo
45
45
  class TestBasic < ::Test::Unit::TestCase # :nodoc:
46
46
 
47
47
 
48
+ def test_has_version
49
+ assert_not_nil(::RGeo::ActiveRecord::VERSION)
50
+ end
51
+
52
+
48
53
  def test_default_factory_generator
49
54
  ::ActiveRecord::Base.rgeo_factory_generator = nil
50
55
  factory_ = ::ActiveRecord::Base.rgeo_factory_for_column(:hello).call(:has_z_coordinate => true, :srid => 4326)
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 4
9
- version: 0.2.4
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Daniel Azuma
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-13 00:00:00 -08:00
17
+ date: 2011-01-26 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -73,10 +73,12 @@ extra_rdoc_files:
73
73
  - README.rdoc
74
74
  files:
75
75
  - lib/rgeo/active_record/adapter_test_helper.rb
76
- - lib/rgeo/active_record/ar_modifications.rb
77
- - lib/rgeo/active_record/arel_modifications.rb
78
- - lib/rgeo/active_record/common.rb
76
+ - lib/rgeo/active_record/ar_factory_settings.rb
77
+ - lib/rgeo/active_record/arel_spatial_queries.rb
78
+ - lib/rgeo/active_record/common_adapter_elements.rb
79
+ - lib/rgeo/active_record/spatial_expressions.rb
79
80
  - lib/rgeo/active_record/task_hacker.rb
81
+ - lib/rgeo/active_record/version.rb
80
82
  - lib/rgeo/active_record.rb
81
83
  - History.rdoc
82
84
  - README.rdoc
@@ -1,148 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- #
3
- # Common tools for spatial adapters for ActiveRecord
4
- #
5
- # -----------------------------------------------------------------------------
6
- # Copyright 2010 Daniel Azuma
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions are met:
12
- #
13
- # * Redistributions of source code must retain the above copyright notice,
14
- # this list of conditions and the following disclaimer.
15
- # * Redistributions in binary form must reproduce the above copyright notice,
16
- # this list of conditions and the following disclaimer in the documentation
17
- # and/or other materials provided with the distribution.
18
- # * Neither the name of the copyright holder, nor the names of any other
19
- # contributors to this software, may be used to endorse or promote products
20
- # derived from this software without specific prior written permission.
21
- #
22
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
- # POSSIBILITY OF SUCH DAMAGE.
33
- # -----------------------------------------------------------------------------
34
- ;
35
-
36
-
37
- module RGeo
38
-
39
- module ActiveRecord
40
-
41
-
42
- # Additional column types for geometries.
43
- GEOMETRY_TYPES = [:geometry, :point, :line_string, :polygon, :geometry_collection, :multi_line_string, :multi_point, :multi_polygon].freeze
44
-
45
- # The default factory generator for ActiveRecord::Base.
46
- DEFAULT_FACTORY_GENERATOR = ::Proc.new do |config_|
47
- if config_.delete(:geographic)
48
- ::RGeo::Geographic.spherical_factory(config_)
49
- else
50
- ::RGeo::Cartesian.preferred_factory(config_)
51
- end
52
- end
53
-
54
-
55
- # A set of common Arel visitor hacks for spatial ToSql visitors.
56
-
57
- module SpatialToSql
58
-
59
- # Map a standard OGC SQL function name to the actual name used by
60
- # a particular database.
61
- def st_func(standard_name_)
62
- standard_name_
63
- end
64
-
65
- # Returns true if the given node is of spatial type-- that is, if
66
- # it is a spatial literal or a reference to a spatial attribute.
67
- def node_has_spatial_type?(node_)
68
- case node_
69
- when ::Arel::Attribute
70
- @connection.instance_variable_set(:@_getting_columns, true)
71
- begin
72
- col_ = @engine.columns_hash[node_.name.to_s] unless @engine == ::ActiveRecord::Base
73
- col_ && col_.respond_to?(:spatial?) && col_.spatial?
74
- ensure
75
- @connection.instance_variable_set(:@_getting_columns, false)
76
- end
77
- when ::RGeo::Feature::Instance
78
- true
79
- else
80
- false
81
- end
82
- end
83
-
84
- # Generates SQL for a spatial node.
85
- def visit_spatial(node_)
86
- case node_
87
- when ::String
88
- "#{st_func('ST_WKTToSQL')}(#{visit_String(node_)})"
89
- when ::RGeo::Feature::Instance
90
- visit_RGeo_Feature_Instance(node_)
91
- else
92
- visit(node_)
93
- end
94
- end
95
-
96
- def visit_Arel_Nodes_Equality(node_) # :nodoc:
97
- right_ = node_.right
98
- left_ = node_.left
99
- if !@connection.instance_variable_get(:@_getting_columns) && (node_has_spatial_type?(right_) || node_has_spatial_type?(left_))
100
- "#{st_func('ST_Equals')}(#{visit_spatial(left_)}, #{visit_spatial(right_)})"
101
- else
102
- super
103
- end
104
- end
105
-
106
- def visit_Arel_Nodes_NotEqual(node_) # :nodoc:
107
- right_ = node_.right
108
- left_ = node_.left
109
- if !@connection.instance_variable_get(:@_getting_columns) && (node_has_spatial_type?(right_) || node_has_spatial_type?(left_))
110
- "NOT #{st_func('ST_Equals')}(#{visit_spatial(left_)}, #{visit_spatial(right_)})"
111
- else
112
- super
113
- end
114
- end
115
-
116
- end
117
-
118
-
119
- # Index definition struct with a spatial flag field.
120
-
121
- class SpatialIndexDefinition < ::Struct.new(:table, :name, :unique, :columns, :lengths, :spatial)
122
- end
123
-
124
-
125
- class << self
126
-
127
-
128
- # Returns a feature type module given a string type.
129
-
130
- def geometric_type_from_name(name_)
131
- case name_.downcase
132
- when 'geometry' then ::RGeo::Feature::Geometry
133
- when 'point' then ::RGeo::Feature::Point
134
- when 'linestring' then ::RGeo::Feature::LineString
135
- when 'polygon' then ::RGeo::Feature::Polygon
136
- when 'geometrycollection' then ::RGeo::Feature::GeometryCollection
137
- when 'multipoint' then ::RGeo::Feature::MultiPoint
138
- when 'multilinestring' then ::RGeo::Feature::MultiLineString
139
- when 'multipolygon' then ::RGeo::Feature::MultiPolygon
140
- else nil
141
- end
142
- end
143
-
144
- end
145
-
146
- end
147
-
148
- end