declare_schema 0.2.0.pre.1 → 0.3.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 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