declare_schema 0.4.0 → 0.4.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: 1e5c665ec203dd84e445290bac1de190491560ddc409fe2bd3f5ba4831e065c5
4
- data.tar.gz: 244148b892d2de22a692c4db50b136995f97d8da38573e87a443303fdee5be7a
3
+ metadata.gz: 2e1d3c68c4e9b2f6ce739eba37ed19c5f80204495a50a9240e9f1f68a09777e3
4
+ data.tar.gz: 63e0a3205c82c2b5e6dacbfbfd10e9aa2e63eee5898d11fd5c102618e73755aa
5
5
  SHA512:
6
- metadata.gz: 44994fd571b97768ffec2c9bbce779b8beef222ba50213a425a3021c66f1dc4e4fd7a50adc95cf3d1a1116e67eaf5c47b11e635408856c726118a22c18188b98
7
- data.tar.gz: 7d3eb3be095a1dce65b6b39b35974a6c96ee940fec31dba3fddbfb528be8be3b42906ec81c6075cde5d8fa590df4afc8ca12887a956c772fc34c084791577526
6
+ metadata.gz: 34ab74d97dd53c426d3a289b622670a42846d84279c2e3967ad0663c8f4871b1470a90b309237b09b09724a7e7c2405fb0f614a00d33902f8000bb1df23459ef
7
+ data.tar.gz: 2bf6ca852f71807e362ef26b4ba14d75b8a452a6245094eb8023fa1889b94d6584cf15856962f9fb85168208e0de4dd23a7942fb18cdcc5335f7c744382ba826
@@ -4,10 +4,15 @@ 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.4.1] - 2020-12-04
8
+ ### Fixed
9
+ - Fixed a bug detecting compound primary keys in Rails 4.
10
+
7
11
  ## [0.4.0] - 2020-11-20
8
12
  ### Added
9
- - Fields may be declared with `serialize: true` or `serialize: <serializeable-class>`, where `<serializeable-class>`
10
- may be `Array` (`Array` stored as YAML) or `Hash` (`Hash` stored as YAML), (`Array` or `Hash` or any scalar value stored as JSON)
13
+ - Fields may be declared with `serialize: true` (any value with a valid `.to_yaml` stored as YAML),
14
+ or `serialize: <serializeable-class>`, where `<serializeable-class>`
15
+ may be `Array` (`Array` stored as YAML) or `Hash` (`Hash` stored as YAML) or `JSON` (any value with a valid `.to_json`, stored as JSON)
11
16
  or any custom serializable class.
12
17
  This invokes `ActiveSupport`'s `serialize` macro for that field, passing the serializable class, if given.
13
18
 
@@ -55,11 +60,11 @@ using the appropriate Rails configuration attributes.
55
60
  ### Changed
56
61
  - Added travis support and created 2 specs as a starting point.
57
62
 
58
-
59
63
  ## [0.1.1] - 2020-09-24
60
64
  ### Added
61
65
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
62
66
 
67
+ [0.4.1]: https://github.com/Invoca/declare_schema/compare/v0.4.0...v0.4.1
63
68
  [0.4.0]: https://github.com/Invoca/declare_schema/compare/v0.3.1...v0.4.0
64
69
  [0.3.1]: https://github.com/Invoca/declare_schema/compare/v0.3.0...v0.3.1
65
70
  [0.3.0]: https://github.com/Invoca/declare_schema/compare/v0.2.0...v0.3.0
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.4.0)
4
+ declare_schema (0.4.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.5.0)
57
+ bootsnap (1.5.1)
58
58
  msgpack (~> 1.0)
59
59
  builder (3.2.4)
60
60
  byebug (11.1.3)
@@ -69,7 +69,7 @@ GEM
69
69
  activesupport (>= 4.2.0)
70
70
  i18n (1.8.5)
71
71
  concurrent-ruby (~> 1.0)
72
- listen (3.2.1)
72
+ listen (3.3.1)
73
73
  rb-fsevent (~> 0.10, >= 0.10.3)
74
74
  rb-inotify (~> 0.9, >= 0.9.10)
