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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dbf606aae084d2032ffbfb12940cfee0d0b9ad3
4
- data.tar.gz: e354a7f339af08dadfba3b2a9bd505be2603778f
3
+ metadata.gz: 654c088e5c985a78e896af87cd0b4ea07a90767d
4
+ data.tar.gz: 559f43436988fb15091f53f6dd1aa95c8ee495f4
5
5
  SHA512:
6
- metadata.gz: 6777fcffd1c201b885ae98ff4b3c44bd64b0d3fcc7585a06ae5a9a966b874a7a96070c4fb428f3762ebf5ea68e4d8ecd688a496d5076328a539e4c348ded62b8
7
- data.tar.gz: 54a567aeb035b438447b7a70aa0837a6d84974f400f606820b53b4046e192189ab54999c61a439e8a4e7739114936f6f26af4d2ea26ed00002c2290ddcb58e1f
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.0
4
- before_install: gem install bundler -v 1.11.2
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
- ### DSL
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
- #### Tables
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
- ##### Fields
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
- ##### Indexes
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
- ##### Foreign keys
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
- ##### Check constraints
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
- #### Enum types
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
- ### Configuration
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
- #### DbSchema.configure
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
- #### DbSchema.configure_from_yaml
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
- #### Configuration options
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
@@ -89,7 +89,16 @@ module DbSchema
89
89
  end
90
90
  end
91
91
 
92
- table_changes + enum_changes
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
@@ -116,6 +116,15 @@ module DbSchema
116
116
  @values = values
117
117
  end
118
118
  end
119
+
120
+ class Extension
121
+ include Dry::Equalizer(:name)
122
+ attr_reader :name
123
+
124
+ def initialize(name)
125
+ @name = name
126
+ end
127
+ end
119
128
  end
120
129
  end
121
130
 
@@ -3,7 +3,11 @@ module DbSchema
3
3
  module Field
4
4
  class << self
5
5
  def build(name, type, **options)
6
- type_class_for(type).new(name, **options)
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,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Chkpass < Base
5
+ register :chkpass
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Citext < Base
5
+ register :citext
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Cube < Base
5
+ register :cube
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Hstore < Base
5
+ register :hstore
6
+ end
7
+ end
8
+ end
9
+ end
@@ -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
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Ltree < Base
5
+ register :ltree
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Seg < Base
5
+ register :seg
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/db_schema/dsl.rb CHANGED
@@ -29,6 +29,10 @@ module DbSchema
29
29
  @schema << Definitions::Enum.new(name.to_sym, values.map(&:to_sym))
30
30
  end
31
31
 
32
+ def extension(name)
33
+ @schema << Definitions::Extension.new(name.to_sym)
34
+ end
35
+
32
36
  class TableYielder
33
37
  attr_reader :table_name
34
38
 
@@ -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
- if data[:type] == 'USER-DEFINED'
216
- Definitions::Field::Custom.new(
217
- data[:name].to_sym,
218
- type_name: data[:custom_type_name].to_sym,
219
- primary_key: primary_key,
220
- null: nullable,
221
- default: default
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)
@@ -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
@@ -1,3 +1,3 @@
1
1
  module DbSchema
2
- VERSION = '0.1'
2
+ VERSION = '0.1.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.1'
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-07-24 00:00:00.000000000 Z
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