rom-sql 2.0.0.beta2 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/lib/rom/plugins/relation/sql/postgres/explain.rb +54 -0
  4. data/lib/rom/sql.rb +1 -1
  5. data/lib/rom/sql/attribute.rb +17 -18
  6. data/lib/rom/sql/errors.rb +3 -0
  7. data/lib/rom/sql/extensions/mysql.rb +1 -1
  8. data/lib/rom/sql/extensions/mysql/type_builder.rb +28 -0
  9. data/lib/rom/sql/extensions/postgres.rb +3 -1
  10. data/lib/rom/sql/extensions/postgres/commands.rb +30 -13
  11. data/lib/rom/sql/extensions/postgres/{attributes_inferrer.rb → type_builder.rb} +24 -28
  12. data/lib/rom/sql/extensions/postgres/type_serializer.rb +39 -0
  13. data/lib/rom/sql/extensions/postgres/types.rb +24 -477
  14. data/lib/rom/sql/extensions/postgres/types/array.rb +163 -0
  15. data/lib/rom/sql/extensions/postgres/types/geometric.rb +135 -0
  16. data/lib/rom/sql/extensions/postgres/types/json.rb +235 -0
  17. data/lib/rom/sql/extensions/postgres/types/network.rb +15 -0
  18. data/lib/rom/sql/extensions/sqlite.rb +1 -1
  19. data/lib/rom/sql/extensions/sqlite/{attributes_inferrer.rb → type_builder.rb} +5 -5
  20. data/lib/rom/sql/extensions/sqlite/types.rb +8 -3
  21. data/lib/rom/sql/foreign_key.rb +17 -0
  22. data/lib/rom/sql/function.rb +86 -8
  23. data/lib/rom/sql/gateway.rb +26 -26
  24. data/lib/rom/sql/index.rb +4 -0
  25. data/lib/rom/sql/migration.rb +3 -3
  26. data/lib/rom/sql/migration/inline_runner.rb +9 -83
  27. data/lib/rom/sql/migration/migrator.rb +35 -12
  28. data/lib/rom/sql/migration/recorder.rb +21 -0
  29. data/lib/rom/sql/migration/runner.rb +115 -0
  30. data/lib/rom/sql/migration/schema_diff.rb +108 -53
  31. data/lib/rom/sql/migration/writer.rb +61 -0
  32. data/lib/rom/sql/relation.rb +2 -1
  33. data/lib/rom/sql/relation/reading.rb +63 -3
  34. data/lib/rom/sql/relation/writing.rb +38 -0
  35. data/lib/rom/sql/schema.rb +9 -3
  36. data/lib/rom/sql/schema/attributes_inferrer.rb +3 -119
  37. data/lib/rom/sql/schema/inferrer.rb +99 -18
  38. data/lib/rom/sql/schema/type_builder.rb +94 -0
  39. data/lib/rom/sql/type_dsl.rb +30 -0
  40. data/lib/rom/sql/type_extensions.rb +11 -6
  41. data/lib/rom/sql/type_serializer.rb +46 -0
  42. data/lib/rom/sql/types.rb +12 -0
  43. data/lib/rom/sql/version.rb +1 -1
  44. metadata +26 -244
  45. data/.codeclimate.yml +0 -15
  46. data/.gitignore +0 -17
  47. data/.rspec +0 -3
  48. data/.travis.yml +0 -39
  49. data/.yardopts +0 -2
  50. data/Gemfile +0 -33
  51. data/Guardfile +0 -24
  52. data/LICENSE.txt +0 -22
  53. data/Rakefile +0 -19
  54. data/circle.yml +0 -10
  55. data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +0 -10
  56. data/lib/rom/sql/relation/sequel_api.rb +0 -133
  57. data/log/.gitkeep +0 -0
  58. data/rom-sql.gemspec +0 -29
  59. data/spec/extensions/postgres/attribute_spec.rb +0 -217
  60. data/spec/extensions/postgres/integration_spec.rb +0 -59
  61. data/spec/extensions/postgres/types_spec.rb +0 -252
  62. data/spec/extensions/sqlite/types_spec.rb +0 -11
  63. data/spec/fixtures/migrations/20150403090603_create_carrots.rb +0 -8
  64. data/spec/integration/associations/many_to_many/custom_fks_spec.rb +0 -76
  65. data/spec/integration/associations/many_to_many/from_view_spec.rb +0 -88
  66. data/spec/integration/associations/many_to_many_spec.rb +0 -162
  67. data/spec/integration/associations/many_to_one/custom_fks_spec.rb +0 -64
  68. data/spec/integration/associations/many_to_one/from_view_spec.rb +0 -84
  69. data/spec/integration/associations/many_to_one/self_ref_spec.rb +0 -53
  70. data/spec/integration/associations/many_to_one_spec.rb +0 -117
  71. data/spec/integration/associations/one_to_many/custom_fks_spec.rb +0 -54
  72. data/spec/integration/associations/one_to_many/from_view_spec.rb +0 -57
  73. data/spec/integration/associations/one_to_many/self_ref_spec.rb +0 -54
  74. data/spec/integration/associations/one_to_many_spec.rb +0 -86
  75. data/spec/integration/associations/one_to_one_spec.rb +0 -69
  76. data/spec/integration/associations/one_to_one_through_spec.rb +0 -92
  77. data/spec/integration/auto_migrations/errors_spec.rb +0 -31
  78. data/spec/integration/auto_migrations/indexes_spec.rb +0 -253
  79. data/spec/integration/auto_migrations/managing_columns_spec.rb +0 -156
  80. data/spec/integration/auto_migrations/postgres/column_types_spec.rb +0 -63
  81. data/spec/integration/combine_with_spec.rb +0 -43
  82. data/spec/integration/commands/create_spec.rb +0 -304
  83. data/spec/integration/commands/delete_spec.rb +0 -84
  84. data/spec/integration/commands/update_spec.rb +0 -90
  85. data/spec/integration/commands/upsert_spec.rb +0 -83
  86. data/spec/integration/gateway_spec.rb +0 -107
  87. data/spec/integration/migration_spec.rb +0 -55
  88. data/spec/integration/plugins/associates/many_to_many_spec.rb +0 -69
  89. data/spec/integration/plugins/associates_spec.rb +0 -250
  90. data/spec/integration/plugins/auto_restrictions_spec.rb +0 -74
  91. data/spec/integration/relation_schema_spec.rb +0 -271
  92. data/spec/integration/schema/call_spec.rb +0 -24
  93. data/spec/integration/schema/inferrer/mysql_spec.rb +0 -45
  94. data/spec/integration/schema/inferrer/postgres_spec.rb +0 -203
  95. data/spec/integration/schema/inferrer/sqlite_spec.rb +0 -37
  96. data/spec/integration/schema/inferrer_spec.rb +0 -390
  97. data/spec/integration/schema/prefix_spec.rb +0 -16
  98. data/spec/integration/schema/qualified_spec.rb +0 -16
  99. data/spec/integration/schema/rename_spec.rb +0 -21
  100. data/spec/integration/schema/view_spec.rb +0 -29
  101. data/spec/integration/sequel_api_spec.rb +0 -36
  102. data/spec/integration/setup_spec.rb +0 -26
  103. data/spec/integration/support/active_support_notifications_spec.rb +0 -24
  104. data/spec/integration/support/rails_log_subscriber_spec.rb +0 -30
  105. data/spec/integration/wrap_spec.rb +0 -91
  106. data/spec/shared/accounts.rb +0 -48
  107. data/spec/shared/database_setup.rb +0 -70
  108. data/spec/shared/notes.rb +0 -23
  109. data/spec/shared/posts.rb +0 -34
  110. data/spec/shared/puppies.rb +0 -15
  111. data/spec/shared/relations.rb +0 -8
  112. data/spec/shared/users.rb +0 -32
  113. data/spec/shared/users_and_tasks.rb +0 -50
  114. data/spec/spec_helper.rb +0 -122
  115. data/spec/support/env_helper.rb +0 -25
  116. data/spec/support/helpers.rb +0 -24
  117. data/spec/support/oracle/create_users.sql +0 -7
  118. data/spec/support/oracle/set_sys_passwords.sql +0 -2
  119. data/spec/support/test_configuration.rb +0 -16
  120. data/spec/unit/attribute_spec.rb +0 -104
  121. data/spec/unit/function_spec.rb +0 -48
  122. data/spec/unit/gateway_spec.rb +0 -70
  123. data/spec/unit/logger_spec.rb +0 -14
  124. data/spec/unit/migration_tasks_spec.rb +0 -111
  125. data/spec/unit/migrator_spec.rb +0 -25
  126. data/spec/unit/order_dsl_spec.rb +0 -43
  127. data/spec/unit/plugin/associates_spec.rb +0 -94
  128. data/spec/unit/plugin/pagination_spec.rb +0 -91
  129. data/spec/unit/plugin/timestamp_spec.rb +0 -117
  130. data/spec/unit/projection_dsl_spec.rb +0 -110
  131. data/spec/unit/relation/assoc_spec.rb +0 -87
  132. data/spec/unit/relation/associations_spec.rb +0 -27
  133. data/spec/unit/relation/avg_spec.rb +0 -11
  134. data/spec/unit/relation/by_pk_spec.rb +0 -62
  135. data/spec/unit/relation/dataset_spec.rb +0 -50
  136. data/spec/unit/relation/distinct_spec.rb +0 -15
  137. data/spec/unit/relation/exclude_spec.rb +0 -11
  138. data/spec/unit/relation/exist_predicate_spec.rb +0 -25
  139. data/spec/unit/relation/exists_spec.rb +0 -18
  140. data/spec/unit/relation/fetch_spec.rb +0 -21
  141. data/spec/unit/relation/group_spec.rb +0 -61
  142. data/spec/unit/relation/having_spec.rb +0 -22
  143. data/spec/unit/relation/inner_join_spec.rb +0 -158
  144. data/spec/unit/relation/inspect_spec.rb +0 -11
  145. data/spec/unit/relation/instrument_spec.rb +0 -45
  146. data/spec/unit/relation/invert_spec.rb +0 -11
  147. data/spec/unit/relation/left_join_spec.rb +0 -55
  148. data/spec/unit/relation/lock_spec.rb +0 -93
  149. data/spec/unit/relation/map_spec.rb +0 -16
  150. data/spec/unit/relation/max_spec.rb +0 -11
  151. data/spec/unit/relation/min_spec.rb +0 -11
  152. data/spec/unit/relation/order_spec.rb +0 -51
  153. data/spec/unit/relation/pluck_spec.rb +0 -11
  154. data/spec/unit/relation/prefix_spec.rb +0 -29
  155. data/spec/unit/relation/primary_key_spec.rb +0 -27
  156. data/spec/unit/relation/project_spec.rb +0 -24
  157. data/spec/unit/relation/qualified_columns_spec.rb +0 -30
  158. data/spec/unit/relation/qualified_spec.rb +0 -25
  159. data/spec/unit/relation/read_spec.rb +0 -25
  160. data/spec/unit/relation/rename_spec.rb +0 -23
  161. data/spec/unit/relation/right_join_spec.rb +0 -57
  162. data/spec/unit/relation/select_append_spec.rb +0 -21
  163. data/spec/unit/relation/select_spec.rb +0 -40
  164. data/spec/unit/relation/sum_spec.rb +0 -11
  165. data/spec/unit/relation/union_spec.rb +0 -19
  166. data/spec/unit/relation/unique_predicate_spec.rb +0 -18
  167. data/spec/unit/relation/where_spec.rb +0 -133
  168. data/spec/unit/restriction_dsl_spec.rb +0 -34
  169. data/spec/unit/schema_spec.rb +0 -25
  170. data/spec/unit/types_spec.rb +0 -65
