enum_kit 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -0
- data/README.md +2 -0
- data/config.ru +2 -2
- data/enum_kit.gemspec +2 -2
- data/lib/enum_kit/active_record_extensions/connection_adapters/postgresql/column_dumper.rb +1 -1
- data/lib/enum_kit/active_record_extensions/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/enum_kit/active_record_extensions/connection_adapters/postgresql_adapter.rb +68 -7
- data/lib/enum_kit/active_record_extensions/migration/command_recorder.rb +44 -5
- data/lib/enum_kit/active_record_extensions/schema_dumper.rb +13 -7
- data/lib/enum_kit/active_record_patches/connection_adapters/postgresql/column_methods.rb +5 -5
- data/lib/enum_kit/active_record_patches/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/enum_kit/active_record_patches/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/enum_kit/active_record_patches/enum.rb +6 -4
- data/lib/enum_kit/constants.rb +1 -1
- data/spec/active_record/base_spec.rb +1 -1
- data/spec/active_record/connection_adapters/postgresql_adapter_spec.rb +213 -19
- data/spec/active_record/schema_dumper_spec.rb +51 -0
- data/spec/active_record/schema_spec.rb +20 -0
- data/spec/active_record/validations/pg_enum_validator_spec.rb +1 -1
- data/spec/internal/db/schema.rb +2 -2
- data/spec/spec_helper.rb +8 -5
- data/travis/gemfiles/5.2.gemfile +8 -0
- data/travis/gemfiles/6.0.gemfile +8 -0
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f5949293c0b433b8a014dd718bdb0db6f65b13293bc3c781d51296e13a3023d
|
4
|
+
data.tar.gz: 5d8e7e994ec2f47c932fb120e1bb9373d6b9d64ab9380cbba98de58483377004
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 281be07f6004fc567ceb889b1bf63574e08ba8a0e3f250a098b37ba19d9e035718e75993248cc9e4a9d80d1640a5453b71ee4c89848a9f125bae84db16ea046b
|
7
|
+
data.tar.gz: 6ecf20f2cf802b611c6a5604dd5940962448b70bb3816668f596f81d6c0b46e5292ebf032eeb562c74cd6ea0a78d17623d39280e6e6b73bf25e4c42f38f90b99
|
data/.travis.yml
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 2.6.5
|
4
|
+
gemfile:
|
5
|
+
- travis/gemfiles/5.2.gemfile
|
6
|
+
- travis/gemfiles/6.0.gemfile
|
4
7
|
services:
|
5
8
|
- postgresql
|
6
9
|
before_install:
|
@@ -8,6 +11,13 @@ before_install:
|
|
8
11
|
- gem install bundler
|
9
12
|
before_script:
|
10
13
|
- psql -c 'CREATE DATABASE enum_kit_test;' -U postgres
|
14
|
+
addons:
|
15
|
+
postgresql: 10
|
16
|
+
apt:
|
17
|
+
packages:
|
18
|
+
- postgresql-10
|
19
|
+
- postgresql-client-10
|
11
20
|
env:
|
12
21
|
global:
|
13
22
|
- DATABASE_URL="postgresql://127.0.0.1:5432/enum_kit_test"
|
23
|
+
- RSPEC_DEFAULT_FORMATTER=doc
|
data/README.md
CHANGED
data/config.ru
CHANGED
data/enum_kit.gemspec
CHANGED
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.metadata['yard.run'] = 'yri'
|
23
23
|
|
24
|
-
spec.add_runtime_dependency 'activerecord', '>=
|
25
|
-
spec.add_runtime_dependency 'activesupport', '>=
|
24
|
+
spec.add_runtime_dependency 'activerecord', '>= 5.2.0'
|
25
|
+
spec.add_runtime_dependency 'activesupport', '>= 5.2.0'
|
26
26
|
spec.add_runtime_dependency 'pg'
|
27
27
|
|
28
28
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
@@ -30,14 +30,14 @@ module EnumKit
|
|
30
30
|
# @return [Hash] The enum types available in the database.
|
31
31
|
#
|
32
32
|
def enums
|
33
|
-
|
33
|
+
select_all(ENUM_QUERY.tr("\n", ' ').strip).each_with_object({}) do |row, enums|
|
34
34
|
enums[row['typname'].to_sym] = row['values'].split("\t\t")
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
# Create a new enum type in the database.
|
39
39
|
#
|
40
|
-
# @param name [Symbol] The
|
40
|
+
# @param name [Symbol] The enum's name.
|
41
41
|
# @param values [Array] The enum's acceptable values.
|
42
42
|
#
|
43
43
|
def create_enum(name, values)
|
@@ -47,27 +47,88 @@ module EnumKit
|
|
47
47
|
execute "CREATE TYPE #{name} AS ENUM #{EnumKit.sqlize(values)}"
|
48
48
|
end
|
49
49
|
|
50
|
+
# Rename an existing enum type.
|
51
|
+
#
|
52
|
+
# @param current_name [Symbol] The enum's current name.
|
53
|
+
# @param new_name [Symbol] The enum's new name.
|
54
|
+
#
|
55
|
+
def rename_enum(current_name, new_name)
|
56
|
+
current_name = EnumKit.sanitize_name!(current_name)
|
57
|
+
new_name = EnumKit.sanitize_name!(new_name)
|
58
|
+
|
59
|
+
execute "ALTER TYPE #{current_name} RENAME TO #{new_name}"
|
60
|
+
end
|
61
|
+
|
50
62
|
# Drop an existing enum type from the database.
|
51
63
|
#
|
52
|
-
# @param name [Symbol] The
|
64
|
+
# @param name [Symbol] The enum's name.
|
53
65
|
#
|
54
66
|
def drop_enum(name)
|
67
|
+
name = EnumKit.sanitize_name!(name)
|
68
|
+
|
55
69
|
execute "DROP TYPE #{name}"
|
56
70
|
end
|
57
71
|
|
72
|
+
# Add a new value to an enum type in the database.
|
73
|
+
#
|
74
|
+
# Note that you can't specify both :before and :after.
|
75
|
+
#
|
76
|
+
# @param name [Symbol] The enum's name.
|
77
|
+
# @param value [String|Symbol] The value to add.
|
78
|
+
# @param after [String|Symbol] An existing value after which the new value should be inserted.
|
79
|
+
# @param before [String|Symbol] An existing value before which the new value should be inserted.
|
80
|
+
#
|
81
|
+
def add_enum_value(name, value, after: nil, before: nil)
|
82
|
+
name = EnumKit.sanitize_name!(name)
|
83
|
+
value = EnumKit.sanitize_value!(value)
|
84
|
+
|
85
|
+
statement = "ALTER TYPE #{name} ADD VALUE #{EnumKit.sqlize(value)}"
|
86
|
+
|
87
|
+
raise ArgumentError, "You can't specify both :before and :after" if before && after
|
88
|
+
|
89
|
+
statement += " AFTER #{EnumKit.sqlize(EnumKit.sanitize_value!(after))}" if after
|
90
|
+
statement += " BEFORE #{EnumKit.sqlize(EnumKit.sanitize_value!(before))}" if before
|
91
|
+
|
92
|
+
execute(statement)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Rename a value within an enum type in the database.
|
96
|
+
#
|
97
|
+
# @param name [Symbol] The enum's name.
|
98
|
+
# @param current_value [String|Symbol] The enum value's current name.
|
99
|
+
# @param new_value [String|Symbol] The enum value's new name.
|
100
|
+
#
|
101
|
+
def rename_enum_value(name, current_name, new_name)
|
102
|
+
ensure_renaming_enum_values_is_supported!
|
103
|
+
|
104
|
+
name = EnumKit.sanitize_name!(name)
|
105
|
+
current_name = EnumKit.sanitize_value!(current_name)
|
106
|
+
new_name = EnumKit.sanitize_value!(new_name)
|
107
|
+
|
108
|
+
execute "ALTER TYPE #{name} RENAME VALUE #{EnumKit.sqlize(current_name)} TO #{EnumKit.sqlize(new_name)}"
|
109
|
+
end
|
110
|
+
|
58
111
|
# :nodoc:
|
59
112
|
#
|
60
113
|
def migration_keys
|
61
|
-
super + [:
|
114
|
+
super + [:enum_type]
|
62
115
|
end
|
63
116
|
|
64
117
|
# :nodoc:
|
65
118
|
#
|
66
|
-
def prepare_column_options(column
|
67
|
-
spec = super
|
68
|
-
spec[:
|
119
|
+
def prepare_column_options(column)
|
120
|
+
spec = super
|
121
|
+
spec[:enum_type] = column.sql_type.inspect if column.type == :enum
|
69
122
|
spec
|
70
123
|
end
|
124
|
+
|
125
|
+
# Raise an exception if the active PostgreSQL version doesn't support renaming enum values.
|
126
|
+
#
|
127
|
+
def ensure_renaming_enum_values_is_supported!
|
128
|
+
return if ActiveRecord::Base.connection.postgresql_version >= 100_000
|
129
|
+
|
130
|
+
raise NotImplementedError, 'PostgreSQL 10.0+ is required to enable renaming of enum values.'
|
131
|
+
end
|
71
132
|
end
|
72
133
|
end
|
73
134
|
end
|
@@ -12,33 +12,72 @@ module EnumKit
|
|
12
12
|
# :nodoc:
|
13
13
|
#
|
14
14
|
module CommandRecorder
|
15
|
-
#
|
15
|
+
# Record the creation of an enum type.
|
16
16
|
#
|
17
17
|
def create_enum(*args)
|
18
18
|
record(:create_enum, args)
|
19
19
|
end
|
20
20
|
|
21
|
-
#
|
21
|
+
# Record the renaming of an enum type.
|
22
|
+
#
|
23
|
+
def rename_enum(*args)
|
24
|
+
record(:rename_enum, args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Record the deletion of an enum type.
|
22
28
|
#
|
23
29
|
def drop_enum(*args)
|
24
30
|
record(:drop_enum, args)
|
25
31
|
end
|
26
32
|
|
27
|
-
#
|
33
|
+
# Record the addition of a value to an enum type.
|
34
|
+
#
|
35
|
+
def add_enum_value(*args)
|
36
|
+
record(:add_enum_value, args)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Record the renaming of a value in an enum type.
|
40
|
+
#
|
41
|
+
def rename_enum_value(*args)
|
42
|
+
record(:rename_enum_value, args)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Invert the creation of an enum type by deleting it.
|
28
46
|
#
|
29
47
|
def invert_create_enum(args)
|
30
48
|
record(:drop_enum, args.first)
|
31
49
|
end
|
32
50
|
|
33
|
-
#
|
51
|
+
# Invert the renaming of an enum by renaming it back to the previous name.
|
52
|
+
#
|
53
|
+
def invert_rename_enum(args)
|
54
|
+
record(:rename_enum, args.reverse)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Invert the deletion of an enum type by creating it.
|
58
|
+
#
|
59
|
+
# Note that `drop_enum` can only be reversed if given a collection of values to call `create_enum` with as the
|
60
|
+
# previously deleted enum values cannot be automatically determined.
|
34
61
|
#
|
35
62
|
def invert_drop_enum(args)
|
36
63
|
unless args.length > 1
|
37
|
-
raise
|
64
|
+
raise ActiveRecord::IrreversibleMigration, 'drop_enum is only reversible if given an Array of values.'
|
38
65
|
end
|
39
66
|
|
40
67
|
record(:create_enum, args)
|
41
68
|
end
|
69
|
+
|
70
|
+
# Invert the addition of a value to an enum type by removing the value.
|
71
|
+
#
|
72
|
+
def invert_add_enum_value(_args)
|
73
|
+
raise ActiveRecord::IrreversibleMigration, 'add_enum_value is not reversible.'
|
74
|
+
end
|
75
|
+
|
76
|
+
# Invert the renaming of an enum's value by renaming it back to the previous value.
|
77
|
+
#
|
78
|
+
def invert_rename_enum_value(args)
|
79
|
+
record(:rename_enum_value, args[0], args[2], args[1])
|
80
|
+
end
|
42
81
|
end
|
43
82
|
end
|
44
83
|
end
|
@@ -12,19 +12,25 @@ module EnumKit
|
|
12
12
|
# :nodoc:
|
13
13
|
#
|
14
14
|
def tables(stream)
|
15
|
-
|
15
|
+
enums(stream)
|
16
16
|
super
|
17
17
|
end
|
18
18
|
|
19
|
-
#
|
19
|
+
# Write `create_enum` statements for each of the enum types created in the database to the specified stream.
|
20
|
+
#
|
21
|
+
# @param stream [IO] The stream to write the statements into.
|
20
22
|
#
|
21
|
-
def
|
22
|
-
@connection.enums
|
23
|
-
|
24
|
-
|
23
|
+
def enums(stream)
|
24
|
+
return unless @connection.respond_to?(:enums)
|
25
|
+
|
26
|
+
statements = @connection.enums.map do |name, values|
|
27
|
+
" create_enum #{name.inspect}, #{values.inspect}"
|
25
28
|
end
|
26
29
|
|
27
|
-
|
30
|
+
return if statements.empty?
|
31
|
+
|
32
|
+
stream.puts statements.join("\n")
|
33
|
+
stream.puts
|
28
34
|
end
|
29
35
|
end
|
30
36
|
end
|
@@ -15,16 +15,16 @@ module ActiveRecord
|
|
15
15
|
# Create an enum column with the provided name.
|
16
16
|
#
|
17
17
|
# By default, the enum type will match the name of the column.
|
18
|
-
# You can change this behaviour by providing the enum type as an option under the `:
|
18
|
+
# You can change this behaviour by providing the enum type as an option under the `:enum_type` key.
|
19
19
|
#
|
20
20
|
# @example Creating a user role.
|
21
|
-
# t.enum :role,
|
21
|
+
# t.enum :role, enum_type: :user_role
|
22
22
|
#
|
23
|
-
# @param
|
24
|
-
# @param options
|
23
|
+
# @param enum_type [String] The name of the enum column.
|
24
|
+
# @param options [Hash] The options (including the enum type).
|
25
25
|
#
|
26
26
|
def enum(name, options = {})
|
27
|
-
column(name, options[:
|
27
|
+
column(name, options[:enum_type] || name, options.except(:enum_type))
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -15,14 +15,14 @@ module ActiveRecord
|
|
15
15
|
# :nodoc:
|
16
16
|
#
|
17
17
|
class Enum < Type::Value
|
18
|
-
# @return [String] The PostgreSQL type
|
18
|
+
# @return [String] The name of the PostgreSQL type representing the enum.
|
19
19
|
#
|
20
|
-
attr_reader :
|
20
|
+
attr_reader :enum_type
|
21
21
|
|
22
22
|
# :nodoc:
|
23
23
|
#
|
24
24
|
def initialize(options = {})
|
25
|
-
@
|
25
|
+
@enum_type = options.delete(:enum_type).to_sym
|
26
26
|
super
|
27
27
|
end
|
28
28
|
end
|
data/lib/enum_kit/active_record_patches/connection_adapters/postgresql/oid/type_map_initializer.rb
CHANGED
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
# :nodoc:
|
19
19
|
#
|
20
20
|
def register_enum_type(row)
|
21
|
-
register row['oid'], ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Enum.new(
|
21
|
+
register row['oid'], ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Enum.new(enum_type: row['typname'])
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -12,12 +12,11 @@ module ActiveRecord
|
|
12
12
|
# @return [Array] The acceptable values for the enum type associated with the column.
|
13
13
|
#
|
14
14
|
def pg_enum_values(name)
|
15
|
-
# Determine the PostgreSQL type
|
16
|
-
type =
|
17
|
-
type = type.instance_eval { subtype } if type.is_a?(ActiveRecord::Enum::EnumType)
|
15
|
+
# Determine the PostgreSQL type for the enum.
|
16
|
+
type = columns_hash[name.to_s].sql_type
|
18
17
|
|
19
18
|
# Query the PostgreSQL database for the enum's acceptable values.
|
20
|
-
connection.enums[type.
|
19
|
+
connection.enums[type.to_sym]
|
21
20
|
end
|
22
21
|
|
23
22
|
# Define a PostgreSQL enum type.
|
@@ -31,6 +30,9 @@ module ActiveRecord
|
|
31
30
|
enum(name => Hash[values])
|
32
31
|
|
33
32
|
enum = type_for_attribute(name)
|
33
|
+
|
34
|
+
raise 'Expected an ActiveRecord::Enum::EnumType' unless enum.is_a?(ActiveRecord::Enum::EnumType)
|
35
|
+
|
34
36
|
enum.disable_exceptions = options.key?(:exceptions) && !options[:exceptions]
|
35
37
|
|
36
38
|
nil
|
data/lib/enum_kit/constants.rb
CHANGED
@@ -1,25 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
RSpec.describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
3
|
+
RSpec.describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter, :unit do
|
4
4
|
subject(:connection) { ActiveRecord::Base.connection }
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
before do
|
7
|
+
# Sanity check to ensure ActiveRecord is configured to use a PostgreSQL database.
|
8
|
+
expect(connection).to be_a(described_class)
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
%i[create_enum rename_enum drop_enum add_enum_value rename_enum_value].each do |method|
|
12
|
+
define_method(method) do |*args, &block|
|
13
|
+
connection.send(method, *args, &block).result_status
|
11
14
|
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#create_enum' do
|
18
|
+
after { connection.execute 'DROP TYPE IF EXISTS sizes' }
|
12
19
|
|
13
20
|
context 'when called with valid arguments' do
|
14
|
-
subject {
|
21
|
+
subject { create_enum(:sizes, [:small, :medium, :large, 'extra large']) }
|
15
22
|
|
16
23
|
it 'creates an enum' do
|
17
|
-
expect(subject
|
24
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
25
|
+
expect(connection.enums[:sizes]).to eq(['small', 'medium', 'large', 'extra large'])
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
21
29
|
context 'when called with a malformed name' do
|
22
|
-
subject {
|
30
|
+
subject { create_enum('bad enum name', [:small, :medium, :large, 'extra large']) }
|
23
31
|
|
24
32
|
it 'raises an ArgumentError' do
|
25
33
|
expect { subject }.to raise_exception(ArgumentError)
|
@@ -27,7 +35,7 @@ RSpec.describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
27
35
|
end
|
28
36
|
|
29
37
|
context 'when called with malformed values' do
|
30
|
-
subject {
|
38
|
+
subject { create_enum(:sizes, [:small, :medium, :large, 'extra large', 'extra+extra+large']) }
|
31
39
|
|
32
40
|
it 'raises an ArgumentError' do
|
33
41
|
expect { subject }.to raise_exception(ArgumentError)
|
@@ -35,24 +43,210 @@ RSpec.describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
35
43
|
end
|
36
44
|
end
|
37
45
|
|
38
|
-
describe '#
|
39
|
-
before
|
40
|
-
|
46
|
+
describe '#rename_enum' do
|
47
|
+
before do
|
48
|
+
connection.execute "CREATE TYPE sizes AS ENUM ('small', 'medium', 'large', 'extra large')"
|
49
|
+
end
|
50
|
+
|
51
|
+
after do
|
52
|
+
connection.execute 'DROP TYPE IF EXISTS lengths'
|
53
|
+
connection.execute 'DROP TYPE IF EXISTS sizes'
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when called with valid arguments' do
|
57
|
+
subject { rename_enum(:sizes, :lengths) }
|
41
58
|
|
42
|
-
|
43
|
-
|
59
|
+
it 'renames the enum' do
|
60
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
61
|
+
expect(connection.enums).to have_key(:lengths)
|
62
|
+
expect(connection.enums).not_to have_key(:sizes)
|
63
|
+
end
|
44
64
|
end
|
45
65
|
|
46
|
-
context 'when called with
|
47
|
-
subject {
|
66
|
+
context 'when called with a non-existent enum name' do
|
67
|
+
subject { rename_enum(:non_existent_enum, :lengths) }
|
68
|
+
|
69
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
70
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when called with a malformed current name' do
|
75
|
+
subject { rename_enum('bad enum name', :lengths) }
|
76
|
+
|
77
|
+
it 'raises an ArgumentError' do
|
78
|
+
expect { subject }.to raise_exception(ArgumentError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when called with a malformed new name' do
|
83
|
+
subject { rename_enum(:sizes, 'bad enum name') }
|
84
|
+
|
85
|
+
it 'raises an ArgumentError' do
|
86
|
+
expect { subject }.to raise_exception(ArgumentError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#drop_enum' do
|
92
|
+
before { connection.execute "CREATE TYPE sizes AS ENUM ('small', 'medium', 'large', 'extra large')" }
|
93
|
+
after { connection.execute 'DROP TYPE IF EXISTS sizes' }
|
94
|
+
|
95
|
+
context 'when called with valid arguments' do
|
96
|
+
subject { drop_enum(:sizes) }
|
48
97
|
|
49
98
|
it 'drops the enum' do
|
50
|
-
expect(subject
|
99
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
100
|
+
expect(connection.enums).not_to have_key(:sizes)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when called with a non-existent enum name' do
|
105
|
+
subject { drop_enum(:non_existent_enum) }
|
106
|
+
|
107
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
108
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when called with a malformed name' do
|
113
|
+
subject { drop_enum('bad enum name') }
|
114
|
+
|
115
|
+
it 'raises an ArgumentError' do
|
116
|
+
expect { subject }.to raise_exception(ArgumentError)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#add_enum_value' do
|
122
|
+
before { connection.execute "CREATE TYPE sizes AS ENUM ('small', 'large', 'extra large')" }
|
123
|
+
after { connection.execute 'DROP TYPE IF EXISTS sizes' }
|
124
|
+
|
125
|
+
context 'when called with valid arguments' do
|
126
|
+
subject { add_enum_value(:sizes, 'extra extra large') }
|
127
|
+
|
128
|
+
it 'appends the value' do
|
129
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
130
|
+
expect(connection.enums[:sizes]).to eq(['small', 'large', 'extra large', 'extra extra large'])
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'with :before' do
|
134
|
+
subject { add_enum_value(:sizes, 'extra small', before: :small) }
|
135
|
+
|
136
|
+
it 'inserts the value at the correct position' do
|
137
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
138
|
+
expect(connection.enums[:sizes]).to eq(['extra small', 'small', 'large', 'extra large'])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'with :after' do
|
143
|
+
subject { add_enum_value(:sizes, :medium, after: :small) }
|
144
|
+
|
145
|
+
it 'inserts the value at the correct position' do
|
146
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
147
|
+
expect(connection.enums[:sizes]).to eq(['small', 'medium', 'large', 'extra large'])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'with :before and :after' do
|
152
|
+
subject { add_enum_value(:sizes, :medium, before: :large, after: :small) }
|
153
|
+
|
154
|
+
it 'raises an ArgumentError' do
|
155
|
+
expect { subject }.to raise_exception(ArgumentError)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'when called with a non-existent enum name' do
|
161
|
+
subject { add_enum_value(:non_existent_enum, :new_value) }
|
162
|
+
|
163
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
164
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'when called with a malformed name' do
|
169
|
+
subject { add_enum_value('bad enum name', :new_value) }
|
170
|
+
|
171
|
+
it 'raises an ArgumentError' do
|
172
|
+
expect { subject }.to raise_exception(ArgumentError)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#rename_enum_value' do
|
178
|
+
before { connection.execute "CREATE TYPE sizes AS ENUM ('small', 'medium', 'large', 'extra large')" }
|
179
|
+
after { connection.execute 'DROP TYPE IF EXISTS sizes' }
|
180
|
+
|
181
|
+
context 'when called with valid arguments' do
|
182
|
+
subject { rename_enum_value(:sizes, 'extra large', :extra_large) }
|
183
|
+
|
184
|
+
it 'renames the value' do
|
185
|
+
expect(subject).to eq(PG::PGRES_COMMAND_OK)
|
186
|
+
expect(connection.enums[:sizes]).to eq(%w[small medium large extra_large])
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'when using PostgreSQL < 10.0' do
|
190
|
+
before do
|
191
|
+
allow(connection).to receive(:postgresql_version).and_return(99_999)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'raises a NotImplementedError' do
|
195
|
+
expect { subject }.to raise_exception(NotImplementedError)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'when called with a non-existent enum name' do
|
201
|
+
subject { rename_enum_value(:non_existent_enum, :previous_value, :new_value) }
|
202
|
+
|
203
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
204
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'when called with a non-existent enum value' do
|
209
|
+
subject { rename_enum_value(:sizes, 'extra extra large', :extra_extra_large) }
|
210
|
+
|
211
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
212
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when called with a malformed name' do
|
217
|
+
subject { rename_enum_value('bad enum name', 'extra large', :extra_large) }
|
218
|
+
|
219
|
+
it 'raises an ArgumentError' do
|
220
|
+
expect { subject }.to raise_exception(ArgumentError)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when called with a malformed current value' do
|
225
|
+
subject { rename_enum_value(:sizes, 'extra-large', :extra_large) }
|
226
|
+
|
227
|
+
it 'raises an ArgumentError' do
|
228
|
+
expect { subject }.to raise_exception(ArgumentError)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'when called with a malformed new value' do
|
233
|
+
subject { rename_enum_value(:sizes, 'extra large', 'extra-large') }
|
234
|
+
|
235
|
+
it 'raises an ArgumentError' do
|
236
|
+
expect { subject }.to raise_exception(ArgumentError)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'when called with an equivalent current value and new value' do
|
241
|
+
subject { rename_enum_value(:sizes, 'extra large', 'extra large') }
|
242
|
+
|
243
|
+
it 'raises ActiveRecord::StatementInvalid' do
|
244
|
+
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
51
245
|
end
|
52
246
|
end
|
53
247
|
|
54
|
-
context 'when called with a
|
55
|
-
subject {
|
248
|
+
context 'when called with a new value matching an existing value' do
|
249
|
+
subject { rename_enum_value(:sizes, 'extra large', :large) }
|
56
250
|
|
57
251
|
it 'raises ActiveRecord::StatementInvalid' do
|
58
252
|
expect { subject }.to raise_exception(ActiveRecord::StatementInvalid)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe ActiveRecord::SchemaDumper, :unit do
|
4
|
+
let(:connection) { ActiveRecord::Base.connection }
|
5
|
+
let(:stream) { StringIO.new }
|
6
|
+
|
7
|
+
subject do
|
8
|
+
options = {
|
9
|
+
table_name_prefix: ActiveRecord::Base.table_name_prefix,
|
10
|
+
table_name_suffix: ActiveRecord::Base.table_name_suffix
|
11
|
+
}
|
12
|
+
|
13
|
+
described_class.send(:new, connection, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#enums' do
|
17
|
+
let(:result) do
|
18
|
+
subject.enums(stream)
|
19
|
+
stream.rewind
|
20
|
+
stream.read
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when the database has enums' do
|
24
|
+
before do
|
25
|
+
expect(connection).to receive(:enums).and_return(color: %w[red green blue], size: %w[small medium large])
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'generates matching `create_enum` statements' do
|
29
|
+
expect(result).to include('create_enum :color, ["red", "green", "blue"]')
|
30
|
+
expect(result).to include('create_enum :size, ["small", "medium", "large"]')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when the database has no enums' do
|
35
|
+
before do
|
36
|
+
expect(connection).to receive(:enums).and_return({})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'generates nothing' do
|
40
|
+
expect(result).to be_empty
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#tables' do
|
46
|
+
it 'invokes #enums' do
|
47
|
+
expect(subject).to receive(:enums).with(stream).once
|
48
|
+
subject.tables(stream)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe 'ActiveRecord Schema', :integration do
|
4
|
+
let(:connection) { ActiveRecord::Base.connection }
|
5
|
+
let(:stream) { StringIO.new }
|
6
|
+
|
7
|
+
subject do
|
8
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
9
|
+
stream.rewind
|
10
|
+
stream.read.split("\n").map { |line| line.gsub(/\s+/, ' ').strip }
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'includes `create_enum` statements' do
|
14
|
+
expect(subject).to include('create_enum :shirt_size, ["small", "medium", "large"]')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'includes `t.enum` statements' do
|
18
|
+
expect(subject).to include('t.enum "size", enum_type: "shirt_size"')
|
19
|
+
end
|
20
|
+
end
|
data/spec/internal/db/schema.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
ActiveRecord::Schema.define do
|
4
|
-
create_enum :shirt_size,
|
4
|
+
create_enum :shirt_size, %w[small medium large]
|
5
5
|
|
6
6
|
create_table :shirts do |t|
|
7
7
|
t.string :name
|
8
|
-
t.enum :size,
|
8
|
+
t.enum :size, enum_type: :shirt_size
|
9
9
|
end
|
10
10
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -70,11 +70,14 @@ RSpec.configure do |config|
|
|
70
70
|
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
71
71
|
config.disable_monkey_patching!
|
72
72
|
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
# Retrieve the default formatter from the current environment.
|
74
|
+
default_formatter = ENV['RSPEC_DEFAULT_FORMATTER']
|
75
|
+
|
76
|
+
if default_formatter.is_a?(String) && !default_formatter.empty?
|
77
|
+
config.default_formatter = default_formatter
|
78
|
+
elsif config.files_to_run.one?
|
79
|
+
# Use the documentation formatter for detailed output when running an individual spec file, unless a formatter has
|
80
|
+
# already been configured (e.g. via a command-line flag or using the RSPEC_DEFAULT_FORMAT environment variable).
|
78
81
|
config.default_formatter = 'doc'
|
79
82
|
end
|
80
83
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enum_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nialto Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-10-
|
11
|
+
date: 2019-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.2.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 5.2.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 5.2.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pg
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,6 +186,8 @@ files:
|
|
186
186
|
- lib/enum_kit/helpers.rb
|
187
187
|
- spec/active_record/base_spec.rb
|
188
188
|
- spec/active_record/connection_adapters/postgresql_adapter_spec.rb
|
189
|
+
- spec/active_record/schema_dumper_spec.rb
|
190
|
+
- spec/active_record/schema_spec.rb
|
189
191
|
- spec/active_record/validations/pg_enum_validator_spec.rb
|
190
192
|
- spec/enum_kit/constants_spec.rb
|
191
193
|
- spec/enum_kit/helpers_spec.rb
|
@@ -194,6 +196,8 @@ files:
|
|
194
196
|
- spec/internal/db/schema.rb
|
195
197
|
- spec/internal/log/.gitignore
|
196
198
|
- spec/spec_helper.rb
|
199
|
+
- travis/gemfiles/5.2.gemfile
|
200
|
+
- travis/gemfiles/6.0.gemfile
|
197
201
|
homepage: https://github.com/nialtoservices/enum_kit
|
198
202
|
licenses:
|
199
203
|
- MIT
|
@@ -221,6 +225,8 @@ summary: Native PostgreSQL enum support for Ruby on Rails.
|
|
221
225
|
test_files:
|
222
226
|
- spec/active_record/base_spec.rb
|
223
227
|
- spec/active_record/connection_adapters/postgresql_adapter_spec.rb
|
228
|
+
- spec/active_record/schema_dumper_spec.rb
|
229
|
+
- spec/active_record/schema_spec.rb
|
224
230
|
- spec/active_record/validations/pg_enum_validator_spec.rb
|
225
231
|
- spec/enum_kit/constants_spec.rb
|
226
232
|
- spec/enum_kit/helpers_spec.rb
|