arql 0.2.10 → 0.3.0

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.
@@ -4,6 +4,9 @@ module Arql
4
4
  def open(options)
5
5
  ActiveRecord::Base.establish_connection(options)
6
6
  $C = ActiveRecord::Base.connection
7
+ $C.define_singleton_method(:dump) do |filename, no_create_db=false|
8
+ Arql::Mysqldump.new.dump_database(filename, no_create_db)
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -1,3 +1,4 @@
1
+ require 'arql/concerns'
1
2
  module Arql
2
3
  module Extension
3
4
  extend ActiveSupport::Concern
@@ -34,6 +35,10 @@ module Arql
34
35
  [self].write_excel(filename, *fields, **options)
35
36
  end
36
37
 
38
+ def dump(filename, batch_size=500)
39
+ [self].dump(filename, batch_size)
40
+ end
41
+
37
42
  included do
38
43
  end
39
44
 
@@ -65,6 +70,10 @@ module Arql
65
70
  def to_create_sql
66
71
  ActiveRecord::Base.connection.exec_query("show create table #{table_name}").rows.last.last
67
72
  end
73
+
74
+ def dump(filename, no_create_table=false)
75
+ Arql::Mysqldump.new.dump_table(filename, table_name, no_create_table)
76
+ end
68
77
  end
69
78
  end
70
79
 
@@ -145,6 +154,7 @@ module Arql
145
154
  @@models = []
146
155
  ActiveRecord::Base.connection.tap do |conn|
147
156
  Object.const_set('ArqlModel', Class.new(ActiveRecord::Base) do
157
+ include ::Arql::Concerns::TableDataDefinition
148
158
  self.abstract_class = true
149
159
 
150
160
  define_singleton_method(:indexes) do
@@ -158,230 +168,6 @@ module Arql
158
168
  }
159
169
  end.t
160
170
  end
