declare_schema 0.7.1 → 0.8.0.pre.5

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: 6317ef2d7c038120278bbff6547c042a3418369d1e0bd8527f10ab57d58aef62
4
- data.tar.gz: 66ce3ad03f6fde8365b287deb682d85b4fd7e983d283447af769aed0ade61571
3
+ metadata.gz: c8a020c30e79336f89851e73eaabbb8dcdfd2affed27883e7d4fe06944f76436
4
+ data.tar.gz: e17f1fe94f3dc8188469df87679ee09c955765f2a5ac45354928e6664c026bae
5
5
  SHA512:
6
- metadata.gz: 1a87ecf0479b94324f43c42d309f254e8a3ca10f3766cc90a756b94505cb559406871eee47a58fa376640ad85acda3e81d771cb03ab92dc08f5253d81bd88079
7
- data.tar.gz: 4cd449cb9543819dd78e51267357f676a7439c89f2ad021f59a5d0f49664a8c8b8141eeebde4184e3eaf1f3f616dfef8a7092f59cc6382c73504de5ffeb41dc3
6
+ metadata.gz: ccf0846c28e2e0a7d20db08d3ad925fc22d7ed1d5a3cb2f75df3585ce4b42a00663f2550b17ce648d9a86e7e58a7d0a367f43ef21138f6fc838ea8f4b39ec8db
7
+ data.tar.gz: a5fa4637da88f364ec303ebe0d615e6e0e99caef81e7965d753cf04d8cb33b787efef874ef5b71db3e6a0f1f850087b617a7f6f811cc9fc8d307f7d01562da25
data/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@ 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.8.0] - UNRELEASED
8
+ ### Removed
9
+ - Removed `sql_type` that was confusing because it was actually the same as `type` (ex: :string) and not
10
+ in fact the SQL type (ex: ``varchar(255)'`).
11
+
7
12
  ## [0.7.1] - 2021-02-17
8
13
  ### Fixed
9
14
  - Exclude unknown options from FieldSpec#sql_options and #schema_attributes.
@@ -125,6 +130,7 @@ using the appropriate Rails configuration attributes.
125
130
  ### Added
126
131
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
127
132
 
133
+ [0.8.0]: https://github.com/Invoca/declare_schema/compare/v0.7.1...v0.8.0
128
134
  [0.7.1]: https://github.com/Invoca/declare_schema/compare/v0.7.0...v0.7.1
129
135
  [0.7.0]: https://github.com/Invoca/declare_schema/compare/v0.6.3...v0.7.0
130
136
  [0.6.4]: https://github.com/Invoca/declare_schema/compare/v0.6.3...v0.6.4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.7.1)
4
+ declare_schema (0.8.0.pre.5)
5
5
  rails (>= 4.2)
6
6
 
7
7
  GEM
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rails'
4
+
3
5
  require 'declare_schema/extensions/module'
4
6
 
5
7
  module DeclareSchema
@@ -99,8 +101,29 @@ module DeclareSchema
99
101
  end
100
102
  end
101
103
 
102
- def primary_key
103
- super || 'id'
104
+ if ::Rails::VERSION::MAJOR < 5
105
+ def primary_key
106
+ super || 'id'
107
+ end
108
+ end
109
+
110
+ # returns the primary key (String) as declared with primary_key =
111
+ # unlike the `primary_key` method, DOES NOT query the database to find the actual primary key in use right now
112
+ # if no explicit primary key set, returns the default_defined_primary_key
113
+ def defined_primary_key
114
+ if defined?(@primary_key)
115
+ @primary_key&.to_s
116
+ end || default_defined_primary_key
117
+ end
118
+
119
+ # if this is a derived class, returns the base class's defined_primary_key
120
+ # otherwise, returns 'id'
121
+ def default_defined_primary_key
122
+ if self == base_class
123
+ 'id'
124
+ else
125
+ base_class.defined_primary_key
126
+ end
104
127
  end
105
128
 
106
129
  private
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- class UnknownSqlTypeError < RuntimeError; end
4
+ class UnknownTypeError < RuntimeError; end
5
5
 
6
6
  module Model
7
7
  # This class is a wrapper for the ActiveRecord::...::Column class
8
8
  class Column
9
9
  class << self
10
10
  def native_type?(type)
11
- type != :primary_key && native_types.has_key?(type)
11
+ type != :primary_key && (native_types.empty? || native_types[type]) # empty will happen with NullDBAdapter used in assets:precompile
12
12
  end
13
13
 
14
14
  # MySQL example:
@@ -48,27 +48,17 @@ module DeclareSchema
48
48
  end
49
49
  end
50
50
 
51
- def sql_type(type)
52
- if native_type?(type)
53
- type
54
- else
55
- if (field_class = DeclareSchema.to_class(type))
56
- field_class::COLUMN_TYPE
57
- end or raise UnknownSqlTypeError, "#{type.inspect} for type #{type.inspect}"
58
- end
59
- end
60
-
61
- def deserialize_default_value(column, sql_type, default_value)
62
- sql_type or raise ArgumentError, "must pass sql_type; got #{sql_type.inspect}"
51
+ def deserialize_default_value(column, type, default_value)
52
+ type or raise ArgumentError, "must pass type; got #{type.inspect}"
63
53
 
64
54
  case Rails::VERSION::MAJOR
65
55
  when 4
66
56
  # TODO: Delete this Rails 4 support ASAP! This could be wrong, since it's using the type of the old column...which
67
- # might be getting migrated to a new type. We should be using just sql_type as below. -Colin
57
+ # might be getting migrated to a new type. We should be using just type as below. -Colin
68
58
  column.type_cast_from_database(default_value)
69
59
  else
70
- cast_type = ActiveRecord::Base.connection.send(:lookup_cast_type, sql_type) or
71
- raise "cast_type not found for #{sql_type}"
60
+ cast_type = ActiveRecord::Base.connection.send(:lookup_cast_type, type) or
61
+ raise "cast_type not found for #{type}"
72
62
  cast_type.deserialize(default_value)
73
63
  end
74
64
  end
@@ -103,13 +93,14 @@ module DeclareSchema
103
93
  end
104
94
  end
105
95
 
106
- attr_reader :sql_type
96
+ attr_reader :type
107
97
 
108
98
  def initialize(model, current_table_name, column)
109
99
  @model = model or raise ArgumentError, "must pass model"
110
100
  @current_table_name = current_table_name or raise ArgumentError, "must pass current_table_name"
111
101
  @column = column or raise ArgumentError, "must pass column"
112
- @sql_type = self.class.sql_type(@column.type)
102
+ @type = @column.type
103
+ self.class.native_type?(@type) or raise UnknownTypeError, "#{@type.inspect}"
113
104
  end
114
105
 
115
106
  SCHEMA_KEYS = [:type, :limit, :precision, :scale, :null, :default].freeze
@@ -120,7 +111,7 @@ module DeclareSchema
120
111
  value =
121
112
  case key
122
113
  when :default
123
- self.class.deserialize_default_value(@column, @sql_type, @column.default)
114
+ self.class.deserialize_default_value(@column, @type, @column.default)
124
115
  else
125
116
  col_value = @column.send(key)
126
117
  if col_value.nil? && (native_type = self.class.native_types[@column.type])
@@ -33,7 +33,7 @@ module DeclareSchema
33
33
  end
34
34
  end
35
35
 
36
- attr_reader :model, :name, :type, :sql_type, :position, :options, :sql_options
36
+ attr_reader :model, :name, :type, :position, :options, :sql_options
37
37
 
38
38
  TYPE_SYNONYMS = { timestamp: :datetime }.freeze # TODO: drop this synonym. -Colin
39
39
 
@@ -47,11 +47,9 @@ module DeclareSchema
47
47
  end
48
48
 
49
49
  def initialize(model, name, type, position: 0, **options)
50
- # TODO: TECH-5116
51
- # Invoca change - searching for the primary key was causing an additional database read on every model load. Assume
52
- # "id" which works for invoca.
53
- # raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key
54
- name == "id" and raise ArgumentError, "you cannot provide a field spec for the primary key"
50
+ defined_primary_key = model.defined_primary_key
51
+
52
+ name.to_s == defined_primary_key and raise ArgumentError, "you may not provide a field spec for the primary key #{name.inspect}"
55
53
 
56
54
  @model = model
57
55
  @name = name.to_sym
@@ -62,13 +60,13 @@ module DeclareSchema
62
60
 
63
61
  @options.has_key?(:null) or @options[:null] = false
64
62
 
65
- case type
63
+ case @type
66
64
  when :text
67
65
  if self.class.mysql_text_limits?
68
66
  @options[:default].nil? or raise MysqlTextMayNotHaveDefault, "when using MySQL, non-nil default may not be given for :text field #{model}##{@name}"
69
67
  @options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit] || MYSQL_LONGTEXT_LIMIT)
