declare_schema 0.2.0.pre.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ce85b3e273a189a4ab8b48e333c2f12b2aea4962c0c277ae3c289f1904cddd8
4
- data.tar.gz: d12345f0a483effa2724e0cd315efa759b2ace23e82ef9a1ae2cdaae28aa2571
3
+ metadata.gz: 8b82febb09d3ef72dd3e219c6900abd790e6401e9b15c9ca05c8cd9fc20c5bf4
4
+ data.tar.gz: 3f2520292af5845df795dba3f26ee62385d1166da51ce5c6a980d1fdc52460a3
5
5
  SHA512:
6
- metadata.gz: 84cfbdd55c37a0404b672fbbca5c12dfeee765b0582cdf369b8b3f95159cfb8f82eb3fb1e6149e830ba4fe4481d6df6ef4df45398e30985380ae6681c731f8a9
7
- data.tar.gz: adeda560e6bb859104b78e3710fb94d438606cf42c10b199527e5d14545505dda6dde3206500a5f7ea38b79530952182954f5e3892779f9d96db9270d59a28f1
6
+ metadata.gz: bb726c72430d5b44d94239c3d00abfa42a1c4e2a9c23ad316bd66ec6567857a81c6f7766136bf3d4d78d1df7a43edec2da7e0162cabd60080892b29a2f6b69ce
7
+ data.tar.gz: 4f39c4534895f33f5d30181ef18b09c2fd9eb535e9da14b0ce502c9117cf28e58404e4c6b2254ffb322a3faea99f0433de6450b9fca96a0585bc87d25047c7b8
@@ -4,14 +4,36 @@ 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.2.0] - Unreleased
7
+ ## [0.3.1] - 2020-11-13
8
+ ### Fixed
9
+ - When passing `belongs_to` to Rails, suppress the `optional:` option in Rails 4, since that option was added in Rails 5.
10
+
11
+ ## [0.3.0] - 2020-11-02
12
+ ### Added
13
+ - Added support for `belongs_to optional:`.
14
+ If given, it is passed through to `ActiveRecord`'s `belong_to`.
15
+ If not given in Rails 5+, the `optional:` value is set equal to the `null:` value (default: `false`) and that
16
+ is passed to `ActiveRecord`'s `belong_to`.
17
+ Similarly, if `null:` is not given, it is inferred from `optional:`.
18
+ If both are given, their values are respected, even if contradictory;
19
+ this is a legitimate case when migrating to/from an optional association.
20
+ - Added a new callback `before_generating_migration` to the `Migrator` that can be
21
+ defined in order to custom load more models that might be missed by `eager_load!`
22
+ ### Fixed
23
+ - 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).
24
+
25
+ ## [0.2.0] - 2020-10-26
8
26
  ### Added
9
27
  - Automatically eager_load! all Rails::Engines before generating migrations.
10
28
 
11
29
  ### Changed
12
30
  - Changed tests from rdoctest to rspec.
13
31
 
14
- ## [0.1.3] - Unreleased
32
+ ### Fixed
33
+ - Fixed a bug where `:text limit: 0xffff_ffff` (max size) was omitted from migrations.
34
+ - Fixed a bug where `:bigint` foreign keys were omitted from the migration.
35
+
36
+ ## [0.1.3] - 2020-10-08
15
37
  ### Changed
16
38
  - Updated the `always_ignore_tables` list in `Migrator` to access Rails metadata table names
17
39
  using the appropriate Rails configuration attributes.
@@ -25,6 +47,8 @@ using the appropriate Rails configuration attributes.
25
47
  ### Added
26
48
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
27
49
 
50
+ [0.3.1]: https://github.com/Invoca/declare_schema/compare/v0.3.0...v0.3.1
51
+ [0.3.0]: https://github.com/Invoca/declare_schema/compare/v0.2.0...v0.3.0
28
52
  [0.2.0]: https://github.com/Invoca/declare_schema/compare/v0.1.3...v0.2.0
29
53
  [0.1.3]: https://github.com/Invoca/declare_schema/compare/v0.1.2...v0.1.3
30
54
  [0.1.2]: https://github.com/Invoca/declare_schema/compare/v0.1.1...v0.1.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.2.0.pre.1)
