activerecord-postgis-array 0.3.4

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.
Files changed (97) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +18 -0
  4. data/CHANGELOG.md +99 -0
  5. data/CONTRIBUTING.md +35 -0
  6. data/Gemfile +12 -0
  7. data/LICENSE +22 -0
  8. data/README.md +87 -0
  9. data/Rakefile +33 -0
  10. data/activerecord-postgis-array.gemspec +30 -0
  11. data/docs/indexes.md +28 -0
  12. data/docs/migrations.md +92 -0
  13. data/docs/querying.md +170 -0
  14. data/docs/type_casting.md +51 -0
  15. data/lib/activerecord-postgis-array.rb +3 -0
  16. data/lib/activerecord-postgis-array/active_record.rb +4 -0
  17. data/lib/activerecord-postgis-array/active_record/connection_adapters.rb +1 -0
  18. data/lib/activerecord-postgis-array/active_record/connection_adapters/postgres_adapter.rb +346 -0
  19. data/lib/activerecord-postgis-array/active_record/relation.rb +2 -0
  20. data/lib/activerecord-postgis-array/active_record/relation/predicate_builder.rb +71 -0
  21. data/lib/activerecord-postgis-array/active_record/relation/query_methods.rb +84 -0
  22. data/lib/activerecord-postgis-array/active_record/sanitization.rb +30 -0
  23. data/lib/activerecord-postgis-array/active_record/schema_dumper.rb +157 -0
  24. data/lib/activerecord-postgis-array/arel.rb +3 -0
  25. data/lib/activerecord-postgis-array/arel/nodes.rb +2 -0
  26. data/lib/activerecord-postgis-array/arel/nodes/array_nodes.rb +9 -0
  27. data/lib/activerecord-postgis-array/arel/nodes/contained_within.rb +20 -0
  28. data/lib/activerecord-postgis-array/arel/predications.rb +25 -0
  29. data/lib/activerecord-postgis-array/arel/visitors.rb +2 -0
  30. data/lib/activerecord-postgis-array/arel/visitors/to_sql.rb +15 -0
  31. data/lib/activerecord-postgis-array/arel/visitors/visitor.rb +38 -0
  32. data/lib/activerecord-postgis-array/version.rb +3 -0
  33. data/spec/arel/arel_spec.rb +30 -0
  34. data/spec/arel/array_spec.rb +77 -0
  35. data/spec/columns/array_spec.rb +120 -0
  36. data/spec/dummy/.gitignore +15 -0
  37. data/spec/dummy/README.rdoc +261 -0
  38. data/spec/dummy/Rakefile +7 -0
  39. data/spec/dummy/app/assets/images/rails.png +0 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  44. data/spec/dummy/app/mailers/.gitkeep +0 -0
  45. data/spec/dummy/app/models/.gitkeep +0 -0
  46. data/spec/dummy/app/models/person.rb +3 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/spec/dummy/config/application.rb +59 -0
  50. data/spec/dummy/config/boot.rb +6 -0
  51. data/spec/dummy/config/database.yml.example +14 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +38 -0
  54. data/spec/dummy/config/environments/production.rb +67 -0
  55. data/spec/dummy/config/environments/test.rb +37 -0
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/locales/en.yml +5 -0
  63. data/spec/dummy/config/routes.rb +58 -0
  64. data/spec/dummy/db/migrate/20120501163758_create_people.rb +12 -0
  65. data/spec/dummy/db/schema.rb +25 -0
  66. data/spec/dummy/db/seeds.rb +7 -0
  67. data/spec/dummy/lib/assets/.gitkeep +0 -0
  68. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  69. data/spec/dummy/log/.gitkeep +0 -0
  70. data/spec/dummy/public/404.html +26 -0
  71. data/spec/dummy/public/422.html +26 -0
  72. data/spec/dummy/public/500.html +25 -0
  73. data/spec/dummy/public/favicon.ico +0 -0
  74. data/spec/dummy/public/index.html +241 -0
  75. data/spec/dummy/public/robots.txt +5 -0
  76. data/spec/dummy/script/rails +6 -0
  77. data/spec/dummy/spec/factories/people.rb +7 -0
  78. data/spec/dummy/test/fixtures/.gitkeep +0 -0
  79. data/spec/dummy/test/functional/.gitkeep +0 -0
  80. data/spec/dummy/test/integration/.gitkeep +0 -0
  81. data/spec/dummy/test/performance/browsing_test.rb +12 -0
  82. data/spec/dummy/test/test_helper.rb +13 -0
  83. data/spec/dummy/test/unit/.gitkeep +0 -0
  84. data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  85. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  86. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  87. data/spec/migrations/active_record_migration_spec.rb +29 -0
  88. data/spec/migrations/array_spec.rb +136 -0
  89. data/spec/migrations/index_spec.rb +67 -0
  90. data/spec/models/array_spec.rb +285 -0
  91. data/spec/queries/array_queries_spec.rb +72 -0
  92. data/spec/queries/sanity_spec.rb +16 -0
  93. data/spec/schema_dumper/array_spec.rb +17 -0
  94. data/spec/schema_dumper/extension_spec.rb +14 -0
  95. data/spec/schema_dumper/index_spec.rb +46 -0
  96. data/spec/spec_helper.rb +29 -0
  97. metadata +318 -0