70
68
  else
71
- @options[:limit] = nil
69
+ @options.delete(:limit)
72
70
  end
73
71
  when :string
74
72
  @options[:limit] or raise "limit: must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want `limit: 255`?"
@@ -77,24 +75,23 @@ module DeclareSchema
77
75
  @options[:limit] = 8
78
76
  end
79
77
 
80
- # TODO: Do we really need to support a :sql_type option? Ideally, drop it. -Colin
81
- @sql_type = @options.delete(:sql_type) || Column.sql_type(@type)
78
+ Column.native_type?(@type) or raise UnknownTypeError, "#{@type.inspect} not found in #{Column.native_types.inspect} for adapter #{ActiveRecord::Base.connection.class.name}"
82
79
 
83
- if @sql_type.in?([:string, :text, :binary, :varbinary, :integer, :enum])
84
- @options[:limit] ||= Column.native_types[@sql_type][:limit]
80
+ if @type.in?([:string, :text, :binary, :varbinary, :integer, :enum])
81
+ @options[:limit] ||= Column.native_types.dig(@type, :limit)
85
82
  else
86
- @sql_type != :decimal && @options.has_key?(:limit) and warn("unsupported limit: for SQL type #{@sql_type} in field #{model}##{@name}")
83
+ @type != :decimal && @options.has_key?(:limit) and warn("unsupported limit: for SQL type #{@type} in field #{model}##{@name}")
87
84
  @options.delete(:limit)
