db_schema 0.2.5 → 0.3.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ module DbSchema
2
+ class Migration
3
+ include Dry::Equalizer(:name, :conditions, :body)
4
+ attr_reader :name, :conditions
5
+ attr_accessor :body
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ @conditions = { apply: [], skip: [] }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,177 @@
1
+ module DbSchema
2
+ class Migrator
3
+ attr_reader :migration
4
+
5
+ def initialize(migration)
6
+ @migration = migration
7
+ end
8
+
9
+ def applicable?(schema)
10
+ migration.conditions[:apply].all? do |condition|
11
+ condition.call(schema)
12
+ end && migration.conditions[:skip].none? do |condition|
13
+ condition.call(schema)
14
+ end
15
+ end
16
+
17
+ def run!(connection)
18
+ migration.body.call(BodyYielder.new(connection)) unless migration.body.nil?
19
+ end
20
+
21
+ class BodyYielder
22
+ attr_reader :connection
23
+
24
+ def initialize(connection)
25
+ @connection = connection
26
+ end
27
+
28
+ def create_table(name, &block)
29
+ table_yielder = DSL::TableYielder.new(name, block)
30
+
31
+ table = Definitions::Table.new(
32
+ name,
33
+ fields: table_yielder.fields,
34
+ indices: table_yielder.indices,
35
+ checks: table_yielder.checks,
36
+ foreign_keys: table_yielder.foreign_keys
37
+ )
38
+
39
+ run Operations::CreateTable.new(table)
40
+
41
+ table.foreign_keys.each do |fkey|
42
+ run Operations::CreateForeignKey.new(table.name, fkey)
43
+ end
44
+ end
45
+
46
+ def drop_table(name)
47
+ run Operations::DropTable.new(name)
48
+ end
49
+
50
+ def rename_table(from, to:)
51
+ run Operations::RenameTable.new(old_name: from, new_name: to)
52
+ end
53
+
54
+ def alter_table(name, &block)
55
+ run AlterTableYielder.new(name).run(block)
56
+ end
57
+
58
+ class AlterTableYielder
59
+ attr_reader :alter_table, :fkey_operations
60
+
61
+ def initialize(table_name)
62
+ @alter_table = Operations::AlterTable.new(table_name)
63
+ @fkey_operations = []
64
+ end
65
+
66
+ def run(block)
67
+ block.call(self)
68
+
69
+ [alter_table, *fkey_operations]
70
+ end
71
+
72
+ def add_column(name, type, **options)
73
+ alter_table.changes << Operations::CreateColumn.new(
74
+ Definitions::Field.build(name, type, options)
75
+ )
76
+ end
77
+
78
+ def drop_column(name)
79
+ alter_table.changes << Operations::DropColumn.new(name)
80
+ end
81
+
82
+ def rename_column(from, to:)
83
+ alter_table.changes << Operations::RenameColumn.new(old_name: from, new_name: to)
84
+ end
85
+
86
+ def alter_column_type(name, new_type, using: nil, **new_attributes)
87
+ alter_table.changes << Operations::AlterColumnType.new(
88
+ name,
89
+ new_type: new_type,
90
+ using: using,
91
+ **new_attributes
92
+ )
93
+ end
94
+
95
+ def allow_null(name)
96
+ alter_table.changes << Operations::AllowNull.new(name)
97
+ end
98
+
99
+ def disallow_null(name)
100
+ alter_table.changes << Operations::DisallowNull.new(name)
101
+ end
102
+
103
+ def alter_column_default(name, new_default)
104
+ alter_table.changes << Operations::AlterColumnDefault.new(name, new_default: new_default)
105
+ end
106
+
107
+ def add_index(*columns, **index_options)
108
+ alter_table.changes << Operations::CreateIndex.new(
109
+ DSL::TableYielder.build_index(
110
+ columns,
111
+ table_name: alter_table.table_name,
112
+ **index_options
113
+ )
114
+ )
115
+ end
116
+
117
+ def drop_index(name)
118
+ alter_table.changes << Operations::DropIndex.new(name)
119
+ end
120
+
121
+ def add_check(name, condition)
122
+ alter_table.changes << Operations::CreateCheckConstraint.new(
123
+ Definitions::CheckConstraint.new(name: name, condition: condition)
124
+ )
125
+ end
126
+
127
+ def drop_check(name)
128
+ alter_table.changes << Operations::DropCheckConstraint.new(name)
129
+ end
130
+
131
+ def add_foreign_key(*fkey_fields, **fkey_options)
132
+ fkey_operations << Operations::CreateForeignKey.new(
133
+ alter_table.table_name,
134
+ DSL::TableYielder.build_foreign_key(
135
+ fkey_fields,
136
+ table_name: alter_table.table_name,
137
+ **fkey_options
138
+ )
139
+ )
140
+ end
141
+
142
+ def drop_foreign_key(fkey_name)
143
+ fkey_operations << Operations::DropForeignKey.new(
144
+ alter_table.table_name,
145
+ fkey_name
146
+ )
147
+ end
148
+ end
149
+
150
+ def create_enum(name, values)
151
+ run Operations::CreateEnum.new(Definitions::Enum.new(name, values))
152
+ end
153
+
154
+ def drop_enum(name)
155
+ run Operations::DropEnum.new(name)
156
+ end
157
+
158
+ def create_extension(name)
159
+ run Operations::CreateExtension.new(Definitions::Extension.new(name))
160
+ end
161
+
162
+ def drop_extension(name)
163
+ run Operations::DropExtension.new(name)
164
+ end
165
+
166
+ def execute(query)
167
+ run Operations::ExecuteQuery.new(query)
168
+ end
169
+
170
+ private
171
+
172
+ def run(operation)
173
+ Runner.new(Array(operation), connection).run!
174
+ end
175
+ end
176
+ end
177
+ end
@@ -2,20 +2,21 @@ require 'digest/md5'
2
2
 
