db_schema 0.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 216fab4c9e20545d7653f46cdb5fe9ce5528a68a
4
- data.tar.gz: a3115a63fd0a6da8289d1b7e6848e57620785563
3
+ metadata.gz: 92f4601dd7a4513014fd782391bfe69f8400f06d
4
+ data.tar.gz: 291caae43bf4582dbf0636b8601de86ebdcf3c25
5
5
  SHA512:
6
- metadata.gz: 2ef95f7dcf532b83a288423f0626943479a4353797e281e0df5aa0788508811b19de3382576a7ed78f40c0b664bcd7f455ce12c335cf8c7a59cbdf33eccf093c
7
- data.tar.gz: 660d11cc3e03fb68b2227ebe4f6febb544d17454d842daa6ad6d8d5cc8ec8af550f3e6ef936c8a7cf18f177ec1edfc56c792e4c0f51407b509868acd39b16b0d
6
+ metadata.gz: 7a8af71a493a89113166e8a1ee0fa1808ba8bbbcbb07b263fa0d9f0884eafe4d297e64d79815a854f460ad78af3bd0c847c82741c96d5175e127452a1333120d
7
+ data.tar.gz: 41fb922d5f307c481f3595d27983c39a13640cd2c5cdc5e9e6a395ad34c509cf712f4d08f79825c730462855c9d66d178961bb1d48d77b599386e10ab90b831a
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # DbSchema [![Build Status](https://travis-ci.org/7even/db_schema.svg?branch=master)](https://travis-ci.org/7even/db_schema) [![Gem Version](https://badge.fury.io/rb/db_schema.svg)](https://badge.fury.io/rb/db_schema) [![Join the chat at https://gitter.im/7even/db_schema](https://badges.gitter.im/7even/db_schema.svg)](https://gitter.im/7even/db_schema?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
1
+ # DbSchema [![Build Status](https://travis-ci.org/db-schema/core.svg?branch=master)](https://travis-ci.org/db-schema/core) [![Gem Version](https://badge.fury.io/rb/db_schema.svg)](https://badge.fury.io/rb/db_schema) [![Join the chat at https://gitter.im/7even/db_schema](https://badges.gitter.im/7even/db_schema.svg)](https://gitter.im/7even/db_schema?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
2
 
3
3
  DbSchema is an opinionated database schema management tool that lets you maintain your DB schema with a single ruby file.
4
4
 
@@ -53,7 +53,7 @@ But you would lose it even with manual migrations.
53
53
  Add this line to your application's Gemfile:
54
54
 
55
55
  ``` ruby
56
- gem 'db_schema', '~> 0.3.0'
56
+ gem 'db_schema', '~> 0.3.1'
57
57
  ```
58
58
 
59
59
  And then execute:
