postgres_ext 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.0.10
2
+
3
+ * Fixes parsing of number arrays when they are set from a string array - Alexey Noskov (@alno)
4
+ * Cleans up spec organization - Dan McClain (@danmcclain)
5
+ * Adds support for index operator classes (:index_opclass) in
6
+ migrations and schema dumps - & Dan McClain (@danmcclain)
7
+ * Fixes Arel Nodes created by postgres_ext - Dan McClain (@danmcclain)
8
+ * Add support to schema.rb to export and import extensions - Keenan
9
+ Brock (@kbrock)
10
+ * Handles PostgreSQL strings when passed in as defaults by fixing the
11
+ quote method
12
+ * Documentation updates. - Dan McClain & Doug Yun (@danmcclain
13
+ @duggieawesome)
14
+ * Fixes #update_column calls - Dan McClain (@danmcclain)
15
+
16
+
1
17
  ## 0.0.9
2
18
 
3
19
  * Fixes #<attribute_name>?, Adds (pending) test case for #update_column - Dan McClain (@danmcclain)
data/CONTRIBUTING.md CHANGED
@@ -6,7 +6,7 @@ If you need to open a new issue you *must* provide the following:
6
6
 
7
7
  1. Version of Rails
8
8
 
9
- Failure to include the above mentioned requirements will result in the
9
+ Failure to include the aforementioned requirements will result in the
10
10
  issue being closed.
11
11
 
12
12
  If you want to ensure that your issue gets fixed *fast* you should
@@ -28,8 +28,8 @@ If you'd like to submit a pull request please adhere to the following:
28
28
  8. Follow Tim Pope's [model for git commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). This will make it
29
29
  much easier to generate change logs and navigate through the logs
30
30
 
31
- Plase note that you must adhere to each of the above mentioned rules.
31
+ Please note that you must adhere to each of the aforementioned rules.
32
32
  Failure to do so will result in an immediate closing of the pull
33
33
  request. If you update and rebase the pull request to follow the
34
34
  guidelines your pull request will be re-opened and considered for
