db_schema 0.1 → 0.1.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 +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 [](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
|