88
85
  end
89
86
 
90
- if @sql_type == :decimal
87
+ if @type == :decimal
91
88
  @options[:precision] or warn("precision: required for :decimal type in field #{model}##{@name}")
92
89
  @options[:scale] or warn("scale: required for :decimal type in field #{model}##{@name}")
93
90
  else
94
- if @sql_type != :datetime
95
- @options.has_key?(:precision) and warn("precision: only allowed for :decimal type or :datetime for SQL type #{@sql_type} in field #{model}##{@name}")
91
+ if @type != :datetime
92
+ @options.has_key?(:precision) and warn("precision: only allowed for :decimal type or :datetime for SQL type #{@type} in field #{model}##{@name}")
96
93
  end
97
- @options.has_key?(:scale) and warn("scale: only allowed for :decimal type for SQL type #{@sql_type} in field #{model}##{@name}")
94
+ @options.has_key?(:scale) and warn("scale: only allowed for :decimal type for SQL type #{@type} in field #{model}##{@name}")
98
95
  end
99
96
 
100
97
  if @type.in?([:text, :string])
@@ -106,8 +103,8 @@ module DeclareSchema
106
103
  @options.delete(:collation)
107
104
  end
108
105
  else
109
- @options[:charset] and warn("charset may only given for :string and :text fields for SQL type #{@sql_type} in field #{model}##{@name}")
110
- @options[:collation] and warne("collation may only given for :string and :text fields for SQL type #{@sql_type} in field #{model}##{@name}")
106
+ @options[:charset] and warn("charset may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
107
+ @options[:collation] and warne("collation may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
111
108
  end
112
109
 
113
110
  @options = Hash[@options.sort_by { |k, _v| OPTION_INDEXES[k] || 9999 }]
@@ -120,7 +117,7 @@ module DeclareSchema
120
117
  # omits keys with nil values
121
118
  def schema_attributes(col_spec)
122
119
  @sql_options.merge(type: @type).tap do |attrs|
123
- attrs[:default] = Column.deserialize_default_value(col_spec, @sql_type, attrs[:default])
120
+ attrs[:default] = Column.deserialize_default_value(col_spec, @type, attrs[:default])
124
121
  end.compact
125
122
  end
126
123
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'index_definition'
4
+
3
5
  module DeclareSchema
4
6
  module Model
5
7
  class ForeignKeyDefinition
@@ -15,10 +17,10 @@ module DeclareSchema
15
17
  @child_table = model.table_name # unless a table rename, which would happen when a class is renamed??
16
18
  @parent_table_name = options[:parent_table]&.to_s
17
19
  @foreign_key_name = options[:foreign_key]&.to_s || @foreign_key
18
- @index_name = options[:index_name]&.to_s || model.connection.index_name(model.table_name, column: @foreign_key_name)
19
20
 
20
- # Empty constraint lets mysql generate the name
21
- @constraint_name = options[:constraint_name]&.to_s || @index_name&.to_s || ''
21
+ @constraint_name = options[:constraint_name]&.to_s ||
22
+ options[:index_name]&.to_s ||
23
+ IndexDefinition.index_name(@foreign_key_name)
22
24
  @on_delete_cascade = options[:dependent] == :delete
23
25
  end
24
26
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeclareSchema
4
+ module Model
5
+ class HabtmModelShim
6
+ class << self
7
+ def from_reflection(refl)
8
+ join_table = refl.join_table
9
+ foreign_keys_and_classes = [
10
+ [refl.foreign_key.to_s, refl.active_record],
11
+ [refl.association_foreign_key.to_s, refl.class_name.constantize]
12
+ ].sort { |a, b| a.first <=> b.first }
13
+ foreign_keys = foreign_keys_and_classes.map(&:first)
14
+ foreign_key_classes = foreign_keys_and_classes.map(&:last)
15
+ # this may fail in weird ways if HABTM is running across two DB connections (assuming that's even supported)
16
+ # figure that anybody who sets THAT up can deal with their own migrations...
17
+ connection = refl.active_record.connection
18
+
19
+ new(join_table, foreign_keys, foreign_key_classes, connection)
20
+ end
21
+ end
22
+
23
+ attr_reader :join_table, :foreign_keys, :foreign_key_classes, :connection
24
+
25
+ def initialize(join_table, foreign_keys, foreign_key_classes, connection)
26
+ @join_table = join_table
27
+ @foreign_keys = foreign_keys
28
+ @foreign_key_classes = foreign_key_classes
29
+ @connection = connection
30
+ end
31
+
32
+ def table_options
33
+ {}
34
+ end
35
+
36
+ def table_name
37
+ join_table
38
+ end
39
+
40
+ def field_specs
41
+ foreign_keys.each_with_index.each_with_object({}) do |(v, position), result|
42
+ result[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: position, null: false)
43
+ end
44
+ end
45
+
46
+ def primary_key
47
+ false # no single-column primary key in database
48
+ end
49
+
50
+ def defined_primary_key
51
+ false # no single-column primary key declared
52
+ end
53
+
54
+ def index_definitions_with_primary_key
55
+ [
56
+ IndexDefinition.new(self, foreign_keys, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME),
57
+ IndexDefinition.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
58
+ ]
59
+ end
60
+
61
+ alias_method :index_definitions, :index_definitions_with_primary_key
62
+
63
+ def ignore_indexes
64
+ []
65
+ end
66
+
67
+ def constraint_specs
68
+ [
69
+ ForeignKeyDefinition.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
70
+ ForeignKeyDefinition.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
71
+ ]
72
+ end
73
+ end
74
+ end
75
+ end
@@ -19,7 +19,7 @@ module DeclareSchema
19
19
  @table = options.delete(:table_name) || model.table_name
20
20
  @fields = Array.wrap(fields).map(&:to_s)
21
21
  @explicit_name = options[:name] unless options.delete(:allow_equivalent)
22
- @name = options.delete(:name) || model.connection.index_name(table, column: @fields).gsub(/index.*_on_/, 'on_')
22
+ @name = options.delete(:name) || self.class.index_name(@fields)
23
23
  @unique = options.delete(:unique) || name == PRIMARY_KEY_NAME || false
24
24
 
25
25
  if @name.length > MYSQL_INDEX_NAME_MAX_LENGTH
@@ -60,6 +60,10 @@ module DeclareSchema
60
60
  index_definitions
61
61
  end
62
62
 
63
+ def index_name(columns)
64
+ "on_#{Array(columns).join("_and_")}"
65
+ end
66
+
63
67
  private
64
68
 
65
69
  # This is the old approach which is still needed for MySQL in Rails 4 and SQLite
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.7.1"
4
+ VERSION = "0.8.0.pre.5"
5
5
  end
@@ -96,7 +96,7 @@ module DeclareSchema
96
96
  end
97
97
  end
98
98
  end
99
- rescue ::DeclareSchema::UnknownSqlTypeError => ex
99
+ rescue ::DeclareSchema::UnknownTypeError => ex
100
100
  say "Invalid field type: #{ex}"
101
101
  end
102
102
 
@@ -6,69 +6,6 @@ require 'active_record/connection_adapters/abstract_adapter'
6
6
  module Generators
7
7
  module DeclareSchema
8
8
  module Migration
9
- HabtmModelShim = Struct.new(:join_table, :foreign_keys, :foreign_key_classes, :connection) do
10
- class << self
11
- def from_reflection(refl)
12
- join_table = refl.join_table
13
- foreign_keys_and_classes = [
14
- [refl.foreign_key.to_s, refl.active_record],
15
- [refl.association_foreign_key.to_s, refl.class_name.constantize]
16
- ].sort { |a, b| a.first <=> b.first }
17
- foreign_keys = foreign_keys_and_classes.map(&:first)
18
- foreign_key_classes = foreign_keys_and_classes.map(&:last)
19
- # this may fail in weird ways if HABTM is running across two DB connections (assuming that's even supported)
20
- # figure that anybody who sets THAT up can deal with their own migrations...
21
- connection = refl.active_record.connection
22
-
23
- new(join_table, foreign_keys, foreign_key_classes, connection)
24
- end
25
- end
26
-
27
- def table_options
28
- {}
29
- end
30
-
31
- def table_name
32
- join_table
33
- end
34
-
35
- def table_exists?
36
- ActiveRecord::Migration.table_exists? table_name
37
- end
38
-
39
- def field_specs
40
- i = 0
41
- foreign_keys.each_with_object({}) do |v, result|
42
- result[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: i, null: false)
43
- i += 1
44
- end
45
- end
46
-
47
- def primary_key
48
- false # no single-column primary key
49
- end
50
-
51
- def index_definitions_with_primary_key
52
- [
53
- ::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME),
54
- ::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
55
- ]
56
- end
57
-
58
- alias_method :index_definitions, :index_definitions_with_primary_key
59
-
60
- def ignore_indexes
61
- []
62
- end
63
-
64
- def constraint_specs
65
- [
66
- ::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
67
- ::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
68
- ]
69
- end
70
- end
71
-
72
9
  class Migrator
73
10
  class Error < RuntimeError; end
74
11
 
@@ -266,7 +203,7 @@ module Generators
266
203
  end
267
204
  # generate shims for HABTM models
268
205
  habtm_tables.each do |name, refls|
269
- models_by_table_name[name] = HabtmModelShim.from_reflection(refls.first)
206
+ models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(refls.first)
270
207
  end
271
208
  model_table_names = models_by_table_name.keys
272
209
 
@@ -328,7 +265,7 @@ module Generators
328
265
  end
329
266
 
330
267
  def create_table(model)
331
- longest_field_name = model.field_specs.values.map { |f| f.sql_type.to_s.length }.max
268
+ longest_field_name = model.field_specs.values.map { |f| f.type.to_s.length }.max
332
269
  disable_auto_increment = model.respond_to?(:disable_auto_increment) && model.disable_auto_increment
333
270
  table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
334
271
  field_definitions = [
@@ -379,7 +316,7 @@ module Generators
379
316
  def create_field(field_spec, field_name_width)
380
317
  options = field_spec.sql_options.merge(fk_field_options(field_spec.model, field_spec.name))
381
318
  args = [field_spec.name.inspect] + format_options(options.compact)
382
- format("t.%-*s %s", field_name_width, field_spec.sql_type, args.join(', '))
319
+ format("t.%-*s %s", field_name_width, field_spec.type, args.join(', '))
383
320
  end
384
321
 
385
322
  def change_table(model, current_table_name)
@@ -417,7 +354,7 @@ module Generators
417
354
  args =
418
355
  if (spec = model.field_specs[c])
419
356
  options = spec.sql_options.merge(fk_field_options(model, c))
420
- [":#{spec.sql_type}", *format_options(options.compact)]
357
+ [":#{spec.type}", *format_options(options.compact)]
421
358
  else
422
359
  [":integer"]
423
360
  end
@@ -6,8 +6,8 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  RSpec.describe DeclareSchema::Model::FieldSpec do
9
- let(:model) { double('model', table_options: {}) }
10
- let(:col_spec) { double('col_spec', sql_type: 'varchar') }
9
+ let(:model) { double('model', table_options: {}, defined_primary_key: 'id') }
10
+ let(:col_spec) { double('col_spec', type: :string) }
11
11
 
12
12
  before do
13
13
  load File.expand_path('prepare_testapp.rb', __dir__)
@@ -22,6 +22,12 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
22
22
  subject = described_class.new(model, :price, :integer, anonymize_using: 'x', null: false, position: 0, limit: 4)
23
23
  expect(subject.options.keys).to eq([:limit, :null, :anonymize_using])
24
24
  end
25
+
26
+ it 'raises exception on unknown field type' do
27
+ expect do
28
+ described_class.new(model, :location, :lat_long, position: 0)
29
+ end.to raise_exception(::DeclareSchema::UnknownTypeError, /:lat_long not found in /)
30
+ end
25
31
  end
26
32
 
27
33
  describe '#schema_attributes' do
@@ -127,7 +133,7 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
127
133
  end
128
134
 
129
135
  describe 'default:' do
130
- let(:col_spec) { double('col_spec', sql_type: :integer) }
136
+ let(:col_spec) { double('col_spec', type: :integer) }
131
137
 
132
138
  it 'typecasts default value' do
133
139
  allow(col_spec).to receive(:type_cast_from_database) { |default| Integer(default) }
@@ -372,14 +372,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
372
372
 
373
373
  add_index :adverts, [:category_id], name: 'on_category_id'
374
374
 
375
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" if defined?(Mysql2)}
375
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" if defined?(Mysql2)}
376
376
  EOS
377
377
  .and migrate_down(<<~EOS.strip)
378
378
  remove_column :adverts, :category_id
379
379
 
380
380
  remove_index :adverts, name: :on_category_id rescue ActiveRecord::StatementInvalid
381
381
 
382
- #{"remove_foreign_key(\"adverts\", name: \"index_adverts_on_category_id\")\n" if defined?(Mysql2)}
382
+ #{"remove_foreign_key(\"adverts\", name: \"on_category_id\")\n" if defined?(Mysql2)}
383
383
  EOS
384
384
  )
385
385
 
@@ -400,8 +400,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
400
400
 
401
401
  add_index :adverts, [:c_id], name: 'on_c_id'
402
402
 
403
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
404
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
403
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
404
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
405
405
  EOS
406
406
  )