35
- inclusion.
35
+ inclusion.
data/README.md CHANGED
@@ -34,12 +34,45 @@ ActiveRecord's data type handling.
34
34
  * [Querying PostgreSQL datatypes](#querying-postgresql-datatypes)
35
35
 
36
36
  ### Usage Notes
37
- Take care when dealing with arrays and other types that allow you to
38
- update their value in place. In place changes are not currently tracked
39
- in Rails (see [this issue](https://github.com/rails/rails/issues/6954)).
40
- To track changes that happen via `#<<` or other instance methods, be
41
- sure to call `<attribute>_will_change!` so that Active Record knows to
42
- persist the change.
37
+ Avoid the use of in place operators (ie `Array#<<`). These changes are
38
+ *not* tracked by Rails ([this issue](https://github.com/rails/rails/issues/6954))
39
+ explains why). In place modifications also modify the default object.
40
+
41
+ Assuming we have the following model:
42
+
43
+ ```ruby
44
+ create_table :items do |t|
45
+ t.string :names, :array => true, :default => []
46
+ end
47
+
48
+ class Item < ActiveRecord::Base
49
+ end
50
+ ```
51
+
52
+ The following will modify the default value of the names attribute.
53
+
54
+ ```ruby
55
+ a = Item.new
56
+ a.names << 'foo'
57
+
58
+ b = Item.new
59
+ puts b.names
60
+ # => ['foo']
61
+ ```
62
+
63
+ The supported way of modifying `a.names`:
64
+
65
+ ```ruby
66
+ a = Item.new
67
+ a.names += 'foo'
68
+
69
+ b = Item.new
70
+ puts b.names
71
+ # => []
72
+ ```
73
+
74
+ As a result, in place operators are discouraged and will not be
75
+ supported in postgres\_ext at this time.
43
76
 
44
77
  ## Migration/Schema.rb support
45
78
 
@@ -188,7 +221,8 @@ predicate for the `&&` operator.
188
221
  ```ruby
189
222
  user_arel = User.arel_table
190
223
 
191
- User.where(user_arel[:tags].array_overlap(['one','two'])).to_sql
224
+ # Execute the query
225
+ User.where(user_arel[:tags].array_overlap(['one','two']))
192
226
  # => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"tags\" && '{one,two}'
193
227
  ```
194
228
 
@@ -220,9 +254,9 @@ user_arel = User.arel_table
220
254
  any_tags_function = Arel::Nodes::NamedFunction.new('ANY', [user_arel[:tags]])
221
255
  predicate = Arel::Nodes::Equality.new('test', any_tags_function)
222
256
 
223
- User.where(predicate).to_sql
257
+ # Execute the query
258
+ User.where(predicate)
224
259
  #=> SELECT \"users\".* FROM \"users\" WHERE 'test' = ANY(\"users\".\"tags\")
225
-
226
260
  ```
227
261
 
228
262
  The ALL version of this same predicate can be generated by swap `'ANY'`
@@ -250,7 +284,8 @@ predicate for the `<<` operator.
250
284
  ```ruby
251
285
  user_arel = User.arel_table
252
286
 
253
- User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24')).to_sql
287
+ # Execute the query
288
+ User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24'))
254
289
  # => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"ip_address\" << '127.0.0.1/24'
255
290
  ```
256
291
 
@@ -258,10 +293,12 @@ User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24')).to_sql
258
293
 
259
294
  ### Index types
260
295
 
261
- Postgres\_ext allows you to specify an index type at index creation.
296
+ Postgres\_ext allows you to specify index type and index operator
297
+ class at index creation.
262
298
 
263
299
  ```ruby
264
300
  add_index :table_name, :column, :index_type => :gin
301
+ add_index :table_name, :column, :index_type => :gin, :index_opclass => :gin_trgm_ops
265
302
  ```
266
303
 
267
304
  ### Where clauses
@@ -1,2 +1,3 @@
1
+ require 'postgres_ext/active_record/sanitization'
1
2
  require 'postgres_ext/active_record/connection_adapters'
2
3
  require 'postgres_ext/active_record/schema_dumper'
@@ -4,9 +4,8 @@ require 'pg_array_parser'
4
4
 
5
5
  module ActiveRecord
6
6
  module ConnectionAdapters
7
- # class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :index_type)
8
7
  class IndexDefinition
9
- attr_accessor :index_type, :where
8
+ attr_accessor :index_type, :where, :index_opclass
10
9
  end
11
10
 
12
11
  class PostgreSQLColumn
@@ -205,8 +204,13 @@ module ActiveRecord
205
204
  if options.is_a? Hash
206
205
  index_type = options[:index_type] ? " USING #{options[:index_type]} " : ""
207
206
  index_options = options[:where] ? " WHERE #{options[:where]}" : ""
207
+ index_opclass = options[:index_opclass]
208
208
  end
209
- execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns})#{index_options}"
209
+ execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns} #{index_opclass})#{index_options}"
210
+ end
211
+
212
+ def add_extension(extension_name, options={})
213
+ execute "CREATE extension if not exists \"#{extension_name}\""
210
214
  end
211
215
 
212
216
  def change_table(table_name, options = {})
@@ -268,15 +272,22 @@ module ActiveRecord
268
272
  "'#{type_cast(value, column)}'"
269
273
  elsif value.is_a? Array
270
274
  "'#{array_to_string(value, column, true)}'"
275
+ elsif column.respond_to?(:array) && column.array && value =~ /^\{.*\}$/
276
+ "'#{value}'"
271
277
  else
272
278
  quote_without_extended_types(value, column)
273
279
  end
274
280
  end
275
281
  alias_method_chain :quote, :extended_types
276
282
 
283
+ def opclasses
284
+ @opclasses ||= select_rows('SELECT opcname FROM pg_opclass').flatten.uniq
285
+ end
286
+
277
287
  # this is based upon rails 4 changes to include different index methods
278
288
  # Returns an array of indexes for the given table.
279
289
  def indexes(table_name, name = nil)
290
+ opclasses
280
291
  result = select_rows(<<-SQL, name)
281
292
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
282
293
  FROM pg_class t
@@ -309,10 +320,15 @@ module ActiveRecord
309
320
  #changed from rails 3.2
310
321
  where = inddef.scan(/WHERE (.+)$/).flatten[0]
311
322
  index_type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
323
+ if index_type
324
+ index_op = inddef.scan(/USING .+? \(.+? (#{opclasses.join('|')})\)/).flatten
325
+ index_op = index_op[0].to_sym if index_op.present?
326
+ end
312
327
  if column_names.present?
313
328
  index_def = IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
314
329
  index_def.where = where
315
330
  index_def.index_type = index_type if index_type && index_type != :btree
331
+ index_def.index_opclass = index_op if index_type && index_type != :btree && index_op
316
332
  index_def
317
333
  # else nil
318
334
  end
@@ -320,6 +336,10 @@ module ActiveRecord
320
336
  end.compact
321
337
  end
322
338
 
339
+ def extensions
340
+ select_rows('select extname from pg_extension', 'extensions').map { |row| row[0] }.delete_if {|name| name == 'plpgsql'}
341
+ end
342
+
323
343
  private
324
344
 
325
345
  def ipaddr_to_string(value)
@@ -331,25 +351,28 @@ module ActiveRecord
331
351
  end
332
352
 
333
353
  def item_to_string(value, column, encode_single_quotes = false)
334
- if value.nil?
335
- 'NULL'
336
- elsif value.is_a? String
337
- value = type_cast(value, column, true).dup
354
+ return 'NULL' if value.nil?
355
+
356
+ casted_value = type_cast(value, column, true)
357
+
358
+ if casted_value.is_a? String
359
+ casted_value = casted_value.dup
338
360
  # Encode backslashes. One backslash becomes 4 in the resulting SQL.
339
361
  # (why 4, and not 2? Trial and error shows 4 works, 2 fails to parse.)
340
- value.gsub!('\\', '\\\\\\\\')
362
+ casted_value.gsub!('\\', '\\\\\\\\')
341
363
  # Encode a bare " in the string as \"
342
- value.gsub!('"', '\\"')
364
+ casted_value.gsub!('"', '\\"')
343
365
  # PostgreSQL parses the string values differently if they are quoted for
344
366
  # use in a statement, or if it will be used as part of a bound argument.
345
367
  # For directly-inserted values (UPDATE foo SET bar='{"array"}') we need to
346
368
  # escape ' as ''. For bound arguments, do not escape them.
347
369
  if encode_single_quotes
348
- value.gsub!("'", "''")
370
+ casted_value.gsub!("'", "''")
349
371
  end
350
- "\"#{value}\""
372
+
373
+ "\"#{casted_value}\""
351
374
  else
352
- type_cast(value, column, true)
375
+ casted_value
353
376
  end
354
377
  end
355
378
  end
@@ -0,0 +1,30 @@
1
+ require 'active_record/sanitization'
2
+
3
+ module ActiveRecord
4
+ module Sanitization
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def sanitize_sql_hash_for_assignment(attrs)
9
+ attrs.map do |attr, value|
10
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value, attr)}"
11
+ end.join(', ')
12
+ end
13
+
14
+ def quote_bound_value(value, column = nil, c = connection)
15
+ if column.present? && column != c
16
+ record_column = self.columns.select {|col| col.name == column}.first
17
+ c.quote(value, record_column)
18
+ elsif value.respond_to?(:map) && !value.acts_like?(:string)
19
+ if value.respond_to?(:empty?) && value.empty?
20
+ c.quote(nil)
21
+ else
22
+ value.map { |v| c.quote(v) }.join(',')
23
+ end
24
+ else
25
+ c.quote(value)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -7,7 +7,26 @@ module ActiveRecord
7
7
  VALID_COLUMN_SPEC_KEYS
8
8
  end
9
9
 
10
+ def dump(stream)
11
+ header(stream)
12
+ # added
13
+ extensions(stream)
14
+ # /added
15
+ tables(stream)
16
+ trailer(stream)
17
+ stream
18
+ end
19
+
10
20
  private
21
+
22
+ def extensions(stream)
23
+ exts=@connection.extensions
24
+
25
+ unless exts.empty?
26
+ stream.puts exts.map { |name| " add_extension \"#{name}\""}.join("\n") + "\n\n"
27
+ end
28
+ end
29
+
11
30
  def table(table, stream)
12
31
  columns = @connection.columns(table)
13
32
  begin
@@ -100,6 +119,7 @@ module ActiveRecord
100
119
  # changed from rails 2.3
101
120
  statement_parts << (':where => ' + index.where.inspect) if index.where
102
121
  statement_parts << (':index_type => ' + index.index_type.inspect) if index.index_type
122
+ statement_parts << (':index_opclass => ' + index.index_opclass.inspect) if index.index_opclass
103
123
  # /changed
104
124
 
105
125
  ' ' + statement_parts.join(', ')
@@ -6,6 +6,7 @@ module Arel
6
6
  end
7
7
 
8
8
  class ArrayOverlap < Arel::Nodes::Binary
9
+ def operator; '&&' end
9
10
  end
10
11
  end
11
12
  end
@@ -1 +1 @@
1
- require 'postgres_ext/arel/visitors/to_sql'
1
+ require 'postgres_ext/arel/visitors/visitor'
@@ -1,7 +1,8 @@
1
- require 'arel/visitors/to_sql'
1
+ require 'arel/visitors/visitor'
2
2
  module Arel
3
3
  module Visitors
4
- class ToSql
4
+ class Visitor
5
+ # We are adding our visitors to the main visitor for the time being until the right spot is found to monkey patch
5
6
  private
6
7
  def visit_Arel_Nodes_ContainedWithin o
7
8
  "#{visit o.left} << #{visit o.right}"
@@ -1,3 +1,3 @@
1
1
  module PostgresExt
2
- VERSION = '0.0.9'
2
+ VERSION = '0.0.10'
3
3
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Don\'t stomp all over the default ActiveRecord queries' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+
6
+ before do
7
+ adapter.create_table :cars, :force => true do |t|
8
+ t.string :make
9
+ t.string :model
10
+ t.timestamps
11
+ end
12
+
13
+ class Car < ActiveRecord::Base
14
+ attr_accessible :make, :model
15
+ end
16
+ end
17
+
18
+ after do
19
+ adapter.drop_table :cars
20
+ Object.send(:remove_const, :Car)
21
+ end
22
+
23
+ describe 'Where Queries' do
24
+ describe 'Set query' do
25
+ it '' do
26
+ Car.where('id in (?)', [1,2]).to_sql.should eq "SELECT \"cars\".* FROM \"cars\" WHERE (id in (1,2))"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -32,15 +32,26 @@ describe 'Array Column Predicates' do
32
32
  arel_table.where(arel_table[:tag_ids].array_overlap([1,2])).to_sql.should match /&& '\{1,2\}'/
33
33
  end
34
34
 
35
+ it 'works with count (and other predicates)' do
36
+ arel_table = ArelArray.arel_table
37
+
38
+ ArelArray.where(arel_table[:tag_ids].array_overlap([1,2])).count.should eq 0
39
+ end
40
+
35
41
  it 'returns matched records' do
36
42
  one = ArelArray.create!(:tags => ['one'])
37
43
  two = ArelArray.create!(:tags => ['two'])
38
44
  arel_table = ArelArray.arel_table
39
45
 
40
- ArelArray.where(arel_table[:tags].array_overlap(['one'])).should include(one)
41
- ArelArray.where(arel_table[:tags].array_overlap(['two'])).should include(two)
42
- ArelArray.where(arel_table[:tags].array_overlap(['two','one'])).should include(two)
43
- ArelArray.where(arel_table[:tags].array_overlap(['two','one'])).should include(one)
46
+ query = arel_table.where(arel_table[:tags].array_overlap(['one'])).project(Arel.sql('*'))
47
+ ArelArray.find_by_sql(query.to_sql).should include(one)
48
+
49
+ query = arel_table.where(arel_table[:tags].array_overlap(['two'])).project(Arel.sql('*'))
50
+ ArelArray.find_by_sql(query.to_sql).should include(two)
51
+
52
+ query = arel_table.where(arel_table[:tags].array_overlap(['two','one'])).project(Arel.sql('*'))
53
+ ArelArray.find_by_sql(query.to_sql).should include(two)
54
+ ArelArray.find_by_sql(query.to_sql).should include(one)
44
55
  end
45
56
  end
46
57
  end
@@ -3,9 +3,9 @@ class CreatePeople < ActiveRecord::Migration
3
3
  create_table :people do |t|
4
4
  t.inet :ip
5
5
  t.cidr :subnet, :subnet2
6
- t.integer_array :arrayzerd
7
- t.inet_array :inet_arrayzerd
8
- t.string_array :str_arrayzerd, :limit => 5
6
+ t.integer :arrayzerd, :array => true
7
+ t.inet :inet_arrayzerd, :array => true
8
+ t.string :str_arrayzerd, :array => true, :limit => 5
9
9
  t.string :test
10
10
 
11
11
  t.timestamps
@@ -13,20 +13,18 @@
13
13
 
14
14
  ActiveRecord::Schema.define(:version => 20120501163758) do
15
15
 
16
- create_table "people", :force => true do |t|
17
- t.inet "ip"
18
- t.cidr "subnet"
19
- t.cidr "subnet2"
20
- t.integer_array "arrayzerd"
21
- t.inet_array "inet_arrayzerd"
22
- t.string_array "str_arrayzerd", :limit => 5
23
- t.string "test"
24
- t.datetime "created_at", :null => false
25
- t.datetime "updated_at", :null => false
26
- end
16
+ add_extension "hstore"
27
17
 
28
- create_table "test", :id => false, :force => true do |t|
29
- t.string "test_a"
18
+ create_table "people", :force => true do |t|
19
+ t.inet "ip"
20
+ t.cidr "subnet"
21
+ t.cidr "subnet2"
22
+ t.integer "arrayzerd", :array => true
23
+ t.inet "inet_arrayzerd", :array => true
24
+ t.string "str_arrayzerd", :limit => 5, :array => true
25
+ t.string "test"
26
+ t.datetime "created_at", :null => false
27
+ t.datetime "updated_at", :null => false
30
28
  end
31
29
 
32
30
  end
@@ -1,11 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'activerecord migrations' do
3
+ describe 'ActiveRecord Migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :generic_data_types }
5
6
  it 'creates non-postgres_ext columns' do
6
-
7
7
  lambda do
8
- DoMigration.new.up
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
9
14
  end.should_not raise_exception
10
15
 
11
16
  columns = connection.columns(:generic_data_types)
@@ -22,15 +27,3 @@ describe 'activerecord migrations' do
22
27
  end
23
28
  end
24
29
 
25
-
26
- class DoMigration < ActiveRecord::Migration
27
- def up
28
- create_table :generic_data_types do |t|
29
- t.integer :col_1
30
- t.string :col_2
31
- t.datetime :col_3
32
- end
33
- add_column :generic_data_types, :col_4, :text
34
- end
35
- end
36
-
@@ -1,47 +1,120 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'array migrations' do
3
+ describe 'Array migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
- it 'creates an cidr column' do
6
- lambda do
7
- connection.create_table :data_types do |t|
8
- t.cidr :cidr_1, :array => true
9
- t.cidr :cidr_2, :cidr_3, :array => true
10
- t.column :cidr_4, :cidr, :array => true
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.cidr :array_1, :array => true
13
+ t.cidr :array_2, :array_3, :array => true
14
+ t.column :array_4, :cidr, :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 'cidr[]'
26
+ array_2.sql_type.should eq 'cidr[]'
27
+ array_3.sql_type.should eq 'cidr[]'
28
+ array_4.sql_type.should eq 'cidr[]'
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
+ it 'creates an array column' do
38
+ lambda do
39
+ connection.add_column :data_types, :array_5, :cidr, :array => true
40
+ end.should_not raise_exception
41
+
42
+ columns = connection.columns(:data_types)
43
+
44
+ array_5 = columns.detect { |c| c.name == 'array_5'}
45
+ array_5.sql_type.should eq 'cidr[]'
11
46
  end
12
- end.should_not raise_exception
47
+ end
13
48
 
14
- lambda do
15
- connection.add_column :data_types, :cidr_5, :cidr, :array => true
16
- end.should_not raise_exception
49
+ describe 'Change table methods' do
50
+ it 'creates an array column' do
51
+ lambda do
52
+ connection.change_table :data_types do |t|
53
+ t.column :array_6, :cidr, :array => true
54
+ t.cidr :array_7, :array => true
55
+ end
56
+ end.should_not raise_exception
17
57
 
18
- lambda do
19
- connection.change_table :data_types do |t|
20
- t.column :cidr_6, :cidr, :array => true
58
+ columns = connection.columns(:data_types)
59
+
60
+ array_6 = columns.detect { |c| c.name == 'array_6'}
61
+ array_7 = columns.detect { |c| c.name == 'array_7'}
62
+
63
+ array_6.sql_type.should eq 'cidr[]'
64
+ array_7.sql_type.should eq 'cidr[]'
21
65
  end
22
- end.should_not raise_exception
66
+ end
67
+ end
68
+
69
+ context 'Default Values' do
70
+ describe 'String defaults' do
71
+ after { connection.drop_table :default_strings }
72
+ it 'creates array column with proper defaults' do
73
+ lambda do
74
+ connection.create_table :default_strings do |t|
75
+ t.string :names_1, :array => true, :default => []
76
+ t.string :names_2, :array => true, :default => '{}'
77
+ t.string :names_3, :array => true, :default => ['something']
78
+ t.string :names_4, :array => true, :default => '{something}'
79
+ end
80
+ end.should_not raise_exception
81
+ columns = connection.columns(:default_strings)
82
+
83
+ names_1 = columns.detect { |c| c.name == 'names_1' }
84
+ names_2 = columns.detect { |c| c.name == 'names_2' }
85
+ names_3 = columns.detect { |c| c.name == 'names_3' }
86
+ names_4 = columns.detect { |c| c.name == 'names_4' }
87
+
88
+ names_1.default.should eq []
89
+ names_2.default.should eq []
90
+ names_3.default.should eq ['something']
91
+ names_4.default.should eq ['something']
92
+ end
93
+ end
94
+
95
+ describe 'Integer defaults' do
96
+ after { connection.drop_table :default_integers }
97
+ it 'creates array column with proper defaults' do
98
+ lambda do
99
+ connection.create_table :default_integers do |t|
100
+ t.integer :numbers_1, :array => true, :default => []
101
+ t.integer :numbers_2, :array => true, :default => '{}'
102
+ t.integer :numbers_3, :array => true, :default => [3]
103
+ t.integer :numbers_4, :array => true, :default => '{4}'
104
+ end
105
+ end.should_not raise_exception
106
+ columns = connection.columns(:default_integers)
107
+
108
+ numbers_1 = columns.detect { |c| c.name == 'numbers_1' }
109
+ numbers_2 = columns.detect { |c| c.name == 'numbers_2' }
110
+ numbers_3 = columns.detect { |c| c.name == 'numbers_3' }
111
+ numbers_4 = columns.detect { |c| c.name == 'numbers_4' }
23
112
 
24
- lambda do
25
- connection.change_table :data_types do |t|
26
- t.cidr :cidr_7, :array => true
113
+ numbers_1.default.should eq []
114
+ numbers_2.default.should eq []
115
+ numbers_3.default.should eq [3]
116
+ numbers_4.default.should eq [4]
27
117
  end
28
- end.should_not raise_exception
29
-
30
- columns = connection.columns(:data_types)
31
- cidr_1 = columns.detect { |c| c.name == 'cidr_1'}
32
- cidr_2 = columns.detect { |c| c.name == 'cidr_2'}
33
- cidr_3 = columns.detect { |c| c.name == 'cidr_3'}
34
- cidr_4 = columns.detect { |c| c.name == 'cidr_4'}
35
- cidr_5 = columns.detect { |c| c.name == 'cidr_5'}
36
- cidr_6 = columns.detect { |c| c.name == 'cidr_6'}
37
- cidr_7 = columns.detect { |c| c.name == 'cidr_7'}
38
-
39
- cidr_1.sql_type.should eq 'cidr[]'
40
- cidr_2.sql_type.should eq 'cidr[]'
41
- cidr_3.sql_type.should eq 'cidr[]'
42
- cidr_4.sql_type.should eq 'cidr[]'
43
- cidr_5.sql_type.should eq 'cidr[]'
44
- cidr_6.sql_type.should eq 'cidr[]'
45
- cidr_7.sql_type.should eq 'cidr[]'
118
+ end
46
119
  end
47
120
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'CIDR migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
5
6
  it 'creates an cidr column' do
6
7
  lambda do
7
8
  connection.create_table :data_types do |t|
@@ -2,21 +2,48 @@ require 'spec_helper'
2
2
 
3
3
  describe 'index migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+
6
+ before do
7
+ connection.create_table :index_types do |t|
8
+ t.integer :col1, :array => true
9
+ t.integer :col2
10
+ t.text :col3
11
+ end
12
+ end
13
+
14
+ after { connection.drop_table :index_types }
15
+
5
16
  it 'creates special index' do
6
17
  lambda do
7
- connection.create_table :index_types do |t|
8
- t.integer :col1, :array => true
9
- t.integer :col2
10
- end
11
18
  connection.add_index(:index_types, :col1, :index_type => :gin)
12
- connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
13
19
  end.should_not raise_exception
14
20
 
15
21
  indexes = connection.indexes(:index_types)
16
22
  index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['col1']}
23
+
24
+ index_1.index_type.to_s.should eq 'gin'
25
+ end
26
+
27
+ it 'creates indexes with where clauses' do
28
+ lambda do
29
+ connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
30
+ end.should_not raise_exception
31
+
32
+ indexes = connection.indexes(:index_types)
17
33
  index_2 = indexes.detect { |c| c.columns.map(&:to_s) == ['col2']}
18
34
 
19
- index_1.index_type.to_s.should eq 'gin'
20
- index_2.where.should match /col2 > 50/
35
+ index_2.where.should match /col2 > 50/
36
+ end
37
+
38
+ it 'creates indexes with operator classes' do
39
+ lambda do
40
+ connection.add_index(:index_types, :col3, :index_type => :gin, :index_opclass => :gin_trgm_ops)
41
+ end.should_not raise_exception
42
+
43
+ indexes = connection.indexes(:index_types)
44
+ index_3 = indexes.detect { |c| c.columns.map(&:to_s) == ['col3']}
45
+
46
+ index_3.index_type.should eq :gin
47
+ index_3.index_opclass.should eq :gin_trgm_ops
21
48
  end
22
49
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'INET migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
5
6
  it 'creates an inet column' do
6
7
  lambda do
7
8
  connection.create_table :data_types do |t|
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'MACADDR migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
5
6
  it 'creates an macaddr column' do
6
7
  lambda do
7
8
  connection.create_table :data_types do |t|
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'UUID migrations' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
5
6
  it 'creates an uuid column' do
6
7
  lambda do
7
8
  connection.create_table :data_types do |t|
@@ -41,6 +41,18 @@ describe 'Models with array columns' do
41
41
  u.reload
42
42
  u.nick_names.should eq ['some', 'things']
43
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
44
56
  end
45
57
  end
46
58
 
@@ -97,7 +109,6 @@ describe 'Models with array columns' do
97
109
  context '#update_column' do
98
110
  describe 'setting a value via update_column' do
99
111
  it 'returns the value set when the record is retrieved' do
100
- pending #This fails, not sure where to fix this, takes different code path than #update_attribute
101
112
  user = User.create(:nick_names => [])
102
113
  user.reload
103
114
 
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'array schema dump' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :testings }
5
6
  it 'correctly generates cidr column statements' do
6
7
  stream = StringIO.new
7
8
  connection.create_table :testings do |t|
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'CIDR schema dump' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :testings }
5
6
  it 'correctly generates cidr column statements' do
6
7
  stream = StringIO.new
7
8
  connection.create_table :testings do |t|
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'extension schema dump' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ it 'correctly creates and exports database extensions' do
6
+ stream = StringIO.new
7
+ connection.add_extension 'hstore'
8
+
9
+ ActiveRecord::SchemaDumper.dump(connection, stream)
10
+ output = stream.string
11
+
12
+ output.should match /add_extension "hstore"/
13
+ end
14
+ end
@@ -1,14 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'index schema dumper' do
3
+ describe 'Index schema dumper' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
- it 'correctly generates index statements' do
5
+
6
+ before do
6
7
  connection.create_table :index_types do |t|
7
8
  t.integer :col1, :array => true
8
9
  t.integer :col2
10
+ t.text :col3
9
11
  end
12
+ end
13
+
14
+ after { connection.drop_table :index_types }
15
+
16
+ it 'handles index type parameters' do
10
17
  connection.add_index(:index_types, :col1, :index_type => :gin)
11
- connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
12
18
 
13
19
  stream = StringIO.new
14
20
  ActiveRecord::SchemaDumper.dump(connection, stream)
@@ -16,6 +22,25 @@ describe 'index schema dumper' do
16
22
 
17
23
  output.should match /:index_type => :gin/
18
24
  output.should_not match /:index_type => :btree/
25
+ end
26
+
27
+ it 'handles index where clauses' do
28
+ connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
29
+
30
+ stream = StringIO.new
31
+ ActiveRecord::SchemaDumper.dump(connection, stream)
32
+ output = stream.string
33
+
19
34
  output.should match /:where => "\(col2 > 50\)"/
20
35
  end
36
+
37
+ it 'dumps index operator classes' do
38
+ connection.add_index(:index_types, :col3, :index_type => :gin, :index_opclass => :gin_trgm_ops)
39
+
40
+ stream = StringIO.new
41
+ ActiveRecord::SchemaDumper.dump(connection, stream)
42
+ output = stream.string
43
+
44
+ output.should match /:index_type => :gin,\s+:index_opclass => :gin_trgm_ops/
45
+ end
21
46
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'INET schema dump' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :testings }
5
6
  it 'correctly generates inet column statements' do
6
7
  stream = StringIO.new
7
8
  connection.create_table :testings do |t|
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'MACADDR schema dump' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :testings }
5
6
  it 'correctly generates macaddr column statements' do
6
7
  stream = StringIO.new
7
8
  connection.create_table :testings do |t|
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'UUID schema dump' do
4
4
  let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :testings }
5
6
  it 'correctly generates uuid column statements' do
6
7
  stream = StringIO.new
7
8
  connection.create_table :testings do |t|
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,7 @@ require File.expand_path('../dummy/config/environment.rb', __FILE__)
5
5
  require 'rspec/rails'
6
6
  require 'rspec/autorun'
7
7
  require 'bourne'
8
+ require 'debugger' unless ENV['CI'] || (RUBY_PLATFORM =~ /java/)
8
9
 
9
10
  ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
10
11
 
@@ -13,8 +14,10 @@ ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
13
14
  Dir[File.join(ENGINE_RAILS_ROOT, 'spec/support/**/*.rb')].each { |f| require f }
14
15
 
15
16
  RSpec.configure do |config|
17
+ config.before(:suite) { ActiveRecord::Base.connection.add_extension('pg_trgm') }
18
+ config.use_transactional_fixtures = false
19
+ config.treat_symbols_as_metadata_keys_with_true_values = true
16
20
  config.mock_with :mocha
17
- config.use_transactional_fixtures = true
18
21
  config.backtrace_clean_patterns = [
19
22
  #/\/lib\d*\/ruby\//,
20
23
  #/bin\//,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postgres_ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-12 00:00:00.000000000 Z
12
+ date: 2012-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -159,15 +159,17 @@ files:
159
159
  - lib/postgres_ext/active_record.rb
160
160
  - lib/postgres_ext/active_record/connection_adapters.rb
161
161
  - lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb
162
+ - lib/postgres_ext/active_record/sanitization.rb
162
163
  - lib/postgres_ext/active_record/schema_dumper.rb
163
164
  - lib/postgres_ext/arel.rb
164
165
  - lib/postgres_ext/arel/nodes.rb
165
166
  - lib/postgres_ext/arel/nodes/contained_within.rb
166
167
  - lib/postgres_ext/arel/predications.rb
167
168
  - lib/postgres_ext/arel/visitors.rb
168
- - lib/postgres_ext/arel/visitors/to_sql.rb
169
+ - lib/postgres_ext/arel/visitors/visitor.rb
169
170
  - lib/postgres_ext/version.rb
170
171
  - postgres_ext.gemspec
172
+ - spec/arel/arel_spec.rb
171
173
  - spec/arel/array_spec.rb
172
174
  - spec/arel/inet_spec.rb
173
175
  - spec/columns/array_spec.rb
@@ -234,6 +236,7 @@ files:
234
236
  - spec/models/inet_spec.rb
235
237
  - spec/schema_dumper/array_spec.rb
236
238
  - spec/schema_dumper/cidr_spec.rb
239
+ - spec/schema_dumper/extension_spec.rb
237
240
  - spec/schema_dumper/index_spec.rb
238
241
  - spec/schema_dumper/inet_spec.rb
239
242
  - spec/schema_dumper/macaddr_spec.rb
@@ -253,7 +256,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
253
256
  version: '0'
254
257
  segments:
255
258
  - 0
256
- hash: -842218502147903775
259
+ hash: 1227873910843194517
257
260
  required_rubygems_version: !ruby/object:Gem::Requirement
258
261
  none: false
259
262
  requirements:
@@ -262,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
265
  version: '0'
263
266
  segments:
264
267
  - 0
265
- hash: -842218502147903775
268
+ hash: 1227873910843194517
266
269
  requirements: []
267
270
  rubyforge_project:
268
271
  rubygems_version: 1.8.23
@@ -270,6 +273,7 @@ signing_key:
270
273
  specification_version: 3
271
274
  summary: Extends ActiveRecord to handle native PostgreSQL data types
272
275
  test_files:
276
+ - spec/arel/arel_spec.rb
273
277
  - spec/arel/array_spec.rb
274
278
  - spec/arel/inet_spec.rb
275
279
  - spec/columns/array_spec.rb
@@ -336,6 +340,7 @@ test_files:
336
340
  - spec/models/inet_spec.rb
337
341
  - spec/schema_dumper/array_spec.rb
338
342
  - spec/schema_dumper/cidr_spec.rb
343
+ - spec/schema_dumper/extension_spec.rb
339
344
  - spec/schema_dumper/index_spec.rb
340
345
  - spec/schema_dumper/inet_spec.rb
341
346
  - spec/schema_dumper/macaddr_spec.rb