declare_schema 0.7.1 → 0.8.0.pre.5

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: 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