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 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