declare_schema 2.0.0 → 2.1.0.pre.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 +4 -4
- data/.devcontainer/Dockerfile +19 -0
- data/.devcontainer/boot.sh +1 -0
- data/.devcontainer/devcontainer.json +55 -0
- data/.devcontainer/docker-compose.yml +40 -0
- data/.github/workflows/declare_schema_build.yml +35 -11
- data/Appraisals +2 -10
- data/CHANGELOG.md +5 -0
- data/CODE-OF-CONDUCT.md +16 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +87 -77
- data/README.md +38 -14
- data/Rakefile +5 -15
- data/catalog-info.yaml +35 -0
- data/config/brakeman.ignore +2 -2
- data/gemfiles/{rails_6_1_sqlite3.gemfile → rails_6_1.gemfile} +3 -0
- data/gemfiles/{rails_7_0_sqlite3.gemfile → rails_7_0.gemfile} +3 -0
- data/gemfiles/{rails_7_1_sqlite3.gemfile → rails_7_1.gemfile} +3 -0
- data/gemfiles/{rails_7_0_mysql2.gemfile → rails_7_2.gemfile} +4 -1
- data/lib/declare_schema/command.rb +2 -8
- data/lib/declare_schema/model/column.rb +1 -1
- data/lib/declare_schema/model/foreign_key_definition.rb +9 -12
- data/lib/declare_schema/model/index_definition.rb +16 -8
- data/lib/declare_schema/model.rb +10 -14
- data/lib/declare_schema/schema_change/base.rb +4 -0
- data/lib/declare_schema/schema_change/primary_key_change.rb +19 -6
- data/lib/declare_schema/version.rb +1 -1
- data/lib/declare_schema.rb +20 -8
- data/lib/generators/declare_schema/migration/migration_generator.rb +15 -2
- data/lib/generators/declare_schema/migration/migrator.rb +16 -9
- data/spec/fixtures/migrations/mysql2/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/mysql2/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/fixtures/migrations/postgresql/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/postgresql/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/fixtures/migrations/sqlite3/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/sqlite3/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/lib/declare_schema/api_spec.rb +1 -3
- data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +3 -3
- data/spec/lib/declare_schema/field_spec_spec.rb +68 -45
- data/spec/lib/declare_schema/generator_spec.rb +2 -4
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +3 -10
- data/spec/lib/declare_schema/migration_generator_spec.rb +248 -249
- data/spec/lib/declare_schema/model/column_spec.rb +89 -41
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +14 -8
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +4 -9
- data/spec/lib/declare_schema/model/index_definition_spec.rb +18 -10
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +11 -11
- data/spec/lib/declare_schema/schema_change/base_spec.rb +5 -7
- data/spec/lib/declare_schema/schema_change/column_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_change_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/index_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +39 -15
- data/spec/lib/declare_schema/schema_change/table_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_change_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +1 -3
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +0 -4
- data/spec/spec_helper.rb +3 -0
- data/spec/support/adapter_specific_test_helpers.rb +25 -0
- data/spec/{lib/declare_schema → support}/prepare_testapp.rb +3 -1
- data/spec/support/test_app_spec_helpers.rb +7 -0
- metadata +22 -9
- data/gemfiles/rails_6_1_mysql2.gemfile +0 -23
- data/gemfiles/rails_7_1_mysql2.gemfile +0 -23
@@ -0,0 +1,15 @@
|
|
1
|
+
create_table :affiliates, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
2
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
3
|
+
t.integer :category_id, limit: 8, null: false
|
4
|
+
end
|
5
|
+
create_table :advertisers, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
6
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
7
|
+
t.integer :category_id, limit: 8, null: false
|
8
|
+
end
|
9
|
+
create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
10
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
11
|
+
end
|
12
|
+
add_index :affiliates, [:category_id], name: :index_affiliates_on_category_id
|
13
|
+
add_index :advertisers, [:category_id], name: :index_advertisers_on_category_id
|
14
|
+
add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
|
15
|
+
add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
|
@@ -3,14 +3,14 @@
|
|
3
3
|
require_relative '../../../lib/declare_schema/field_declaration_dsl'
|
4
4
|
|
5
5
|
RSpec.describe DeclareSchema::FieldDeclarationDsl do
|
6
|
+
include_context 'prepare test app'
|
7
|
+
|
6
8
|
let(:model) { TestModel.new }
|
7
9
|
subject { declared_class.new(model) }
|
8
10
|
|
9
11
|
context 'Using declare_schema' do
|
10
12
|
before do
|
11
|
-
|
12
|
-
|
13
|
-
class TestModel < ActiveRecord::Base
|
13
|
+
class TestModel < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
14
14
|
declare_schema do
|
15
15
|
string :name, limit: 127
|
16
16
|
|
@@ -1,26 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
begin
|
4
|
-
require 'mysql2'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'sqlite3'
|
10
|
-
rescue LoadError
|
11
|
-
end
|
12
|
-
|
13
3
|
RSpec.describe DeclareSchema::Model::FieldSpec do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
include_context 'prepare test app'
|
5
|
+
|
6
|
+
let(:model) { double('model', _table_options: {}, _declared_primary_key: 'id') }
|
7
|
+
let(:col_spec) do
|
8
|
+
if current_adapter == 'postgresql'
|
9
|
+
instance_double(ActiveRecord::ConnectionAdapters::PostgreSQL::Column, type: :string, sql_type: 'character varying(100)', oid: 1043, fmod: 2052)
|
10
|
+
else
|
11
|
+
instance_double(ActiveRecord::ConnectionAdapters::Column, type: :string, sql_type: :string)
|
12
|
+
end
|
19
13
|
end
|
20
14
|
|
21
15
|
describe '#initialize' do
|
16
|
+
subject { described_class.new(model, :price, :integer, anonymize_using: 'x', null: false, position: 0, limit: 4) }
|
22
17
|
it 'normalizes option order' do
|
23
|
-
subject = described_class.new(model, :price, :integer, anonymize_using: 'x', null: false, position: 0, limit: 4)
|
24
18
|
expect(subject.options.keys).to eq([:limit, :null, :anonymize_using])
|
25
19
|
end
|
26
20
|
|
@@ -33,48 +27,54 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
33
27
|
|
34
28
|
describe '#schema_attributes' do
|
35
29
|
describe 'integer 4' do
|
30
|
+
subject { described_class.new(model, :price, :integer, limit: 4, null: false, position: 0) }
|
36
31
|
it 'returns schema attributes' do
|
37
|
-
subject = described_class.new(model, :price, :integer, limit: 4, null: false, position: 0)
|
38
32
|
expect(subject.schema_attributes(col_spec)).to eq(type: :integer, limit: 4, null: false)
|
39
33
|
end
|
40
34
|
end
|
41
35
|
|
42
36
|
describe 'integer 8' do
|
37
|
+
subject { described_class.new(model, :price, :integer, limit: 8, null: true, position: 2) }
|
43
38
|
it 'returns schema attributes' do
|
44
|
-
subject = described_class.new(model, :price, :integer, limit: 8, null: true, position: 2)
|
45
39
|
expect(subject.schema_attributes(col_spec)).to eq(type: :integer, limit: 8, null: true)
|
46
40
|
end
|
47
41
|
end
|
48
42
|
|
49
43
|
describe 'bigint' do
|
44
|
+
subject { described_class.new(model, :price, :bigint, null: false, position: 2) }
|
50
45
|
it 'returns schema attributes' do
|
51
|
-
subject = described_class.new(model, :price, :bigint, null: false, position: 2)
|
52
46
|
expect(subject.schema_attributes(col_spec)).to eq(type: :integer, limit: 8, null: false)
|
53
47
|
end
|
54
48
|
end
|
55
49
|
|
56
50
|
describe 'string' do
|
51
|
+
subject { described_class.new(model, :title, :string, limit: 100, null: true, charset: 'utf8mb4', position: 0) }
|
52
|
+
|
57
53
|
it 'returns schema attributes (including charset/collation iff mysql)' do
|
58
|
-
|
59
|
-
|
54
|
+
case current_adapter
|
55
|
+
when 'mysql2'
|
60
56
|
expect(subject.schema_attributes(col_spec)).to eq(type: :string, limit: 100, null: true, charset: 'utf8mb4', collation: 'utf8mb4_bin')
|
61
57
|
else
|
62
58
|
expect(subject.schema_attributes(col_spec)).to eq(type: :string, limit: 100, null: true)
|
63
59
|
end
|
64
60
|
end
|
65
61
|
|
66
|
-
|
62
|
+
context 'MySQL only' do
|
63
|
+
include_context 'skip unless' do
|
64
|
+
let(:adapter) { 'mysql2' }
|
65
|
+
end
|
66
|
+
|
67
67
|
context 'when running on MySQL 8.0' do
|
68
68
|
around do |spec|
|
69
69
|
DeclareSchema.mysql_version = Gem::Version.new('8.0.21')
|
70
70
|
spec.run
|
71
71
|
ensure
|
72
|
-
DeclareSchema.remove_instance_variable('@mysql_version') rescue nil
|
72
|
+
DeclareSchema.remove_instance_variable('@mysql_version') rescue nil # rubocop:disable Style/RescueModifier
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
|
-
subject = described_class.new(model, :title, :string, limit: 100, null: true, charset: 'utf8', collation: 'utf8_general', position: 0)
|
75
|
+
subject { described_class.new(model, :title, :string, limit: 100, null: true, charset: 'utf8', collation: 'utf8_general', position: 0) }
|
77
76
|
|
77
|
+
it 'normalizes charset and collation' do
|
78
78
|
expect(subject.schema_attributes(col_spec)).to eq(type: :string, limit: 100, null: true, charset: 'utf8mb3', collation: 'utf8mb3_general')
|
79
79
|
end
|
80
80
|
end
|
@@ -89,9 +89,9 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
89
89
|
end
|
90
90
|
|
91
91
|
describe 'text' do
|
92
|
+
subject { described_class.new(model, :title, :text, limit: 200, null: true, charset: 'utf8mb4', position: 2) }
|
92
93
|
it 'returns schema attributes (including charset/collation iff mysql)' do
|
93
|
-
|
94
|
-
if defined?(Mysql2)
|
94
|
+
if current_adapter == 'mysql2'
|
95
95
|
expect(subject.schema_attributes(col_spec)).to eq(type: :text, limit: 255, null: true, charset: 'utf8mb4', collation: 'utf8mb4_bin')
|
96
96
|
else
|
97
97
|
expect(subject.schema_attributes(col_spec)).to eq(type: :text, null: true)
|
@@ -99,7 +99,7 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
99
99
|
end
|
100
100
|
|
101
101
|
it 'allows a default to be set unless mysql' do
|
102
|
-
if
|
102
|
+
if current_adapter == 'mysql2'
|
103
103
|
expect do
|
104
104
|
described_class.new(model, :title, :text, limit: 200, null: true, default: 'none', charset: 'utf8mb4', position: 2)
|
105
105
|
end.to raise_exception(DeclareSchema::MysqlTextMayNotHaveDefault)
|
@@ -110,10 +110,10 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
110
110
|
end
|
111
111
|
|
112
112
|
describe 'limit' do
|
113
|
+
subject { described_class.new(model, :title, :text, null: true, charset: 'utf8mb4', position: 2) }
|
113
114
|
it 'uses default_text_limit option when not explicitly set in field spec' do
|
114
115
|
allow(::DeclareSchema).to receive(:default_text_limit) { 100 }
|
115
|
-
|
116
|
-
if defined?(Mysql2)
|
116
|
+
if current_adapter == 'mysql2'
|
117
117
|
expect(subject.schema_attributes(col_spec)).to eq(type: :text, limit: 255, null: true, charset: 'utf8mb4', collation: 'utf8mb4_bin')
|
118
118
|
else
|
119
119
|
expect(subject.schema_attributes(col_spec)).to eq(type: :text, null: true)
|
@@ -121,11 +121,9 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
it 'raises error when default_text_limit option is nil when not explicitly set in field spec' do
|
124
|
-
if
|
124
|
+
if current_adapter == 'mysql2'
|
125
125
|
expect(::DeclareSchema).to receive(:default_text_limit) { nil }
|
126
|
-
expect
|
127
|
-
described_class.new(model, :title, :text, null: true, charset: 'utf8mb4', position: 2)
|
128
|
-
end.to raise_error(/limit: must be provided for :text field/)
|
126
|
+
expect { subject }.to raise_error(/limit: must be provided for :text field/)
|
129
127
|
end
|
130
128
|
end
|
131
129
|
end
|
@@ -173,18 +171,22 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
173
171
|
end
|
174
172
|
end
|
175
173
|
|
176
|
-
|
174
|
+
context 'MySQL only' do
|
175
|
+
include_context 'skip unless' do
|
176
|
+
let(:adapter) { 'mysql2' }
|
177
|
+
end
|
178
|
+
|
177
179
|
describe 'varbinary' do # TODO: :varbinary is an Invoca addition to Rails; make it a configurable option
|
180
|
+
subject { described_class.new(model, :binary_dump, :varbinary, limit: 200, null: false, position: 2) }
|
178
181
|
it 'is supported' do
|
179
|
-
subject = described_class.new(model, :binary_dump, :varbinary, limit: 200, null: false, position: 2)
|
180
182
|
expect(subject.schema_attributes(col_spec)).to eq(type: :varbinary, limit: 200, null: false)
|
181
183
|
end
|
182
184
|
end
|
183
185
|
end
|
184
186
|
|
185
187
|
describe 'decimal' do
|
188
|
+
subject { described_class.new(model, :quantity, :decimal, precision: 8, scale: 10, null: true, position: 3) }
|
186
189
|
it 'allows precision: and scale:' do
|
187
|
-
subject = described_class.new(model, :quantity, :decimal, precision: 8, scale: 10, null: true, position: 3)
|
188
190
|
expect(subject.schema_attributes(col_spec)).to eq(type: :decimal, precision: 8, scale: 10, null: true)
|
189
191
|
end
|
190
192
|
|
@@ -199,10 +201,18 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
199
201
|
end
|
200
202
|
end
|
201
203
|
|
202
|
-
[:integer, :bigint, :string, :text, :binary, :datetime, :date, :time,
|
204
|
+
[:integer, :bigint, :string, :text, :binary, :datetime, :date, :time, :varbinary].compact.each do |t|
|
203
205
|
describe t.to_s do
|
204
206
|
let(:extra) { t == :string ? { limit: 100 } : {} }
|
205
207
|
|
208
|
+
around do |spec|
|
209
|
+
if t == :varbinary && current_adapter != 'mysql2'
|
210
|
+
spec.skip
|
211
|
+
else
|
212
|
+
spec.run
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
206
216
|
it 'does not allow precision:' do
|
207
217
|
expect_any_instance_of(described_class).to receive(:warn).with(/precision: only allowed for :decimal type/)
|
208
218
|
described_class.new(model, :quantity, t, **{ precision: 8, null: true, position: 3 }.merge(extra))
|
@@ -216,25 +226,31 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
216
226
|
end
|
217
227
|
|
218
228
|
describe 'datetime' do
|
229
|
+
subject { described_class.new(model, :created_at, :datetime, null: false, position: 1) }
|
219
230
|
it 'keeps type as "datetime"' do
|
220
|
-
subject = described_class.new(model, :created_at, :datetime, null: false, position: 1)
|
221
231
|
expect(subject.schema_attributes(col_spec)).to eq(type: :datetime, null: false)
|
222
232
|
end
|
223
233
|
end
|
224
234
|
|
225
235
|
describe 'timestamp' do
|
236
|
+
subject { described_class.new(model, :created_at, :timestamp, null: true, position: 2) }
|
226
237
|
it 'normalizes type to "datetime"' do
|
227
|
-
subject = described_class.new(model, :created_at, :timestamp, null: true, position: 2)
|
228
238
|
expect(subject.schema_attributes(col_spec)).to eq(type: :datetime, null: true)
|
229
239
|
end
|
230
240
|
end
|
231
241
|
|
232
242
|
describe 'default:' do
|
233
|
-
|
243
|
+
subject { described_class.new(model, :price, :integer, limit: 4, default: '42', null: true, position: 2) }
|
244
|
+
|
245
|
+
let(:col_spec) do
|
246
|
+
if current_adapter == 'postgresql'
|
247
|
+
instance_double(ActiveRecord::ConnectionAdapters::PostgreSQL::Column, type: :integer, sql_type: "integer", limit: 4, oid: 23, fmod: -1)
|
248
|
+
else
|
249
|
+
instance_double(ActiveRecord::ConnectionAdapters::Column, type: :integer, sql_type: "integer", limit: 4)
|
250
|
+
end
|
251
|
+
end
|
234
252
|
|
235
253
|
it 'typecasts default value' do
|
236
|
-
allow(col_spec).to receive(:type_cast_from_database) { |default| Integer(default) }
|
237
|
-
subject = described_class.new(model, :price, :integer, limit: 4, default: '42', null: true, position: 2)
|
238
254
|
expect(subject.schema_attributes(col_spec)).to eq(type: :integer, limit: 4, default: 42, null: true)
|
239
255
|
end
|
240
256
|
end
|
@@ -242,8 +258,15 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
242
258
|
|
243
259
|
describe '#schema_attributes' do
|
244
260
|
let(:col_spec) do
|
245
|
-
|
246
|
-
|
261
|
+
if current_adapter == 'postgresql'
|
262
|
+
instance_double(ActiveRecord::ConnectionAdapters::PostgreSQL::Column,
|
263
|
+
name: "price", type: :integer, sql_type: "integer", limit: 4,
|
264
|
+
null: false, default: nil, default_function: "adverts", oid: 23, fmod: -1)
|
265
|
+
else
|
266
|
+
instance_double(ActiveRecord::ConnectionAdapters::Column,
|
267
|
+
name: "price", type: :integer, sql_type: "integer(8)", limit: 8,
|
268
|
+
null: false, default: nil, default_function: "adverts")
|
269
|
+
end
|
247
270
|
end
|
248
271
|
|
249
272
|
it 'returns the attributes except name, position, and non-SQL options' do
|
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe 'DeclareSchema Migration Generator' do
|
4
|
-
|
5
|
-
load File.expand_path('prepare_testapp.rb', __dir__)
|
6
|
-
end
|
4
|
+
include_context 'prepare test app'
|
7
5
|
|
8
6
|
it "generates nested models" do
|
9
7
|
generate_model 'alpha/beta', 'one:string', 'two:integer'
|
@@ -57,7 +55,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
57
55
|
|
58
56
|
expect(File.exist?('db/schema.rb')).to be_truthy
|
59
57
|
|
60
|
-
if
|
58
|
+
if current_adapter == 'sqlite3'
|
61
59
|
if ActiveSupport.version >= Gem::Version.new('7.1.0')
|
62
60
|
expect(File.exist?("storage/development.sqlite3") || File.exist?("storage/test.sqlite3")).to be_truthy
|
63
61
|
else
|
@@ -1,14 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
begin
|
4
|
-
require 'mysql2'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
7
|
-
|
8
3
|
RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
|
9
|
-
|
10
|
-
load File.expand_path('prepare_testapp.rb', __dir__)
|
11
|
-
end
|
4
|
+
include_context 'prepare test app'
|
12
5
|
|
13
6
|
context 'Using declare_schema' do
|
14
7
|
it "allows alternate primary keys" do
|
@@ -56,12 +49,12 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
|
|
56
49
|
# (0.1ms) DROP TABLE "afoos"
|
57
50
|
# (pry):17
|
58
51
|
# (0.9ms) commit transaction
|
59
|
-
if
|
52
|
+
if current_adapter == 'sqlite3'
|
60
53
|
ActiveRecord::Base.connection.execute("drop table foos")
|
61
54
|
ActiveRecord::Base.connection.execute("CREATE TABLE foos (id integer PRIMARY KEY AUTOINCREMENT NOT NULL)")
|
62
55
|
end
|
63
56
|
|
64
|
-
|
57
|
+
unless current_adapter == 'mysql2' # TODO TECH-4814 Put this test back for Mysql2
|
65
58
|
# replace custom primary_key
|
66
59
|
class Foo < ActiveRecord::Base
|
67
60
|
declare_schema do
|