postgres_ext 0.3.1 → 0.4.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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +15 -5
  3. data/CHANGELOG.md +3 -0
  4. data/Gemfile +2 -2
  5. data/README.md +4 -0
  6. data/docs/type_casting.md +19 -0
  7. data/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb +149 -3
  8. data/lib/postgres_ext/active_record/relation/predicate_builder.rb +1 -1
  9. data/lib/postgres_ext/version.rb +1 -1
  10. data/spec/columns/array_spec.rb +3 -4
  11. data/spec/columns/ranges/daterange_spec.rb +37 -0
  12. data/spec/columns/ranges/int4range_spec.rb +38 -0
  13. data/spec/columns/ranges/int8range_spec.rb +38 -0
  14. data/spec/columns/ranges/numrange_spec.rb +37 -0
  15. data/spec/columns/ranges/tsrange_spec.rb +37 -0
  16. data/spec/dummy/app/models/person.rb +1 -1
  17. data/spec/dummy/config/application.rb +1 -1
  18. data/spec/dummy/db/migrate/20120501163758_create_people.rb +1 -0
  19. data/spec/dummy/db/schema.rb +9 -8
  20. data/spec/migrations/array_spec.rb +20 -0
  21. data/spec/migrations/ranges/daterange_spec.rb +27 -0
  22. data/spec/migrations/ranges/int4range_spec.rb +27 -0
  23. data/spec/migrations/ranges/int8range_spec.rb +27 -0
  24. data/spec/migrations/ranges/numrange_spec.rb +27 -0
  25. data/spec/migrations/ranges/tsrange_spec.rb +27 -0
  26. data/spec/migrations/ranges/tstzrange_spec.rb +27 -0
  27. data/spec/models/ranges/daterange_spec.rb +88 -0
  28. data/spec/models/ranges/int4range_spec.rb +85 -0
  29. data/spec/models/ranges/int8range_spec.rb +85 -0
  30. data/spec/models/ranges/numrange_spec.rb +85 -0
  31. data/spec/models/ranges/tsrange_spec.rb +89 -0
  32. data/spec/models/ranges/tstzrange_spec.rb +89 -0
  33. data/spec/queries/sanity_spec.rb +1 -0
  34. data/spec/schema_dumper/array_spec.rb +1 -1
  35. data/spec/schema_dumper/ranges/daterange_spec.rb +18 -0
  36. data/spec/schema_dumper/ranges/int4range_spec.rb +18 -0
  37. data/spec/schema_dumper/ranges/int8range_spec.rb +18 -0
  38. data/spec/schema_dumper/ranges/numrange_spec.rb +18 -0
  39. data/spec/schema_dumper/ranges/tsrange_spec.rb +18 -0
  40. data/spec/schema_dumper/ranges/tstzrange_spec.rb +17 -0
  41. metadata +53 -27
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'int8range column' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+ let!(:int8_range_column) { ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new 'field', nil, 'int8range'}
6
+
7
+ describe '#type_class' do
8
+ it 'converts an end-inclusive PostgreSQL integer range to a Ruby range' do
9
+ int8_range_column.type_cast('[0,4]').should eq 0..4
10
+ end
11
+
12
+ it 'converts an end-exclusive PostgreSQL integer range to a Ruby range' do
13
+ int8_range_column.type_cast('[0,4)').should eq 0...4
14
+ end
15
+
16
+ it 'converts an infinite PostgreSQL integer range to a Ruby range' do
17
+ int8_range_column.type_cast('(,4)').should eq -(1.0/0.0)...4
18
+ int8_range_column.type_cast('[0,)').should eq 0..(1.0/0.0)
19
+ end
20
+ end
21
+
22
+ describe 'int8 range to SQL statment conversion' do
23
+ it 'returns an end-inclusive PostgreSQL range' do
24
+ value = int8_range_column.type_cast('[0,4]')
25
+ adapter.type_cast(value, int8_range_column).should eq '[0,4]'
26
+ end
27
+ it 'returns an end-exclusive PostgreSQL range' do
28
+ value = int8_range_column.type_cast('[0,4)')
29
+ adapter.type_cast(value, int8_range_column).should eq '[0,4)'
30
+ end
31
+ it 'converts an infinite PostgreSQL integer range to a Ruby range' do
32
+ value = int8_range_column.type_cast('(,4)')
33
+ adapter.type_cast(value, int8_range_column).should eq '(,4)'
34
+ value = int8_range_column.type_cast('[0,)')
35
+ adapter.type_cast(value, int8_range_column).should eq '[0,)'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'numrange column' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+ let!(:numeric_range_column) { ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new 'field', nil, 'numrange'}
6
+ describe '#type_class' do
7
+ it 'converts an end-inclusive PostgreSQL integer range to a Ruby range' do
8
+ numeric_range_column.type_cast('[0,4.1]').should eq 0.0..4.1
9
+ end
10
+
11
+ it 'converts an end-exclusive PostgreSQL integer range to a Ruby range' do
12
+ numeric_range_column.type_cast('[0,4)').should eq 0.0...4.0
13
+ end
14
+
15
+ it 'converts an infinite PostgreSQL integer range to a Ruby range' do
16
+ numeric_range_column.type_cast('(,4)').should eq -(1.0/0.0)...4.0
17
+ numeric_range_column.type_cast('[0,)').should eq 0.0..(1.0/0.0)
18
+ end
19
+ end
20
+
21
+ describe 'Numeric range to SQL statment conversion' do
22
+ it 'returns an end-inclusive PostgreSQL range' do
23
+ value = numeric_range_column.type_cast('[0,4.0]')
24
+ adapter.type_cast(value, numeric_range_column).should eq '[0.0,4.0]'
25
+ end
26
+ it 'returns an end-exclusive PostgreSQL range' do
27
+ value = numeric_range_column.type_cast('[0,4.0)')
28
+ adapter.type_cast(value, numeric_range_column).should eq '[0.0,4.0)'
29
+ end
30
+ it 'converts an infinite PostgreSQL integer range to a Ruby range' do
31
+ value = numeric_range_column.type_cast('(,4.0)')
32
+ adapter.type_cast(value, numeric_range_column).should eq '(,4.0)'
33
+ value = numeric_range_column.type_cast('[0,)')
34
+ adapter.type_cast(value, numeric_range_column).should eq '[0.0,)'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'tsrange column' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+ let!(:ts_range_column) { ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new 'field', nil, 'tsrange'}
6
+
7
+ describe '#type_class' do
8
+ it 'converts an end-inclusive PostgreSQL integer range to a Ruby range' do
9
+ ts_range_column.type_cast('[2011-01-01 12:34:00,2012-01-31 08:00:01]').should eq Time.new(2011, 01, 01, 12, 34)..Time.new(2012, 01, 31, 8, 0, 1)
10
+ end
11
+
12
+ it 'converts an end-exclusive PostgreSQL integer range to a Ruby range' do
13
+ ts_range_column.type_cast('[2011-01-01 12:34:00,2012-01-31 08:00:01)').should eq Time.new(2011, 01, 01, 12, 34)...Time.new(2012, 01, 31, 8, 0, 1)
14
+ end
15
+
16
+ #it 'converts an infinite PostgreSQL integer range to a Ruby range' do
17
+ ## Cannot have a range from -Infinity to a ts
18
+ ## ts_range_column.type_cast('(,2011-01-01)').should eq -(1.0/0.0)...4
19
+ #ts_range_column.type_cast('[2011-01-01 12:34:00,)').should eq Time.new(2011,01,01, 12, 34)..(1.0/0.0)
20
+ #end
21
+ end
22
+
23
+ describe 'Time range to SQL statment conversion' do
24
+ it 'returns an end-inclusive PostgreSQL range' do
25
+ value = ts_range_column.type_cast('[2011-01-01 12:34:00,2012-01-31 08:00:01]')
26
+ adapter.type_cast(value, ts_range_column).should eq '[2011-01-01 12:34:00,2012-01-31 08:00:01]'
27
+ end
28
+ it 'returns an end-exclusive PostgreSQL range' do
29
+ value = ts_range_column.type_cast('[2011-01-01 12:34:00,2012-01-31 08:00:01)')
30
+ adapter.type_cast(value, ts_range_column).should eq '[2011-01-01 12:34:00,2012-01-31 08:00:01)'
31
+ end
32
+ #it 'converts an infinite PostgreSQL integer range to a Ruby range' do
33
+ #value = ts_range_column.type_cast('[2011-01-01 12:34:00,)')
34
+ #adapter.type_cast(value, ts_range_column).should eq '[2011-01-01 12:34:00,)'
35
+ #end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  class Person < ActiveRecord::Base
2
- attr_accessible :ip, :tags
2
+ attr_accessible :ip, :tags, :tag_ids, :biography, :lucky_number, :int_range
3
3
  end