161
- # Add a new +type+ column named +column_name+ to +table_name+.
162
- #
163
- # The +type+ parameter is normally one of the migrations native types,
164
- # which is one of the following:
165
- # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
166
- # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
167
- # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
168
- # <tt>:binary</tt>, <tt>:boolean</tt>.
169
- #
170
- # You may use a type not in this list as long as it is supported by your
171
- # database (for example, "polygon" in MySQL), but this will not be database
172
- # agnostic and should usually be avoided.
173
- #
174
- # Available options are (none of these exists by default):
175
- # * <tt>:limit</tt> -
176
- # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
177
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
178
- # This option is ignored by some backends.
179
- # * <tt>:default</tt> -
180
- # The column's default value. Use +nil+ for +NULL+.
181
- # * <tt>:null</tt> -
182
- # Allows or disallows +NULL+ values in the column.
183
- # * <tt>:precision</tt> -
184
- # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
185
- # <tt>:datetime</tt>, and <tt>:time</tt> columns.
186
- # * <tt>:scale</tt> -
187
- # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
188
- # * <tt>:collation</tt> -
189
- # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
190
- # column will have the same collation as the table.
191
- # * <tt>:comment</tt> -
192
- # Specifies the comment for the column. This option is ignored by some backends.
193
- #
194
- # Note: The precision is the total number of significant digits,
195
- # and the scale is the number of digits that can be stored following
196
- # the decimal point. For example, the number 123.45 has a precision of 5
197
- # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
198
- # range from -999.99 to 999.99.
199
- #
200
- # Please be aware of different RDBMS implementations behavior with
201
- # <tt>:decimal</tt> columns:
202
- # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
203
- # <tt>:precision</tt>, and makes no comments about the requirements of
204
- # <tt>:precision</tt>.
205
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
206
- # Default is (10,0).
207
- # * PostgreSQL: <tt>:precision</tt> [1..infinity],
208
- # <tt>:scale</tt> [0..infinity]. No default.
209
- # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
210
- # but the maximum supported <tt>:precision</tt> is 16. No default.
211
- # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
212
- # Default is (38,0).
213
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
214
- # Default unknown.
215
- # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
216
- # Default (38,0).
217
- #
218
- # == Examples
219
- #
220
- # User.add_column(:picture, :binary, limit: 2.megabytes)
221
- # # ALTER TABLE "users" ADD "picture" blob(2097152)
222
- #
223
- # Article.add_column(:status, :string, limit: 20, default: 'draft', null: false)
224
- # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
225
- #
226
- # Answer.add_column(:bill_gates_money, :decimal, precision: 15, scale: 2)
227
- # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
228
- #
229
- # Measurement.add_column(:sensor_reading, :decimal, precision: 30, scale: 20)
230
- # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
231
- #
232
- # # While :scale defaults to zero on most databases, it
233
- # # probably wouldn't hurt to include it.
234
- # Measurement.add_column(:huge_integer, :decimal, precision: 30)
235
- # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
236
- #
237
- # # Defines a column that stores an array of a type.
238
- # User.add_column(:skills, :text, array: true)
239
- # # ALTER TABLE "users" ADD "skills" text[]
240
- #
241
- # # Defines a column with a database-specific type.
242
- # Shape.add_column(:triangle, 'polygon')
243
- # # ALTER TABLE "shapes" ADD "triangle" polygon
244
- define_singleton_method(:add_column) do |column_name, type, **options|
245
- conn.add_column(table_name, column_name, type, **options)
246
- end
247
-
248
- # Changes the column's definition according to the new options.
249
- # See TableDefinition#column for details of the options you can use.
250
- #
251
- # Supplier.change_column(:name, :string, limit: 80)
252
- # Post.change_column(:description, :text)
253
- #
254
- define_singleton_method(:change_column) do |column_name, type, options = {}|
255
- conn.change_column(table_name, column_name, type, options)
256
- end
257
-
258
- # Removes the column from the table definition.
259
- #
260
- # Supplier.remove_column(:qualification)
261
- #
262
- # The +type+ and +options+ parameters will be ignored if present. It can be helpful
263
- # to provide these in a migration's +change+ method so it can be reverted.
264
- # In that case, +type+ and +options+ will be used by #add_column.
265
- # Indexes on the column are automatically removed.
266
- define_singleton_method(:remove_column) do |column_name, type = nil, **options|
267
- conn.remove_column(table_name, column_name, type, **options)
268
- end
269
-
270
- # Adds a new index to the table. +column_name+ can be a single Symbol, or
271
- # an Array of Symbols.
272
- #
273
- # The index will be named after the table and the column name(s), unless
274
- # you pass <tt>:name</tt> as an option.
275
- #
276
- # ====== Creating a simple index
277
- #
278
- # Supplier.add_index(:name)
279
- #
280
- # generates:
281
- #
282
- # CREATE INDEX suppliers_name_index ON suppliers(name)
283
- #
284
- # ====== Creating a unique index
285
- #
286
- # Account.add_index([:branch_id, :party_id], unique: true)
287
- #
288
- # generates:
289
- #
290
- # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
291
- #
292
- # ====== Creating a named index
293
- #
294
- # Account.add_index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
295
- #
296
- # generates:
297
- #
298
- # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
299
- #
300
- # ====== Creating an index with specific key length
301
- #
302
- # Account.add_index(:name, name: 'by_name', length: 10)
303
- #
304
- # generates:
305
- #
306
- # CREATE INDEX by_name ON accounts(name(10))
307
- #
308
- # ====== Creating an index with specific key lengths for multiple keys
309
- #
310
- # Account.add_index([:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
311
- #
312
- # generates:
313
- #
314
- # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
315
- #
316
- # Note: SQLite doesn't support index length.
317
- #
318
- # ====== Creating an index with a sort order (desc or asc, asc is the default)
319
- #
320
- # Account.add_index([:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
321
- #
322
- # generates:
323
- #
324
- # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
325
- #
326
- # Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
327
- #
328
- # ====== Creating a partial index
329
- #
330
- # Account.add_index([:branch_id, :party_id], unique: true, where: "active")
331
- #
332
- # generates:
333
- #
334
- # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
335
- #
336
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
337
- #
338
- # ====== Creating an index with a specific method
339
- #
340
- # Developer.add_index(:name, using: 'btree')
341
- #
342
- # generates:
343
- #
344
- # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
345
- # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
346
- #
347
- # Note: only supported by PostgreSQL and MySQL
348
- #
349
- # ====== Creating an index with a specific operator class
350
- #
351
- # Developer.add_index(:name, using: 'gist', opclass: :gist_trgm_ops)
352
- # # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
353
- #
354
- # Developer.add_index([:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
355
- # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
356
- #
357
- # Developer.add_index([:name, :city], using: 'gist', opclass: :gist_trgm_ops)
358
- # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
359
- #
360
- # Note: only supported by PostgreSQL
361
- #
362
- # ====== Creating an index with a specific type
363
- #
364
- # Developer.add_index(:name, type: :fulltext)
365
- #
366
- # generates:
367
- #
368
- # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
369
- #
370
- # Note: only supported by MySQL.
371
- #
372
- # ====== Creating an index with a specific algorithm
373
- #
374
- # Developer.add_index(:name, algorithm: :concurrently)
375
- # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
376
- #
377
- # Note: only supported by PostgreSQL.
378
- #
379
- # Concurrently adding an index is not supported in a transaction.
380
- #
381
- # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
382
- define_singleton_method(:add_index) do |column_name, options = {}|
383
- conn.add_index(table_name, column_name, options)
384
- end
385
171
  end)
