enum_kit 0.1.0 → 0.2.1
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 +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
|