@@ -27,7 +27,7 @@ module Dummy
27
27
 
28
28
  # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
29
  # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
- # config.time_zone = 'Central Time (US & Canada)'
30
+ config.time_zone = 'Eastern Time (US & Canada)'
31
31
 
32
32
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
33
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
@@ -7,6 +7,7 @@ class CreatePeople < ActiveRecord::Migration
7
7
  t.string :tags, :array => true
8
8
  t.text :biography
9
9
  t.integer :lucky_number
10
+ t.numrange :num_range
10
11
 
11
12
  t.timestamps
12
13
  end
@@ -14,14 +14,15 @@
14
14
  ActiveRecord::Schema.define(:version => 20120501163758) do
15
15
 
16
16
  create_table "people", :force => true do |t|
17
- t.inet "ip"
18
- t.cidr "subnet"
19
- t.integer "tag_ids", :array => true
20
- t.string "tags", :array => true
21
- t.text "biography"
22
- t.integer "lucky_number"
23
- t.datetime "created_at", :null => false
24
- t.datetime "updated_at", :null => false
17
+ t.inet "ip"
18
+ t.cidr "subnet"
19
+ t.integer "tag_ids", :array => true
20
+ t.string "tags", :array => true
21
+ t.text "biography"
22
+ t.integer "lucky_number"
23
+ t.num_rang "num_range"
24
+ t.datetime "created_at", :null => false
25
+ t.datetime "updated_at", :null => false
25
26
  end