3
3
  module DbSchema
4
4
  class Normalizer
5
- attr_reader :schema
5
+ attr_reader :schema, :connection
6
6
 
7
- def initialize(schema)
8
- @schema = schema
7
+ def initialize(schema, connection)
8
+ @schema = schema
9
+ @connection = connection
9
10
  end
10
11
 
11
12
  def normalize_tables
12
- DbSchema.connection.transaction do
13
+ connection.transaction do
13
14
  create_extensions!
14
15
  create_enums!
15
16
 
16
17
  schema.tables = schema.tables.map do |table|
17
18
  if table.has_expressions?
18
- Table.new(table, hash).normalized_table
19
+ Table.new(table, hash, connection).normalized_table
19
20
  else
20
21
  table
21
22
  end
@@ -27,19 +28,19 @@ module DbSchema
27
28
 
28
29
  private
29
30
  def create_extensions!
30
- operations = (schema.extensions - Reader.read_extensions).map do |extension|
31
- Changes::CreateExtension.new(extension)
31
+ operations = (schema.extensions - Reader.read_extensions(connection)).map do |extension|
32
+ Operations::CreateExtension.new(extension)
32
33
  end
33
34
 
34
- Runner.new(operations).run!
35
+ Runner.new(operations, connection).run!
35
36
  end
36
37
 
37
38
  def create_enums!
38
39
  operations = schema.enums.map do |enum|
39
- Changes::CreateEnum.new(enum.with_name(append_hash(enum.name)))
40
+ Operations::CreateEnum.new(enum.with_name(append_hash(enum.name)))
40
41
  end
41
42
 
42
- Runner.new(operations).run!
43
+ Runner.new(operations, connection).run!
43
44
  end
44
45
 
45
46
  def append_hash(name)
@@ -57,11 +58,12 @@ module DbSchema
57
58
  end
58
59
 
59
60
  class Table
60
- attr_reader :table, :hash
61
+ attr_reader :table, :hash, :connection
61
62
 
62
- def initialize(table, hash)
63
- @table = table
64
- @hash = hash
63
+ def initialize(table, hash, connection)
64
+ @table = table
65
+ @hash = hash
66
+ @connection = connection
65
67
  end
66
68
 
67
69
  def normalized_table
@@ -71,17 +73,17 @@ module DbSchema
71
73
 
72
74
  private
73
75
  def create_temporary_table!