386
172
  conn.tables.each do |table_name|
387
173
  table_comment = conn.table_comment(table_name)
@@ -459,6 +245,9 @@ module Arql
459
245
  records.write_excel(filename, *fields, **options)
460
246
  end
461
247
 
248
+ def dump(filename, batch_size=500)
249
+ records.dump(filename, batch_size)
250
+ end
462
251
  end
463
252
  end
464
253
  end
@@ -83,11 +83,17 @@ class Array
83
83
  end
84
84
  csv << fields
85
85
  end
86
+ if size > 0 && first.is_a?(Hash)
87
+ if fields.empty?
88
+ fields = first.keys
89
+ end
90
+ csv << fields
91
+ end
86
92
  each do |row|
87
93
  if row.is_a?(Array)
88
94
  csv << row.map(&:to_s)
89
95
  else
90
- csv << row.slice(fields).values.map(&:to_s)
96
+ csv << row.slice(*fields).values.map(&:to_s)
91
97
  end
92
98
  end
93
99
  end
@@ -105,14 +111,29 @@ class Array
105
111
  end
106
112
  sheet.add_row(fields, types: [:string] * fields.size)
107
113
  end
114
+ if size > 0 && first.is_a?(Hash)
115
+ if fields.empty?
116
+ fields = first.keys
117
+ end
118
+ sheet.add_row(fields, types: [:string] * fields.size)
119
+ end
108
120
  each do |row|
109
121
  if row.is_a?(Array)
110
122
  sheet.add_row(row.map(&:to_s), types: [:string] * row.size)
111
123
  else
112
- sheet.add_row(row.slice(fields).values.map(&:to_s), types: [:string] * fields.size)
124
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
113
125
  end
114
126
  end
115
127
  end
116
128
  end
117
129
  end
130
+
131
+ def dump(filename, batch_size=500)
132
+ File.open(File.expand_path(filename), 'w') do |file|
133
+ group_by(&:class).each do |(klass, records)|
134
+ file.puts(klass.to_upsert_sql(records, batch_size))
135
+ end
136
+ end
137
+ {size: size, file: File.expand_path(filename)}
138
+ end
118
139
  end
@@ -3,19 +3,35 @@ class Hash
3
3
  generate_excel(filename) do |workbook|
4
4
  each do |sheet_name, sheet_data|
5
5
  workbook.add_worksheet(name: sheet_name) do |sheet|
