db_schema 0.1 → 0.1.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 +8 -2
- data/README.md +43 -13
- data/lib/db_schema/awesome_print.rb +5 -0
- data/lib/db_schema/changes.rb +20 -1
- data/lib/db_schema/definitions.rb +9 -0
- data/lib/db_schema/definitions/field.rb +14 -1
- data/lib/db_schema/definitions/field/extensions/chkpass.rb +9 -0
- data/lib/db_schema/definitions/field/extensions/citext.rb +9 -0
- data/lib/db_schema/definitions/field/extensions/cube.rb +9 -0
- data/lib/db_schema/definitions/field/extensions/hstore.rb +9 -0
- data/lib/db_schema/definitions/field/extensions/isn.rb +37 -0
- data/lib/db_schema/definitions/field/extensions/ltree.rb +9 -0
- data/lib/db_schema/definitions/field/extensions/seg.rb +9 -0
- data/lib/db_schema/dsl.rb +4 -0
- data/lib/db_schema/reader.rb +22 -19
- data/lib/db_schema/runner.rb +15 -1
- data/lib/db_schema/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 654c088e5c985a78e896af87cd0b4ea07a90767d
|
4
|
+
data.tar.gz: 559f43436988fb15091f53f6dd1aa95c8ee495f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf94f686cf6d2659ab01f5dcce429255becf74cf2038979b283f41d2f1ea9fa486704e35d378abad9b26fdd8264f46d8fb9523923a0972ce2ba1647a17304e12
|
7
|
+
data.tar.gz: a1fb24c2ae4f4fd0a663cb438c7418cb991f9291e1e855572ab3dacf02ebcf000f6809de5bfabe18faeda768675858466c1544fd0c61624b2e53c84a0cc98ef7
|
data/.travis.yml
CHANGED
@@ -1,4 +1,10 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.3.
|
4
|
-
before_install: gem install bundler -v 1.
|
3
|
+
- 2.3.1
|
4
|
+
before_install: gem install bundler -v 1.12.5
|
5
|
+
services:
|
6
|
+
- postgresql
|
7
|
+
addons:
|
8
|
+
postgresql: 9.4
|
9
|
+
before_script:
|
10
|
+
- psql -c 'CREATE DATABASE db_schema_test;' -U postgres
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# DbSchema
|
1
|
+
# DbSchema [![Build Status](https://travis-ci.org/7even/db_schema.svg?branch=master)](https://travis-ci.org/7even/db_schema)
|
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
|
|
@@ -32,7 +32,10 @@ to a different branch only to see something like this?
|
|
32
32
|
Yeah, you must remember the oldest `NO FILE` migration,
|
33
33
|
switch back to the previous branch,
|
34
34
|
roll back every migration up to that `NO FILE`,
|
35
|
+
discard all changes in `schema.rb`/`structure.sql` (and model annotations if you have any),
|
35
36
|
then switch the branch again and migrate these `down` migrations.
|
37
|
+
If you already wrote some code to be committed to the new branch
|
38
|
+
you need to make sure it won't get discarded so a simple `git reset --hard` won't do.
|
36
39
|
Every migration or rollback loads the whole app, resulting in 10+ seconds wasted.
|
37
40
|
And at the end of it all you are trying to recall why did you ever
|
38
41
|
want to switch to that branch.
|
@@ -50,7 +53,7 @@ But you would lose it even with manual migrations.
|
|
50
53
|
Add this line to your application's Gemfile:
|
51
54
|
|
52
55
|
``` ruby
|
53
|
-
gem 'db_schema'
|
56
|
+
gem 'db_schema', '~> 0.1.1'
|
54
57
|
```
|
55
58
|
|
56
59
|
And then execute:
|
@@ -130,7 +133,7 @@ end
|
|
130
133
|
|
131
134
|
Then you just call `rake db:schema:apply` from your deploy script before restarting the app.
|
132
135
|
|
133
|
-
|
136
|
+
## DSL
|
134
137
|
|
135
138
|
Database schema is defined with a block passed to `DbSchema.describe` method.
|
136
139
|
This block receives a `db` object on which you can call `#table` to define a table
|
@@ -139,6 +142,8 @@ is described in a block passed to `#table`.
|
|
139
142
|
|
140
143
|
``` ruby
|
141
144
|
DbSchema.describe do |db|
|
145
|
+
db.extension :hstore
|
146
|
+
|
142
147
|
db.table :users do |t|
|
143
148
|
t.primary_key :id
|
144
149
|
t.varchar :email, null: false
|
@@ -146,6 +151,7 @@ DbSchema.describe do |db|
|
|
146
151
|
t.varchar :name, null: false
|
147
152
|
t.integer :age
|
148
153
|
t.user_status :status, null: false, default: 'registered'
|
154
|
+
t.hstore :tracking, null: false, default: ''
|
149
155
|
|
150
156
|
t.index :email, unique: true
|
151
157
|
end
|
@@ -165,7 +171,7 @@ DbSchema.describe do |db|
|
|
165
171
|
end
|
166
172
|
```
|
167
173
|
|
168
|
-
|
174
|
+
### Tables
|
169
175
|
|
170
176
|
Tables are described with the `#table` method; you pass it the name of the table and describe the table structure in the block:
|
171
177
|
|
@@ -176,7 +182,7 @@ db.table :users do |t|
|
|
176
182
|
end
|
177
183
|
```
|
178
184
|
|
179
|
-
|
185
|
+
#### Fields
|
180
186
|
|
181
187
|
You can define a field of any type by calling the corresponding method inside the table block passing it the field name and it's attributes. Most of the attributes are optional.
|
182
188
|
|
@@ -249,6 +255,20 @@ Other attributes are type specific, like `:length` for varchars; the following t
|
|
249
255
|
| `tsrange` | |
|
250
256
|
| `tstzrange` | |
|
251
257
|
| `daterange` | |
|
258
|
+
| `chkpass` | |
|
259
|
+
| `citext` | |
|
260
|
+
| `cube` | |
|
261
|
+
| `hstore` | |
|
262
|
+
| `ean13` | |
|
263
|
+
| `isbn13` | |
|
264
|
+
| `ismn13` | |
|
265
|
+
| `issn13` | |
|
266
|
+
| `isbn` | |
|
267
|
+
| `ismn` | |
|
268
|
+
| `issn` | |
|
269
|
+
| `upc` | |
|
270
|
+
| `ltree` | |
|
271
|
+
| `seg` | |
|
252
272
|
|
253
273
|
The `of` attribute of the array type is the only required attribute (you need to specify the array element type here); other attributes either have default values or can be omitted at all.
|
254
274
|
|
@@ -267,7 +287,7 @@ end
|
|
267
287
|
|
268
288
|
**Important: you can't rename a table or a column just by changing it's name in the schema definition - this will result in a column with the old name being deleted and a column with the new name being added; all data in that table or column will be lost.**
|
269
289
|
|
270
|
-
|
290
|
+
#### Indexes
|
271
291
|
|
272
292
|
Indexes are created using the `#index` method: you pass it the field name you want to index:
|
273
293
|
|
@@ -336,7 +356,7 @@ end
|
|
336
356
|
|
337
357
|
Be warned though that you have to specify the condition exactly as PostgreSQL outputs it in `psql` with `\d table_name` command; otherwise your index will be recreated on each DbSchema run. This will be fixed in a later DbSchema version.
|
338
358
|
|
339
|
-
|
359
|
+
#### Foreign keys
|
340
360
|
|
341
361
|
The `#foreign_key` method defines a foreign key. In it's minimal form it takes a referencing field name and referenced table name:
|
342
362
|
|
@@ -390,7 +410,7 @@ There are 3 more options to the `#foreign_key` method: `:on_update`, `:on_delete
|
|
390
410
|
|
391
411
|
Passing `deferrable: true` defines a foreign key that is checked at the end of transaction.
|
392
412
|
|
393
|
-
|
413
|
+
#### Check constraints
|
394
414
|
|
395
415
|
A check constraint is like a validation on the database side: it checks if the inserted/updated row has valid values.
|
396
416
|
|
@@ -408,7 +428,7 @@ end
|
|
408
428
|
|
409
429
|
As with partial index conditions, for now you have to specify the SQL exactly as `psql` outputs it (otherwise the constraint will be recreated on each run).
|
410
430
|
|
411
|
-
|
431
|
+
### Enum types
|
412
432
|
|
413
433
|
PostgreSQL allows developers to create custom enum types; value of enum type is one of a fixed set of values stored in the type definition.
|
414
434
|
|
@@ -434,11 +454,21 @@ db.enum :user_status, [:guest, :registered, :sent_confirmation_email, :confirmed
|
|
434
454
|
|
435
455
|
Reordering and deleting values from enum types is not supported.
|
436
456
|
|
437
|
-
###
|
457
|
+
### Extensions
|
458
|
+
|
459
|
+
PostgreSQL has a [wide variety](https://www.postgresql.org/docs/9.5/static/contrib.html) of extensions providing additional data types, functions and operators. You can use DbSchema to add and remove extensions in your database:
|
460
|
+
|
461
|
+
``` ruby
|
462
|
+
db.extension :hstore
|
463
|
+
```
|
464
|
+
|
465
|
+
*Note that adding and removing extensions in Postgres requires superuser privileges.*
|
466
|
+
|
467
|
+
## Configuration
|
438
468
|
|
439
469
|
DbSchema must be configured prior to applying the schema. There are 2 methods you can use for that: `configure` and `configure_from_yaml`.
|
440
470
|
|
441
|
-
|
471
|
+
### DbSchema.configure
|
442
472
|
|
443
473
|
`configure` is a generic method that receives a hash with all configuration options:
|
444
474
|
|
@@ -453,7 +483,7 @@ DbSchema.configure(
|
|
453
483
|
)
|
454
484
|
```
|
455
485
|
|
456
|
-
|
486
|
+
### DbSchema.configure_from_yaml
|
457
487
|
|
458
488
|
`configure_from_yaml` is designed to use with Rails so you don't have to duplicate database connection settings from your `database.yml` in DbSchema configuration. Pass it the full path to your `database.yml` file and your current application environment (`development`, `production` etc), and it will read the db connection settings from that file.
|
459
489
|
|
@@ -471,7 +501,7 @@ DbSchema.configure_from_yaml(
|
|
471
501
|
)
|
472
502
|
```
|
473
503
|
|
474
|
-
|
504
|
+
### Configuration options
|
475
505
|
|
476
506
|
All configuration options are described in the following table:
|
477
507
|
|
@@ -29,6 +29,8 @@ if defined?(AwesomePrint)
|
|
29
29
|
:dbschema_foreign_key
|
30
30
|
when ::DbSchema::Definitions::Enum
|
31
31
|
:dbschema_enum
|
32
|
+
when ::DbSchema::Definitions::Extension
|
33
|
+
:dbschema_column_operation
|
32
34
|
when ::DbSchema::Changes::CreateTable
|
33
35
|
:dbschema_create_table
|
34
36
|
when ::DbSchema::Changes::DropTable
|
@@ -68,6 +70,9 @@ if defined?(AwesomePrint)
|
|
68
70
|
:dbschema_column_operation
|
69
71
|
when ::DbSchema::Changes::AddValueToEnum
|
70
72
|
:dbschema_add_value_to_enum
|
73
|
+
when ::DbSchema::Changes::CreateExtension,
|
74
|
+
::DbSchema::Changes::DropExtension
|
75
|
+
:dbschema_column_operation
|
71
76
|
else
|
72
77
|
cast_without_dbschema(object, type)
|
73
78
|
end
|
data/lib/db_schema/changes.rb
CHANGED
@@ -89,7 +89,16 @@ module DbSchema
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
|
92
|
+
desired_extensions = extract_extensions(desired_schema)
|
93
|
+
actual_extensions = extract_extensions(actual_schema)
|
94
|
+
|
95
|
+
extension_changes = (desired_extensions - actual_extensions).map do |extension|
|
96
|
+
CreateExtension.new(extension.name)
|
97
|
+
end + (actual_extensions - desired_extensions).map do |extension|
|
98
|
+
DropExtension.new(extension.name)
|
99
|
+
end
|
100
|
+
|
101
|
+
table_changes + enum_changes + extension_changes
|
93
102
|
end
|
94
103
|
|
95
104
|
private
|
@@ -225,6 +234,10 @@ module DbSchema
|
|
225
234
|
def extract_enums(schema)
|
226
235
|
Utils.filter_by_class(schema, Definitions::Enum)
|
227
236
|
end
|
237
|
+
|
238
|
+
def extract_extensions(schema)
|
239
|
+
Utils.filter_by_class(schema, Definitions::Extension)
|
240
|
+
end
|
228
241
|
end
|
229
242
|
|
230
243
|
class CreateTable
|
@@ -392,5 +405,11 @@ module DbSchema
|
|
392
405
|
before.nil?
|
393
406
|
end
|
394
407
|
end
|
408
|
+
|
409
|
+
class CreateExtension < Definitions::Extension
|
410
|
+
end
|
411
|
+
|
412
|
+
class DropExtension < ColumnOperation
|
413
|
+
end
|
395
414
|
end
|
396
415
|
end
|
@@ -3,7 +3,11 @@ module DbSchema
|
|
3
3
|
module Field
|
4
4
|
class << self
|
5
5
|
def build(name, type, **options)
|
6
|
-
|
6
|
+
if registry.key?(type)
|
7
|
+
type_class_for(type).new(name, **options)
|
8
|
+
else
|
9
|
+
Custom.new(name, type_name: type, **options)
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
def type_class_for(type)
|
@@ -35,4 +39,13 @@ require_relative 'field/uuid'
|
|
35
39
|
require_relative 'field/json'
|
36
40
|
require_relative 'field/array'
|
37
41
|
require_relative 'field/range'
|
42
|
+
|
43
|
+
require_relative 'field/extensions/chkpass'
|
44
|
+
require_relative 'field/extensions/citext'
|
45
|
+
require_relative 'field/extensions/cube'
|
46
|
+
require_relative 'field/extensions/hstore'
|
47
|
+
require_relative 'field/extensions/isn'
|
48
|
+
require_relative 'field/extensions/ltree'
|
49
|
+
require_relative 'field/extensions/seg'
|
50
|
+
|
38
51
|
require_relative 'field/custom'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module DbSchema
|
2
|
+
module Definitions
|
3
|
+
module Field
|
4
|
+
class EAN13 < Base
|
5
|
+
register :ean13
|
6
|
+
end
|
7
|
+
|
8
|
+
class ISBN13 < Base
|
9
|
+
register :isbn13
|
10
|
+
end
|
11
|
+
|
12
|
+
class ISMN13 < Base
|
13
|
+
register :ismn13
|
14
|
+
end
|
15
|
+
|
16
|
+
class ISSN13 < Base
|
17
|
+
register :issn13
|
18
|
+
end
|
19
|
+
|
20
|
+
class ISBN < Base
|
21
|
+
register :isbn
|
22
|
+
end
|
23
|
+
|
24
|
+
class ISMN < Base
|
25
|
+
register :ismn
|
26
|
+
end
|
27
|
+
|
28
|
+
class ISSN < Base
|
29
|
+
register :issn
|
30
|
+
end
|
31
|
+
|
32
|
+
class UPC < Base
|
33
|
+
register :upc
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/db_schema/dsl.rb
CHANGED
data/lib/db_schema/reader.rb
CHANGED
@@ -87,12 +87,22 @@ LEFT JOIN pg_am
|
|
87
87
|
GROUP BY name
|
88
88
|
SQL
|
89
89
|
|
90
|
+
EXTENSIONS_QUERY = <<-SQL.freeze
|
91
|
+
SELECT extname
|
92
|
+
FROM pg_extension
|
93
|
+
WHERE extname != 'plpgsql'
|
94
|
+
SQL
|
95
|
+
|
90
96
|
class << self
|
91
97
|
def read_schema
|
92
98
|
enums = DbSchema.connection[ENUMS_QUERY].map do |enum_data|
|
93
99
|
Definitions::Enum.new(enum_data[:name].to_sym, enum_data[:values].map(&:to_sym))
|
94
100
|
end
|
95
101
|
|
102
|
+
extensions = DbSchema.connection[EXTENSIONS_QUERY].map do |extension_data|
|
103
|
+
Definitions::Extension.new(extension_data[:extname].to_sym)
|
104
|
+
end
|
105
|
+
|
96
106
|
tables = DbSchema.connection.tables.map do |table_name|
|
97
107
|
primary_key_name = DbSchema.connection.primary_key(table_name)
|
98
108
|
|
@@ -124,7 +134,7 @@ GROUP BY name
|
|
124
134
|
)
|
125
135
|
end
|
126
136
|
|
127
|
-
enums + tables
|
137
|
+
enums + extensions + tables
|
128
138
|
end
|
129
139
|
|
130
140
|
def indices_data_for(table_name)
|
@@ -164,6 +174,9 @@ GROUP BY name
|
|
164
174
|
private
|
165
175
|
def build_field(data, primary_key: false)
|
166
176
|
type = data[:type].to_sym.downcase
|
177
|
+
if type == :'user-defined'
|
178
|
+
type = data[:custom_type_name].to_sym
|
179
|
+
end
|
167
180
|
|
168
181
|
nullable = (data[:null] != 'NO')
|
169
182
|
|
@@ -212,24 +225,14 @@ GROUP BY name
|
|
212
225
|
{}
|
213
226
|
end
|
214
227
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
else
|
224
|
-
Definitions::Field.build(
|
225
|
-
data[:name].to_sym,
|
226
|
-
type,
|
227
|
-
primary_key: primary_key,
|
228
|
-
null: nullable,
|
229
|
-
default: default,
|
230
|
-
**options
|
231
|
-
)
|
232
|
-
end
|
228
|
+
Definitions::Field.build(
|
229
|
+
data[:name].to_sym,
|
230
|
+
type,
|
231
|
+
primary_key: primary_key,
|
232
|
+
null: nullable,
|
233
|
+
default: default,
|
234
|
+
**options
|
235
|
+
)
|
233
236
|
end
|
234
237
|
|
235
238
|
def build_foreign_key(data)
|
data/lib/db_schema/runner.rb
CHANGED
@@ -24,6 +24,10 @@ module DbSchema
|
|
24
24
|
self.class.create_enum(change)
|
25
25
|
when Changes::DropEnum
|
26
26
|
self.class.drop_enum(change)
|
27
|
+
when Changes::CreateExtension
|
28
|
+
self.class.create_extension(change)
|
29
|
+
when Changes::DropExtension
|
30
|
+
self.class.drop_extension(change)
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
@@ -39,6 +43,7 @@ module DbSchema
|
|
39
43
|
Utils.sort_by_class(
|
40
44
|
changes,
|
41
45
|
[
|
46
|
+
Changes::CreateExtension,
|
42
47
|
Changes::AddValueToEnum,
|
43
48
|
Changes::DropForeignKey,
|
44
49
|
Changes::CreateEnum,
|
@@ -46,7 +51,8 @@ module DbSchema
|
|
46
51
|
Changes::AlterTable,
|
47
52
|
Changes::DropTable,
|
48
53
|
Changes::DropEnum,
|
49
|
-
Changes::CreateForeignKey
|
54
|
+
Changes::CreateForeignKey,
|
55
|
+
Changes::DropExtension
|
50
56
|
]
|
51
57
|
)
|
52
58
|
end
|
@@ -187,6 +193,14 @@ module DbSchema
|
|
187
193
|
end
|
188
194
|
end
|
189
195
|
|
196
|
+
def create_extension(change)
|
197
|
+
DbSchema.connection.run(%Q(CREATE EXTENSION "#{change.name}"))
|
198
|
+
end
|
199
|
+
|
200
|
+
def drop_extension(change)
|
201
|
+
DbSchema.connection.run(%Q(DROP EXTENSION "#{change.name}"))
|
202
|
+
end
|
203
|
+
|
190
204
|
def map_options(type, options)
|
191
205
|
mapping = case type
|
192
206
|
when :char, :varchar, :bit, :varbit
|
data/lib/db_schema/version.rb
CHANGED
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:
|
4
|
+
version: 0.1.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: 2016-
|
11
|
+
date: 2016-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -198,6 +198,13 @@ files:
|
|
198
198
|
- lib/db_schema/definitions/field/character.rb
|
199
199
|
- lib/db_schema/definitions/field/custom.rb
|
200
200
|
- lib/db_schema/definitions/field/datetime.rb
|
201
|
+
- lib/db_schema/definitions/field/extensions/chkpass.rb
|
202
|
+
- lib/db_schema/definitions/field/extensions/citext.rb
|
203
|
+
- lib/db_schema/definitions/field/extensions/cube.rb
|
204
|
+
- lib/db_schema/definitions/field/extensions/hstore.rb
|
205
|
+
- lib/db_schema/definitions/field/extensions/isn.rb
|
206
|
+
- lib/db_schema/definitions/field/extensions/ltree.rb
|
207
|
+
- lib/db_schema/definitions/field/extensions/seg.rb
|
201
208
|
- lib/db_schema/definitions/field/geometric.rb
|
202
209
|
- lib/db_schema/definitions/field/json.rb
|
203
210
|
- lib/db_schema/definitions/field/monetary.rb
|