@@ -0,0 +1,21 @@
1
+ module ROM
2
+ module SQL
3
+ module Migration
4
+ # @api private
5
+ class Recorder
6
+ attr_reader :operations
7
+
8
+ def initialize(&block)
9
+ @operations = []
10
+
11
+ instance_exec(&block) if block
12
+ end
13
+
14
+ def method_missing(m, *args, &block)
15
+ nested = block ? Recorder.new(&block).operations : EMPTY_ARRAY
16
+ @operations << [m, args, nested]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,115 @@
1
+ module ROM
2
+ module SQL
3
+ module Migration
4
+ # @api private
5
+ class Runner
6
+ attr_reader :writer
7
+
8
+ def initialize(writer)
9
+ @writer = writer
10
+ end
11
+
12
+ def call(changes)
13
+ changes.each { |diff| apply_schema(diff) }
14
+ changes.each { |diff| apply_constraints(diff) }
15
+
16
+ self
17
+ end
18
+
19
+ def apply_schema(diff)
20
+ case diff
21
+ when SchemaDiff::TableCreated
22
+ create_table(diff)
23
+ when SchemaDiff::TableAltered
24
+ alter_table(diff)
25
+ end
26
+ end
27
+
28
+ def apply_constraints(diff)
29
+ case diff
30
+ when SchemaDiff::TableCreated
31
+ alter_foreign_keys(diff, diff.foreign_keys)
32
+ when SchemaDiff::TableAltered
33
+ alter_foreign_keys(diff, diff.foreign_key_changes)
34
+ end
35
+ end
36
+
37
+ def create_table(diff)
38
+ writer.migration do |connection|
39
+ connection.create_table(diff.table_name) do
40
+ diff.attributes.each do |attribute|
41
+ if attribute.primary_key?
42
+ primary_key attribute.name
43
+ else
44
+ column attribute.name, attribute.type, null: attribute.null?
45
+ end
46
+ end
47
+
48
+ diff.indexes.each do |index|
49
+ index index.attributes, index.options
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def alter_table(diff)
56
+ return if diff.meta?
57
+
58
+ writer.migration do |connection|
59
+ connection.alter_table(diff.table_name) do
60
+ diff.attribute_changes.each do |attribute|
61
+ case attribute
62
+ when SchemaDiff::AttributeAdded
63
+ add_column attribute.name, attribute.type, null: attribute.null?
64
+ when SchemaDiff::AttributeRemoved
65
+ drop_column attribute.name
66
+ when SchemaDiff::AttributeChanged
67
+ if attribute.type_changed?
68
+ from, to = attribute.current.unwrap, attribute.target.unwrap
69
+ raise UnsupportedConversion.new(
70
+ "Don't know how to convert #{ from.inspect } to #{ to.inspect }"
71
+ )
72
+ end
73
+
74
+ if attribute.nullability_changed?
75
+ if attribute.null?
76
+ set_column_allow_null attribute.name
77
+ else
78
+ set_column_not_null attribute.name
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ diff.index_changes.each do |index|
85
+ case index
86
+ when SchemaDiff::IndexAdded
87
+ add_index index.attributes, index.options
88
+ when SchemaDiff::IndexRemoved
89
+ drop_index index.attributes, index.options
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def alter_foreign_keys(diff, foreign_key_changes)
97
+ return if foreign_key_changes.empty?
98
+
99
+ writer.migration do |connection|
100
+ connection.alter_table(diff.table_name) do
101
+ foreign_key_changes.map do |fk|
102
+ case fk
103
+ when SchemaDiff::ForeignKeyAdded
104
+ add_foreign_key fk.child_keys, fk.parent
105
+ when SchemaDiff::ForeignKeyRemoved
106
+ drop_foreign_key fk.child_keys
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,14 +1,22 @@
1
+ require 'rom/sql/type_serializer'
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  module Migration
6
+ # @api private
4
7
  class SchemaDiff