@@ -83,7 +83,7 @@ DbSchema.configure(
83
83
  ```
84
84
 
85
85
  There is also a Rails' `database.yml`-compatible `configure_from_yaml` method. DbSchema configuration
86
- is discussed in detail [here](https://github.com/7even/db_schema/wiki/Configuration).
86
+ is discussed in detail [here](https://github.com/db-schema/core/wiki/Configuration).
87
87
 
88
88
  After DbSchema is configured you can load your schema definition file:
89
89
 
@@ -113,7 +113,7 @@ DbSchema.describe do |db|
113
113
  end
114
114
  ```
115
115
 
116
- Database schema definition DSL is documented [here](https://github.com/7even/db_schema/wiki/Schema-definition-DSL).
116
+ Database schema definition DSL is documented [here](https://github.com/db-schema/core/wiki/Schema-definition-DSL).
117
117
 
118
118
  If you want to analyze your database structure in any way from your app (e.g. defining methods
119
119
  with `#define_method` for each enum value) you can use `DbSchema.current_schema` - it returns
@@ -121,7 +121,7 @@ a cached copy of the database structure as a `DbSchema::Definitions::Schema` obj
121
121
  can query in different ways. It is available after the schema was applied by DbSchema
122
122
  (`DbSchema.describe` remembers the current schema of the database and exposes it
123
123
  at `.current_schema`). Documentation for schema analysis DSL can be found
124
- [here](https://github.com/7even/db_schema/wiki/Schema-analysis-DSL).
124
+ [here](https://github.com/db-schema/core/wiki/Schema-analysis-DSL).
125
125
 
126
126
  ### Production setup
127
127
 
@@ -217,12 +217,12 @@ instead you have to provide some conditions required to run the migration (the g
217
217
  conditions that a) will only trigger if the migration wasn't applied yet and b) are necessary for the
218
218
  migration to work) - like "rename the `users`
219
219
  table to `people` only if the database has a `users` table" (DbSchema also provides
220
- a [simple DSL](https://github.com/7even/db_schema/wiki/Schema-analysis-DSL) for schema analysis).
220
+ a [simple DSL](https://github.com/db-schema/core/wiki/Schema-analysis-DSL) for schema analysis).
221
221
  This way the migration won't be applied again and the whole DbSchema process stays idempotent.
222
222
  Also you don't have to keep these migrations forever - once a migration is applied to databases
223
223
  in all environments you can safely delete it (though you can give your teammates a week or two to keep up).
224
224
 
225
- Conditional migrations are described [here](https://github.com/7even/db_schema/wiki/Conditional-Migrations).
225
+ Conditional migrations are described [here](https://github.com/db-schema/core/wiki/Conditional-Migrations).
226
226
 
227
227
  ## Known problems and limitations
228
228
 
@@ -237,7 +237,7 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
237
237
 
238
238
  ## Contributing
239
239
 
240
- Bug reports and pull requests are welcome on GitHub at [7even/db_schema](https://github.com/7even/db_schema).
240
+ Bug reports and pull requests are welcome on GitHub at [db-schema/core](https://github.com/db-schema/core).
241
241
 
242
242
  ## License
243
243
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = 'Declarative database schema definition.'
12
12
  spec.description = 'A database schema management tool that reads a "single-source-of-truth" schema definition from a ruby file and auto-migrates the database to conform to it.'
13
- spec.homepage = 'https://github.com/7even/db_schema'
13
+ spec.homepage = 'https://github.com/db-schema/core'
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r(^spec/)) }
@@ -87,7 +87,7 @@ if defined?(AwesomePrint)
87
87
 
88
88
  def awesome_dbschema_table(object)
89
89
  data = ["fields: #{object.fields.ai}"]
90
- data << "indices: #{object.indices.ai}" if object.indices.any?
90
+ data << "indexes: #{object.indexes.ai}" if object.indexes.any?
91
91
  data << "checks: #{object.checks.ai}" if object.checks.any?
92
92
  data << "foreign_keys: #{object.foreign_keys.ai}" if object.foreign_keys.any?
93
93
 
@@ -190,7 +190,7 @@ if defined?(AwesomePrint)
190
190
 
191
191
  def awesome_dbschema_create_table(object)
192
192
  data = ["fields: #{object.table.fields.ai}"]
193
- data << "indices: #{object.table.indices.ai}" if object.table.indices.any?
193
+ data << "indexes: #{object.table.indexes.ai}" if object.table.indexes.any?
194
194
  data << "checks: #{object.table.checks.ai}" if object.table.checks.any?
195
195
 
196
196
  data_string = indent_lines(data.join(', '))
@@ -5,59 +5,155 @@ module DbSchema
5
5
  module Changes
6
6
  class << self
7
7
  def between(desired_schema, actual_schema)
8
- table_names = [desired_schema.tables, actual_schema.tables].flatten.map(&:name).uniq
9
-
10
- table_changes = table_names.each.with_object([]) do |table_name, changes|
11
- desired = desired_schema.tables.find { |table| table.name == table_name }
12
- actual = actual_schema.tables.find { |table| table.name == table_name }
13
-
14
- if desired && !actual
15
- changes << Operations::CreateTable.new(desired)
8
+ sort_all_changes(
9
+ [
10
+ table_changes(desired_schema, actual_schema),
11
+ enum_changes(desired_schema, actual_schema),
12
+ extension_changes(desired_schema, actual_schema)
13
+ ].reduce(:+)
14
+ )
15
+ end
16
16
 
17
- fkey_operations = desired.foreign_keys.map do |fkey|
18
- Operations::CreateForeignKey.new(table_name, fkey)
17
+ private
18
+ def table_changes(desired_schema, actual_schema)
19
+ compare_collections(
20
+ desired_schema.tables,
21
+ actual_schema.tables,
22
+ create: -> (table) do
23
+ fkey_operations = table.foreign_keys.map do |fkey|
24
+ Operations::CreateForeignKey.new(table.name, fkey)
19
25
  end
20
- changes.concat(fkey_operations)
21
- elsif actual && !desired
22
- changes << Operations::DropTable.new(table_name)
23
26
 
24
- actual.foreign_keys.each do |fkey|
25
- changes << Operations::DropForeignKey.new(table_name, fkey.name)
27
+ [Operations::CreateTable.new(table), *fkey_operations]
28
+ end,
29
+ drop: -> (table) do
30
+ fkey_operations = table.foreign_keys.map do |fkey|
31
+ Operations::DropForeignKey.new(table.name, fkey.name)
26
32
  end
27
- elsif actual != desired
28
- field_operations = field_changes(desired.fields, actual.fields)
29
- index_operations = index_changes(desired.indices, actual.indices)
30
- check_operations = check_changes(desired.checks, actual.checks)
31
- fkey_operations = foreign_key_changes(table_name, desired.foreign_keys, actual.foreign_keys)
32
-
33
- if field_operations.any? || index_operations.any? || check_operations.any?
34
- changes << Operations::AlterTable.new(
35
- table_name,
36
- sort_alter_table_changes(field_operations + index_operations + check_operations)
33
+
34
+ [Operations::DropTable.new(table.name), *fkey_operations]
35
+ end,
36
+ change: -> (desired, actual) do
37
+ fkey_operations = foreign_key_changes(desired, actual)
38
+
39
+ alter_table_operations = [
40
+ field_changes(desired, actual),
41
+ index_changes(desired, actual),
42
+ check_changes(desired, actual)
43
+ ].reduce(:+)
44
+
45
+ if alter_table_operations.any?
46
+ alter_table = Operations::AlterTable.new(
47
+ desired.name,
48
+ sort_alter_table_changes(alter_table_operations)
37
49
  )
50
+
51
+ [alter_table, *fkey_operations]
52
+ else
53
+ fkey_operations
38
54
  end
55
+ end
56
+ )
57
+ end
58
+
59
+ def field_changes(desired_table, actual_table)
60
+ compare_collections(
61
+ desired_table.fields,
62
+ actual_table.fields,
63
+ create: -> (field) { Operations::CreateColumn.new(field) },
64
+ drop: -> (field) { Operations::DropColumn.new(field.name) },
65
+ change: -> (desired, actual) do
66
+ [].tap do |operations|
67
+ if (actual.type != desired.type) || (actual.attributes != desired.attributes)
68
+ operations << Operations::AlterColumnType.new(
69
+ actual.name,
70
+ new_type: desired.type,
71
+ **desired.attributes
72
+ )
73
+ end
74
+
75
+ if desired.primary_key? && !actual.primary_key?
76
+ operations << Operations::CreatePrimaryKey.new(actual.name)
77
+ end
78
+
79
+ if actual.primary_key? && !desired.primary_key?
80
+ operations << Operations::DropPrimaryKey.new(actual.name)
81
+ end
39
82
 
40
- changes.concat(fkey_operations)
83
+ if desired.null? && !actual.null?
84
+ operations << Operations::AllowNull.new(actual.name)
85
+ end
86
+
87
+ if actual.null? && !desired.null?
88
+ operations << Operations::DisallowNull.new(actual.name)
89
+ end
90
+
91
+ if actual.default != desired.default
92
+ operations << Operations::AlterColumnDefault.new(actual.name, new_default: desired.default)
93
+ end
94
+ end
41
95
  end
42
- end
96
+ )
97
+ end
43
98
 
44
- enum_names = [desired_schema.enums, actual_schema.enums].flatten.map(&:name).uniq
99
+ def index_changes(desired_table, actual_table)
100
+ compare_collections(
101
+ desired_table.indexes,
102
+ actual_table.indexes,
103
+ create: -> (index) { Operations::CreateIndex.new(index) },
104
+ drop: -> (index) { Operations::DropIndex.new(index.name) },
105
+ change: -> (desired, actual) do
106
+ [
107
+ Operations::DropIndex.new(actual.name),
108
+ Operations::CreateIndex.new(desired)
109
+ ]
110
+ end
111
+ )
112
+ end
45
113
 
46
- enum_changes = enum_names.each_with_object([]) do |enum_name, changes|
47
- desired = desired_schema.enums.find { |enum| enum.name == enum_name }
48
- actual = actual_schema.enums.find { |enum| enum.name == enum_name }
114
+ def check_changes(desired_table, actual_table)
115
+ compare_collections(
116
+ desired_table.checks,
117
+ actual_table.checks,
118
+ create: -> (check) { Operations::CreateCheckConstraint.new(check) },
119
+ drop: -> (check) { Operations::DropCheckConstraint.new(check.name) },
120
+ change: -> (desired, actual) do
121
+ [
122
+ Operations::DropCheckConstraint.new(actual.name),
123
+ Operations::CreateCheckConstraint.new(desired)
124
+ ]
125
+ end
126
+ )
127
+ end
49
128
 
50
- if desired && !actual
51
- changes << Operations::CreateEnum.new(desired)
52
- elsif actual && !desired
53
- changes << Operations::DropEnum.new(enum_name)
54
- elsif actual != desired
129
+ def foreign_key_changes(desired_table, actual_table)
130
+ compare_collections(
131
+ desired_table.foreign_keys,
132
+ actual_table.foreign_keys,
133
+ create: -> (foreign_key) { Operations::CreateForeignKey.new(actual_table.name, foreign_key) },
134
+ drop: -> (foreign_key) { Operations::DropForeignKey.new(actual_table.name, foreign_key.name) },
135
+ change: -> (desired, actual) do
136
+ [
137
+ Operations::DropForeignKey.new(actual_table.name, actual.name),
138
+ Operations::CreateForeignKey.new(actual_table.name, desired)
139
+ ]
140
+ end
141
+ )
142
+ end
143
+
144
+ def enum_changes(desired_schema, actual_schema)
145
+ compare_collections(
146
+ desired_schema.enums,
147
+ actual_schema.enums,
148
+ create: -> (enum) { Operations::CreateEnum.new(enum) },
149
+ drop: -> (enum) { Operations::DropEnum.new(enum.name) },
150
+ change: -> (desired, actual) do
55
151
  fields = actual_schema.tables.flat_map do |table|
56
152
  table.fields.select do |field|
57
153
  if field.array?
58
- field.attributes[:element_type] == enum_name
154
+ field.attributes[:element_type] == actual.name
59
155
  else
60
- field.type == enum_name
156
+ field.type == actual.name
61
157
  end
62
158
  end.map do |field|
63
159
  if desired_field = desired_schema[table.name][field.name]
@@ -73,115 +169,33 @@ module DbSchema
73
169
  end
74
170
  end
75
171
 
76
- changes << Operations::AlterEnumValues.new(enum_name, desired.values, fields)
77
- end
78
- end
79
-
80
- extension_changes = (desired_schema.extensions - actual_schema.extensions).map do |extension|
81
- Operations::CreateExtension.new(extension)
82
- end + (actual_schema.extensions - desired_schema.extensions).map do |extension|
83
- Operations::DropExtension.new(extension.name)
84
- end
85
-
86
- sort_all_changes(table_changes + enum_changes + extension_changes)
87
- end
88
-
89
- private
90
- def field_changes(desired_fields, actual_fields)
91
- field_names = [desired_fields, actual_fields].flatten.map(&:name).uniq
92
-
93
- field_names.each.with_object([]) do |field_name, table_changes|
94
- desired = desired_fields.find { |field| field.name == field_name }
95
- actual = actual_fields.find { |field| field.name == field_name }
96
-
97
- if desired && !actual
98
- table_changes << Operations::CreateColumn.new(desired)
99
- elsif actual && !desired
100
- table_changes << Operations::DropColumn.new(field_name)
101
- elsif actual != desired
102
- if (actual.type != desired.type) || (actual.attributes != desired.attributes)
103
- table_changes << Operations::AlterColumnType.new(
104
- field_name,
105
- new_type: desired.type,
106
- **desired.attributes
107
- )
108
- end
109
-
110
- if desired.primary_key? && !actual.primary_key?
111
- table_changes << Operations::CreatePrimaryKey.new(field_name)
112
- end
113
-
114
- if actual.primary_key? && !desired.primary_key?
115
- table_changes << Operations::DropPrimaryKey.new(field_name)
116
- end
117
-
118
- if desired.null? && !actual.null?
119
- table_changes << Operations::AllowNull.new(field_name)
120
- end
121
-
122
- if actual.null? && !desired.null?
123
- table_changes << Operations::DisallowNull.new(field_name)
124
- end
125
-
126
- if actual.default != desired.default
127
- table_changes << Operations::AlterColumnDefault.new(field_name, new_default: desired.default)
128
- end
129
- end
130
- end
131
- end
132
-
133
- def index_changes(desired_indices, actual_indices)
134
- index_names = [desired_indices, actual_indices].flatten.map(&:name).uniq
135
-
136
- index_names.each.with_object([]) do |index_name, table_changes|
137
- desired = desired_indices.find { |index| index.name == index_name }
138
- actual = actual_indices.find { |index| index.name == index_name }
139
-
140
- if desired && !actual
141
- table_changes << Operations::CreateIndex.new(desired)
142
- elsif actual && !desired
143
- table_changes << Operations::DropIndex.new(index_name)
144
- elsif actual != desired
145
- table_changes << Operations::DropIndex.new(index_name)
146
- table_changes << Operations::CreateIndex.new(desired)
172
+ Operations::AlterEnumValues.new(actual.name, desired.values, fields)
147
173
  end
148
- end
174
+ )
149
175
  end
150
176
 
151
- def check_changes(desired_checks, actual_checks)
152
- check_names = [desired_checks, actual_checks].flatten.map(&:name).uniq
153
-
154
- check_names.each.with_object([]) do |check_name, table_changes|
155
- desired = desired_checks.find { |check| check.name == check_name }
156
- actual = actual_checks.find { |check| check.name == check_name }
157
-
158
- if desired && !actual
159
- table_changes << Operations::CreateCheckConstraint.new(desired)
160
- elsif actual && !desired
161
- table_changes << Operations::DropCheckConstraint.new(check_name)
162
- elsif actual != desired
163
- table_changes << Operations::DropCheckConstraint.new(check_name)
164
- table_changes << Operations::CreateCheckConstraint.new(desired)
165
- end
166
- end
177
+ def extension_changes(desired_schema, actual_schema)
178
+ compare_collections(
179
+ desired_schema.extensions,
180
+ actual_schema.extensions,
181
+ create: -> (extension) { Operations::CreateExtension.new(extension) },
182
+ drop: -> (extension) { Operations::DropExtension.new(extension.name) }
183
+ )
167
184
  end
168
185
 
169
- def foreign_key_changes(table_name, desired_foreign_keys, actual_foreign_keys)
170
- key_names = [desired_foreign_keys, actual_foreign_keys].flatten.map(&:name).uniq
171
-
172
- key_names.each.with_object([]) do |key_name, table_changes|
173
- desired = desired_foreign_keys.find { |key| key.name == key_name }
174
- actual = actual_foreign_keys.find { |key| key.name == key_name }
175
-
176
- if desired && !actual
177
- table_changes << Operations::CreateForeignKey.new(table_name, desired)
178
- elsif actual && !desired
179
- table_changes << Operations::DropForeignKey.new(table_name, key_name)
180
- elsif actual != desired
181
- table_changes << Operations::DropForeignKey.new(table_name, key_name)
182
- table_changes << Operations::CreateForeignKey.new(table_name, desired)
186
+ def compare_collections(desired, actual, create:, drop:, change: -> (*) {})
187
+ desired_hash = Utils.to_hash(desired, :name)
188
+ actual_hash = Utils.to_hash(actual, :name)
189
+
190
+ (desired_hash.keys + actual_hash.keys).uniq.flat_map do |name|
191
+ if desired_hash.key?(name) && !actual_hash.key?(name)
192
+ create.(desired_hash[name])
193
+ elsif actual_hash.key?(name) && !desired_hash.key?(name)
194
+ drop.(actual_hash[name])
195
+ elsif actual_hash[name] != desired_hash[name]
196
+ change.(desired_hash[name], actual_hash[name])
183
197
  end
184
- end
198
+ end.compact
185
199
  end
186
200
 
187
201
  def sort_all_changes(changes)
@@ -8,6 +8,10 @@ module DbSchema
8
8
  @name = name.to_sym
9
9
  @condition = condition
10
10
  end
11
+
12
+ def with_condition(new_condition)
13
+ CheckConstraint.new(name: name, condition: new_condition)
14
+ end
11
15
  end
12
16
 
13
17
  class NullCheckConstraint < CheckConstraint
@@ -64,6 +64,10 @@ module DbSchema
64
64
  Field.build(name, type, **options, primary_key: primary_key?, attr_name => attr_value)
65
65
  end
66
66
 
67
+ def with_default(new_default)
68
+ Field.build(name, type, **options, primary_key: primary_key?, default: new_default)
69
+ end
70
+
67
71
  class << self
68
72
  def register(*types)
69
73
  types.each do |type|
@@ -41,6 +41,16 @@ module DbSchema
41
41
  condition: condition
42
42
  )
43
43
  end
44
+
45
+ def with_condition(new_condition)
46
+ Index.new(
47
+ name: name,
48
+ columns: columns,
49
+ unique: unique?,
50
+ type: type,
51
+ condition: new_condition
52
+ )
53
+ end
44
54
  end
45
55
 
46
56
  class NullIndex < Index
@@ -1,20 +1,20 @@
1
1
  module DbSchema
2
2
  module Definitions
3
3
  class Table
4
- include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
5
- attr_reader :name, :fields, :indices, :checks, :foreign_keys
4
+ include Dry::Equalizer(:name, :fields, :indexes, :checks, :foreign_keys)
5
+ attr_reader :name, :fields, :indexes, :checks, :foreign_keys
6
6
 
7
- def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
7
+ def initialize(name, fields: [], indexes: [], checks: [], foreign_keys: [])
8
8
  @name = name.to_sym
9
9
  @fields = fields
10
- @indices = indices
10
+ @indexes = indexes
11
11
  @checks = checks
12
12
  @foreign_keys = foreign_keys
13
13
  end
14
14
 
15
15
  def has_expressions?
16
16
  fields.any?(&:default_is_expression?) ||
17
- indices.any?(&:has_expressions?) ||
17
+ indexes.any?(&:has_expressions?) ||
18
18
  checks.any?
19
19
  end
20
20
 
@@ -28,7 +28,7 @@ module DbSchema
28
28
  end
29
29
 
30
30
  def index(index_name)
31
- indices.find { |index| index.name == index_name } || NullIndex.new
31
+ indexes.find { |index| index.name == index_name } || NullIndex.new
32
32
  end
33
33
 
34
34
  def has_index?(index_name)
@@ -36,13 +36,13 @@ module DbSchema
36
36
  end
37
37
 
38
38
  def has_index_on?(*field_names)
39
- indices.any? do |index|
39
+ indexes.any? do |index|
40
40
  index.columns.none?(&:expression?) && index.columns.map(&:name) == field_names
41
41
  end
42
42
  end
43
43
 
44
44
  def has_unique_index_on?(*field_names)
45
- indices.any? do |index|
45
+ indexes.any? do |index|
46
46
  index.unique? && index.columns.none?(&:expression?) && index.columns.map(&:name) == field_names
47
47
  end
48
48
  end
@@ -71,7 +71,7 @@ module DbSchema
71
71
  Table.new(
72
72
  new_name,
73
73
  fields: fields,
74
- indices: indices,
74
+ indexes: indexes,
75
75
  checks: checks,
76
76
  foreign_keys: foreign_keys
77
77
  )
@@ -81,27 +81,37 @@ module DbSchema
81
81
  Table.new(
82
82
  name,
83
83
  fields: new_fields,
84
- indices: indices,
84
+ indexes: indexes,
85
85
  checks: checks,
86
86
  foreign_keys: foreign_keys
87
87
  )
88
88
  end
89
89
 
90
- def with_indices(new_indices)
90
+ def with_indexes(new_indexes)
91
91
  Table.new(
92
92
  name,
93
93
  fields: fields,
94
- indices: new_indices,
94
+ indexes: new_indexes,
95
95
  checks: checks,
96
96
  foreign_keys: foreign_keys
97
97
  )
98
98
  end
99
99
 
100
+ def with_checks(new_checks)
101
+ Table.new(
102
+ name,
103
+ fields: fields,
104
+ indexes: indexes,
105
+ checks: new_checks,
106
+ foreign_keys: foreign_keys
107
+ )
108
+ end
109
+
100
110
  def with_foreign_keys(new_foreign_keys)
101
111
  Table.new(
102
112
  name,
103
113
  fields: fields,
104
- indices: indices,
114
+ indexes: indexes,
105
115
  checks: checks,
106
116
  foreign_keys: new_foreign_keys
107
117
  )
@@ -111,7 +121,7 @@ module DbSchema
111
121
  class NullTable < Table
112
122
  def initialize
113
123
  @fields = []
114
- @indices = []
124
+ @indexes = []
115
125
  @checks = []
116
126
  @foreign_keys = []
117
127
  end
@@ -17,7 +17,7 @@ module DbSchema
17
17
  @schema.tables << Definitions::Table.new(
18
18
  name,
19
19
  fields: table_yielder.fields,
20
- indices: table_yielder.indices,
20
+ indexes: table_yielder.indexes,
21
21
  checks: table_yielder.checks,
22
22
  foreign_keys: table_yielder.foreign_keys
23
23
  )
@@ -64,7 +64,7 @@ module DbSchema
64
64
  end
65
65
 
66
66
  def index(*columns, **index_options)
67
- indices << TableYielder.build_index(
67
+ indexes << TableYielder.build_index(
68
68
  columns,
69
69
  table_name: table_name,
70
70
  **index_options
@@ -105,8 +105,8 @@ module DbSchema
105
105
  @fields ||= []
106
106
  end
107
107
 
108
- def indices
109
- @indices ||= []
108
+ def indexes
109
+ @indexes ||= []
110
110
  end
111
111
 
112
112
  def checks
@@ -31,7 +31,7 @@ module DbSchema
31
31
  table = Definitions::Table.new(
32
32
  name,
33
33
  fields: table_yielder.fields,
34
- indices: table_yielder.indices,
34
+ indexes: table_yielder.indexes,
35
35
  checks: table_yielder.checks,
36
36
  foreign_keys: table_yielder.foreign_keys
37
37
  )
@@ -16,7 +16,7 @@ module DbSchema
16
16
 
17
17
  schema.tables = schema.tables.map do |table|
18
18
  if table.has_expressions?
19
- Table.new(table, hash, connection).normalized_table
19
+ Table.new(table, hash, schema.enums.map(&:name), connection).normalized_table
20
20
  else
21
21
  table
22
22
  end
@@ -50,7 +50,7 @@ module DbSchema
50
50
  def hash
51
51
  @hash ||= begin
52
52
  names = schema.tables.flat_map do |table|
53
- [table.name] + table.fields.map(&:name) + table.indices.map(&:name) + table.checks.map(&:name)
53
+ [table.name] + table.fields.map(&:name) + table.indexes.map(&:name) + table.checks.map(&:name)
54
54
  end
55
55
 
56
56
  Digest::MD5.hexdigest(names.join(','))[0..9]
@@ -58,11 +58,12 @@ module DbSchema
58
58
  end
59
59
 
60
60
  class Table
61
- attr_reader :table, :hash, :connection
61
+ attr_reader :table, :hash, :enum_names, :connection
62
62
 
63
- def initialize(table, hash, connection)
63
+ def initialize(table, hash, enum_names, connection)
64
64
  @table = table
65
65
  @hash = hash
66
+ @enum_names = enum_names
66
67
  @connection = connection
67
68
  end
68
69
 
@@ -76,7 +77,8 @@ module DbSchema
76
77
  operation = Operations::CreateTable.new(
77
78
  table.with_name(temporary_table_name)
78
79
  .with_fields(rename_types(table.fields))
79
- .with_indices(rename_indices(table.indices))
80
+ .with_indexes(rename_indexes(table.indexes))
81
+ .with_checks(rename_types_in_checks(table.checks))
80
82
  )
81
83
 
82
84
  Runner.new([operation], connection).run!
@@ -87,43 +89,72 @@ module DbSchema
87
89
 
88
90
  temporary_table.with_name(table.name)
89
91
  .with_fields(rename_types_back(temporary_table.fields))
90
- .with_indices(rename_indices_back(temporary_table.indices))
92
+ .with_indexes(rename_indexes_back(temporary_table.indexes))
93
+ .with_checks(rename_types_in_checks_back(temporary_table.checks))
91
94
  .with_foreign_keys(table.foreign_keys)
92
95
  end
93
96
 
94
97
  def rename_types(fields)
95
98
  fields.map do |field|
99
+ new_default = if field.default_is_expression?
100
+ rename_all_types_in(field.default.to_s).to_sym
101
+ else
102
+ field.default
103
+ end
104
+
96
105
  if field.custom?
97
106
  field.with_type(append_hash(field.type))
98
107
  elsif field.array? && field.custom_element_type?
99
108
  field.with_attribute(:element_type, append_hash(field.element_type.type).to_sym)
100
109
  else
101
110
  field
102
- end
111
+ end.with_default(new_default)
103
112
  end
104
113
  end
105
114
 
106
115
  def rename_types_back(fields)
107
116
  fields.map do |field|
117
+ new_default = if field.default_is_expression?
118
+ rename_all_types_back_in(field.default.to_s).to_sym
119
+ else
120
+ field.default
121
+ end
122
+
108
123
  if field.custom?
109
124
  field.with_type(remove_hash(field.type))
110
125
  elsif field.array? && field.custom_element_type?
111
126
  field.with_attribute(:element_type, remove_hash(field.element_type.type).to_sym)
112
127
  else
113
128
  field
114
- end
129
+ end.with_default(new_default)
130
+ end
131
+ end
132
+
133
+ def rename_indexes(indexes)
134
+ indexes.map do |index|
135
+ index
136
+ .with_name(append_hash(index.name))
137
+ .with_condition(rename_all_types_in(index.condition))
115
138
  end
116
139
  end
117
140
 
118
- def rename_indices(indices)
119
- indices.map do |index|
120
- index.with_name(append_hash(index.name))
141
+ def rename_indexes_back(indexes)
142
+ indexes.map do |index|
143
+ index
144
+ .with_name(remove_hash(index.name))
145
+ .with_condition(rename_all_types_back_in(index.condition))
121
146
  end
122
147
  end
123
148
 
124
- def rename_indices_back(indices)
125
- indices.map do |index|
126
- index.with_name(remove_hash(index.name))
149
+ def rename_types_in_checks(checks)
150
+ checks.map do |check|
151
+ check.with_condition(rename_all_types_in(check.condition))
152
+ end
153
+ end
154
+
155
+ def rename_types_in_checks_back(checks)
156
+ checks.map do |check|
157
+ check.with_condition(rename_all_types_back_in(check.condition))
127
158
  end
128
159
  end
129
160
 
@@ -138,6 +169,28 @@ module DbSchema
138
169
  def remove_hash(name)
139
170
  name.to_s.sub(/_#{Regexp.escape(hash)}$/, '').to_sym
140
171
  end
172
+
173
+ def rename_all_types_in(string)
174
+ return string unless string.is_a?(String)
175
+
176
+ enum_renaming.reduce(string) do |new_string, (from, to)|
177
+ new_string.gsub(from, to)
178
+ end
179
+ end
180
+
181
+ def rename_all_types_back_in(string)
182
+ return string unless string.is_a?(String)
183
+
184
+ enum_renaming.invert.reduce(string) do |new_string, (from, to)|
185
+ new_string.gsub(from, to)
186
+ end
187
+ end
188
+
189
+ def enum_renaming
190
+ enum_names.reduce({}) do |hash, enum_name|
191
+ hash.merge("::#{enum_name}" => "::#{append_hash(enum_name)}")
192
+ end
193
+ end
141
194
  end
142
195
  end
143
196
  end
@@ -78,7 +78,7 @@ SELECT conname AS name,
78
78
  AND contype = 'c'
79
79
  SQL
80
80
 
81
- INDICES_QUERY = <<-SQL.freeze
81
+ INDEXES_QUERY = <<-SQL.freeze
82
82
  SELECT relname AS name,
83
83
  indkey AS column_positions,
84
84
  indisunique AS unique,
@@ -102,7 +102,7 @@ LEFT JOIN pg_am
102
102
  GROUP BY name, column_positions, indisunique, index_options, condition, index_type, index_oid
103
103
  SQL
104
104
 
105
- EXPRESSION_INDICES_QUERY = <<-SQL.freeze
105
+ EXPRESSION_INDEXES_QUERY = <<-SQL.freeze
106
106
  WITH index_ids AS (SELECT unnest(?) AS index_id),
107
107
  elements AS (SELECT unnest(?) AS element)
108
108
  SELECT index_id,
@@ -142,7 +142,7 @@ SELECT extname
142
142
  build_field(column_data, primary_key: column_data[:name] == primary_key_name)
143
143
  end
144
144
 
145
- indices = indices_data_for(table_name, connection).map do |index_data|
145
+ indexes = indexes_data_for(table_name, connection).map do |index_data|
146
146
  Definitions::Index.new(index_data)
147
147
  end.sort_by(&:name)
148
148
 
@@ -160,21 +160,21 @@ SELECT extname
160
160
  Definitions::Table.new(
161
161
  table_name,
162
162
  fields: fields,
163
- indices: indices,
163
+ indexes: indexes,
164
164
  checks: checks,
165
165
  foreign_keys: foreign_keys
166
166
  )
167
167
  end
168
168
 
169
- def indices_data_for(table_name, connection)
169
+ def indexes_data_for(table_name, connection)
170
170
  column_names = connection[COLUMN_NAMES_QUERY, table_name.to_s].reduce({}) do |names, column|
171
171
  names.merge(column[:pos] => column[:name].to_sym)
172
172
  end
173
173
 
174
- indices_data = connection[INDICES_QUERY, table_name.to_s].to_a
175
- expressions_data = index_expressions_data(indices_data, connection)
174
+ indexes_data = connection[INDEXES_QUERY, table_name.to_s].to_a
175
+ expressions_data = index_expressions_data(indexes_data, connection)
176
176
 
177
- indices_data.map do |index|
177
+ indexes_data.map do |index|
178
178
  positions = index[:column_positions].split(' ').map(&:to_i)
179
179
  options = index[:index_options].split(' ').map(&:to_i)
180
180
 
@@ -227,10 +227,10 @@ SELECT extname
227
227
  end
228
228
 
229
229
  private
230
- def index_expressions_data(indices_data, connection)
230
+ def index_expressions_data(indexes_data, connection)
231
231
  all_positions, max_position = {}, 0
232
232
 
233
- indices_data.each do |index_data|
233
+ indexes_data.each do |index_data|
234
234
  positions = index_data[:column_positions].split(' ').map(&:to_i)
235
235
  expression_positions = positions.each_index.select { |i| positions[i].zero? }
236
236
 
@@ -242,7 +242,7 @@ SELECT extname
242
242
 
243
243
  if all_positions.any?
244
244
  connection[
245
- EXPRESSION_INDICES_QUERY,
245
+ EXPRESSION_INDEXES_QUERY,
246
246
  Sequel.pg_array(all_positions.keys),
247
247
  Sequel.pg_array((1..max_position.succ).to_a)
248
248
  ].each_with_object({}) do |index_data, indexes_data|
@@ -52,7 +52,7 @@ module DbSchema
52
52
  end
53
53
  end
54
54
 
55
- change.table.indices.each do |index|
55
+ change.table.indexes.each do |index|
56
56
  index(
57
57
  index.columns_to_sequel,
58
58
  name: index.name,
@@ -44,6 +44,12 @@ module DbSchema
44
44
  end
45
45
  end
46
46
 
47
+ def to_hash(array, attribute)
48
+ array.reduce({}) do |hash, object|
49
+ hash.merge(object.public_send(attribute) => object)
50
+ end
51
+ end
52
+
47
53
  def sort_by_class(array, sorted_classes)
48
54
  sorted_classes.flat_map do |klass|
49
55
  array.select { |object| object.is_a?(klass) }
@@ -41,7 +41,7 @@ module DbSchema
41
41
 
42
42
  field_names = table.fields.map(&:name)
43
43
 
44
- table.indices.each do |index|
44
+ table.indexes.each do |index|
45
45
  index.columns.reject(&:expression?).map(&:name).each do |field_name|
46
46
  unless field_names.include?(field_name)
47
47
  error_message = %(Index "#{index.name}" refers to a missing field "#{table.name}.#{field_name}")
@@ -1,3 +1,3 @@
1
1
  module DbSchema
2
- VERSION = '0.3'
2
+ VERSION = '0.3.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.3'
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vsevolod Romashov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-24 00:00:00.000000000 Z
11
+ date: 2017-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -234,7 +234,7 @@ files:
234
234
  - lib/db_schema/utils.rb
235
235
  - lib/db_schema/validator.rb
236
236
  - lib/db_schema/version.rb
237
- homepage: https://github.com/7even/db_schema
237
+ homepage: https://github.com/db-schema/core
238
238
  licenses:
239
239
  - MIT
240
240
  metadata: {}