6
- if sheet_data.is_a?(Hash) && sheet_data[:fields].present?
7
- fields = sheet_data[:fields].map(&:to_s)
8
- else
9
- fields = sheet_data[:data].first.attributes.keys
10
- end
6
+ if sheet_data.is_a?(Hash)
7
+ fields = sheet_data[:fields].map(&:to_s)
11
8
  sheet.add_row(fields, types: [:string] * fields.size)
12
- sheet_data = sheet_data[:data]
9
+ sheet_data[:data].each do |row|
10
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
11
+ end
13
12
  end
14
- sheet_data.each do |row|
15
- if row.is_a?(Array)
16
- sheet.add_row(row.map(&:to_s), types: [:string] * row.size)
17
- else
18
- sheet.add_row(row.slice(fields).values.map(&:to_s), types: [:string] * fields.size)
13
+
14
+ if sheet_data.is_a?(Array)
15
+ if sheet_data.size > 0 && sheet_data.first.is_a?(ActiveModel::Base)
16
+ fields = sheet_data.first.attributes.keys
17
+ sheet.add_row(fields, types: [:string] * fields.size)
18
+ sheet_data.each do |row|
19
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
20
+ end
21
+ end
22
+
23
+ if sheet_data.size > 0 && sheet_data.first.is_a?(Hash)
24
+ fields = sheet_data.first.keys
25
+ sheet.add_row(fields, types: [:string] * fields.size)
26
+ sheet_data.each do |row|
27
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
28
+ end
29
+ end
30
+
31
+ if sheet_data.size > 0 && sheet_data.first.is_a?(Array)
32
+ sheet_data.each do |row|
33
+ sheet.add_row(row.map(&:to_s), types: [:string] * fields.size)
34
+ end
19
35
  end
20
36
  end
21
37
  end
@@ -1,6 +1,9 @@
1
+ require 'arql/concerns'
1
2
  module Kernel
2
3
  CSV_BOM = "\xef\xbb\xbf"
3
4
 
5
+ include ::Arql::Concerns::GlobalDataDefinition
6
+
4
7
  def sql(sql)
5
8
  ActiveRecord::Base.connection.exec_query(sql)
6
9
  end