8
+ extend Initializer
9
+
10
+ param :database_type
11
+
12
+ option :type_serializer, default: -> { ROM::SQL::TypeSerializer[database_type] }
13
+
5
14
  class TableDiff
6
- attr_reader :current_schema, :target_schema
15
+ extend Initializer
7
16
 
8
- def initialize(current_schema: nil, target_schema: nil)
9
- @current_schema = current_schema
10
- @target_schema = target_schema
11
- end
17
+ option :current_schema, optional: true
18
+
19
+ option :target_schema, optional: true
12
20
 
13
21
  def empty?
14
22
  false
@@ -27,33 +35,33 @@ module ROM
27
35
 
28
36
  class TableCreated < TableDiff
29
37
  alias_method :schema, :target_schema
30
- attr_reader :attributes, :indexes
31
38
 
32
- def initialize(attributes:, indexes: EMPTY_ARRAY, **rest)
33
- super(rest)
39
+ option :attributes
34
40
 
35
- @attributes = attributes
36
- @indexes = indexes
37
- end
41
+ option :indexes, default: -> { EMPTY_ARRAY }
42
+
43
+ option :foreign_keys, default: -> { EMPTY_ARRAY }
38
44
  end
39
45
 
40
46
  class TableAltered < TableDiff
41
- attr_reader :attribute_changes, :index_changes
42
47
 