75
75
  loofah (2.7.0)
@@ -37,16 +37,9 @@ module DeclareSchema
37
37
  def for_model(model, old_table_name = nil)
38
38
  t = old_table_name || model.table_name
39
39
 
40
- primary_key_columns = Array(model.connection.primary_key(t)).presence || begin
41
- cols = model.connection.columns(t)
42
- Array(
43
- if cols.any? { |col| col.name == 'id' }
44
- 'id'
45
- else
46
- cols.find { |col| col.type.to_s.include?('int') }&.name or raise "could not guess primary key for #{t} in #{cols.inspect}"
47
- end
48
- )
49
- end
40
+ primary_key_columns = Array(model.connection.primary_key(t)).presence || sqlite_compound_primary_key(model, t) or
41
+ raise "could not find primary key for table #{t} in #{model.connection.columns(t).inspect}"
42
+
50
43
  primary_key_found = false
51
44
  index_definitions = model.connection.indexes(t).map do |i|
52
45
  model.ignore_indexes.include?(i.name) and next
@@ -55,7 +48,8 @@ module DeclareSchema
55
48
  raise "primary key on #{t} was not unique on #{primary_key_columns} (was unique=#{i.unique} on #{i.columns})"
56
49
  primary_key_found = true
57
50
  elsif i.columns == primary_key_columns && i.unique
58
- raise "found primary key index on #{t}.#{primary_key_columns} but it was called #{i.name}"
51
+ # skip this primary key index since we'll create it below, with PRIMARY_KEY_NAME
52
+ next
59
53
  end
60
54
  new(model, i.columns, name: i.name, unique: i.unique, where: i.where, table_name: old_table_name)
61
55
  end.compact
@@ -65,6 +59,30 @@ module DeclareSchema
65
59
  end
66
60
  index_definitions
67
61
  end
62
+
63
+ private
64
+
65
+ # This is the old approach which is still needed for SQLite
66
+ def sqlite_compound_primary_key(model, table)
67
+ ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) or return nil
68
+
69
+ connection = model.connection.dup
70
+
71
+ class << connection # defeat Rails MySQL driver code that skips the primary key by changing its name to a symbol
72
+ def each_hash(result)
73
+ super do |hash|
74
+ if hash[:Key_name] == PRIMARY_KEY_NAME
75
+ hash[:Key_name] = PRIMARY_KEY_NAME.to_sym
76
+ end
77
+ yield hash
78
+ end
79
+ end
80
+ end
81
+
82
+ pk_index = connection.indexes(table).find { |index| index.name.to_s == PRIMARY_KEY_NAME } or return nil
83
+
84
+ Array(pk_index.columns)
85
+ end
68
86
  end
69
87
 
70
88
  def primary_key?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  end
@@ -39,7 +39,7 @@ RSpec.describe 'DeclareSchema API' do
39
39
 
40
40
  load_models
41
41
 
42
- unless Rails::VERSION::MAJOR >= 6
42
+ if Rails::VERSION::MAJOR == 5
43
43
  # TODO: get this to work on Travis for Rails 6
44
44
  generate_migrations '-n', '-m'
45
45
  end
@@ -43,6 +43,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
43
43
  ActiveRecord::Migration.class_eval(up)
44
44
  expect(Advert.columns.map(&:name)).to eq(["id", "name"])
45
45
 
46
+ if Rails::VERSION::MAJOR < 5
47
+ # Rails 4 sqlite driver doesn't create PK properly. Fix that by dropping and recreating.
48
+ ActiveRecord::Base.connection.execute("drop table adverts")
49
+ ActiveRecord::Base.connection.execute('CREATE TABLE "adverts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255))')
50
+ end
51
+
46
52
  class Advert < ActiveRecord::Base
47
53
  fields do
48
54
  name :string, limit: 255, null: true
@@ -610,9 +616,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
610
616
  # Dropping tables is where the automatic down-migration really comes in handy:
611
617
 
612
618
  rails4_table_create = <<~EOS.strip