407
407
 
@@ -420,8 +420,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
420
420
  migrate_up(<<~EOS.strip)
421
421
  add_column :adverts, :category_id, :integer, limit: 8, null: false
422
422
 
423
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
424
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
423
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
424
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
425
425
  EOS
426
426
  )
427
427
 
@@ -442,8 +442,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
442
442
 
443
443
  add_index :adverts, [:category_id], name: 'my_index'
444
444
 
445
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
446
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
445
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
446
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
447
447
  EOS
448
448
  )
449
449
 
@@ -468,16 +468,16 @@ RSpec.describe 'DeclareSchema Migration Generator' do
468
468
  add_column :adverts, :updated_at, :datetime, null: true
469
469
  add_column :adverts, :lock_version, :integer#{lock_version_limit}, null: false, default: 1
470
470
 
471
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
472
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
471
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
472
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
473
473
  EOS
474
474
  .and migrate_down(<<~EOS.strip)
475
475
  remove_column :adverts, :created_at
476
476
  remove_column :adverts, :updated_at
477
477
  remove_column :adverts, :lock_version
478
478
 
479
- #{"remove_foreign_key(\"adverts\", name: \"index_adverts_on_category_id\")\n" +
480
- "remove_foreign_key(\"adverts\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
479
+ #{"remove_foreign_key(\"adverts\", name: \"on_category_id\")\n" +
480
+ "remove_foreign_key(\"adverts\", name: \"on_c_id\")" if defined?(Mysql2)}
481
481
  EOS
