schema_plus_pg_types 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.idea/vcs.xml +6 -0
- data/.travis.yml +16 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +116 -0
- data/Rakefile +9 -0
- data/gemfiles/Gemfile.base +4 -0
- data/gemfiles/activerecord-5.0/Gemfile.base +3 -0
- data/gemfiles/activerecord-5.0/Gemfile.postgresql +10 -0
- data/lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/adapter.rb +29 -0
- data/lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/column_methods.rb +24 -0
- data/lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/oid/interval.rb +47 -0
- data/lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/schema_statements.rb +27 -0
- data/lib/schema_plus/pg_types/active_support/duration_fixes.rb +46 -0
- data/lib/schema_plus/pg_types/version.rb +5 -0
- data/lib/schema_plus/pg_types.rb +17 -0
- data/lib/schema_plus_pg_types.rb +1 -0
- data/migration_spec.rb +116 -0
- data/schema_dev.yml +6 -0
- data/schema_plus_pg_types.gemspec +29 -0
- data/spec/migration_spec.rb +97 -0
- data/spec/sanity_spec.rb +13 -0
- data/spec/spec_helper.rb +21 -0
- metadata +189 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ab8c1d45e50a98115b2794690905697521819a2a
|
4
|
+
data.tar.gz: d20941350da12ca252504e923683b1809e9a7783
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c04aef0c17a5ef56363a4a5585ad893caeb6e585e0707e2175aa698df7621f99b444fba1c7143ccff5d8c4d1e81649c0bd9f4831f5a3bb48614fb70f3eb9cdae
|
7
|
+
data.tar.gz: ae033ed292e5c7d0ef043b7c7b667316b1ba3e89e362f9568686a373725f141dd448ef65789ffa683da62f062b6d05f1960ab84f861c22882736632ff4adc157
|
data/.gitignore
ADDED
data/.idea/vcs.xml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was auto-generated by the schema_dev tool, based on the data in
|
2
|
+
# ./schema_dev.yml
|
3
|
+
# Please do not edit this file; any changes will be overwritten next time
|
4
|
+
# schema_dev gets run.
|
5
|
+
---
|
6
|
+
sudo: false
|
7
|
+
rvm:
|
8
|
+
- 2.3.1
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/activerecord-5.0/Gemfile.postgresql
|
11
|
+
env: POSTGRESQL_DB_USER=postgres
|
12
|
+
addons:
|
13
|
+
postgresql: '9.4'
|
14
|
+
before_script: bundle exec rake create_databases
|
15
|
+
after_script: bundle exec rake drop_databases
|
16
|
+
script: bundle exec rake travis
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Boaz Yaniv
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
[](http://badge.fury.io/rb/schema_plus_pg_types)
|
2
|
+
[](http://travis-ci.org/SchemaPlus/schema_plus_pg_types)
|
3
|
+
[](https://coveralls.io/r/SchemaPlus/schema_plus_pg_types)
|
4
|
+
[](https://gemnasium.com/SchemaPlus/schema_plus_pg_types)
|
5
|
+
|
6
|
+
# SchemaPlus::PgTypes
|
7
|
+
|
8
|
+
SchemaPlus::PgTypes adds better support for PostgreSQL-specific types to
|
9
|
+
ActiveRecord, mapping them to higher-level types instead of raw strings.
|
10
|
+
Currently this only includes the PostgreSQL `interval` type, which is mapped
|
11
|
+
to `ActiveSupport::Duration`.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - begin -->
|
16
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
17
|
+
As usual:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "schema_plus_pg_types" # in a Gemfile
|
21
|
+
gem.add_dependency "schema_plus_pg_types" # in a .gemspec
|
22
|
+
```
|
23
|
+
|
24
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - end -->
|
25
|
+
|
26
|
+
|
27
|
+
## Compatibility
|
28
|
+
|
29
|
+
SchemaPlus::PgTypes is tested on:
|
30
|
+
|
31
|
+
<!-- SCHEMA_DEV: MATRIX - begin -->
|
32
|
+
<!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
|
33
|
+
* ruby **2.3.1** with activerecord **5.0**, using **postgresql**
|
34
|
+
|
35
|
+
<!-- SCHEMA_DEV: MATRIX - end -->
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
This gem adds a little more syntactic sugar for defining PostgreSQL types
|
40
|
+
in your migrations. You can now simply write:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
create_table :events do |t|
|
44
|
+
t.interval :event_duration
|
45
|
+
end
|
46
|
+
````
|
47
|
+
|
48
|
+
Instead of:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
create_table :events do |t|
|
52
|
+
t.column :event_duration, :interval
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
The `event_duration` column will be represented as `ActiveSupport::Duration`
|
57
|
+
in your model, so you should be able to use it without the usual fuss:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
long_events = Event.where(1.hours..10.hours)
|
61
|
+
````
|
62
|
+
|
63
|
+
SchemaPlus::PgTypes is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails ActiveRecord extension gems.
|
64
|
+
|
65
|
+
|
66
|
+
## History
|
67
|
+
|
68
|
+
* 0.1.0 - Initial release
|
69
|
+
|
70
|
+
## Development & Testing
|
71
|
+
|
72
|
+
Are you interested in contributing to SchemaPlus::PgTypes? Thanks! Please follow
|
73
|
+
the standard protocol: fork, feature branch, develop, push, and issue pull
|
74
|
+
request.
|
75
|
+
|
76
|
+
Some things to know about to help you develop and test:
|
77
|
+
|
78
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - begin -->
|
79
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
80
|
+
* **schema_dev**: SchemaPlus::PgTypes uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
|
81
|
+
facilitate running rspec tests on the matrix of ruby, activerecord, and database
|
82
|
+
versions that the gem supports, both locally and on
|
83
|
+
[travis-ci](http://travis-ci.org/SchemaPlus/schema_plus_pg_types)
|
84
|
+
|
85
|
+
To to run rspec locally on the full matrix, do:
|
86
|
+
|
87
|
+
$ schema_dev bundle install
|
88
|
+
$ schema_dev rspec
|
89
|
+
|
90
|
+
You can also run on just one configuration at a time; For info, see `schema_dev --help` or the [schema_dev](https://github.com/SchemaPlus/schema_dev) README.
|
91
|
+
|
92
|
+
The matrix of configurations is specified in `schema_dev.yml` in
|
93
|
+
the project root.
|
94
|
+
|
95
|
+
|
96
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - end -->
|
97
|
+
|
98
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - begin -->
|
99
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
100
|
+
* **schema_plus_core**: SchemaPlus::PgTypes uses the SchemaPlus::Core API that
|
101
|
+
provides middleware callback stacks to make it easy to extend
|
102
|
+
ActiveRecord's behavior. If that API is missing something you need for
|
103
|
+
your contribution, please head over to
|
104
|
+
[schema_plus_core](https://github.com/SchemaPlus/schema_plus_core) and open
|
105
|
+
an issue or pull request.
|
106
|
+
|
107
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - end -->
|
108
|
+
|
109
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - begin -->
|
110
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
111
|
+
* **schema_monkey**: SchemaPlus::PgTypes is implemented as a
|
112
|
+
[schema_monkey](https://github.com/SchemaPlus/schema_monkey) client,
|
113
|
+
using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s
|
114
|
+
convention-based protocols for extending ActiveRecord and using middleware stacks.
|
115
|
+
|
116
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - end -->
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
2
|
+
require_relative 'oid/interval'
|
3
|
+
|
4
|
+
module SchemaPlus::PgTypes
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module PostgreSQLAdapter
|
8
|
+
def configure_connection
|
9
|
+
super
|
10
|
+
|
11
|
+
|
12
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
13
|
+
execute('SET intervalstyle = iso_8601', 'SCHEMA')
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_type_map(m)
|
17
|
+
super(m)
|
18
|
+
m.register_type 'interval' do |*_, sql_type|
|
19
|
+
precision = extract_precision(sql_type)
|
20
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Interval.new(precision: precision)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:interval] = { name: 'interval' }
|
29
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SchemaPlus::PgTypes
|
2
|
+
module ActiveRecord
|
3
|
+
module ConnectionAdapters
|
4
|
+
module PostgreSQL
|
5
|
+
module ColumnMethods
|
6
|
+
def interval(*args, **options)
|
7
|
+
args.each { |name| column(name, :interval, options) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
module Table
|
11
|
+
def interval(*args, **options)
|
12
|
+
args.each { |name| column(name, :interval, options) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module TableDefinition
|
17
|
+
def interval(*args, **options)
|
18
|
+
args.each { |name| column(name, :interval, options) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/duration'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module PostgreSQL
|
6
|
+
module OID # :nodoc:
|
7
|
+
class Interval < Type::Value # :nodoc:
|
8
|
+
def type
|
9
|
+
:interval
|
10
|
+
end
|
11
|
+
|
12
|
+
def cast_value(value)
|
13
|
+
case value
|
14
|
+
when ::ActiveSupport::Duration
|
15
|
+
value
|
16
|
+
when ::String
|
17
|
+
# Will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+ on string of unknown format
|
18
|
+
::ActiveSupport::Duration.parse(value)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Dump the ISO8601 date inside the schema
|
25
|
+
def type_cast_for_schema(value)
|
26
|
+
"\"#{value.iso8601}\""
|
27
|
+
end
|
28
|
+
|
29
|
+
def serialize(value)
|
30
|
+
case value
|
31
|
+
when ::ActiveSupport::Duration
|
32
|
+
value.iso8601(precision: self.precision)
|
33
|
+
when ::Numeric
|
34
|
+
# Sometimes operations on Times returns just float number of seconds so we need to handle that.
|
35
|
+
# Example: Time.current - (Time.current + 1.hour) # => -3600.000001776 (Float)
|
36
|
+
duration = ::ActiveSupport::Duration.new(value.to_i, seconds: value.to_i)
|
37
|
+
duration.iso8601(precision: self.precision)
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/schema_statements.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'oid/interval'
|
2
|
+
|
3
|
+
module SchemaPlus::PgTypes
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
module PostgreSQL
|
7
|
+
module SchemaStatements
|
8
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
9
|
+
case type.to_s
|
10
|
+
when 'interval'
|
11
|
+
return super(type, limit, precision, scale) unless precision
|
12
|
+
|
13
|
+
case precision
|
14
|
+
when 0..6; "interval(#{precision})"
|
15
|
+
else raise(ActiveRecordError, "No interval type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super(type, limit, precision, scale)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/duration'
|
2
|
+
require 'active_support/duration/iso8601_serializer'
|
3
|
+
|
4
|
+
class ActiveSupport::Duration
|
5
|
+
# Patch rails inconsistent handling of ISO8601 dates.
|
6
|
+
# ActiveSupport::Duration uses fixes values for date and time elements:
|
7
|
+
# 1 Year = 365.25 days, 1 Month = 30 days, 1 Day = 24 hours, etc.
|
8
|
+
# The original parse() implementation advanced the current time by the
|
9
|
+
# specified date and time elements and then subtracted the current time
|
10
|
+
# again to get the duration. This produces inconsistent results since
|
11
|
+
# actual months and years can vary in length.
|
12
|
+
# In short, without this fix, the following expression wouldn't necessarily be true:
|
13
|
+
# 2.months == ActiveSupport::Duration.parse(2.months)
|
14
|
+
def self.parse(iso8601duration)
|
15
|
+
parts = ISO8601Parser.new(iso8601duration).parse!
|
16
|
+
total_seconds = 0
|
17
|
+
parts.each do |part, value|
|
18
|
+
total_seconds += value.send part.to_sym
|
19
|
+
end
|
20
|
+
new(total_seconds, parts)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Patch rails to treat zero durations
|
24
|
+
# This bug was fixed in the following commit, but is still not part of Rails 5.0
|
25
|
+
# https://github.com/rails/rails/commit/629dde297ce5e4b7614fbb0218f2593effaf7e28
|
26
|
+
class ISO8601Serializer
|
27
|
+
def serialize
|
28
|
+
parts, sign = normalize
|
29
|
+
return 'PT0S'.freeze if parts.empty?
|
30
|
+
|
31
|
+
output = 'P'
|
32
|
+
output << "#{parts[:years]}Y" if parts.key?(:years)
|
33
|
+
output << "#{parts[:months]}M" if parts.key?(:months)
|
34
|
+
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
35
|
+
output << "#{parts[:days]}D" if parts.key?(:days)
|
36
|
+
time = ''
|
37
|
+
time << "#{parts[:hours]}H" if parts.key?(:hours)
|
38
|
+
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
|
39
|
+
if parts.key?(:seconds)
|
40
|
+
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
|
41
|
+
end
|
42
|
+
output << "T#{time}" if time.present?
|
43
|
+
"#{sign}#{output}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'schema_plus/core'
|
2
|
+
|
3
|
+
require_relative 'pg_types/active_record/connection_adapters/postgresql/adapter'
|
4
|
+
require_relative 'pg_types/active_record/connection_adapters/postgresql/schema_statements'
|
5
|
+
require_relative 'pg_types/active_record/connection_adapters/postgresql/column_methods'
|
6
|
+
require_relative 'pg_types/active_support/duration_fixes'
|
7
|
+
require_relative 'pg_types/version'
|
8
|
+
|
9
|
+
# Load any mixins to ActiveRecord modules, such as:
|
10
|
+
#
|
11
|
+
#require_relative 'pg_types/active_record/base'
|
12
|
+
|
13
|
+
# Load any middleware, such as:
|
14
|
+
#
|
15
|
+
# require_relative 'pg_types/middleware/model'
|
16
|
+
|
17
|
+
SchemaMonkey.register SchemaPlus::PgTypes
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'schema_plus/pg_types'
|
data/migration_spec.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Item < ActiveRecord::Base
|
4
|
+
end
|
5
|
+
|
6
|
+
class AOnes < ActiveRecord::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
class ABOnes < ActiveRecord::Base
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Migration" do
|
13
|
+
|
14
|
+
let(:migration) { ActiveRecord::Migration }
|
15
|
+
let(:connection) { ActiveRecord::Base.connection }
|
16
|
+
let(:schema) { ActiveRecord::Schema }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
define_schema_and_data
|
20
|
+
end
|
21
|
+
|
22
|
+
context "creation" do
|
23
|
+
it "should create correct views" do
|
24
|
+
expect(AOnes.all.collect(&:s)).to eq(%W[one_one one_two])
|
25
|
+
expect(ABOnes.all.collect(&:s)).to eq(%W[one_one])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "duplicate creation" do
|
30
|
+
before(:each) do
|
31
|
+
migration.create_view('dupe_me', 'SELECT * FROM items WHERE (a=1)')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should raise an error by default" do
|
35
|
+
expect {migration.create_view('dupe_me', 'SELECT * FROM items WHERE (a=2)')}.to raise_error ActiveRecord::StatementInvalid
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should override existing definition if :force true" do
|
39
|
+
migration.create_view('dupe_me', 'SELECT * FROM items WHERE (a=2)', :force => true)
|
40
|
+
expect(connection.view_definition('dupe_me')).to match(%r{WHERE .*a.*=.*2}i)
|
41
|
+
end
|
42
|
+
|
43
|
+
context "Postgres and MySQL only", :sqlite3 => :skip do
|
44
|
+
it "should override existing definition if :allow_replace is true" do
|
45
|
+
migration.create_view('dupe_me', 'SELECT * FROM items WHERE (a=2)', :allow_replace => true)
|
46
|
+
expect(connection.view_definition('dupe_me')).to match(%r{WHERE .*a.*=.*2}i)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "dropping" do
|
52
|
+
it "should raise an error if the view doesn't exist" do
|
53
|
+
expect { migration.drop_view('doesnt_exist') }.to raise_error ActiveRecord::StatementInvalid
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should fail silently when using if_exists option" do
|
57
|
+
expect { migration.drop_view('doesnt_exist', :if_exists => true) }.not_to raise_error
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with a view that exists" do
|
61
|
+
before { migration.create_view('view_that_exists', 'SELECT * FROM items WHERE (a=1)') }
|
62
|
+
|
63
|
+
it "should succeed" do
|
64
|
+
migration.drop_view('view_that_exists')
|
65
|
+
expect(connection.views).not_to include('view_that_exists')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "rollback" do
|
71
|
+
it "properly rolls back a create_view" do
|
72
|
+
m = Class.new ::ActiveRecord::Migration.latest_version do
|
73
|
+
define_method(:change) {
|
74
|
+
create_view :copy, "SELECT * FROM items"
|
75
|
+
}
|
76
|
+
end
|
77
|
+
m.migrate(:up)
|
78
|
+
expect(connection.views).to include("copy")
|
79
|
+
m.migrate(:down)
|
80
|
+
expect(connection.views).not_to include("copy")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "raises error for drop_view" do
|
84
|
+
m = Class.new ::ActiveRecord::Migration.latest_version do
|
85
|
+
define_method(:change) {
|
86
|
+
drop_view :a_ones
|
87
|
+
}
|
88
|
+
end
|
89
|
+
expect { m.migrate(:down) }.to raise_error(::ActiveRecord::IrreversibleMigration)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def define_schema_and_data
|
97
|
+
connection.views.each do |view| connection.drop_view view end
|
98
|
+
connection.tables_only.each do |table| connection.drop_table table, cascade: true end
|
99
|
+
|
100
|
+
schema.define do
|
101
|
+
|
102
|
+
create_table :items, :force => true do |t|
|
103
|
+
t.integer :a
|
104
|
+
t.integer :b
|
105
|
+
t.string :s
|
106
|
+
end
|
107
|
+
|
108
|
+
create_view :a_ones, Item.select('b, s').where(:a => 1)
|
109
|
+
create_view :ab_ones, "select s from a_ones where b = 1"
|
110
|
+
end
|
111
|
+
connection.execute "insert into items (a, b, s) values (1, 1, 'one_one')"
|
112
|
+
connection.execute "insert into items (a, b, s) values (1, 2, 'one_two')"
|
113
|
+
connection.execute "insert into items (a, b, s) values (2, 1, 'two_one')"
|
114
|
+
connection.execute "insert into items (a, b, s) values (2, 2, 'two_two')"
|
115
|
+
end
|
116
|
+
end
|
data/schema_dev.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'schema_plus/pg_types/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "schema_plus_pg_types"
|
8
|
+
gem.version = SchemaPlus::PgTypes::VERSION
|
9
|
+
gem.authors = ["Boaz Yaniv"]
|
10
|
+
gem.email = ["boazyan@gmail.com"]
|
11
|
+
gem.summary = %q{Adds supports for PostgreSQL types that were left out by Rails}
|
12
|
+
gem.homepage = "https://github.com/SchemaPlus/schema_plus_pg_types"
|
13
|
+
gem.license = "MIT"
|
14
|
+
|
15
|
+
gem.files = `git ls-files -z`.split("\x0")
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "activerecord", "~> 5.0"
|
21
|
+
gem.add_dependency "schema_plus_core", "~> 2.0", ">= 2.0.1"
|
22
|
+
|
23
|
+
gem.add_development_dependency "bundler", "~> 1.7"
|
24
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
25
|
+
gem.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
gem.add_development_dependency "schema_dev", "~> 3.8"
|
27
|
+
gem.add_development_dependency "simplecov"
|
28
|
+
gem.add_development_dependency "simplecov-gem-profile"
|
29
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record/schema_dumper'
|
3
|
+
|
4
|
+
class Item < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
class DefaultItem < ActiveRecord::Base
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'Migration' do
|
12
|
+
|
13
|
+
let(:migration) { ActiveRecord::Migration }
|
14
|
+
let(:connection) { ActiveRecord::Base.connection }
|
15
|
+
let(:schema) { ActiveRecord::Schema }
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
define_schema
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'INTERVAL data type' do
|
22
|
+
TEST_DURATIONS = [
|
23
|
+
0.seconds,
|
24
|
+
0.days,
|
25
|
+
0.years,
|
26
|
+
27.seconds,
|
27
|
+
28.minutes,
|
28
|
+
3.hours,
|
29
|
+
15.days,
|
30
|
+
3.weeks,
|
31
|
+
2.months,
|
32
|
+
1.year,
|
33
|
+
5.years,
|
34
|
+
5.years + 7.months + 10.days + 6.hours + 7.minutes + 8.seconds
|
35
|
+
]
|
36
|
+
|
37
|
+
it 'should convert data from ActiveSupport::Duration (round trip)' do
|
38
|
+
TEST_DURATIONS.each{|duration| Item.create(duration: duration)}
|
39
|
+
durations = Item.pluck(:duration)
|
40
|
+
expect(durations).to all(be_an(ActiveSupport::Duration))
|
41
|
+
expect(durations).to eq(TEST_DURATIONS)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should convert data to ActiveSupport::Duration' do
|
45
|
+
connection.execute(<<~EOF
|
46
|
+
INSERT INTO items (duration)
|
47
|
+
VALUES
|
48
|
+
('0 seconds'),
|
49
|
+
('0'),
|
50
|
+
('0 years'),
|
51
|
+
('27 seconds'),
|
52
|
+
('28 minutes'),
|
53
|
+
('3 hours'),
|
54
|
+
('15 days'),
|
55
|
+
('3 weeks'),
|
56
|
+
('2 months'),
|
57
|
+
('1 year'),
|
58
|
+
('5 years'),
|
59
|
+
('5 years 7 months 10 days 6 hours 7 minutes 8 seconds');
|
60
|
+
EOF
|
61
|
+
)
|
62
|
+
durations = Item.pluck(:duration)
|
63
|
+
expect(durations).to all(be_an(ActiveSupport::Duration))
|
64
|
+
expect(durations).to eq(TEST_DURATIONS)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should support default values' do
|
68
|
+
DefaultItem.create
|
69
|
+
item_with_default_value = DefaultItem.first
|
70
|
+
expect(item_with_default_value.duration).to be_an(ActiveSupport::Duration)
|
71
|
+
expect(item_with_default_value.duration).to eq(2.years + 5.minutes)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should properly dump the schema' do
|
75
|
+
stream = StringIO.new
|
76
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def define_schema
|
84
|
+
connection.data_sources.each do |table| connection.drop_table table, cascade: true end
|
85
|
+
|
86
|
+
schema.define do
|
87
|
+
|
88
|
+
create_table :items, force: true do |t|
|
89
|
+
t.interval :duration
|
90
|
+
end
|
91
|
+
|
92
|
+
create_table :default_items, force: true do |t|
|
93
|
+
t.interval :duration, default: 2.years + 5.minutes
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/spec/sanity_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
# A basic sanity check to have as a spec when first starting. Feel free to delete this
|
5
|
+
# once you've got real content.
|
6
|
+
|
7
|
+
describe "Sanity Check" do
|
8
|
+
|
9
|
+
it "database is connected" do
|
10
|
+
expect(ActiveRecord::Base).to be_connected
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'simplecov-gem-profile'
|
3
|
+
SimpleCov.start "gem"
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
|
8
|
+
require 'rspec'
|
9
|
+
require 'active_record'
|
10
|
+
require 'schema_plus_pg_types'
|
11
|
+
require 'schema_dev/rspec'
|
12
|
+
|
13
|
+
SchemaDev::Rspec.setup
|
14
|
+
|
15
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.warnings = true
|
19
|
+
end
|
20
|
+
|
21
|
+
SimpleCov.command_name "[ruby#{RUBY_VERSION}-activerecord#{::ActiveRecord.version}-#{ActiveRecord::Base.connection.adapter_name}]"
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: schema_plus_pg_types
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Boaz Yaniv
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: schema_plus_core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 2.0.1
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.0'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.0.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.7'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.7'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '10.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '10.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: schema_dev
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.8'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.8'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: simplecov
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: simplecov-gem-profile
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
description:
|
132
|
+
email:
|
133
|
+
- boazyan@gmail.com
|
134
|
+
executables: []
|
135
|
+
extensions: []
|
136
|
+
extra_rdoc_files: []
|
137
|
+
files:
|
138
|
+
- ".gitignore"
|
139
|
+
- ".idea/vcs.xml"
|
140
|
+
- ".travis.yml"
|
141
|
+
- Gemfile
|
142
|
+
- LICENSE.txt
|
143
|
+
- README.md
|
144
|
+
- Rakefile
|
145
|
+
- gemfiles/Gemfile.base
|
146
|
+
- gemfiles/activerecord-5.0/Gemfile.base
|
147
|
+
- gemfiles/activerecord-5.0/Gemfile.postgresql
|
148
|
+
- lib/schema_plus/pg_types.rb
|
149
|
+
- lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/adapter.rb
|
150
|
+
- lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/column_methods.rb
|
151
|
+
- lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/oid/interval.rb
|
152
|
+
- lib/schema_plus/pg_types/active_record/connection_adapters/postgresql/schema_statements.rb
|
153
|
+
- lib/schema_plus/pg_types/active_support/duration_fixes.rb
|
154
|
+
- lib/schema_plus/pg_types/version.rb
|
155
|
+
- lib/schema_plus_pg_types.rb
|
156
|
+
- migration_spec.rb
|
157
|
+
- schema_dev.yml
|
158
|
+
- schema_plus_pg_types.gemspec
|
159
|
+
- spec/migration_spec.rb
|
160
|
+
- spec/sanity_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
homepage: https://github.com/SchemaPlus/schema_plus_pg_types
|
163
|
+
licenses:
|
164
|
+
- MIT
|
165
|
+
metadata: {}
|
166
|
+
post_install_message:
|
167
|
+
rdoc_options: []
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
requirements: []
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 2.5.1
|
183
|
+
signing_key:
|
184
|
+
specification_version: 4
|
185
|
+
summary: Adds supports for PostgreSQL types that were left out by Rails
|
186
|
+
test_files:
|
187
|
+
- spec/migration_spec.rb
|
188
|
+
- spec/sanity_spec.rb
|
189
|
+
- spec/spec_helper.rb
|