74
- operation = Changes::CreateTable.new(
76
+ operation = Operations::CreateTable.new(
75
77
  table.with_name(temporary_table_name)
76
78
  .with_fields(rename_types(table.fields))
77
79
  .with_indices(rename_indices(table.indices))
78
80
  )
79
81
 
80
- Runner.new([operation]).run!
82
+ Runner.new([operation], connection).run!
81
83
  end
82
84
 
83
85
  def read_temporary_table
84
- temporary_table = Reader.read_table(temporary_table_name)
86
+ temporary_table = Reader.read_table(temporary_table_name, connection)
85
87
 
86
88
  temporary_table.with_name(table.name)
87
89
  .with_fields(rename_types_back(temporary_table.fields))
@@ -0,0 +1,211 @@
1
+ module DbSchema
2
+ module Operations
3
+ class CreateTable
4
+ include Dry::Equalizer(:table)
5
+ attr_reader :table
6
+
7
+ def initialize(table)
8
+ @table = table
9
+ end
10
+ end
11
+
12
+ class DropTable
13
+ include Dry::Equalizer(:name)
14
+ attr_reader :name
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ end
19
+ end
20
+
21
+ class RenameTable
22
+ include Dry::Equalizer(:old_name, :new_name)
23
+ attr_reader :old_name, :new_name
24
+
25
+ def initialize(old_name:, new_name:)
26
+ @old_name = old_name
27
+ @new_name = new_name
28
+ end
29
+ end
30
+
31
+ class AlterTable
32
+ include Dry::Equalizer(:table_name, :changes)
33
+ attr_reader :table_name, :changes
34
+
35
+ def initialize(table_name, changes = [])
36
+ @table_name = table_name
37
+ @changes = changes
38
+ end
39
+ end
40
+
41
+ # Abstract base class for single-column toggle operations.
42
+ class ColumnOperation
43
+ include Dry::Equalizer(:name)
44
+ attr_reader :name
45
+
46
+ def initialize(name)
47
+ @name = name
48
+ end
49
+ end
50
+
51
+ class CreateColumn
52
+ include Dry::Equalizer(:field)
53
+ attr_reader :field
54
+
55
+ def initialize(field)
56
+ @field = field
57
+ end
58
+
59
+ def name
60
+ field.name
61
+ end
62
+
63
+ def type
64
+ field.type
65
+ end
66
+
67
+ def primary_key?
68
+ field.primary_key?
69
+ end
70
+
71
+ def options
72
+ field.options
73
+ end
74
+ end
75
+
76
+ class DropColumn < ColumnOperation
77
+ end
78
+
79
+ class RenameColumn
80
+ include Dry::Equalizer(:old_name, :new_name)
81
+ attr_reader :old_name, :new_name
82
+
83
+ def initialize(old_name:, new_name:)
84
+ @old_name = old_name
85
+ @new_name = new_name
86
+ end
87
+ end
88
+
89
+ class AlterColumnType
90
+ include Dry::Equalizer(:name, :new_type, :using, :new_attributes)
91
+ attr_reader :name, :new_type, :using, :new_attributes
92
+
93
+ def initialize(name, new_type:, using: nil, **new_attributes)
94
+ @name = name
95
+ @new_type = new_type
96
+ @using = using
97
+ @new_attributes = new_attributes
98
+ end
99
+ end
100
+
101
+ class CreatePrimaryKey < ColumnOperation
102
+ end
103
+
104
+ class DropPrimaryKey < ColumnOperation
105
+ end
106
+
107
+ class AllowNull < ColumnOperation
108
+ end
109
+
110
+ class DisallowNull < ColumnOperation
111
+ end
112
+
113
+ class AlterColumnDefault
114
+ include Dry::Equalizer(:name, :new_default)
115
+ attr_reader :name, :new_default
116
+
117
+ def initialize(name, new_default:)
118
+ @name = name
119
+ @new_default = new_default
120
+ end
121
+ end
122
+
123
+ class CreateIndex
124
+ include Dry::Equalizer(:index)
125
+ attr_reader :index
126
+
127
+ def initialize(index)
128
+ @index = index
129
+ end
130
+ end
131
+
132
+ class DropIndex < ColumnOperation
133
+ end
134
+
135
+ class CreateCheckConstraint
136
+ include Dry::Equalizer(:check)
137
+ attr_reader :check
138
+
139
+ def initialize(check)
140
+ @check = check
141
+ end
142
+ end
143
+
144
+ class DropCheckConstraint < ColumnOperation
145
+ end
146
+
147
+ class CreateForeignKey
148
+ include Dry::Equalizer(:table_name, :foreign_key)
149
+ attr_reader :table_name, :foreign_key
150
+
151
+ def initialize(table_name, foreign_key)
152
+ @table_name = table_name
153
+ @foreign_key = foreign_key
154
+ end
155
+ end
156
+
157
+ class DropForeignKey
158
+ include Dry::Equalizer(:table_name, :fkey_name)
159
+ attr_reader :table_name, :fkey_name
160
+
161
+ def initialize(table_name, fkey_name)
162
+ @table_name = table_name
163
+ @fkey_name = fkey_name
164
+ end
165
+ end
166
+
167
+ class CreateEnum
168
+ include Dry::Equalizer(:enum)
169
+ attr_reader :enum
170
+
171
+ def initialize(enum)
172
+ @enum = enum
173
+ end
174
+ end
175
+
176
+ class DropEnum < ColumnOperation
177
+ end
178
+
179
+ class AlterEnumValues
180
+ include Dry::Equalizer(:enum_name, :new_values, :enum_fields)
181
+ attr_reader :enum_name, :new_values, :enum_fields
182
+
183
+ def initialize(enum_name, new_values, enum_fields)
184
+ @enum_name = enum_name
185
+ @new_values = new_values
186
+ @enum_fields = enum_fields
187
+ end
188
+ end
189
+
190
+ class CreateExtension
191
+ include Dry::Equalizer(:extension)
192
+ attr_reader :extension
193
+
194
+ def initialize(extension)
195
+ @extension = extension
196
+ end
197
+ end
198
+
199
+ class DropExtension < ColumnOperation
200
+ end
201
+
202
+ class ExecuteQuery
203
+ include Dry::Equalizer(:query)
204
+ attr_reader :query
205
+
206
+ def initialize(query)
207
+ @query = query
208
+ end
209
+ end
210
+ end
211
+ end