482
482
  )
483
483
 
@@ -501,8 +501,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
501
501
 
502
502
  add_index :adverts, [:title], name: 'on_title'
503
503
 
504
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
505
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
504
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
505
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
506
506
  EOS
507
507
  )
508
508
 
@@ -522,8 +522,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
522
522
 
523
523
  add_index :adverts, [:title], unique: true, name: 'on_title'
524
524
 
525
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
526
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
525
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
526
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
527
527
  EOS
528
528
  )
529
529
 
@@ -543,8 +543,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
543
543
 
544
544
  add_index :adverts, [:title], name: 'my_index'
545
545
 
546
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
547
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
546
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
547
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
548
548
  EOS
549
549
  )
550
550
 
@@ -562,8 +562,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
562
562
 
563
563
  add_index :adverts, [:title], name: 'on_title'
564
564
 
565
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
566
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
565
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
566
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
567
567
  EOS
568
568
  )
569
569
 
@@ -581,8 +581,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
581
581
 
582
582
  add_index :adverts, [:title], unique: true, name: 'my_index'
583
583
 
584
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
585
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
584
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
585
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
586
586
  EOS
587
587
  )
588
588
 
@@ -600,8 +600,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
600
600
 
601
601
  add_index :adverts, [:title, :category_id], name: 'on_title_and_category_id'
