declare_schema 0.3.0.pre.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/Gemfile.lock +1 -1
- data/lib/declare_schema/model.rb +27 -23
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/templates/migration.rb.erb +1 -1
- data/spec/lib/declare_schema/migration_generator_spec.rb +128 -19
- data/spec/lib/declare_schema/prepare_testapp.rb +2 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3ae0814480b116b32a822887c83612b33458cc910384ae0c2122e547757f141
|
4
|
+
data.tar.gz: 133b7bd6d7d91dae9320a4e8002a8591e43ebb97f57013cbe266cef40a169088
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77b840eb5feb07d38c4fd96f4212d4c81f19c61a2aa2b4bbffb4f4f11594ba55c30e65ca4b77a68e7b29448f7762dba52782f1845307c5bbc02f7603f080c2e8
|
7
|
+
data.tar.gz: 2f85affbd44a6973595883afa01766ede6c950a70c4e783f3064e5c2a59ec5c71a3731d7d484bab133c18c698584077e2c14a8b98c81ce024e2e55bae59dabba
|
data/CHANGELOG.md
CHANGED
@@ -4,10 +4,19 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
4
4
|
|
5
5
|
Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
## [0.3.0] -
|
7
|
+
## [0.3.0] - 2020-11-02
|
8
8
|
### Added
|
9
|
+
- Added support for `belongs_to optional:`.
|
10
|
+
If given, it is passed through to `ActiveRecord`'s `belong_to`.
|
11
|
+
If not given in Rails 5+, the `optional:` value is set equal to the `null:` value (default: `false`) and that
|
12
|
+
is passed to `ActiveRecord`'s `belong_to`.
|
13
|
+
Similarly, if `null:` is not given, it is inferred from `optional:`.
|
14
|
+
If both are given, their values are respected, even if contradictory;
|
15
|
+
this is a legitimate case when migrating to/from an optional association.
|
9
16
|
- Added a new callback `before_generating_migration` to the `Migrator` that can be
|
10
17
|
defined in order to custom load more models that might be missed by `eager_load!`
|
18
|
+
### Fixed
|
19
|
+
- Migrations are now generated where the `[4.2]` is only applied after `ActiveRecord::Migration` in Rails 5+ (since Rails 4 didn't know about that notation).
|
11
20
|
|
12
21
|
## [0.2.0] - 2020-10-26
|
13
22
|
### Added
|
data/Gemfile.lock
CHANGED
data/lib/declare_schema/model.rb
CHANGED
@@ -106,17 +106,14 @@ module DeclareSchema
|
|
106
106
|
end
|
107
107
|
|
108
108
|
# Extend belongs_to so that it creates a FieldSpec for the foreign key
|
109
|
-
def belongs_to(name,
|
110
|
-
if args.size == 0 || (args.size == 1 && args[0].is_a?(Proc))
|
111
|
-
options = {}
|
112
|
-
args.push(options)
|
113
|
-
elsif args.size == 1
|
114
|
-
options = args[0]
|
115
|
-
else
|
116
|
-
options = args[1]
|
117
|
-
end
|
109
|
+
def belongs_to(name, scope = nil, **options, &block)
|
118
110
|
column_options = {}
|
119
|
-
|
111
|
+
|
112
|
+
column_options[:null] = if options.has_key?(:null)
|
113
|
+
options.delete(:null)
|
114
|
+
elsif options.has_key?(:optional)
|
115
|
+
options[:optional] # infer :null from :optional
|
116
|
+
end || false
|
120
117
|
column_options[:default] = options.delete(:default) if options.has_key?(:default)
|
121
118
|
column_options[:limit] = options.delete(:limit) if options.has_key?(:limit)
|
122
119
|
|
@@ -129,20 +126,27 @@ module DeclareSchema
|
|
129
126
|
fk_options[:constraint_name] = options.delete(:constraint) if options.has_key?(:constraint)
|
130
127
|
fk_options[:index_name] = index_options[:name]
|
131
128
|
|
129
|
+
fk = options[:foreign_key]&.to_s || "#{name}_id"
|
130
|
+
|
131
|
+
if !options.has_key?(:optional) && Rails::VERSION::MAJOR >= 5
|
132
|
+
options[:optional] = column_options[:null] # infer :optional from :null
|
133
|
+
end
|
134
|
+
|
132
135
|
fk_options[:dependent] = options.delete(:far_end_dependent) if options.has_key?(:far_end_dependent)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
136
|
+
|
137
|
+
super(name, scope, options)
|
138
|
+
|
139
|
+
refl = reflections[name.to_s] or raise "Couldn't find reflection #{name} in #{reflections.keys}"
|
140
|
+
fkey = refl.foreign_key or raise "Couldn't find foreign_key for #{name} in #{refl.inspect}"
|
141
|
+
declare_field(fkey.to_sym, :integer, column_options)
|
142
|
+
if refl.options[:polymorphic]
|
143
|
+
foreign_type = options[:foreign_type] || "#{name}_type"
|
144
|
+
declare_polymorphic_type_field(foreign_type, column_options)
|
145
|
+
index([foreign_type, fkey], index_options) if index_options[:name] != false
|
146
|
+
else
|
147
|
+
index(fkey, index_options) if index_options[:name] != false
|
148
|
+
options[:constraint_name] = options
|
149
|
+
constraint(fkey, fk_options) if fk_options[:constraint_name] != false
|
146
150
|
end
|
147
151
|
end
|
148
152
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rails'
|
4
|
+
|
3
5
|
RSpec.describe 'DeclareSchema Migration Generator' do
|
4
6
|
before do
|
5
7
|
load File.expand_path('prepare_testapp.rb', __dir__)
|
@@ -53,7 +55,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
53
55
|
|
54
56
|
add_index :adverts, [:id], unique: true, name: 'PRIMARY_KEY'
|
55
57
|
EOS
|
56
|
-
# TODO: ^ add_index should not be there
|
58
|
+
# TODO: ^ TECH-4975 add_index should not be there
|
57
59
|
|
58
60
|
expect(down).to eq(<<~EOS.strip)
|
59
61
|
remove_column :adverts, :body
|
@@ -129,7 +131,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
132
|
-
up
|
134
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
133
135
|
expect(up).to eq("add_column :adverts, :price, :integer, limit: 2")
|
134
136
|
|
135
137
|
# Now run the migration, then change the limit:
|
@@ -154,7 +156,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
154
156
|
end
|
155
157
|
end
|
156
158
|
|
157
|
-
up
|
159
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
158
160
|
expect(up).to eq("add_column :adverts, :price, :decimal")
|
159
161
|
|
160
162
|
# Limits are generally not needed for `text` fields, because by default, `text` fields will use the maximum size
|
@@ -170,7 +172,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
170
172
|
end
|
171
173
|
end
|
172
174
|
|
173
|
-
up
|
175
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
174
176
|
expect(up).to eq(<<~EOS.strip)
|
175
177
|
add_column :adverts, :price, :decimal
|
176
178
|
add_column :adverts, :notes, :text, null: false
|
@@ -194,7 +196,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
194
196
|
end
|
195
197
|
end
|
196
198
|
|
197
|
-
up
|
199
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
198
200
|
expect(up).to eq(<<~EOS.strip)
|
199
201
|
add_column :adverts, :notes, :text, null: false, limit: 4294967295
|
200
202
|
add_column :adverts, :description, :text, null: false, limit: 255
|
@@ -266,7 +268,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
266
268
|
end
|
267
269
|
end
|
268
270
|
|
269
|
-
up
|
271
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
270
272
|
ActiveRecord::Migration.class_eval up
|
271
273
|
Advert.connection.schema_cache.clear!
|
272
274
|
Advert.reset_column_information
|
@@ -278,6 +280,9 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
278
280
|
|
279
281
|
class Category < ActiveRecord::Base; end
|
280
282
|
class Advert < ActiveRecord::Base
|
283
|
+
fields do
|
284
|
+
name :string, limit: 255, null: true
|
285
|
+
end
|
281
286
|
belongs_to :category
|
282
287
|
end
|
283
288
|
|
@@ -298,9 +303,10 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
298
303
|
|
299
304
|
class Category < ActiveRecord::Base; end
|
300
305
|
class Advert < ActiveRecord::Base
|
306
|
+
fields { }
|
301
307
|
belongs_to :category, foreign_key: "c_id", class_name: 'Category'
|
302
308
|
end
|
303
|
-
up
|
309
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
304
310
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
305
311
|
add_column :adverts, :c_id, :integer, limit: 8, null: false
|
306
312
|
add_index :adverts, [:c_id], name: 'on_c_id'
|
@@ -313,9 +319,10 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
313
319
|
|
314
320
|
class Category < ActiveRecord::Base; end
|
315
321
|
class Advert < ActiveRecord::Base
|
322
|
+
fields { }
|
316
323
|
belongs_to :category, index: false
|
317
324
|
end
|
318
|
-
up
|
325
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
319
326
|
expect(up.gsub(/\n+/, "\n")).to eq("add_column :adverts, :category_id, :integer, limit: 8, null: false")
|
320
327
|
|
321
328
|
Advert.field_specs.delete(:category_id)
|
@@ -325,9 +332,10 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
325
332
|
|
326
333
|
class Category < ActiveRecord::Base; end
|
327
334
|
class Advert < ActiveRecord::Base
|
335
|
+
fields { }
|
328
336
|
belongs_to :category, index: 'my_index'
|
329
337
|
end
|
330
|
-
up
|
338
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
331
339
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
332
340
|
add_column :adverts, :category_id, :integer, limit: 8, null: false
|
333
341
|
add_index :adverts, [:category_id], name: 'my_index'
|
@@ -372,7 +380,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
372
380
|
title :string, index: true, limit: 255, null: true
|
373
381
|
end
|
374
382
|
end
|
375
|
-
up
|
383
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
376
384
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
377
385
|
add_column :adverts, :title, :string, limit: 255
|
378
386
|
add_index :adverts, [:title], name: 'on_title'
|
@@ -387,7 +395,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
387
395
|
title :string, index: true, unique: true, null: true, limit: 255
|
388
396
|
end
|
389
397
|
end
|
390
|
-
up
|
398
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
391
399
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
392
400
|
add_column :adverts, :title, :string, limit: 255
|
393
401
|
add_index :adverts, [:title], unique: true, name: 'on_title'
|
@@ -402,20 +410,20 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
402
410
|
title :string, index: 'my_index', limit: 255, null: true
|
403
411
|
end
|
404
412
|
end
|
405
|
-
up
|
413
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
406
414
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
407
415
|
add_column :adverts, :title, :string, limit: 255
|
408
416
|
add_index :adverts, [:title], name: 'my_index'
|
409
417
|
EOS
|
410
418
|
|
411
|
-
Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
|
419
|
+
Advert.index_specs.delete_if { |spec| spec.fields==["title"] }
|
412
420
|
|
413
421
|
# You can ask for an index outside of the fields block
|
414
422
|
|
415
423
|
class Advert < ActiveRecord::Base
|
416
424
|
index :title
|
417
425
|
end
|
418
|
-
up
|
426
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
419
427
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
420
428
|
add_column :adverts, :title, :string, limit: 255
|
421
429
|
add_index :adverts, [:title], name: 'on_title'
|
@@ -428,7 +436,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
428
436
|
class Advert < ActiveRecord::Base
|
429
437
|
index :title, unique: true, name: 'my_index'
|
430
438
|
end
|
431
|
-
up
|
439
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
432
440
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
433
441
|
add_column :adverts, :title, :string, limit: 255
|
434
442
|
add_index :adverts, [:title], unique: true, name: 'my_index'
|
@@ -441,7 +449,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
441
449
|
class Advert < ActiveRecord::Base
|
442
450
|
index [:title, :category_id]
|
443
451
|
end
|
444
|
-
up
|
452
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
445
453
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
446
454
|
add_column :adverts, :title, :string, limit: 255
|
447
455
|
add_index :adverts, [:title, :category_id], name: 'on_title_and_category_id'
|
@@ -544,7 +552,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
544
552
|
title :string, default: "Untitled", limit: 255, null: true
|
545
553
|
end
|
546
554
|
end
|
547
|
-
up
|
555
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
548
556
|
ActiveRecord::Migration.class_eval(up)
|
549
557
|
|
550
558
|
class FancyAdvert < Advert
|
@@ -613,7 +621,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
613
621
|
created_at :datetime
|
614
622
|
end
|
615
623
|
end
|
616
|
-
up
|
624
|
+
up = Generators::DeclareSchema::Migration::Migrator.run(adverts: :ads).first
|
617
625
|
expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
|
618
626
|
rename_table :adverts, :ads
|
619
627
|
add_column :ads, :created_at, :datetime, null: false
|
@@ -682,5 +690,106 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
682
690
|
ActiveRecord::Migration.class_eval(up)
|
683
691
|
expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
|
684
692
|
end
|
685
|
-
end
|
686
693
|
|
694
|
+
if Rails::VERSION::MAJOR >= 5
|
695
|
+
describe 'belongs_to' do
|
696
|
+
before do
|
697
|
+
unless defined?(AdCategory)
|
698
|
+
class AdCategory < ActiveRecord::Base
|
699
|
+
fields { }
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
class Advert < ActiveRecord::Base
|
704
|
+
fields do
|
705
|
+
name :string, limit: 255, null: true
|
706
|
+
category_id :integer, limit: 8
|
707
|
+
nullable_category_id :integer, limit: 8, null: true
|
708
|
+
end
|
709
|
+
end
|
710
|
+
up = Generators::DeclareSchema::Migration::Migrator.run.first
|
711
|
+
ActiveRecord::Migration.class_eval(up)
|
712
|
+
end
|
713
|
+
|
714
|
+
it 'passes through optional: when given' do
|
715
|
+
class AdvertBelongsTo < ActiveRecord::Base
|
716
|
+
self.table_name = 'adverts'
|
717
|
+
fields { }
|
718
|
+
reset_column_information
|
719
|
+
belongs_to :ad_category, optional: true
|
720
|
+
end
|
721
|
+
expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional: true)
|
722
|
+
end
|
723
|
+
|
724
|
+
describe 'contradictory settings' do # contradictory settings are ok during migration
|
725
|
+
it 'passes through optional: true, null: false' do
|
726
|
+
class AdvertBelongsTo < ActiveRecord::Base
|
727
|
+
self.table_name = 'adverts'
|
728
|
+
fields { }
|
729
|
+
reset_column_information
|
730
|
+
belongs_to :ad_category, optional: true, null: false
|
731
|
+
end
|
732
|
+
expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional: true)
|
733
|
+
expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(false)
|
734
|
+
end
|
735
|
+
|
736
|
+
it 'passes through optional: false, null: true' do
|
737
|
+
class AdvertBelongsTo < ActiveRecord::Base
|
738
|
+
self.table_name = 'adverts'
|
739
|
+
fields { }
|
740
|
+
reset_column_information
|
741
|
+
belongs_to :ad_category, optional: false, null: true
|
742
|
+
end
|
743
|
+
expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional: false)
|
744
|
+
expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(true)
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
[false, true].each do |nullable|
|
749
|
+
context "nullable=#{nullable}" do
|
750
|
+
it 'infers optional: from null:' do
|
751
|
+
eval <<~EOS
|
752
|
+
class AdvertBelongsTo < ActiveRecord::Base
|
753
|
+
fields { }
|
754
|
+
belongs_to :ad_category, null: #{nullable}
|
755
|
+
end
|
756
|
+
EOS
|
757
|
+
expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional: nullable)
|
758
|
+
expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
|
759
|
+
end
|
760
|
+
|
761
|
+
it 'infers null: from optional:' do
|
762
|
+
eval <<~EOS
|
763
|
+
class AdvertBelongsTo < ActiveRecord::Base
|
764
|
+
fields { }
|
765
|
+
belongs_to :ad_category, optional: #{nullable}
|
766
|
+
end
|
767
|
+
EOS
|
768
|
+
expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional: nullable)
|
769
|
+
expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
|
770
|
+
end
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
describe 'migration base class' do
|
777
|
+
it 'adapts to Rails 4' do
|
778
|
+
class Advert < active_record_base_class.constantize
|
779
|
+
fields do
|
780
|
+
title :string, limit: 100
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
|
785
|
+
|
786
|
+
migrations = Dir.glob('db/migrate/*declare_schema_migration*.rb')
|
787
|
+
expect(migrations.size).to eq(1), migrations.inspect
|
788
|
+
|
789
|
+
migration_content = File.read(migrations.first)
|
790
|
+
first_line = migration_content.split("\n").first
|
791
|
+
base_class = first_line.split(' < ').last
|
792
|
+
expect(base_class).to eq("(Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
|
793
|
+
end
|
794
|
+
end
|
795
|
+
end
|
@@ -18,6 +18,8 @@ require "#{TESTAPP_PATH}/config/environment"
|
|
18
18
|
require 'rails/generators'
|
19
19
|
Rails::Generators.configure!(Rails.application.config.generators)
|
20
20
|
|
21
|
+
ActiveRecord::Base.connection.schema_cache.clear!
|
22
|
+
|
21
23
|
(ActiveRecord::Base.connection.tables - Generators::DeclareSchema::Migration::Migrator.always_ignore_tables).each do |table|
|
22
24
|
ActiveRecord::Base.connection.execute("DROP TABLE #{ActiveRecord::Base.connection.quote_table_name(table)}")
|
23
25
|
end
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: declare_schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.0
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Invoca Development adapted from hobo_fields by Tom Locke
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2020-11-02 00:00:00.000000000 Z
|
@@ -84,7 +84,7 @@ homepage: https://github.com/Invoca/declare_schema
|
|
84
84
|
licenses: []
|
85
85
|
metadata:
|
86
86
|
allowed_push_host: https://rubygems.org
|
87
|
-
post_install_message:
|
87
|
+
post_install_message:
|
88
88
|
rdoc_options: []
|
89
89
|
require_paths:
|
90
90
|
- lib
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
version: 1.3.6
|
101
101
|
requirements: []
|
102
102
|
rubygems_version: 3.0.3
|
103
|
-
signing_key:
|
103
|
+
signing_key:
|
104
104
|
specification_version: 4
|
105
105
|
summary: Database migration generator for Rails
|
106
106
|
test_files: []
|