File without changes
File without changes
File without changes
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ActiveRecord Migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :generic_data_types }
6
+ it 'creates non-activerecord-postgis-array columns' do
7
+ lambda do
8
+ connection.create_table :generic_data_types do |t|
9
+ t.integer :col_1
10
+ t.string :col_2
11
+ t.datetime :col_3
12
+ end
13
+ connection.add_column :generic_data_types, :col_4, :text
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:generic_data_types)
17
+ col_1 = columns.detect { |c| c.name == 'col_1'}
18
+ col_2 = columns.detect { |c| c.name == 'col_2'}
19
+ col_3 = columns.detect { |c| c.name == 'col_3'}
20
+ col_4 = columns.detect { |c| c.name == 'col_4'}
21
+
22
+
23
+ col_1.sql_type.should eq 'integer'
24
+ col_2.sql_type.should eq 'character varying(255)'
25
+ col_3.sql_type.should eq 'timestamp without time zone'
26
+ col_4.sql_type.should eq 'text'
27
+ end
28
+ end
29
+
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Array migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+
6
+ context 'New Table' do
7
+ after { connection.drop_table :data_types }
8
+ describe 'Create table methods' do
9
+ it 'creates an array column' do
10
+ lambda do
11
+ connection.create_table :data_types do |t|
12
+ t.integer :array_1, :array => true
13
+ t.integer :array_2, :array_3, :array => true
14
+ t.column :array_4, :integer, :array => true
15
+ end
16
+ end.should_not raise_exception
17
+
18
+ columns = connection.columns(:data_types)
19
+
20
+ array_1 = columns.detect { |c| c.name == 'array_1'}
21
+ array_2 = columns.detect { |c| c.name == 'array_2'}
22
+ array_3 = columns.detect { |c| c.name == 'array_3'}
23
+ array_4 = columns.detect { |c| c.name == 'array_4'}
24
+
25
+ array_1.sql_type.should eq 'integer[]'
26
+ array_2.sql_type.should eq 'integer[]'
27
+ array_3.sql_type.should eq 'integer[]'
28
+ array_4.sql_type.should eq 'integer[]'
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'Existing Table' do
34
+ before { connection.create_table :data_types }
35
+ after { connection.drop_table :data_types }
36
+ describe 'Add Column' do
37
+ context 'no default' do
38
+ it 'creates an array column' do
39
+ lambda do
40
+ connection.add_column :data_types, :array_5, :integer, :array => true
41
+ end.should_not raise_exception
42
+
43
+ columns = connection.columns(:data_types)
44
+
45
+ array_5 = columns.detect { |c| c.name == 'array_5'}
46
+ array_5.sql_type.should eq 'integer[]'
47
+ end
48
+ end
49
+
50
+ context 'with default' do
51
+ it 'creates an array column' do
52
+ lambda do
53
+ connection.add_column :data_types, :array_6, :integer, :array => true, :default => []
54
+ end.should_not raise_exception
55
+
56
+ columns = connection.columns(:data_types)
57
+
58
+ array_6 = columns.detect { |c| c.name == 'array_6'}
59
+ array_6.sql_type.should eq 'integer[]'
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ describe 'Change table methods' do
66
+ it 'creates an array column' do
67
+ lambda do
68
+ connection.change_table :data_types do |t|
69
+ t.column :array_6, :integer, :array => true
70
+ t.integer :array_7, :array => true
71
+ end
72
+ end.should_not raise_exception
73
+
74
+ columns = connection.columns(:data_types)
75
+
76
+ array_6 = columns.detect { |c| c.name == 'array_6'}
77
+ array_7 = columns.detect { |c| c.name == 'array_7'}
78
+
79
+ array_6.sql_type.should eq 'integer[]'
80
+ array_7.sql_type.should eq 'integer[]'
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'Default Values' do
86
+ describe 'String defaults' do
87
+ after { connection.drop_table :default_strings }
88
+ it 'creates array column with proper defaults' do
89
+ lambda do
90
+ connection.create_table :default_strings do |t|
91
+ t.string :names_1, :array => true, :default => []
92
+ t.string :names_2, :array => true, :default => '{}'
93
+ t.string :names_3, :array => true, :default => ['something']
94
+ t.string :names_4, :array => true, :default => '{something}'
95
+ end
96
+ end.should_not raise_exception
97
+ columns = connection.columns(:default_strings)
98
+
99
+ names_1 = columns.detect { |c| c.name == 'names_1' }
100
+ names_2 = columns.detect { |c| c.name == 'names_2' }
101
+ names_3 = columns.detect { |c| c.name == 'names_3' }
102
+ names_4 = columns.detect { |c| c.name == 'names_4' }
103
+
104
+ names_1.default.should eq []
105
+ names_2.default.should eq []
106
+ names_3.default.should eq ['something']
107
+ names_4.default.should eq ['something']
108
+ end
109
+ end
110
+
111
+ describe 'Integer defaults' do
112
+ after { connection.drop_table :default_integers }
113
+ it 'creates array column with proper defaults' do
114
+ lambda do
115
+ connection.create_table :default_integers do |t|
116
+ t.integer :numbers_1, :array => true, :default => []
117
+ t.integer :numbers_2, :array => true, :default => '{}'
118
+ t.integer :numbers_3, :array => true, :default => [3]
119
+ t.integer :numbers_4, :array => true, :default => '{4}'
120
+ end
121
+ end.should_not raise_exception
122
+ columns = connection.columns(:default_integers)
123
+
124
+ numbers_1 = columns.detect { |c| c.name == 'numbers_1' }
125
+ numbers_2 = columns.detect { |c| c.name == 'numbers_2' }
126
+ numbers_3 = columns.detect { |c| c.name == 'numbers_3' }
127
+ numbers_4 = columns.detect { |c| c.name == 'numbers_4' }
128
+
129
+ numbers_1.default.should eq []
130
+ numbers_2.default.should eq []
131
+ numbers_3.default.should eq [3]
132
+ numbers_4.default.should eq [4]
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Index migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ let!(:stream) { StringIO.new }
6
+ before do
7
+ ActiveRecord::Base.logger = ActiveSupport::TaggedLogging.new(Logger.new stream)
8
+ end
9
+
10
+ after do
11
+ [:tag_ids, :lucky_number, :biography].each do |column|
12
+ begin
13
+ connection.remove_index :people, column
14
+ rescue ArgumentError
15
+ end
16
+ end
17
+ end
18
+
19
+ it 'creates special index' do
20
+ lambda do
21
+ connection.add_index(:people, :tag_ids, :using => :gin)
22
+ end.should_not raise_exception
23
+
24
+ indexes = connection.indexes(:people)
25
+ index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['tag_ids']}
26
+
27
+ index_1.using.should eq :gin
28
+ end
29
+
30
+ it 'creates indexes with where clauses' do
31
+ lambda do
32
+ connection.add_index(:people, :lucky_number, :where => '(lucky_number > 50)')
33
+ end.should_not raise_exception
34
+
35
+ indexes = connection.indexes(:people)
36
+ index_2 = indexes.detect { |c| c.columns.map(&:to_s) == ['lucky_number']}
37
+
38
+ index_2.where.should match /lucky_number > 50/
39
+ end
40
+
41
+ it 'creates index concurrently' do
42
+ lambda do
43
+ connection.add_index(:people, :lucky_number, :algorithm => :concurrently)
44
+ end.should_not raise_exception
45
+
46
+ output = stream.string
47
+ output.should match /CREATE INDEX CONCURRENTLY "index_people_on_lucky_number" ON "people"\(\"lucky_number\" \)/
48
+ end
49
+
50
+ it 'rejects bad algorithm arguments' do
51
+ lambda do
52
+ connection.add_index(:people, :lucky_number, :algorithm => :conurrently)
53
+ end.should raise_exception
54
+ end
55
+
56
+ it 'creates indexes with operator classes', :if => ActiveRecord::Base.connection.supports_extensions? do
57
+ lambda do
58
+ connection.add_index(:people, :biography, :using => :gin, :index_opclass => :gin_trgm_ops)
59
+ end.should_not raise_exception
60
+
61
+ indexes = connection.indexes(:people)
62
+ index_3 = indexes.detect { |c| c.columns.map(&:to_s) == ['biography']}
63
+
64
+ index_3.using.should eq :gin
65
+ index_3.index_opclass.should eq :gin_trgm_ops
66
+ end
67
+ end
@@ -0,0 +1,285 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Models with array columns' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+
6
+ context 'no default value, string array' do
7
+ before do
8
+ adapter.create_table :users, :force => true do |t|
9
+ t.string :nick_names, :array => true
10
+ t.integer :favorite_numbers, :array => true
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ class User < ActiveRecord::Base
16
+ attr_accessible :nick_names, :favorite_numbers
17
+ end
18
+ end
19
+
20
+ after do
21
+ adapter.drop_table :users
22
+ Object.send(:remove_const, :User)
23
+ end
24
+
25
+ context 'no default value, string array' do
26
+ describe '#create' do
27
+ it 'creates a user when there is no array assignment' do
28
+ u = User.create()
29
+ u.reload
30
+ u.nick_names.should eq nil
31
+ end
32
+
33
+ it 'creates a user with an empty array' do
34
+ u = User.create(:nick_names => [])
35
+ u.reload
36
+ u.nick_names.should eq []
37
+ end
38
+
39
+ it 'creates a user with an array of some values' do
40
+ u = User.create(:nick_names => ['some', 'things'])
41
+ u.reload
42
+ u.nick_names.should eq ['some', 'things']
43
+ end
44
+
45
+ it 'creates a user with an int array of some values' do
46
+ u = User.create(:favorite_numbers => [2,3,5])
47
+ u.reload
48
+ u.favorite_numbers.should eq [2,3,5]
49
+ end
50
+
51
+ it 'creates a user with an int array of string values' do
52
+ u = User.create(:favorite_numbers => ['2','3','5'])
53
+ u.reload
54
+ u.favorite_numbers.should eq [2,3,5]
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'Setting values' do
60
+ it 'returns the value set when the record is retrieved' do
61
+ u = User.create(:nick_names => ['some', 'things'])
62
+ u.reload
63
+
64
+ u.nick_names = ['different', 'values']
65
+ u.save
66
+
67
+ u.reload
68
+ u.nick_names.should eq ['different', 'values']
69
+ end
70
+
71
+ it 'creates a user with an int array of some values' do
72
+ u = User.create(:favorite_numbers => [2,3,5])
73
+ u.reload
74
+
75
+ u.favorite_numbers = [1,2,3]
76
+ u.save
77
+
78
+ u.reload
79
+ u.favorite_numbers.should eq [1,2,3]
80
+ end
81
+ end
82
+
83
+ context '#update_attribute' do
84
+ describe 'setting a value via update_attribute' do
85
+ it 'returns the value set when the record is retrieved' do
86
+ user = User.create(:nick_names => [])
87
+ user.reload
88
+
89
+ user.update_attribute(:nick_names, ['some', 'values'])
90
+ user.save
91
+
92
+ user.reload
93
+ user.nick_names.should eq ['some', 'values']
94
+ end
95
+ end
96
+ end
97
+
98
+ context '#<column>? do' do
99
+ describe 'checking a value via <attribute_name>? 'do
100
+ it 'returns false if it\'s an empty array' do
101
+ user = User.create(:nick_names => [], :favorite_numbers => [])
102
+ user.reload
103
+
104
+ user.nick_names?.should be_false
105
+ user.favorite_numbers?.should be_false
106
+ end
107
+
108
+ it 'returns false if it\'s an empty array' do
109
+ user = User.create(:nick_names => ['bob'], :favorite_numbers => [0])
110
+ user.reload
111
+
112
+ user.nick_names?.should be_true
113
+ user.favorite_numbers?.should be_true
114
+ end
115
+ end
116
+ end
117
+
118
+ context '#update_column' do
119
+ describe 'setting a value via update_column' do
120
+ it 'returns the value set when the record is retrieved' do
121
+ user = User.create(:nick_names => [])
122
+ user.reload
123
+
124
+ user.update_column(:nick_names, ['some', 'values'])
125
+ user.save
126
+
127
+ user.reload
128
+ user.nick_names.should eq ['some', 'values']
129
+ end
130
+ end
131
+ end
132
+
133
+ context '#update_attributes' do
134
+ describe 'setting a value via update_attributes' do
135
+ it 'returns the value set when the record is retrieved' do
136
+ user = User.create(:nick_names => [])
137
+ user.reload
138
+
139
+ user.update_attributes(:nick_names => ['some', 'values'])
140
+ user.save
141
+
142
+ user.reload
143
+ user.nick_names.should eq ['some', 'values']
144
+ end
145
+ end
146
+ end
147
+
148
+ describe 'strings contain special characters' do
149
+ context '#save' do
150
+ it 'contains: \'' do
151
+ data = ['some\'thing']
152
+ u = User.create
153
+ u.nick_names = data
154
+ u.save!
155
+ u.reload
156
+ u.nick_names.should eq data
157
+ end
158
+
159
+ it 'contains: {' do
160
+ data = ['some{thing']
161
+ u = User.create
162
+ u.nick_names = data
163
+ u.save!
164
+ u.reload
165
+ u.nick_names.should eq data
166
+ end
167
+
168
+ it 'contains: }' do
169
+ data = ['some}thing']
170
+ u = User.create
171
+ u.nick_names = data
172
+ u.save!
173
+ u.reload
174
+ u.nick_names.should eq data
175
+ end
176
+
177
+ it 'contains: backslash' do
178
+ data = ['some\\thing']
179
+ u = User.create
180
+ u.nick_names = data
181
+ u.save!
182
+ u.reload
183
+ u.nick_names.should eq data
184
+ end
185
+
186
+ it 'contains: "' do
187
+ data = ['some"thing']
188
+ u = User.create
189
+ u.nick_names = data
190
+ u.save!
191
+ u.reload
192
+ u.nick_names.should eq data
193
+ end
194
+ end
195
+
196
+ context '#create' do
197
+ it 'contains: \'' do
198
+ data = ['some\'thing']
199
+ u = User.create(:nick_names => data)
200
+ u.reload
201
+ u.nick_names.should eq data
202
+ end
203
+
204
+ it 'contains: {' do
205
+ data = ['some{thing']
206
+ u = User.create(:nick_names => data)
207
+ u.reload
208
+ u.nick_names.should eq data
209
+ end
210
+
211
+ it 'contains: }' do
212
+ data = ['some}thing']
213
+ u = User.create(:nick_names => data)
214
+ u.reload
215
+ u.nick_names.should eq data
216
+ end
217
+
218
+ it 'contains: backslash' do
219
+ data = ['some\\thing']
220
+ u = User.create(:nick_names => data)
221
+ u.reload
222
+ u.nick_names.should eq data
223
+ end
224
+
225
+ it 'contains: "' do
226
+ data = ['some"thing']
227
+ u = User.create(:nick_names => data)
228
+ u.reload
229
+ u.nick_names.should eq data
230
+ end
231
+ end
232
+ end
233
+
234
+ describe 'overlap' do
235
+ it "works" do
236
+ arel = User.arel_table
237
+ User.create(:nick_names => ['this'])
238
+ x = User.create
239
+ x.nick_names = ["s'o{m}e", 'thing']
240
+ x.save
241
+ u = User.where(arel[:nick_names].overlap(["s'o{m}e"]))
242
+ u.first.should_not be_nil
243
+ u.first.nick_names.should eq ["s'o{m}e", 'thing']
244
+ end
245
+ end
246
+ end
247
+
248
+ context 'default values' do
249
+ before do
250
+ adapter.create_table :defaulted_users, :force => true do |t|
251
+ t.string :nick_names, :array => true, :default => '{}'
252
+ end
253
+ class DefaultedUser < ActiveRecord::Base
254
+ attr_accessible :nick_names
255
+ end
256
+ end
257
+
258
+ after do
259
+ adapter.drop_table :defaulted_users
260
+ Object.send(:remove_const, :DefaultedUser)
261
+ end
262
+
263
+ context 'model creation' do
264
+ describe '#create' do
265
+ it 'creates a user when there is no array assignment' do
266
+ u = DefaultedUser.create()
267
+ u.reload
268
+ u.nick_names.should eq []
269
+ end
270
+
271
+ it 'creates a user with an nil' do
272
+ u = DefaultedUser.create(:nick_names => nil)
273
+ u.reload
274
+ u.nick_names.should eq nil
275
+ end
276
+
277
+ it 'creates a user with an array of some values' do
278
+ u = DefaultedUser.create(:nick_names => ['some', 'things'])
279
+ u.reload
280
+ u.nick_names.should eq ['some', 'things']
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end