602
602
 
603
- #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
604
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")" if defined?(Mysql2)}
603
+ #{"add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
604
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")" if defined?(Mysql2)}
605
605
  EOS
606
606
  )
607
607
 
@@ -637,8 +637,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
637
637
  "add_index :ads, [:id], unique: true, name: 'PRIMARY'\n"
638
638
  elsif defined?(Mysql2)
639
639
  "execute \"ALTER TABLE ads DROP PRIMARY KEY, ADD PRIMARY KEY (id)\"\n\n" +
640
- "add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"index_adverts_on_category_id\")\n" +
641
- "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"index_adverts_on_c_id\")"
640
+ "add_foreign_key(\"adverts\", \"categories\", column: \"category_id\", name: \"on_category_id\")\n" +
641
+ "add_foreign_key(\"adverts\", \"categories\", column: \"c_id\", name: \"on_c_id\")"
642
642
  end}
643
643
  EOS
644
644
  .and migrate_down(<<~EOS.strip)
@@ -651,8 +651,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
651
651
  "add_index :adverts, [:id], unique: true, name: 'PRIMARY'\n"
652
652
  elsif defined?(Mysql2)
653
653
  "execute \"ALTER TABLE adverts DROP PRIMARY KEY, ADD PRIMARY KEY (id)\"\n\n" +
654
- "remove_foreign_key(\"adverts\", name: \"index_adverts_on_category_id\")\n" +
655
- "remove_foreign_key(\"adverts\", name: \"index_adverts_on_c_id\")"
654
+ "remove_foreign_key(\"adverts\", name: \"on_category_id\")\n" +
655
+ "remove_foreign_key(\"adverts\", name: \"on_c_id\")"
656
656
  end}
657
657
  EOS
658
658
  )
@@ -37,6 +37,11 @@ RSpec.describe DeclareSchema::Model::Column do
37
37
  expect(described_class.native_type?(type)).to be_falsey
38
38
  end
39
39
  end
40
+
41
+ it "is truthy when there's a NullDbAdapter (like for assets:precompile) that doesn't have any native types" do
42
+ allow(described_class).to receive(:native_types).and_return({})
43
+ expect(described_class.native_type?(:integer)).to be_truthy
44
+ end
40
45
  end