@@ -25,7 +28,7 @@ module Kernel
25
28
  Terminal::Table.new { |t|
26
29
  t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
27
30
  t.rows = table[:columns].map { |column|
28
- pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column_name)
31
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
29
32
  'Y'
30
33
  else
31
34
  ''
@@ -44,7 +47,7 @@ module Kernel
44
47
  Terminal::Table.new { |t|
45
48
  t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
46
49
  t.rows = table[:columns].map { |column|
47
- pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column_name)
50
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
48
51
  'Y'
49
52
  else
50
53
  ''
@@ -92,7 +95,7 @@ module Kernel
92
95
  def generate_excel(filename)
93
96
  Axlsx::Package.new do |package|
94
97
  yield(package.workbook)
95
- package.serialize(filename)
98
+ package.serialize(File.expand_path(filename))
96
99
  end
97
100
  end
98
101
 
@@ -106,159 +109,4 @@ module Kernel
106
109
  end
107
110
  end
108
111
 
109
- # Example:
110
- #
111
- # create_table :post, id: false, primary_key: :id do |t|
112
- # t.column :id, :bigint, precison: 19, comment: 'ID'
113
- # t.column :name, :string, comment: '名称'
114
- # t.column :gmt_created, :datetime, comment: '创建时间'
115
- # t.column :gmt_modified, :datetime, comment: '最后修改时间'
116
- # end
117
- #
118
- # Creates a new table with the name +table_name+. +table_name+ may either
119
- # be a String or a Symbol.
120
- #
121
- # There are two ways to work with #create_table. You can use the block
122
- # form or the regular form, like this:
123
- #
124
- # === Block form
125
- #
126
- # # create_table() passes a TableDefinition object to the block.
127
- # # This form will not only create the table, but also columns for the
128
- # # table.
129
- #
130
- # create_table(:suppliers) do |t|
131
- # t.column :name, :string, limit: 60
132
- # # Other fields here
133
- # end
134
- #
135
- # === Block form, with shorthand
136
- #
137
- # # You can also use the column types as method calls, rather than calling the column method.
138
- # create_table(:suppliers) do |t|
139
- # t.string :name, limit: 60
140
- # # Other fields here
141
- # end
142
- #
143
- # === Regular form
144
- #
145
- # # Creates a table called 'suppliers' with no columns.
146
- # create_table(:suppliers)
147
- # # Add a column to 'suppliers'.
148
- # add_column(:suppliers, :name, :string, {limit: 60})
149
- #
150
- # The +options+ hash can include the following keys:
151
- # [<tt>:id</tt>]
152
- # Whether to automatically add a primary key column. Defaults to true.
153
- # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
154
- #
155
- # A Symbol can be used to specify the type of the generated primary key column.
156
- # [<tt>:primary_key</tt>]
157
- # The name of the primary key, if one is to be added automatically.
158
- # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
159
- #
160
- # If an array is passed, a composite primary key will be created.
161
- #
162
- # Note that Active Record models will automatically detect their
163
- # primary key. This can be avoided by using
164
- # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
165
- # to define the key explicitly.
166
- #
167
- # [<tt>:options</tt>]
168
- # Any extra options you want appended to the table definition.
169
- # [<tt>:temporary</tt>]
170
- # Make a temporary table.
171
- # [<tt>:force</tt>]
172
- # Set to true to drop the table before creating it.
173
- # Set to +:cascade+ to drop dependent objects as well.
174
- # Defaults to false.
175
- # [<tt>:if_not_exists</tt>]
176
- # Set to true to avoid raising an error when the table already exists.
177
- # Defaults to false.
178
- # [<tt>:as</tt>]
179
- # SQL to use to generate the table. When this option is used, the block is
180
- # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
181
- #
182
- # ====== Add a backend specific option to the generated SQL (MySQL)
183
- #
184
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
185
- #
186
- # generates:
187
- #
188
- # CREATE TABLE suppliers (
189
- # id bigint auto_increment PRIMARY KEY
190
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
191
- #
192
- # ====== Rename the primary key column
193
- #
194
- # create_table(:objects, primary_key: 'guid') do |t|
195
- # t.column :name, :string, limit: 80
196
- # end
197
- #
198
- # generates:
199
- #
200
- # CREATE TABLE objects (
201
- # guid bigint auto_increment PRIMARY KEY,
202
- # name varchar(80)
203
- # )
204
- #
205
- # ====== Change the primary key column type
206
- #
207
- # create_table(:tags, id: :string) do |t|
208
- # t.column :label, :string
209
- # end
210
- #
211
- # generates:
212
- #
213
- # CREATE TABLE tags (
214
- # id varchar PRIMARY KEY,
215
- # label varchar
216
- # )
217
- #
218
- # ====== Create a composite primary key
219
- #
220
- # create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
221
- # t.belongs_to :product
222
- # t.belongs_to :client
223
- # end
224
- #
225
- # generates:
226
- #
227
- # CREATE TABLE order (
228
- # product_id bigint NOT NULL,
229
- # client_id bigint NOT NULL
230
- # );
231
- #
232
- # ALTER TABLE ONLY "orders"
233
- # ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
234
- #
235
- # ====== Do not add a primary key column
236
- #
237
- # create_table(:categories_suppliers, id: false) do |t|
238
- # t.column :category_id, :bigint
239
- # t.column :supplier_id, :bigint
240
- # end
241
- #
242
- # generates:
243
- #
244
- # CREATE TABLE categories_suppliers (
245
- # category_id bigint,
246
- # supplier_id bigint
247
- # )
248
- #
249
- # ====== Create a temporary table based on a query
250
- #
251
- # create_table(:long_query, temporary: true,
252
- # as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
253
- #
254
- # generates:
255
- #
256
- # CREATE TEMPORARY TABLE long_query AS
257
- # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
258
- #
259
- # See also TableDefinition#column for details on how to create columns.
260
-
261
- def create_table(table_name, **options, &blk)
262
- ActiveRecord::Base.connection.create_table(table_name, **options, &blk)
263
- end
264
112
  end