4
+ declare_schema (0.3.1)
5
5
  rails (>= 4.2)
6
6
 
7
7
  GEM
@@ -54,7 +54,7 @@ GEM
54
54
  thor (>= 0.14.0)
55
55
  arel (9.0.0)
56
56
  ast (2.4.1)
57
- bootsnap (1.4.8)
57
+ bootsnap (1.5.0)
58
58
  msgpack (~> 1.0)
59
59
  builder (3.2.4)
60
60
  byebug (11.1.3)
@@ -134,19 +134,19 @@ GEM
134
134
  actionpack (>= 5.0)
135
135
  railties (>= 5.0)
136
136
  rexml (3.2.4)
137
- rspec (3.9.0)
138
- rspec-core (~> 3.9.0)
139
- rspec-expectations (~> 3.9.0)
140
- rspec-mocks (~> 3.9.0)
141
- rspec-core (3.9.2)
142
- rspec-support (~> 3.9.3)
143
- rspec-expectations (3.9.2)
137
+ rspec (3.10.0)
138
+ rspec-core (~> 3.10.0)
139
+ rspec-expectations (~> 3.10.0)
140
+ rspec-mocks (~> 3.10.0)
141
+ rspec-core (3.10.0)
142
+ rspec-support (~> 3.10.0)
143
+ rspec-expectations (3.10.0)
144
144
  diff-lcs (>= 1.2.0, < 2.0)
145
- rspec-support (~> 3.9.0)
146
- rspec-mocks (3.9.1)
145
+ rspec-support (~> 3.10.0)
146
+ rspec-mocks (3.10.0)
147
147
  diff-lcs (>= 1.2.0, < 2.0)
148
- rspec-support (~> 3.9.0)
149
- rspec-support (3.9.3)
148
+ rspec-support (~> 3.10.0)
149
+ rspec-support (3.10.0)
150
150
  rubocop (0.91.0)
151
151
  parallel (~> 1.10)
152
152
  parser (>= 2.7.1.1)