43
- def initialize(attribute_changes: EMPTY_ARRAY, index_changes: EMPTY_ARRAY, **rest)
44
- super(rest)
48
+ option :attribute_changes, default: -> { EMPTY_ARRAY }
49
+
50
+ option :index_changes, default: -> { EMPTY_ARRAY }
45
51
 
46
- @attribute_changes = attribute_changes
47
- @index_changes = index_changes
52
+ option :foreign_key_changes, default: -> { EMPTY_ARRAY }
53
+
54
+ def meta?
55
+ attribute_changes.empty? && index_changes.empty?
48
56
  end
49
57
  end
50
58
 
51
59
  class AttributeDiff
52
- attr_reader :attr
60
+ extend Initializer
53
61
 
54
- def initialize(attr)
55
- @attr = attr
56
- end
62
+ param :attr
63
+
64
+ option :type_serializer
57
65
 
58
66
  def name
59
67
  attr.name
@@ -74,7 +82,7 @@ module ROM
74
82
 
75
83
  class AttributeAdded < AttributeDiff
76
84
  def type
77
- unwrap(attr).primitive
85
+ type_serializer.(unwrap(attr).type)
78
86
  end
79
87
  end
80
88
 
@@ -82,25 +90,21 @@ module ROM
82
90
  end
83
91
 