613
- create_table "adverts", id: false, force: :cascade do |t|
614
- t.integer "id", limit: 8
615
- t.string "name", limit: 255
619
+ create_table "adverts", force: :cascade do |t|
620
+ t.string "name", limit: 255
616
621
  end
617
622
  EOS
618
623
 
@@ -13,16 +13,25 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
13
13
  before do
14
14
  load File.expand_path('../prepare_testapp.rb', __dir__)
15
15
 
16
- class IndexSettingsTestModel < ActiveRecord::Base
16
+ class IndexDefinitionTestModel < ActiveRecord::Base
17
17
  fields do
18
18
  name :string, limit: 127, index: true
19
19
 
20
20
  timestamps
21
21
  end
22
22
  end
23
+
24
+ class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
25
+ fields do
26
+ fk1_id :integer
27
+ fk2_id :integer
28
+
29
+ timestamps
30
+ end
31
+ end
23
32
  end
24
33
 
25
- let(:model_class) { IndexSettingsTestModel }
34
+ let(:model_class) { IndexDefinitionTestModel }
26
35
 
27
36
  describe 'instance methods' do
28
37
  let(:model) { model_class.new }
@@ -53,13 +62,20 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
53
62
  context 'with a migrated database' do
54
63
  before do
55
64
  ActiveRecord::Base.connection.execute <<~EOS
56
- CREATE TABLE index_settings_test_models (
65
+ CREATE TABLE index_definition_test_models (
57
66
  id INTEGER NOT NULL PRIMARY KEY,
58
67
  name TEXT NOT NULL
59
68
  )
60
69
  EOS
61
70
  ActiveRecord::Base.connection.execute <<~EOS
62
- CREATE UNIQUE INDEX index_settings_test_models_on_name ON index_settings_test_models(name)
71
+ CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
72
+ EOS
73
+ ActiveRecord::Base.connection.execute <<~EOS
74
+ CREATE TABLE index_definition_compound_index_models (
75
+ fk1_id INTEGER NULL,
76
+ fk2_id INTEGER NULL,
77
+ PRIMARY KEY (fk1_id, fk2_id)
78
+ )
63
79
  EOS
64
80
  ActiveRecord::Base.connection.schema_cache.clear!
65
81
  end
@@ -67,14 +83,38 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
67
83
  describe 'for_model' do
68
84
  subject { described_class.for_model(model_class) }
69
85
 
70
- it 'returns the indexes for the model' do
71
- expect(subject.size).to eq(2), subject.inspect
72
- expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
73
- ['index_settings_test_models_on_name', ['name'], true]
74
- )
75
- expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
76
- ['PRIMARY', ['id'], true]
77
- )
86
+ context 'with single-column PK' do
87
+ it 'returns the indexes for the model' do
88
+ expect(subject.size).to eq(2), subject.inspect
89
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
90
+ ['index_definition_test_models_on_name', ['name'], true]
91
+ )
92
+ expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
93
+ ['PRIMARY', ['id'], true]
94
+ )
95
+ end
96
+ end
97
+
98
+ context 'with compound-column PK' do
99
+ let(:model_class) { IndexDefinitionCompoundIndexModel }
100
+
101
+ it 'returns the indexes for the model' do
102
+ # Simulate MySQL for Rails 4 work-around
103
+ if Rails::VERSION::MAJOR < 5
104
+ expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
105
+ connection_stub = instance_double(ActiveRecord::ConnectionAdapters::SQLite3Adapter, "connection")
106
+ expect(connection_stub).to receive(:indexes).
107
+ with('index_definition_compound_index_models').
108
+ and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
109
+
110
+ expect(model_class.connection).to receive(:dup).and_return(connection_stub)
111
+ end
112
+
113
+ expect(subject.size).to eq(1), subject.inspect
114
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
115
+ ['PRIMARY', ['fk1_id', 'fk2_id'], true]
116
+ )
117
+ end
78
118
  end
79
119
  end
80
120
  end
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.4.0
4
+ version: 0.4.1
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: 2020-11-20 00:00:00.000000000 Z
11
+ date: 2020-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails