activerecord-postgis-array 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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