84
92
  class AttributeChanged < AttributeDiff
85
- attr_reader :current
93
+ param :current
86
94
  alias_method :target, :attr
87
95
 
88
- def initialize(current, target)
89
- super(target)
90
-
91
- @current = current
92
- end
93
-
94
- def to_a
95
- [current, target]
96
- end
97
-
98
96
  def nullability_changed?
99
97
  current.optional? ^ target.optional?
100
98
  end
101
99
 
102
100
  def type_changed?
103
- unwrap(current).meta(index: Set.new) != unwrap(target).meta(index: Set.new)
101
+ clean(current.qualified) != clean(target.qualified)
102
+ end
103
+
104
+ private
105
+
106
+ def clean(type)
107
+ unwrap(type).meta(index: nil, foreign_key: nil, target: nil)
104
108
  end
105
109
  end
106
110
 
@@ -112,55 +116,87 @@ module ROM
112
116
  end
113
117
 
114
118
  def attributes
115
- index.attributes.map(&:name)
119
+ list = index.attributes.map(&:name)
120
+
121
+ if list.size == 1
122
+ list[0]
123
+ else
124
+ list
125
+ end
116
126
  end
117
127
 
118
128
  def name
119
129
  index.name
120
130
  end
131
+ end
121
132
 
122
- def unique?
123
- index.unique?
133
+ class IndexAdded < IndexDiff
134
+ def options
135
+ options = {}
136
+ options[:name] = index.name if !index.name.nil?
137
+ options[:unique] = true if index.unique?
138
+ options[:type] = index.type if !index.type.nil?
139
+ options[:where] = index.predicate if !index.predicate.nil?
140
+ options
124
141
  end
142
+ end
125
143
 
126
- def type
127
- index.type
144
+ class IndexRemoved < IndexDiff
145
+ def options
146
+ options = {}
147
+ options[:name] = index.name if !index.name.nil?
148
+ options
149
+ end
150
+ end
151
+
152
+ class ForeignKeyDiff
153
+ attr_reader :foreign_key
154
+
155
+ def initialize(foreign_key)
156
+ @foreign_key = foreign_key
157
+ end
158
+
159
+ def parent
160
+ foreign_key.parent_table
128
161
  end
129
162
 
130
- def predicate
131
- index.predicate
163
+ def parent_keys
164
+ foreign_key.parent_keys
132
165
  end
133
166
 
134
- def partial?
135
- !predicate.nil?
167
+ def child_keys
168
+ foreign_key.attributes.map(&:name)
136
169
  end
137
170
  end
138
171
 
139
- class IndexAdded < IndexDiff
172
+ class ForeignKeyAdded < ForeignKeyDiff
140
173
  end
141
174
 
142
- class IndexRemoved < IndexDiff
175
+ class ForeignKeyRemoved < ForeignKeyDiff
143
176
  end
144
177
 
145
178
  def call(current, target)
146
179
  if current.empty?
147
180
  TableCreated.new(
148
181
  target_schema: target,
149
- attributes: target.map { |attr| AttributeAdded.new(attr) },
150
- indexes: target.indexes.map { |idx| IndexAdded.new(idx) }
182
+ attributes: map_attributes(target.to_h, AttributeAdded),
183
+ indexes: target.indexes.map { |idx| IndexAdded.new(idx) },
184
+ foreign_keys: target.foreign_keys.map { |fk| ForeignKeyAdded.new(fk) }
151
185
  )
152
186
  else
153
187
  attribute_changes = compare_attributes(current.to_h, target.to_h)
154
188
  index_changes = compare_indexes(current, target)
189
+ fk_changes = compare_foreign_key_constraints(current, target)
155
190
 
156
- if attribute_changes.empty? && index_changes.empty?
191
+ if attribute_changes.empty? && index_changes.empty? && fk_changes.empty?
157
192
  Empty.new(current_schema: current, target_schema: target)
