db_schema 0.1.3 → 0.2

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: f262094701b7ff8ff410c8854af2676daff44f4f
4
- data.tar.gz: 0ba850facc23b85c344314a7e1e8b567ac762e4a
3
+ metadata.gz: 3851833bd5308ba174d2d70c217b6fc73c7d583b
4
+ data.tar.gz: 95071a258ce6202862e38a64d0d57fee8a0cfea9
5
5
  SHA512:
6
- metadata.gz: 6fb1c3a34728124816899da8c6f67d82d70a38a0f40228cb65fca5597db8db9984fa5490554c64f0484e4160f33da94220fc385cc4b78298c6103aad189b9a6e
7
- data.tar.gz: daeb82268ecfa4202dee52f97d92c8f4e2c6cd01a0af5c24ebb8391f16128836137a8914b8ed752eef3a4d07a478853dc75a1315358e2bc2b51df447e9d451a1
6
+ metadata.gz: 424334e2045a9cf08bdf6fedddbc8710c05f1d101ac2bd22350191e71e8dccc201118bb3511590ff56d67610c80494e6c7aed98d2d2e263b582343f910179567
7
+ data.tar.gz: cac4af5351c520d8610a98c54e57d7c93a884bcc0f0b33e953eedb45291e43e9cfd5fccfef47ee791e3ab07dcde72a546c6c3f0d22959c3acdf8f9b24fe1f1c6
data/README.md CHANGED
@@ -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.1.3'
56
+ gem 'db_schema', '~> 0.2.0'
57
57
  ```
58
58
 
59
59
  And then execute:
@@ -207,7 +207,7 @@ db.table :people do |t|
207
207
  end
208
208
  ```
209
209
 
210
- Passing `null: false` to the field definition makes it `NOT NULL`; passing some value under the `:default` key makes it the default value. A symbol passed as a default is interpreted as a function call so `t.timestamp :created_at, default: :now` defines a field with a default value of `NOW()`; strings, numbers, timestamps etc are evaluated "as is".
210
+ Passing `null: false` to the field definition makes it `NOT NULL`; passing some value under the `:default` key makes it the default value. You can use `String`s as SQL strings, `Fixnum`s as integers, `Float`s as floating point numbers, `true` & `false` as their SQL counterparts, `Date`s as SQL dates and `Time`s as timestamps. A symbol passed as a default is a special case: it is interpreted as an SQL expression so `t.timestamp :created_at, default: :'now()'` defines a field with a default value of `NOW()`.
211
211
 
212
212
  Other attributes are type specific, like `:length` for varchars; the following table lists them all (values in parentheses are default attribute values).
213
213
 
@@ -354,8 +354,6 @@ db.table :users do |t|
354
354
  end
355
355
  ```
356
356
 
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.
358
-
359
357
  If you need an index on expression you can use the same syntax replacing column names with SQL strings containing the expressions:
360
358
 
361
359
  ``` ruby
@@ -367,8 +365,6 @@ end
367
365
 
368
366
  Expression indexes syntax allows specifying an order exactly like in a common index on table fields - just use a hash form like `t.index 'date(created_at)' => :desc`. You can also use an expression in a multiple index.
369
367
 
370
- As with partial index condition (and all other SQL segments in `db_schema`), you must write the expression in a way `psql` outputs it, so instead of `lower(email)` you should use `lower(email::text)` (assuming that `email` is a varchar field).
371
-
372
368
  #### Foreign keys
373
369
 
374
370
  The `#foreign_key` method defines a foreign key. In it's minimal form it takes a referencing field name and referenced table name:
@@ -439,8 +435,6 @@ db.table :users do |t|
439
435
  end