26
27
 
27
28
  end
@@ -82,6 +82,26 @@ describe 'Array migrations' do
82
82
  end
83
83
  end
84
84
 
85
+ context 'Change Column' do
86
+ after { connection.drop_table :data_types }
87
+ it 'updates the column definitions' do
88
+ lambda do
89
+ connection.create_table :data_types do |t|
90
+ t.integer :array_1, :array => true, :default => []
91
+ end
92
+
93
+ connection.change_column :data_types, :array_1, :integer, :array => true, :default => [], :null => false
94
+ end.should_not raise_exception
95
+
96
+ columns = connection.columns(:data_types)
97
+
98
+ array_1 = columns.detect { |c| c.name == 'array_1'}
99
+ array_1.sql_type.should eq 'integer[]'
100
+ array_1.default.should eq []
101
+ array_1.null.should be_false
102
+ end
103
+ end
104
+
85
105
  context 'Default Values' do
86
106
  describe 'String defaults' do
87
107
  after { connection.drop_table :default_strings }
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'daterange migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an daterange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.daterange :date_range_1
11
+ t.daterange :date_range_2, :date_range_3
12
+ t.column :date_range_4, :daterange
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ date_range_1 = columns.detect { |c| c.name == 'date_range_1'}
18
+ date_range_2 = columns.detect { |c| c.name == 'date_range_2'}
19
+ date_range_3 = columns.detect { |c| c.name == 'date_range_3'}
20
+ date_range_4 = columns.detect { |c| c.name == 'date_range_4'}
21
+
22
+ date_range_1.sql_type.should eq 'daterange'
23
+ date_range_2.sql_type.should eq 'daterange'
24
+ date_range_3.sql_type.should eq 'daterange'
25
+ date_range_4.sql_type.should eq 'daterange'
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'int4range migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an numrange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.int4range :range_1
11
+ t.int4range :range_2, :range_3
12
+ t.column :range_4, :int4range
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ range_1 = columns.detect { |c| c.name == 'range_1'}
18
+ range_2 = columns.detect { |c| c.name == 'range_2'}
19
+ range_3 = columns.detect { |c| c.name == 'range_3'}
20
+ range_4 = columns.detect { |c| c.name == 'range_4'}
21
+
22
+ range_1.sql_type.should eq 'int4range'
23
+ range_2.sql_type.should eq 'int4range'
24
+ range_3.sql_type.should eq 'int4range'
25
+ range_4.sql_type.should eq 'int4range'
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'int8range migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an numrange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.int8range :range_1
11
+ t.int8range :range_2, :range_3
12
+ t.column :range_4, :int8range
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ range_1 = columns.detect { |c| c.name == 'range_1'}
18
+ range_2 = columns.detect { |c| c.name == 'range_2'}
19
+ range_3 = columns.detect { |c| c.name == 'range_3'}
20
+ range_4 = columns.detect { |c| c.name == 'range_4'}
21
+
22
+ range_1.sql_type.should eq 'int8range'
23
+ range_2.sql_type.should eq 'int8range'
24
+ range_3.sql_type.should eq 'int8range'
25
+ range_4.sql_type.should eq 'int8range'
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'numrange migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an numrange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.numrange :num_range_1
11
+ t.numrange :num_range_2, :num_range_3
12
+ t.column :num_range_4, :numrange
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ num_range_1 = columns.detect { |c| c.name == 'num_range_1'}
18
+ num_range_2 = columns.detect { |c| c.name == 'num_range_2'}
19
+ num_range_3 = columns.detect { |c| c.name == 'num_range_3'}
20
+ num_range_4 = columns.detect { |c| c.name == 'num_range_4'}
21
+
22
+ num_range_1.sql_type.should eq 'numrange'
23
+ num_range_2.sql_type.should eq 'numrange'
24
+ num_range_3.sql_type.should eq 'numrange'
25
+ num_range_4.sql_type.should eq 'numrange'
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'tsrange migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an tsrange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.tsrange :ts_range_1
11
+ t.tsrange :ts_range_2, :ts_range_3
12
+ t.column :ts_range_4, :tsrange
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ ts_range_1 = columns.detect { |c| c.name == 'ts_range_1'}
18
+ ts_range_2 = columns.detect { |c| c.name == 'ts_range_2'}
19
+ ts_range_3 = columns.detect { |c| c.name == 'ts_range_3'}
20
+ ts_range_4 = columns.detect { |c| c.name == 'ts_range_4'}
21
+
22
+ ts_range_1.sql_type.should eq 'tsrange'
23
+ ts_range_2.sql_type.should eq 'tsrange'
24
+ ts_range_3.sql_type.should eq 'tsrange'
25
+ ts_range_4.sql_type.should eq 'tsrange'
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'tstzrange migrations' do
4
+ let!(:connection) { ActiveRecord::Base.connection }
5
+ after { connection.drop_table :data_types }
6
+
7
+ it 'creates an tstzrange column' do
8
+ lambda do
9
+ connection.create_table :data_types do |t|
10
+ t.tstzrange :tstz_range_1
11
+ t.tstzrange :tstz_range_2, :tstz_range_3
12
+ t.column :tstz_range_4, :tstzrange
13
+ end
14
+ end.should_not raise_exception
15
+
16
+ columns = connection.columns(:data_types)
17
+ tstz_range_1 = columns.detect { |c| c.name == 'tstz_range_1'}
18
+ tstz_range_2 = columns.detect { |c| c.name == 'tstz_range_2'}
19
+ tstz_range_3 = columns.detect { |c| c.name == 'tstz_range_3'}
20
+ tstz_range_4 = columns.detect { |c| c.name == 'tstz_range_4'}
21
+
22
+ tstz_range_1.sql_type.should eq 'tstzrange'
23
+ tstz_range_2.sql_type.should eq 'tstzrange'
24
+ tstz_range_3.sql_type.should eq 'tstzrange'
25
+ tstz_range_4.sql_type.should eq 'tstzrange'
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Models with daterange columns' do
4
+ let!(:adapter) { ActiveRecord::Base.connection }
5
+
6
+ context 'no default value, range' do
7
+ before do
8
+ adapter.create_table :date_rangers, :force => true do |t|
9
+ t.daterange :best_estimate
10
+ end
11
+ class DateRanger < ActiveRecord::Base
12
+ attr_accessible :best_estimate
13
+ end
14
+ end
15
+
16
+ after do
17
+ adapter.drop_table :date_rangers
18
+ Object.send(:remove_const, :DateRanger)
19
+ end
20
+
21
+ describe '#create' do
22
+ it 'creates an record when there is no assignment' do
23
+ range = DateRanger.create()
24
+ range.reload
25
+ range.best_estimate.should eq nil
26
+ end
27
+
28
+ it 'creates an record with a range' do
29
+ date_range = Date.new(2011, 01, 01)..Date.new(2012, 01, 31)
30
+ range = DateRanger.create( :best_estimate => date_range)
31
+ range.reload
32
+ range.best_estimate.should eq Date.new(2011, 01, 01)...Date.new(2012, 02, 01)
33
+ end
34
+ end
35
+
36
+ describe 'range assignment' do
37
+ it 'updates an record with an range string' do
38
+ date_range = Date.new(2011, 01, 01)..Date.new(2012, 01, 31)
39
+ new_date_range = Date.new(2012, 01, 01)...Date.new(2012, 02, 01)
40
+ range = DateRanger.create( :best_estimate => date_range)
41
+ range.best_estimate = new_date_range
42
+ range.save
43
+
44
+ range.reload
45
+ range.best_estimate.should eq new_date_range
46
+ end
47
+
48
+ it 'converts empty strings to nil' do
49
+ range = DateRanger.create
50
+ range.best_estimate = ''
51
+ range.save
52
+
53
+ range.reload
54
+ range.best_estimate.should eq nil
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'default value, date range' do
60
+ before do
61
+ adapter.create_table :date_default_rangers, :force => true do |t|
62
+ t.daterange :best_estimate, :default => Date.new(2011, 01, 01)..Date.new(2011, 01, 31)
63
+ end
64
+ class DateDefaultRanger < ActiveRecord::Base
65
+ attr_accessible :best_estimate
66
+ end
67
+ end
68
+
69
+ after do
70
+ adapter.drop_table :date_default_rangers
71
+ Object.send(:remove_const, :DateDefaultRanger)
72
+ end
73
+
74
+ describe '#create' do
75
+ it 'creates an record when there is no assignment' do
76
+ range = DateDefaultRanger.create()
77
+ range.reload
78
+ range.best_estimate.should eq Date.new(2011, 01, 01)...Date.new(2011, 02, 01)
79
+ end
80
+
81
+ it 'creates an record with a range' do
82
+ range = DateDefaultRanger.create(:best_estimate => Date.new(2012, 01, 01)..Date.new(2012, 12, 31))
83
+ range.reload
84
+ range.best_estimate.should eq Date.new(2012, 01, 01)...Date.new(2013, 01, 01)
85
+ end
86
+ end
87
+ end
88
+ end