158
193
  else
159
194
  TableAltered.new(
160
195
  current_schema: current,
161
196
  target_schema: target,
162
197
  attribute_changes: attribute_changes,
163
- index_changes: index_changes
198
+ index_changes: index_changes,
199
+ foreign_key_changes: fk_changes
164
200
  )
165
201
  end
166
202
  end
@@ -168,16 +204,16 @@ module ROM
168
204
 
169
205
  def compare_attributes(current, target)
170
206
  changed_attributes = target.select { |name, attr|
171
- current.key?(name) && current[name] != attr
207
+ current.key?(name) && !attributes_equal?(current[name], attr)
172
208
  }.map { |name, target_attr|
173
- [name, [current[name], target_attr]]
209
+ [name, [target_attr, current[name]]]
174
210
  }.to_h
175
211
  added_attributes = target.select { |name, _| !current.key?(name) }
176
212
  removed_attributes = current.select { |name, _| !target.key?(name) }
177
213
 
178
- removed_attributes.values.map { |attr| AttributeRemoved.new(attr) } +
179
- added_attributes.values.map { |attr| AttributeAdded.new(attr) } +
180
- changed_attributes.values.map { |attrs| AttributeChanged.new(*attrs) }
214
+ map_attributes(removed_attributes, AttributeRemoved) +
215
+ map_attributes(added_attributes, AttributeAdded) +
216
+ map_attributes(changed_attributes, AttributeChanged)
181
217
  end
182
218
 
183
219
  def compare_indexes(current, target)
@@ -191,6 +227,25 @@ module ROM
191
227
  removed_indexes.map { |idx| IndexRemoved.new(idx) } +
192
228
  added_indexes.map { |idx| IndexAdded.new(idx) }
193
229
  end
230
+
231
+ def compare_foreign_key_constraints(current, target)
232
+ target_fks = target.foreign_keys
233
+ current_fks = current.foreign_keys
234
+
235
+ added_fks = target_fks - current_fks
236
+ removed_fks = current_fks - target_fks
237
+
238
+ removed_fks.map { |fk| ForeignKeyRemoved.new(fk) } +
239
+ added_fks.map { |fk| ForeignKeyAdded.new(fk) }
240
+ end
241
+
242
+ def attributes_equal?(a, b)
243
+ a.qualified == b.qualified
244
+ end
245
+
246
+ def map_attributes(attributes, change_type)
247
+ attributes.values.map { |args| change_type.new(*args, type_serializer: type_serializer) }
248
+ end
194
249
  end
195
250
  end
196
251
  end
@@ -0,0 +1,61 @@
1
+ require 'rom/sql/migration/recorder'
2
+
3
+ module ROM
4
+ module SQL
5
+ module Migration
6
+ # @api private
7
+ class Writer
8
+ MIGRATION_BEGIN = "ROM::SQL.migration do\n change do".freeze
9
+ MIGRATION_END = "\n end\nend\n".freeze
10
+
11
+ attr_reader :yield_migration
12
+
13
+ def initialize(&block)
14
+ @yield_migration = block
15
+ end
16
+
17
+ def migration
18
+ recorder = Recorder.new
19
+ yield(recorder)
20
+ yield_migration.(create_migration(recorder.operations))
21
+ end
22
+
23
+ def create_migration(ops)
24
+ out = MIGRATION_BEGIN.dup
25
+ write(ops, out, "\n ")
26
+ out << MIGRATION_END
27
+
28
+ [migration_name(ops[0]), out]
29
+ end
30
+
31
+ def write(operations, buffer, indent)
32
+ operations.each do |operation|
33
+ op, args, nested = operation
34
+ buffer << indent << op.to_s << ' '
35
+ write_arguments(buffer, *args)
36
+
37
+ if !nested.empty?
38
+ buffer << ' do'
39
+ write(nested, buffer, indent + ' ')
40
+ buffer << indent << 'end'
41
+ end
42
+ end
43
+ end
44
+
45
+ def write_arguments(buffer, *args, **kwargs)
46
+ buffer << args.map(&:inspect).join(', ')
47
+ kwargs.each do |key, value|
48
+ buffer << ', ' << key.to_s << ': ' << value.inspect
49
+ end
50
+ end
51
+
52
+ def migration_name(op)
53
+ create_or_alter, args = op
54
+ table_name = args[0]
55
+
56
+ "#{ create_or_alter.to_s.sub('_table', '') }_#{ table_name }"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end