41
46
 
42
47
  describe '.native_types' do
@@ -59,26 +64,6 @@ RSpec.describe DeclareSchema::Model::Column do
59
64
  end
60
65
  end
61
66
 
62
- describe '.sql_type' do
63
- it 'returns the sql type for :string' do
64
- expect(described_class.sql_type(:string)).to eq(:string)
65
- end
66
-
67
- it 'returns the sql type for :integer' do
68
- expect(described_class.sql_type(:integer)).to match(:integer)
69
- end
70
-
71
- it 'returns the sql type for :datetime' do
72
- expect(described_class.sql_type(:datetime)).to eq(:datetime)
73
- end
74
-
75
- it 'raises UnknownSqlType' do
76
- expect do
77
- described_class.sql_type(:email)
78
- end.to raise_exception(::DeclareSchema::UnknownSqlTypeError, /:email for type :email/)
79
- end
80
- end
81
-
82
67
  describe '.deserialize_default_value' do
83
68
  require 'rails'
84
69
 
@@ -109,10 +94,11 @@ RSpec.describe DeclareSchema::Model::Column do
109
94
  end
110
95
  end
111
96
  let(:model) { ColumnTestModel }
97
+ let(:type) { :integer }
112
98
  let(:current_table_name) { model.table_name }
113
99
  let(:column) { double("ActiveRecord Column",
114
100
  name: 'count',
115
- type: :integer,
101
+ type: type,
116
102
  limit: nil,
117
103
  precision: nil,
118
104
  scale: nil,
@@ -122,9 +108,9 @@ RSpec.describe DeclareSchema::Model::Column do
122
108
  sql_type_metadata: {}) }
123
109
  subject { described_class.new(model, current_table_name, column) }
124
110
 
125
- describe '#sql_type' do
126
- it 'returns sql type' do
127
- expect(subject.sql_type).to match(/int/)
111
+ describe '#type' do
112
+ it 'returns type' do
113
+ expect(subject.type).to eq(type)
128
114
  end
129
115
  end