440
436
  ```
441
437
 
442
- 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).
443
-
444
438
  ### Enum types
445
439
 
446
440
  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.
@@ -532,9 +526,7 @@ All configuration options are described in the following table:
532
526
 
533
527
  By default DbSchema logs the changes it applies to your database; you can disable that by setting `log_changes` to false.
534
528
 
535
- DbSchema provides an opt-out post-run schema check; it ensures that there are no remaining differences between your `schema.rb` and the actual database schema. If DbSchema still sees any differences it will keep applying them on each run - usually this is harmless (because it does not really change your schema) but in the case of a partial index with a complex condition or an index on some expression it may rebuild the index which is an expensive operation on a large table. You can set `post_check` to false if you are 100% sure that your persistent changes are not a problem for you but I strongly recommend that you turn it on from time to time just to make sure nothing dangerous appears in these persistent changes.
536
-
537
- The `post_check` option is likely to become off by default when DbSchema becomes more stable and battle-tested, and when the partial index problem will be solved.
529
+ DbSchema provides an opt-out post-run schema check; it ensures that the schema was applied correctly and there are no remaining differences between your `schema.rb` and the actual database schema. The corresponding `post_check` option is likely to become off by default when DbSchema becomes more stable and battle-tested.
538
530
 
539
531
  There is also a dry run mode which does not apply the changes to your database - it just logs the necessary changes (if you leave `log_changes` set to `true`). Post check is also skipped in that case.
540
532
 
@@ -543,12 +535,8 @@ Dry run may be useful while you are building your schema definition for an exist
543
535
  ## Known problems and limitations
544
536
 
545
537
  * primary keys are hardcoded to a single NOT NULL integer field with a postgres sequence attached
546
- * "partial index problem": some conditions of partial indexes and check constraints can cause
547
- a false positive result of checking for differences between `schema.rb` and actual database schema,
548
- resulting in unwanted operations on each run (the worst of them being the recreation of an index
549
- on a large table)
550
538
  * array element type attributes are not supported
551
- * precision in time & datetime types isn't supported
539
+ * precision in all date/time types isn't supported
552
540
  * no support for databases other than PostgreSQL
553
541
  * no support for renaming tables & columns
554
542
 
@@ -13,6 +13,8 @@ if defined?(AwesomePrint)
13
13
 
14
14
  def cast_with_dbschema(object, type)
15
15
  case object
16
+ when ::DbSchema::Definitions::Schema
17
+ :dbschema_schema
16
18
  when ::DbSchema::Definitions::Table
17
19
  :dbschema_table
18
20
  when ::DbSchema::Definitions::Field::Custom
@@ -79,6 +81,15 @@ if defined?(AwesomePrint)
79
81
  end
80
82
 
81
83
  private
84
+ def awesome_dbschema_schema(object)
85
+ data = ["tables: #{object.tables.ai}"]
86
+ data << "enums: #{object.enums.ai}" if object.enums.any?
87
+ data << "extensions: #{object.extensions.ai}" if object.extensions.any?
88
+
89
+ data_string = data.join(', ')
90
+ "#<DbSchema::Definitions::Schema #{data_string}>"
91
+ end
92
+
82
93
  def awesome_dbschema_table(object)
83
94
  data = ["fields: #{object.fields.ai}"]
84
95
  data << "indices: #{object.indices.ai}" if object.indices.any?
@@ -92,7 +103,12 @@ if defined?(AwesomePrint)
92
103
  def awesome_dbschema_field(object)
93
104
  options = object.options.map do |k, v|
94
105
  key = colorize("#{k}:", :symbol)
95
- "#{key} #{v.ai}"
106
+
107
+ if (k == :default) && v.is_a?(Symbol)
108
+ "#{key} #{colorize(v.to_s, :string)}"
109
+ else
110
+ "#{key} #{v.ai}"
111
+ end
96
112
  end.unshift(nil).join(', ')
97
113
 
98
114
  primary_key = if object.primary_key?
@@ -107,7 +123,12 @@ if defined?(AwesomePrint)
107
123
  def awesome_dbschema_custom_field(object)
108
124
  options = object.options.map do |k, v|
109
125
  key = colorize("#{k}:", :symbol)
110
- "#{key} #{v.ai}"
126
+
127
+ if (k == :default) && v.is_a?(Symbol)
128
+ "#{key} #{colorize(v.to_s, :string)}"
129
+ else
130
+ "#{key} #{v.ai}"
131
+ end
111
132
  end.unshift(nil).join(', ')
112
133
 
113
134
  primary_key = if object.primary_key?
@@ -212,7 +233,13 @@ if defined?(AwesomePrint)
212
233
  end
213
234
 
214
235
  def awesome_dbschema_alter_column_default(object)
215
- "#<DbSchema::Changes::AlterColumnDefault #{object.name.ai}, #{object.new_default.ai}>"
236
+ new_default = if object.new_default.is_a?(Symbol)
237
+ colorize(object.new_default.to_s, :string)
238
+ else
239
+ object.new_default.ai
240
+ end
241
+
242
+ "#<DbSchema::Changes::AlterColumnDefault #{object.name.ai}, #{new_default}>"
216
243
  end
217
244
 
218
245
  def awesome_dbschema_create_foreign_key(object)
@@ -5,14 +5,11 @@ module DbSchema
5
5
  module Changes
6
6
  class << self
7
7
  def between(desired_schema, actual_schema)
8
- desired_tables = extract_tables(desired_schema)
9
- actual_tables = extract_tables(actual_schema)
10
-
11
- table_names = [desired_tables, actual_tables].flatten.map(&:name).uniq
8
+ table_names = [desired_schema.tables, actual_schema.tables].flatten.map(&:name).uniq
12
9
 
13
10
  table_changes = table_names.each.with_object([]) do |table_name, changes|
14
- desired = desired_tables.find { |table| table.name == table_name }
15
- actual = actual_tables.find { |table| table.name == table_name }
11
+ desired = desired_schema.tables.find { |table| table.name == table_name }
12
+ actual = actual_schema.tables.find { |table| table.name == table_name }
16
13
 
17
14
  if desired && !actual
18
15
  changes << CreateTable.new(
@@ -51,14 +48,11 @@ module DbSchema
51
48
  end
52
49
  end
53
50
 
54
- desired_enums = extract_enums(desired_schema)
55
- actual_enums = extract_enums(actual_schema)
56
-
57
- enum_names = [desired_enums, actual_enums].flatten.map(&:name).uniq
51
+ enum_names = [desired_schema.enums, actual_schema.enums].flatten.map(&:name).uniq
58
52
 
59
53
  enum_changes = enum_names.each_with_object([]) do |enum_name, changes|
60
- desired = desired_enums.find { |enum| enum.name == enum_name }
61
- actual = actual_enums.find { |enum| enum.name == enum_name }
54
+ desired = desired_schema.enums.find { |enum| enum.name == enum_name }
55
+ actual = actual_schema.enums.find { |enum| enum.name == enum_name }
62
56
 
63
57
  if desired && !actual
64
58
  changes << CreateEnum.new(enum_name, desired.values)
@@ -89,12 +83,9 @@ module DbSchema
89
83
  end
90
84
  end
91
85
 
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|
86
+ extension_changes = (desired_schema.extensions - actual_schema.extensions).map do |extension|
96
87
  CreateExtension.new(extension.name)
97
- end + (actual_extensions - desired_extensions).map do |extension|
88
+ end + (actual_schema.extensions - desired_schema.extensions).map do |extension|
98
89
  DropExtension.new(extension.name)
99
90
  end
100
91
 
@@ -226,18 +217,6 @@ module DbSchema
226
217
  end
227
218
  end
228
219
  end
229
-
230
- def extract_tables(schema)
231
- Utils.filter_by_class(schema, Definitions::Table)
232
- end
233
-
234
- def extract_enums(schema)
235
- Utils.filter_by_class(schema, Definitions::Enum)
236
- end
237
-
238
- def extract_extensions(schema)
239
- Utils.filter_by_class(schema, Definitions::Extension)
240
- end
241
220
  end
242
221
 
243
222
  class CreateTable
@@ -9,7 +9,7 @@ module DbSchema
9
9
  @name = name
10
10
  @primary_key = primary_key
11
11
  @null = null
12
- @default = process_default(default)
12
+ @default = default
13
13
  @attributes = attributes
14
14
  end
15
15
 
@@ -21,6 +21,10 @@ module DbSchema
21
21
  !primary_key? && @null
22
22
  end
23
23
 
24
+ def default_is_expression?
25
+ default.is_a?(Symbol)
26
+ end
27
+
24
28
  def options
25
29
  attributes.tap do |options|
26
30
  options[:null] = false unless null?
@@ -48,14 +52,6 @@ module DbSchema
48
52
  self.class.type
49
53
  end
50
54
 
51
- def process_default(default)
52
- if default.is_a?(Symbol)
53
- Sequel.function(default)
54
- else
55
- default
56
- end
57
- end
58
-
59
55
  class << self
60
56
  def register(*types)
61
57
  types.each do |type|
@@ -23,7 +23,7 @@ module DbSchema
23
23
 
24
24
  class Interval < Base
25
25
  register :interval
26
- attributes :fields, :precision
26
+ attributes :fields
27
27
  end
28
28
  end
29
29
  end
@@ -2,6 +2,37 @@ require 'dry/equalizer'
2
2
 
3
3
  module DbSchema
4
4
  module Definitions
5
+ class Schema
6
+ include Dry::Equalizer(:tables, :enums, :extensions)
7
+ attr_reader :tables, :enums, :extensions
8
+ attr_writer :tables
9
+
10
+ def initialize(tables: [], enums: [], extensions: [])
11
+ @tables = tables
12
+ @enums = enums
13
+ @extensions = extensions
14
+ end
15
+ end
16
+
17
+ class Table
18
+ include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
19
+ attr_reader :name, :fields, :indices, :checks, :foreign_keys
20
+
21
+ def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
22
+ @name = name.to_sym
23
+ @fields = fields
24
+ @indices = indices
25
+ @checks = checks
26
+ @foreign_keys = foreign_keys
27
+ end
28
+
29
+ def has_expressions?
30
+ fields.any?(&:default_is_expression?) ||
31
+ indices.any?(&:has_expressions?) ||
32
+ checks.any?
33
+ end
34
+ end
35
+
5
36
  class Index
6
37
  include Dry::Equalizer(:name, :columns, :unique?, :type, :condition)
7
38
  attr_reader :name, :columns, :type, :condition
@@ -30,6 +61,10 @@ module DbSchema
30
61
  end
31
62
  end
32
63
 
64
+ def has_expressions?
65
+ !condition.nil? || columns.any?(&:expression?)
66
+ end
67
+
33
68
  class Column
34
69
  include Dry::Equalizer(:name, :order, :nulls)
35
70
  attr_reader :name, :order, :nulls
@@ -130,19 +165,6 @@ module DbSchema
130
165
  end
131
166
  end
132
167
 
133
- class Table
134
- include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
135
- attr_reader :name, :fields, :indices, :checks, :foreign_keys
136
-
137
- def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
138
- @name = name.to_sym
139
- @fields = fields
140
- @indices = indices
141
- @checks = checks
142
- @foreign_keys = foreign_keys
143
- end
144
- end
145
-
146
168
  class Enum
147
169
  include Dry::Equalizer(:name, :values)
148
170
  attr_reader :name, :values
data/lib/db_schema/dsl.rb CHANGED
@@ -4,7 +4,7 @@ module DbSchema
4
4
 
5
5
  def initialize(block)
6
6
  @block = block
7
- @schema = []
7
+ @schema = Definitions::Schema.new
8
8
  end
9
9
 
10
10
  def schema
@@ -16,7 +16,7 @@ module DbSchema
16
16
  def table(name, &block)
17
17
  table_yielder = TableYielder.new(name, block)
18
18
 
19
- @schema << Definitions::Table.new(
19
+ @schema.tables << Definitions::Table.new(
20
20
  name,
21
21
  fields: table_yielder.fields,
22
22
  indices: table_yielder.indices,
@@ -26,11 +26,11 @@ module DbSchema
26
26
  end
27
27
 
28
28
  def enum(name, values)
29
- @schema << Definitions::Enum.new(name.to_sym, values.map(&:to_sym))
29
+ @schema.enums << Definitions::Enum.new(name.to_sym, values.map(&:to_sym))
30
30
  end
31
31
 
32
32
  def extension(name)
33
- @schema << Definitions::Extension.new(name.to_sym)
33
+ @schema.extensions << Definitions::Extension.new(name.to_sym)
34
34
  end
35
35
 
36
36
  class TableYielder
@@ -0,0 +1,89 @@
1
+ require 'digest/md5'
2
+
3
+ module DbSchema
4
+ class Normalizer
5
+ attr_reader :table
6
+
7
+ def initialize(table)
8
+ @table = table
9
+ end
10
+
11
+ def normalized_table
12
+ create_temporary_table!
13
+ read_temporary_table
14
+ ensure
15
+ cleanup!
16
+ end
17
+
18
+ private
19
+ def create_temporary_table!
20
+ operation = Changes::CreateTable.new(
21
+ temporary_table_name,
22
+ fields: table.fields,
23
+ indices: rename_indices(table.indices),
24
+ checks: table.checks
25
+ )
26
+
27
+ Runner.new([operation]).run!
28
+ end
29
+
30
+ def read_temporary_table
31
+ temporary_table = Reader.read_table(temporary_table_name)
32
+
33
+ Definitions::Table.new(
34
+ remove_hash(temporary_table.name),
35
+ fields: temporary_table.fields,
36
+ indices: rename_indices_back(temporary_table.indices),
37
+ checks: temporary_table.checks,
38
+ foreign_keys: table.foreign_keys
39
+ )
40
+ end
41
+
42
+ def cleanup!
43
+ DbSchema.connection.drop_table(temporary_table_name, if_exists: true)
44
+ end
45
+
46
+ def rename_indices(indices)
47
+ indices.map do |index|
48
+ Definitions::Index.new(
49
+ name: append_hash(index.name),
50
+ columns: index.columns,
51
+ unique: index.unique?,
52
+ type: index.type,
53
+ condition: index.condition
54
+ )
55
+ end
56
+ end
57
+
58
+ def rename_indices_back(indices)
59
+ indices.map do |index|
60
+ Definitions::Index.new(
61
+ name: remove_hash(index.name),
62
+ columns: index.columns,
63
+ unique: index.unique?,
64
+ type: index.type,
65
+ condition: index.condition
66
+ )
67
+ end
68
+ end
69
+
70
+ def temporary_table_name
71
+ append_hash(table.name)
72
+ end
73
+
74
+ def append_hash(name)
75
+ "#{name}_#{hash}"
76
+ end
77
+
78
+ def remove_hash(name)
79
+ name.to_s.sub(/_#{Regexp.escape(hash)}$/, '').to_sym
80
+ end
81
+
82
+ def hash
83
+ @hash ||= begin
84
+ names = [table.name] + table.fields.map(&:name) + table.indices.map(&:name) + table.checks.map(&:name)
85
+ Digest::MD5.hexdigest(names.join(','))[0..9]
86
+ end
87
+ end
88
+ end
89
+ end
@@ -5,6 +5,10 @@ module DbSchema
5
5
  adapter.read_schema
6
6
  end
7
7
 
8
+ def read_table(table_name)
9
+ adapter.read_table(table_name)
10
+ end
11
+
8
12
  def adapter
9
13
  adapter_name = DbSchema.configuration.adapter
10
14
  registry.fetch(adapter_name) do |adapter_name|
@@ -19,7 +23,19 @@ module DbSchema
19
23
  end
20
24
 
21
25
  module Postgres
22
- DEFAULT_VALUE = /\A(('(?<string>.*)')|(?<float>\d+\.\d+)|(?<integer>\d+)|(?<boolean>true|false)|((?<function>[A-Za-z_]+)\(\)))/
26
+ DEFAULT_VALUE = /\A(
27
+ ('(?<date>\d{4}-\d{2}-\d{2})'::date)
28
+ |
29
+ ('(?<time>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}([+-]\d{2})?)'::timestamp)
30
+ |
31
+ ('(?<string>.*)')
32
+ |
33
+ (?<float>\d+\.\d+)
34
+ |
35
+ (?<integer>\d+)
36
+ |
37
+ (?<boolean>true|false)
38
+ )/x
23
39
 
24
40
  COLUMN_NAMES_QUERY = <<-SQL.freeze
25
41
  SELECT c.column_name AS name,
@@ -104,6 +120,10 @@ SELECT extname
104
120
 
105
121
  class << self
106
122
  def read_schema
123
+ tables = DbSchema.connection.tables.map do |table_name|
124
+ read_table(table_name)
125
+ end
126
+
107
127
  enums = DbSchema.connection[ENUMS_QUERY].map do |enum_data|
108
128
  Definitions::Enum.new(enum_data[:name].to_sym, enum_data[:values].map(&:to_sym))
109
129
  end
@@ -112,38 +132,38 @@ SELECT extname
112
132
  Definitions::Extension.new(extension_data[:extname].to_sym)
113
133
  end
114
134
 
115
- tables = DbSchema.connection.tables.map do |table_name|
116
- primary_key_name = DbSchema.connection.primary_key(table_name)
135
+ Definitions::Schema.new(tables: tables, enums: enums, extensions: extensions)
136
+ end
117
137
 
118
- fields = DbSchema.connection[COLUMN_NAMES_QUERY, table_name.to_s].map do |column_data|
119
- build_field(column_data, primary_key: column_data[:name] == primary_key_name)
120
- end
138
+ def read_table(table_name)
139
+ primary_key_name = DbSchema.connection.primary_key(table_name)
121
140
 
122
- indices = indices_data_for(table_name).map do |index_data|
123
- Definitions::Index.new(index_data)
124
- end.sort_by(&:name)
141
+ fields = DbSchema.connection[COLUMN_NAMES_QUERY, table_name.to_s].map do |column_data|
142
+ build_field(column_data, primary_key: column_data[:name] == primary_key_name)
143
+ end
125
144
 
126
- foreign_keys = DbSchema.connection.foreign_key_list(table_name).map do |foreign_key_data|
127
- build_foreign_key(foreign_key_data)
128
- end
145
+ indices = indices_data_for(table_name).map do |index_data|
146
+ Definitions::Index.new(index_data)
147
+ end.sort_by(&:name)
129
148
 
130
- checks = DbSchema.connection[CONSTRAINTS_QUERY, table_name.to_s].map do |check_data|
131
- Definitions::CheckConstraint.new(
132
- name: check_data[:name].to_sym,
133
- condition: check_data[:condition]
134
- )
135
- end
149
+ foreign_keys = DbSchema.connection.foreign_key_list(table_name).map do |foreign_key_data|
150
+ build_foreign_key(foreign_key_data)
151
+ end
136
152
 
137
- Definitions::Table.new(
138
- table_name,
139
- fields: fields,
140
- indices: indices,
141
- checks: checks,
142
- foreign_keys: foreign_keys
153
+ checks = DbSchema.connection[CONSTRAINTS_QUERY, table_name.to_s].map do |check_data|
154
+ Definitions::CheckConstraint.new(
155
+ name: check_data[:name].to_sym,
156
+ condition: check_data[:condition]
143
157
  )
144
158
  end
145
159
 
146
- enums + extensions + tables
160
+ Definitions::Table.new(
161
+ table_name,
162
+ fields: fields,
163
+ indices: indices,
164
+ checks: checks,
165
+ foreign_keys: foreign_keys
166
+ )
147
167
  end
148
168
 
149
169
  def indices_data_for(table_name)
@@ -227,8 +247,12 @@ SELECT extname
227
247
  nullable = (data[:null] != 'NO')
228
248
 
229
249
  unless primary_key || data[:default].nil?
230
- if match = DEFAULT_VALUE.match(data[:default])
231
- default = if match[:string]
250
+ default = if match = DEFAULT_VALUE.match(data[:default])
251
+ if match[:date]
252
+ Date.parse(match[:date])
253
+ elsif match[:time]
254
+ Time.parse(match[:time])
255
+ elsif match[:string]
232
256
  match[:string]
233
257
  elsif match[:integer]
234
258
  match[:integer].to_i
@@ -236,9 +260,9 @@ SELECT extname
236
260
  match[:float].to_f
237
261
  elsif match[:boolean]
238
262
  match[:boolean] == 'true'
239
- elsif match[:function]
240
- match[:function].to_sym
241
263
  end
264
+ else
265
+ data[:default].to_sym
242
266
  end
243
267
  end
244
268
 
@@ -133,7 +133,7 @@ module DbSchema
133
133
  when Changes::DisallowNull
134
134
  set_column_not_null(element.name)
135
135
  when Changes::AlterColumnDefault
136
- set_column_default(element.name, element.new_default)
136
+ set_column_default(element.name, Runner.default_to_sequel(element.new_default))
137
137
  when Changes::CreateIndex
138
138
  add_index(
139
139
  element.columns_to_sequel,
@@ -216,6 +216,20 @@ module DbSchema
216
216
  else
217
217
  options
218
218
  end
219
+
220
+ if mapping.key?(:default)
221
+ mapping.merge(default: default_to_sequel(mapping[:default]))
222
+ else
223
+ mapping
224
+ end
225
+ end
226
+
227
+ def default_to_sequel(default)
228
+ if default.is_a?(Symbol)
229
+ Sequel.lit(default.to_s)
230
+ else
231
+ default
232
+ end
219
233
  end
220
234
  end
221
235
  end
@@ -2,10 +2,7 @@ module DbSchema
2
2
  module Validator
3
3
  class << self
4
4
  def validate(schema)
5
- tables = Utils.filter_by_class(schema, Definitions::Table)
6
- enums = Utils.filter_by_class(schema, Definitions::Enum)
7
-
8
- table_errors = tables.each_with_object([]) do |table, errors|
5
+ table_errors = schema.tables.each_with_object([]) do |table, errors|
9
6
  primary_keys_count = table.fields.select(&:primary_key?).count
10
7
  if primary_keys_count > 1
11
8
  error_message = %(Table "#{table.name}" has #{primary_keys_count} primary keys)
@@ -14,7 +11,7 @@ module DbSchema
14
11
 
15
12
  table.fields.each do |field|
16
13
  if field.is_a?(Definitions::Field::Custom)
17
- unless enums.map(&:name).include?(field.type_name)
14
+ unless schema.enums.map(&:name).include?(field.type_name)
18
15
  error_message = %(Field "#{table.name}.#{field.name}" has unknown type "#{field.type_name}")
19
16
  errors << error_message
20
17
  end
@@ -40,7 +37,7 @@ module DbSchema
40
37
  end
41
38
  end
42
39
 
43
- if referenced_table = schema.find { |table| table.name == fkey.table }
40
+ if referenced_table = schema.tables.find { |table| table.name == fkey.table }
44
41
  if fkey.references_primary_key?
45
42
  unless referenced_table.fields.any?(&:primary_key?)
46
43
  error_message = %(Foreign key "#{fkey.name}" refers to primary key of table "#{fkey.table}" which does not have a primary key)
@@ -63,7 +60,7 @@ module DbSchema
63
60
  end
64
61
  end
65
62
 
66
- enum_errors = enums.each_with_object([]) do |enum, errors|
63
+ enum_errors = schema.enums.each_with_object([]) do |enum, errors|
67
64
  if enum.values.empty?
68
65
  error_message = %(Enum "#{enum.name}" contains no values)
69
66
  errors << error_message
@@ -1,3 +1,3 @@
1
1
  module DbSchema
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2'
3
3
  end
data/lib/db_schema.rb CHANGED
@@ -7,6 +7,7 @@ require 'db_schema/definitions'
7
7
  require 'db_schema/awesome_print'
8
8
  require 'db_schema/dsl'
9
9
  require 'db_schema/validator'
10
+ require 'db_schema/normalizer'
10
11
  require 'db_schema/reader'
11
12
  require 'db_schema/changes'
12
13
  require 'db_schema/runner'
@@ -17,6 +18,7 @@ module DbSchema
17
18
  def describe(&block)
18
19
  desired_schema = DSL.new(block).schema
19
20
  validate(desired_schema)
21
+ normalize(desired_schema)
20
22
 
21
23
  actual_schema = Reader.read_schema
22
24
  changes = Changes.between(desired_schema, actual_schema)
@@ -88,6 +90,18 @@ module DbSchema
88
90
  end
89
91
  end
90
92
 
93
+ def normalize(schema)
94
+ normalized_tables = schema.tables.map do |table|
95
+ if table.has_expressions?
96
+ Normalizer.new(table).normalized_table
97
+ else
98
+ table
99
+ end
100
+ end
101
+
102
+ schema.tables = normalized_tables
103
+ end
104
+
91
105
  def log_changes(changes)
92
106
  return if changes.empty?
93
107
 
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.3
4
+ version: '0.2'
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-10-18 00:00:00.000000000 Z
11
+ date: 2016-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -214,6 +214,7 @@ files:
214
214
  - lib/db_schema/definitions/field/text_search.rb
215
215
  - lib/db_schema/definitions/field/uuid.rb
216
216
  - lib/db_schema/dsl.rb
217
+ - lib/db_schema/normalizer.rb
217
218
  - lib/db_schema/reader.rb
218
219
  - lib/db_schema/runner.rb
219
220
  - lib/db_schema/utils.rb