data/README.md CHANGED
@@ -50,6 +50,26 @@ Migration filename: [<enter>=declare_schema_migration_1|<custom_name>]: add_comp
50
50
  ```
51
51
  Note that the migration generator is interactive -- it can't tell the difference between renaming something vs. adding one thing and removing another, so sometimes it will ask you to clarify.
52
52
 
53
+ ## Migrator Configuration
54
+
55
+ The following configuration options are available for the gem and can be used
56
+ during the initialization of your Rails application.
57
+
58
+ ### before_generating_migration callback
59
+
60
+ During the initializtion process for generating migrations, `DeclareSchema` will
61
+ trigger the `eager_load!` on the `Rails` application and all `Rails::Engine`s loaded
62
+ into scope. If you need to generate migrations for models that aren't automatically loaded by `eager_load!`,
63
+ load them in the `before_generating_migration` block.
64
+
65
+ **Example Configuration**
66
+
67
+ ```ruby
68
+ DeclareSchema::Migration::Migrator.before_generating_migration do
69
+ require 'lib/some/hidden/models.rb'
70
+ end
71
+ ```
72
+
53
73
  ## Installing
54
74
 
55
75
  Install the `DeclareSchema` gem directly:
@@ -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, *args, &block)
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)
118
110
  column_options = {}
119
- column_options[:null] = options.delete(:null) || false
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,30 @@ 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)
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
- super(name, *args, &block).tap do |_bt|
134
- refl = reflections[name.to_s] or raise "Couldn't find reflection #{name} in #{reflections.keys}"
135
- fkey = refl.foreign_key
136
- declare_field(fkey.to_sym, :integer, column_options)
137
- if refl.options[:polymorphic]
138
- foreign_type = options[:foreign_type] || "#{name}_type"
139
- declare_polymorphic_type_field(foreign_type, column_options)
140
- index([foreign_type, fkey], index_options) if index_options[:name] != false
141
- else
142
- index(fkey, index_options) if index_options[:name] != false
143
- options[:constraint_name] = options
144
- constraint(fkey, fk_options) if fk_options[:constraint_name] != false
145
- end
136
+
137
+ if Rails::VERSION::MAJOR >= 5
138
+ super
139
+ else
140
+ super(name, scope, options.except(:optional))
141
+ end
142
+
143
+ refl = reflections[name.to_s] or raise "Couldn't find reflection #{name} in #{reflections.keys}"
144
+ fkey = refl.foreign_key or raise "Couldn't find foreign_key for #{name} in #{refl.inspect}"
145
+ declare_field(fkey.to_sym, :integer, column_options)
146
+ if refl.options[:polymorphic]
147
+ foreign_type = options[:foreign_type] || "#{name}_type"
148
+ declare_polymorphic_type_field(foreign_type, column_options)
149
+ index([foreign_type, fkey], index_options) if index_options[:name] != false
150
+ else
151
+ index(fkey, index_options) if index_options[:name] != false
152
+ constraint(fkey, fk_options) if fk_options[:constraint_name] != false
146
153
  end
147
154
  end
148
155
 
@@ -53,7 +53,7 @@ module DeclareSchema
53
53
  @options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit] || MYSQL_LONGTEXT_LIMIT)
54
54
  end
55
55
  when :string
56
- @options[:limit] or raise "limit must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want 255?"
56
+ @options[:limit] or raise "limit must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want `limit: 255`?"
57
57
  end
58
58
  @position = position_option || model.field_specs.length
59
59
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.2.0.pre.1"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -69,12 +69,14 @@ module Generators
69
69
  class Migrator
70
70
  class Error < RuntimeError; end
71
71
 
72
- @ignore_models = []
73
- @ignore_tables = []
74
- @active_record_class = ActiveRecord::Base
72
+ @ignore_models = []
73
+ @ignore_tables = []
74
+ @before_generating_migration_callback = nil
75
+ @active_record_class = ActiveRecord::Base
75
76
 
76
77
  class << self
77
78
  attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints, :active_record_class
79
+ attr_reader :before_generating_migration_callback
78
80
 
79
81
  def active_record_class
80
82
  @active_record_class.is_a?(Class) or @active_record_class = @active_record_class.to_s.constantize
@@ -96,6 +98,25 @@ module Generators
96
98
  def connection
97
99
  ActiveRecord::Base.connection
98
100
  end
101
+
102
+ def fix_native_types(types)
103
+ case connection.class.name
104
+ when /mysql/i
105
+ types[:integer][:limit] ||= 11
106
+ types[:text][:limit] ||= 0xffff
107
+ types[:binary][:limit] ||= 0xffff
108
+ end
109
+ types
110
+ end
111
+
112
+ def native_types
113
+ @native_types ||= fix_native_types(connection.native_database_types)
114
+ end
115
+
116
+ def before_generating_migration(&block)
117
+ block or raise ArgumentError, 'A block is required when setting the before_generating_migration callback'
118
+ @before_generating_migration_callback = block
119
+ end
99
120
  end
100
121
 
101
122
  def initialize(ambiguity_resolver = {})
@@ -106,14 +127,12 @@ module Generators
106
127
 
107
128
  attr_accessor :renames
108
129
 
109
- # TODO: Add an application callback (maybe an initializer in a special group?) that
110
- # the application can use to load other models that live in the database, to support DeclareSchema migrations
111
- # for them.
112
130
  def load_rails_models
113
131
  ActiveRecord::Migration.verbose = false
114
132
 
115
133
  Rails.application.eager_load!
116
134
  Rails::Engine.subclasses.each(&:eager_load!)
135
+ self.class.before_generating_migration_callback&.call
117
136
  end
118
137
 
119
138
  # Returns an array of model classes that *directly* extend
@@ -129,22 +148,6 @@ module Generators
129
148
  self.class.connection
130
149
  end
131
150
 
132
- class << self
133
- def fix_native_types(types)
134
- case connection.class.name
135
- when /mysql/i
136
- types[:integer][:limit] ||= 11
137
- types[:text][:limit] ||= 0xffff
138
- types[:binary][:limit] ||= 0xffff
139
- end
140
- types
141
- end
142
-
143
- def native_types
144
- @native_types ||= fix_native_types(connection.native_database_types)
145
- end
146
- end
147
-
148
151
  def native_types
149
152
  self.class.native_types
150
153
  end
@@ -410,9 +413,9 @@ module Generators
410
413
  col_name = old_names[c] || c
411
414
  col = db_columns[col_name]
412
415
  spec = model.field_specs[c]
413
- if spec.different_to?(col) # TODO: DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
416
+ if spec.different_to?(col) # TODO: TECH-4814 DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
414
417
  change_spec = fk_field_options(model, c)
415
- change_spec[:limit] = spec.limit if (spec.sql_type != :text ||
418
+ change_spec[:limit] ||= spec.limit if (spec.sql_type != :text ||
416
419
  ::DeclareSchema::Model::FieldSpec.mysql_text_limits?) &&
417
420
  (spec.limit || col.limit)
418
421
  change_spec[:precision] = spec.precision unless spec.precision.nil?
@@ -518,8 +521,7 @@ module Generators
518
521
  next if k == :null && v == true
519
522
  end
520
523
 
521
- next if k == :limit && type == :text &&
522
- (!::DeclareSchema::Model::FieldSpec.mysql_text_limits? || v == ::DeclareSchema::Model::FieldSpec::MYSQL_LONGTEXT_LIMIT)
524
+ next if k == :limit && type == :text && !::DeclareSchema::Model::FieldSpec.mysql_text_limits?
523
525
 
524
526
  if k.is_a?(Symbol)
525
527
  "#{k}: #{v.inspect}"
@@ -530,10 +532,20 @@ module Generators
530
532
  end
531
533
 
532
534
  def fk_field_options(model, field_name)
533
- if (foreign_key = model.constraint_specs.find { |fk| field_name == fk.foreign_key.to_s }) && (parent_table = foreign_key.parent_table_name)
535
+ foreign_key = model.constraint_specs.find { |fk| field_name == fk.foreign_key.to_s }
536
+ if foreign_key && (parent_table = foreign_key.parent_table_name)
534
537
  parent_columns = connection.columns(parent_table) rescue []
535
- pk_column = parent_columns.find { |column| column.name == "id" } # right now foreign keys assume id is the target
536
- pk_limit = pk_column ? pk_column.cast_type.limit : 8
538
+ pk_limit =
539
+ if (pk_column = parent_columns.find { |column| column.name.to_s == "id" }) # right now foreign keys assume id is the target
540
+ if Rails::VERSION::MAJOR <= 4
541
+ pk_column.cast_type.limit
542
+ else
543
+ pk_column.limit
544
+ end
545
+ else
546
+ 8
547
+ end
548
+
537
549
  { limit: pk_limit }
538
550
  else
539
551
  {}
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration<%= ('[4.2]' if Rails::VERSION::MAJOR >= 5) %>
1
+ class <%= @migration_class_name %> < (Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)
2
2
  def self.up
3
3
  <%= @up %>
4
4
  end
@@ -10,14 +10,14 @@ RSpec.describe 'DeclareSchema API' do
10
10
 
11
11
  describe 'example models' do
12
12
  it 'generates a model' do
13
- expect(system("bundle exec rails generate declare_schema:model advert title:string body:text")).to be_truthy
13
+ generate_model 'advert', 'title:string', 'body:text'
14
14
 
15
15
  # The above will generate the test, fixture and a model file like this:
16
16
  # model_declaration = Rails::Generators.invoke('declare_schema:model', ['advert2', 'title:string', 'body:text'])
17
17
  # expect(model_declaration.first).to eq([["Advert"], nil, "app/models/advert.rb", nil,
18
18
  # [["AdvertTest"], "test/models/advert_test.rb", nil, "test/fixtures/adverts.yml"]])
19
19
 
20
- expect(File.read("#{TESTAPP_PATH}/app/models/advert.rb")).to eq(<<~EOS)
20
+ expect_model_definition_to_eq('advert', <<~EOS)
21
21
  class Advert < #{active_record_base_class}
22
22
 
23
23
  fields do
@@ -27,7 +27,8 @@ RSpec.describe 'DeclareSchema API' do
27
27
 
28
28
  end
29
29
  EOS
30
- system("rm -rf #{TESTAPP_PATH}/app/models/advert2.rb #{TESTAPP_PATH}/test/models/advert2.rb #{TESTAPP_PATH}/test/fixtures/advert2.rb")
30
+
31
+ clean_up_model('advert2')
31
32
 
32
33
  # The migration generator uses this information to create a migration.
33
34
  # The following creates and runs the migration:
@@ -36,13 +37,11 @@ RSpec.describe 'DeclareSchema API' do
36
37
 
37
38
  # We're now ready to start demonstrating the API
38
39
 
39
- Rails.application.config.autoload_paths += ["#{TESTAPP_PATH}/app/models"]
40
-
41
- $LOAD_PATH << "#{TESTAPP_PATH}/app/models"
40
+ load_models
42
41
 
43
42
  unless Rails::VERSION::MAJOR >= 6
44
43
  # TODO: get this to work on Travis for Rails 6
45
- Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
44
+ generate_migrations '-n', '-m'
46
45
  end
47
46
 
48
47
  require 'advert'
@@ -6,22 +6,20 @@ RSpec.describe 'DeclareSchema Migration Generator' do
6
6
  end
7
7
 
8
8
  it "generates nested models" do
9
- Rails::Generators.invoke('declare_schema:model', %w[alpha/beta one:string two:integer])
9
+ generate_model 'alpha/beta', 'one:string', 'two:integer'
10
10
 
11
- expect(File.exist?('app/models/alpha/beta.rb')).to be_truthy
12
-
13
- expect(File.read('app/models/alpha/beta.rb')).to eq(<<~EOS)
11
+ expect_model_definition_to_eq('alpha/beta', <<~EOS)
14
12
  class Alpha::Beta < #{active_record_base_class}
15
-
13
+
16
14
  fields do
17
15
  one :string, limit: 255
18
16
  two :integer
19
17
  end
20
-
18
+
21
19
  end
22
20
  EOS
23
21
 
24
- expect(File.read('app/models/alpha.rb')).to eq(<<~EOS)
22
+ expect_model_definition_to_eq('alpha', <<~EOS)
25
23
  module Alpha
26
24
  def self.table_name_prefix
27
25
  'alpha_'
@@ -29,9 +27,9 @@ RSpec.describe 'DeclareSchema Migration Generator' do
29
27
  end
30
28
  EOS
31
29
 
32
- expect(File.read('test/models/alpha/beta_test.rb')).to eq(<<~EOS)
30
+ expect_test_definition_to_eq('alpha/beta', <<~EOS)
33
31
  require 'test_helper'
34
-
32
+
35
33
  class Alpha::BetaTest < ActiveSupport::TestCase
36
34
  # test "the truth" do
37
35
  # assert true
@@ -39,7 +37,50 @@ RSpec.describe 'DeclareSchema Migration Generator' do
39
37
  end
40
38
  EOS
41
39
 
42
- expect(File.exist?('test/fixtures/alpha/beta.yml')).to be_truthy
40
+ case Rails::VERSION::MAJOR
41
+ when 4
42
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
43
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
44
+
45
+ # This model initially had no columns defined. If you add columns to the
46
+ # model remove the '{}' from the fixture names and add the columns immediately
47
+ # below each fixture, per the syntax in the comments below
48
+ #
49
+ one: {}
50
+ # column: value
51
+ #
52
+ two: {}
53
+ # column: value
54
+ EOS
55
+ when 5
56
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
57
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
58
+
59
+ # This model initially had no columns defined. If you add columns to the
60
+ # model remove the '{}' from the fixture names and add the columns immediately
61
+ # below each fixture, per the syntax in the comments below
62
+ #
63
+ one: {}
64
+ # column: value
65
+ #
66
+ two: {}
67
+ # column: value
68
+ EOS
69
+ when 6
70
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
71
+ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
72
+
73
+ # This model initially had no columns defined. If you add columns to the
74
+ # model remove the '{}' from the fixture names and add the columns immediately
75
+ # below each fixture, per the syntax in the comments below
76
+ #
77
+ one: {}
78
+ # column: value
79
+ #
80
+ two: {}
81
+ # column: value
82
+ EOS
83
+ end
43
84
 
44
85
  $LOAD_PATH << "#{TESTAPP_PATH}/app/models"
45
86