130
116
 
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ begin
6
+ require 'mysql2'
7
+ rescue LoadError
8
+ end
9
+
10
+ require_relative '../../../../lib/declare_schema/model/habtm_model_shim'
11
+
12
+ RSpec.describe DeclareSchema::Model::HabtmModelShim do
13
+ let(:join_table) { "parent_1_parent_2" }
14
+ let(:foreign_keys) { ["parent_1_id", "parent_2_id"] }
15
+ let(:foreign_key_classes) { [Parent1, Parent2] }
16
+
17
+ before do
18
+ load File.expand_path('../prepare_testapp.rb', __dir__)
19
+
20
+ class Parent1 < ActiveRecord::Base
21
+ self.table_name = "parent_1s"
22
+ end
23
+
24
+ class Parent2 < ActiveRecord::Base
25
+ self.table_name = "parent_2s"
26
+ end
27
+ end
28
+
29
+ describe 'class methods' do
30
+ describe '.from_reflection' do
31
+ let(:reflection) { double("reflection", join_table: join_table,
32
+ foreign_key: foreign_keys.first,
33
+ association_foreign_key: foreign_keys.last,
34
+ active_record: foreign_key_classes.first,
35
+ class_name: 'Parent1') }
36
+ it 'returns a new object' do
37
+ result = described_class.from_reflection(reflection)
38
+
39
+ expect(result).to be_a(described_class)
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'instance methods' do
45
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class, "connection") }
46
+
47
+ subject { described_class.new(join_table, foreign_keys, foreign_key_classes, connection) }
48
+
49
+ describe '#initialize' do
50
+ it 'stores initialization attributes' do
51
+ expect(subject.join_table).to eq(join_table)
52
+ expect(subject.foreign_keys).to eq(foreign_keys)
53
+ expect(subject.foreign_key_classes).to be(foreign_key_classes)
54
+ expect(subject.connection).to be(connection)
55
+ end
56
+ end
57
+
58
+ describe '#table_options' do
59
+ it 'returns empty hash' do
60
+ expect(subject.table_options).to eq({})
61
+ end
62
+ end
63
+
64
+ describe '#table_name' do
65
+ it 'returns join_table' do
66
+ expect(subject.table_name).to eq(join_table)
67
+ end
68
+ end
69
+
70
+ describe '#field_specs' do
71
+ it 'returns 2 field specs' do
72
+ result = subject.field_specs
73
+ expect(result.size).to eq(2), result.inspect
74
+
75
+ expect(result[foreign_keys.first]).to be_a(::DeclareSchema::Model::FieldSpec)
76
+ expect(result[foreign_keys.first].model).to eq(subject)
77
+ expect(result[foreign_keys.first].name.to_s).to eq(foreign_keys.first)
78
+ expect(result[foreign_keys.first].type).to eq(:integer)
79
+ expect(result[foreign_keys.first].position).to eq(0)
80
+
81
+ expect(result[foreign_keys.last]).to be_a(::DeclareSchema::Model::FieldSpec)
82
+ expect(result[foreign_keys.last].model).to eq(subject)
83
+ expect(result[foreign_keys.last].name.to_s).to eq(foreign_keys.last)
84
+ expect(result[foreign_keys.last].type).to eq(:integer)
85
+ expect(result[foreign_keys.last].position).to eq(1)
86
+ end
87
+ end
88
+
89
+ describe '#primary_key' do
90
+ it 'returns false' do
91
+ expect(subject.primary_key).to eq(false)
92
+ end
93
+ end
94
+
95
+ describe '#defined_primary_key' do
96
+ it 'returns false' do
97
+ expect(subject.defined_primary_key).to eq(false)
98
+ end
99
+ end
100
+
101
+ describe '#index_definitions_with_primary_key' do
102
+ it 'returns 2 index definitions' do
103
+ result = subject.index_definitions_with_primary_key
104
+ expect(result.size).to eq(2), result.inspect
105
+
106
+ expect(result.first).to be_a(::DeclareSchema::Model::IndexDefinition)
107
+ expect(result.first.name).to eq('PRIMARY')
108
+ expect(result.first.fields).to eq(['parent_1_id', 'parent_2_id'])
109
+ expect(result.first.unique).to be_truthy
110
+
111
+ expect(result.last).to be_a(::DeclareSchema::Model::IndexDefinition)
112
+ expect(result.last.name).to eq('on_parent_2_id')
113
+ expect(result.last.unique).to be_falsey
114
+ expect(result.last.fields).to eq(['parent_2_id'])
115
+ end
116
+ end
117
+
118
+ describe '#index_definitions' do
119
+ it 'returns index_definitions_with_primary_key' do
120
+ result = subject.index_definitions
121
+ expect(result.size).to eq(2), result.inspect
122
+ end
123
+ end
124
+
125
+ describe 'ignore_indexes' do
126
+ it 'returns empty array' do
127
+ expect(subject.ignore_indexes).to eq([])
128
+ end
129
+ end
130
+
131
+ describe '#constraint_specs' do
132
+ it 'returns 2 foreign keys' do
133
+ result = subject.constraint_specs
134
+ expect(result.size).to eq(2), result.inspect
135
+
136
+ expect(result.first).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
137
+ expect(result.first.foreign_key).to eq(foreign_keys.first)
138
+ expect(result.first.parent_table_name).to be(Parent1.table_name)
139
+ expect(result.first.on_delete_cascade).to be_truthy
140
+
141
+ expect(result.last).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
142
+ expect(result.last.foreign_key).to eq(foreign_keys.last)
143
+ expect(result.last.parent_table_name).to be(Parent2.table_name)
144
+ expect(result.last.on_delete_cascade).to be_truthy
145
+ end
146
+ end
147
+ end
148
+ end
@@ -58,7 +58,17 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
58
58
  end
59
59
  end
60
60
 
61
- describe 'class << self' do
61
+ describe 'class methods' do
62
+ describe 'index_name' do
63
+ it 'works with a single column' do
64
+ expect(described_class.index_name('parent_id')).to eq('on_parent_id')
65
+ end
66
+
67
+ it 'works with many columns' do
68
+ expect(described_class.index_name(['a', 'b', 'c'])).to eq('on_a_and_b_and_c')
69
+ end
70
+ end
71
+
62
72
  context 'with a migrated database' do
63
73
  before do
64
74
  ActiveRecord::Base.connection.execute <<~EOS
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: declare_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0.pre.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca Development adapted from hobo_fields by Tom Locke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-17 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -64,6 +64,7 @@ files:
64
64
  - lib/declare_schema/model/column.rb
65
65
  - lib/declare_schema/model/field_spec.rb
66
66
  - lib/declare_schema/model/foreign_key_definition.rb
67
+ - lib/declare_schema/model/habtm_model_shim.rb
67
68
  - lib/declare_schema/model/index_definition.rb
68
69
  - lib/declare_schema/model/table_options_definition.rb
69
70
  - lib/declare_schema/railtie.rb
@@ -85,6 +86,7 @@ files:
85
86
  - spec/lib/declare_schema/migration_generator_spec.rb
86
87
  - spec/lib/declare_schema/model/column_spec.rb
87
88
  - spec/lib/declare_schema/model/foreign_key_definition_spec.rb
89
+ - spec/lib/declare_schema/model/habtm_model_shim_spec.rb
88
90
  - spec/lib/declare_schema/model/index_definition_spec.rb
89
91
  - spec/lib/declare_schema/model/table_options_definition_spec.rb
90
92
  - spec/lib/declare_schema/prepare_testapp.rb