activerecord-spanner-adapter 0.6.0 → 1.0.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/blunderbuss.yml +2 -0
  3. data/.github/sync-repo-settings.yaml +1 -1
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +1 -1
  5. data/.github/workflows/acceptance-tests-on-production.yaml +2 -2
  6. data/.github/workflows/ci.yaml +1 -1
  7. data/.github/workflows/release-please-label.yml +4 -4
  8. data/.github/workflows/release-please.yml +10 -9
  9. data/.github/workflows/rubocop.yaml +2 -2
  10. data/.release-please-manifest.json +3 -0
  11. data/.toys/release.rb +2 -2
  12. data/CHANGELOG.md +46 -23
  13. data/CONTRIBUTING.md +1 -1
  14. data/Gemfile +1 -1
  15. data/README.md +3 -3
  16. data/acceptance/cases/models/query_test.rb +24 -0
  17. data/acceptance/cases/type/all_types_test.rb +16 -14
  18. data/examples/rails/README.md +9 -9
  19. data/examples/snippets/generated-column/db/schema.rb +3 -3
  20. data/examples/snippets/hints/README.md +19 -0
  21. data/examples/snippets/{interleaved-tables → hints}/Rakefile +2 -2
  22. data/examples/snippets/hints/application.rb +47 -0
  23. data/examples/snippets/{interleaved-tables → hints}/config/database.yml +0 -0
  24. data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
  25. data/examples/snippets/{interleaved-tables → hints}/db/schema.rb +10 -14
  26. data/examples/snippets/{interleaved-tables → hints}/db/seeds.rb +0 -11
  27. data/examples/snippets/hints/models/album.rb +9 -0
  28. data/examples/snippets/hints/models/singer.rb +9 -0
  29. data/examples/snippets/partitioned-dml/README.md +16 -0
  30. data/examples/snippets/partitioned-dml/Rakefile +13 -0
  31. data/examples/snippets/partitioned-dml/application.rb +48 -0
  32. data/examples/snippets/partitioned-dml/config/database.yml +8 -0
  33. data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
  34. data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
  35. data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
  36. data/examples/snippets/partitioned-dml/models/album.rb +9 -0
  37. data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
  38. data/lib/active_record/connection_adapters/spanner_adapter.rb +1 -1
  39. data/lib/active_record/tasks/spanner_database_tasks.rb +1 -1
  40. data/lib/active_record/type/spanner/array.rb +19 -5
  41. data/lib/activerecord_spanner_adapter/base.rb +31 -10
  42. data/lib/activerecord_spanner_adapter/connection.rb +46 -20
  43. data/lib/activerecord_spanner_adapter/information_schema.rb +2 -1
  44. data/lib/activerecord_spanner_adapter/transaction.rb +52 -21
  45. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  46. data/lib/arel/visitors/spanner.rb +39 -0
  47. data/lib/spanner_client_ext.rb +4 -0
  48. data/release-please-config.json +19 -0
  49. metadata +24 -13
  50. data/examples/snippets/interleaved-tables/README.md +0 -152
  51. data/examples/snippets/interleaved-tables/application.rb +0 -109
  52. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +0 -44
  53. data/examples/snippets/interleaved-tables/models/album.rb +0 -15
  54. data/examples/snippets/interleaved-tables/models/singer.rb +0 -20
  55. data/examples/snippets/interleaved-tables/models/track.rb +0 -25
@@ -0,0 +1,19 @@
1
+ {
2
+ "bump-minor-pre-major": true,
3
+ "bump-patch-for-minor-pre-major": false,
4
+ "draft": false,
5
+ "include-component-in-tag": true,
6
+ "include-v-in-tag": true,
7
+ "prerelease": false,
8
+ "release-type": "ruby-yoshi",
9
+ "skip-github-release": false,
10
+ "separate-pull-requests": true,
11
+ "tag-separator": "/",
12
+ "sequential-calls": true,
13
+ "packages": {
14
+ ".": {
15
+ "component": "activerecord-spanner-adapter",
16
+ "version-file": "lib/activerecord_spanner_adapter/version.rb"
17
+ }
18
+ }
19
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-spanner-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google LLC
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-10 00:00:00.000000000 Z
11
+ date: 2022-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-cloud-spanner
@@ -214,6 +214,7 @@ extensions: []
214
214
  extra_rdoc_files: []
215
215
  files:
216
216
  - ".github/CODEOWNERS"
217
+ - ".github/blunderbuss.yml"
217
218
  - ".github/sync-repo-settings.yaml"
218
219
  - ".github/workflows/acceptance-tests-on-emulator.yaml"
219
220
  - ".github/workflows/acceptance-tests-on-production.yaml"
@@ -229,6 +230,7 @@ files:
229
230
  - ".kokoro/release.cfg"
230
231
  - ".kokoro/release.sh"
231
232
  - ".kokoro/trampoline_v2.sh"
233
+ - ".release-please-manifest.json"
232
234
  - ".rubocop.yml"
233
235
  - ".toys/release.rb"
234
236
  - ".trampolinerc"
@@ -369,16 +371,15 @@ files:
369
371
  - examples/snippets/generated-column/db/schema.rb
370
372
  - examples/snippets/generated-column/db/seeds.rb
371
373
  - examples/snippets/generated-column/models/singer.rb
372
- - examples/snippets/interleaved-tables/README.md
373
- - examples/snippets/interleaved-tables/Rakefile
374
- - examples/snippets/interleaved-tables/application.rb
375
- - examples/snippets/interleaved-tables/config/database.yml
376
- - examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb
377
- - examples/snippets/interleaved-tables/db/schema.rb
378
- - examples/snippets/interleaved-tables/db/seeds.rb
379
- - examples/snippets/interleaved-tables/models/album.rb
380
- - examples/snippets/interleaved-tables/models/singer.rb
381
- - examples/snippets/interleaved-tables/models/track.rb
374
+ - examples/snippets/hints/README.md
375
+ - examples/snippets/hints/Rakefile
376
+ - examples/snippets/hints/application.rb
377
+ - examples/snippets/hints/config/database.yml
378
+ - examples/snippets/hints/db/migrate/01_create_tables.rb
379
+ - examples/snippets/hints/db/schema.rb
380
+ - examples/snippets/hints/db/seeds.rb
381
+ - examples/snippets/hints/models/album.rb
382
+ - examples/snippets/hints/models/singer.rb
382
383
  - examples/snippets/migrations/README.md
383
384
  - examples/snippets/migrations/Rakefile
384
385
  - examples/snippets/migrations/application.rb
@@ -407,6 +408,15 @@ files:
407
408
  - examples/snippets/optimistic-locking/db/seeds.rb
408
409
  - examples/snippets/optimistic-locking/models/album.rb
409
410
  - examples/snippets/optimistic-locking/models/singer.rb
411
+ - examples/snippets/partitioned-dml/README.md
412
+ - examples/snippets/partitioned-dml/Rakefile
413
+ - examples/snippets/partitioned-dml/application.rb
414
+ - examples/snippets/partitioned-dml/config/database.yml
415
+ - examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb
416
+ - examples/snippets/partitioned-dml/db/schema.rb
417
+ - examples/snippets/partitioned-dml/db/seeds.rb
418
+ - examples/snippets/partitioned-dml/models/album.rb
419
+ - examples/snippets/partitioned-dml/models/singer.rb
410
420
  - examples/snippets/quickstart/README.md
411
421
  - examples/snippets/quickstart/Rakefile
412
422
  - examples/snippets/quickstart/application.rb
@@ -481,6 +491,7 @@ files:
481
491
  - lib/activerecord_spanner_adapter/version.rb
482
492
  - lib/arel/visitors/spanner.rb
483
493
  - lib/spanner_client_ext.rb
494
+ - release-please-config.json
484
495
  - renovate.json
485
496
  homepage: https://github.com/googleapis/ruby-spanner-activerecord
486
497
  licenses:
@@ -501,7 +512,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
501
512
  - !ruby/object:Gem::Version
502
513
  version: '0'
503
514
  requirements: []
504
- rubygems_version: 3.2.17
515
+ rubygems_version: 3.3.5
505
516
  signing_key:
506
517
  specification_version: 4
507
518
  summary: Rails ActiveRecord connector for Google Spanner Database
@@ -1,152 +0,0 @@
1
- # Sample - Interleaved Tables
2
-
3
- This example shows how to use interleaved tables with the Spanner ActiveRecord adapter.
4
-
5
- See https://cloud.google.com/spanner/docs/schema-and-data-model#creating-interleaved-tables for more information
6
- on interleaved tables if you are not familiar with this concept.
7
-
8
- ## Creating Interleaved Tables in ActiveRecord
9
- You can create interleaved tables using migrations in ActiveRecord by using the following Spanner ActiveRecord specific
10
- methods that are defined on `TableDefinition`:
11
- * `interleave_in`: Specifies which parent table a child table should be interleaved in and optionally whether
12
- deletes of a parent record should automatically cascade delete all child records.
13
- * `parent_key`: Creates a column that is a reference to (a part of) the primary key of the parent table. Each child
14
- table must include all the primary key columns of the parent table as a `parent_key`.
15
-
16
- Cloud Spanner requires a child table to include the exact same primary key columns as the parent table in addition to
17
- the primary key column(s) of the child table. This means that the default `id` primary key column of ActiveRecord is
18
- not usable in combination with interleaved tables. Instead each primary key column should be prefixed with the table
19
- name of the table that it references, or use some other unique name.
20
-
21
- This example uses the following table schema:
22
-
23
- ```sql
24
- CREATE TABLE singers (
25
- singerid INT64 NOT NULL,
26
- first_name STRING(MAX),
27
- last_name STRING(MAX)
28
- ) PRIMARY KEY (singerid);
29
-
30
- CREATE TABLE albums (
31
- albumid INT64 NOT NULL,
32
- singerid INT64 NOT NULL,
33
- title STRING(MAX)
34
- ) PRIMARY KEY (singerid, albumid), INTERLEAVE IN PARENT singers;
35
-
36
- CREATE TABLE tracks (
37
- trackid INT64 NOT NULL,
38
- singerid INT64 NOT NULL,
39
- albumid INT64 NOT NULL,
40
- title STRING(MAX),
41
- duration NUMERIC
42
- ) PRIMARY KEY (singerid, albumid, trackid), INTERLEAVE IN PARENT albums ON DELETE CASCADE;
43
- ```
44
-
45
- This schema can be created in ActiveRecord as follows:
46
-
47
- ```ruby
48
- create_table :singers, id: false do |t|
49
- # Explicitly define the primary key with a custom name to prevent all primary key columns from being named `id`.
50
- t.primary_key :singerid
51
- t.string :first_name
52
- t.string :last_name
53
- end
54
-
55
- create_table :albums, id: false do |t|
56
- # Interleave the `albums` table in the parent table `singers`.
57
- t.interleave_in :singers
58
- t.primary_key :albumid
59
- # `singerid` is defined as a `parent_key` which makes it a part of the primary key in the table definition, but
60
- # it is not presented to ActiveRecord as part of the primary key, to prevent ActiveRecord from considering this
61
- # to be an entity with a composite primary key (which is not supported by ActiveRecord).
62
- t.parent_key :singerid
63
- t.string :title
64
- end
65
-
66
- create_table :tracks, id: false do |t|
67
- # Interleave the `tracks` table in the parent table `albums` and cascade delete all tracks that belong to an
68
- # album when an album is deleted.
69
- t.interleave_in :albums, :cascade
70
- # `trackid` is considered the only primary key column by ActiveRecord.
71
- t.primary_key :trackid
72
- # `singerid` and `albumid` form the parent key of `tracks`. These are part of the primary key definition in the
73
- # database, but are presented as parent keys to ActiveRecord.
74
- t.parent_key :singerid
75
- t.parent_key :albumid
76
- t.string :title
77
- t.numeric :duration
78
- end
79
- ```
80
-
81
- ## Models for Interleaved Tables
82
- An interleaved table parent/child relationship must be modelled as a `belongs_to`/`has_many` association in
83
- ActiveRecord. As the columns that are used to reference a parent record use a custom column name, it is required to also
84
- include the custom column name in the `belongs_to` and `has_many` definitions.
85
-
86
- Instances of these models can be used in the same way as any other association in ActiveRecord, but with a couple of
87
- inherent limitations:
88
- * It is not possible to change the parent record of a child record. For instance, changing the singer of an album in the
89
- above example is impossible, as Cloud Spanner does not allow such an update.
90
- * It is not possible to de-reference a parent record by setting it to null.
91
- * It is only possible to delete a parent record with existing child records, if the child records are also deleted. This
92
- can be done by enabling ON DELETE CASCADE in Cloud Spanner, or by deleting the child records using ActiveRecord.
93
-
94
- ### Example Models
95
-
96
- ```ruby
97
- class Singer < ActiveRecord::Base
98
- # `albums` is defined as INTERLEAVE IN PARENT `singers`. The primary key of `albums` is (`singerid`, `albumid`), but
99
- # only `albumid` is used by ActiveRecord as the primary key. The `singerid` column is defined as a `parent_key` of
100
- # `albums` (see also the `db/migrate/01_create_tables.rb` file).
101
- has_many :albums, foreign_key: "singerid"
102
-
103
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `tracks` is
104
- # (`singerid`, `albumid`, `trackid`), but only `trackid` is used by ActiveRecord as the primary key. The `singerid`
105
- # and `albumid` columns are defined as `parent_key` of `tracks` (see also the `db/migrate/01_create_tables.rb` file).
106
- # The `singerid` column can therefore be used to associate tracks with a singer without the need to go through albums.
107
- # Note also that the inclusion of `singerid` as a column in `tracks` is required in order to make `tracks` a child
108
- # table of `albums` which has primary key (`singerid`, `albumid`).
109
- has_many :tracks, foreign_key: "singerid"
110
- end
111
-
112
- class Album < ActiveRecord::Base
113
- # `albums` is defined as INTERLEAVE IN PARENT `singers`. The primary key of `singers` is `singerid`.
114
- belongs_to :singer, foreign_key: "singerid"
115
-
116
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `albums` is (`singerid`, `albumid`), but
117
- # only `albumid` is used by ActiveRecord as the primary key. The `singerid` column is defined as a `parent_key` of
118
- # `albums` (see also the `db/migrate/01_create_tables.rb` file).
119
- has_many :tracks, foreign_key: "albumid"
120
- end
121
-
122
- class Track < ActiveRecord::Base
123
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `albums` is ()`singerid`, `albumid`).
124
- belongs_to :album, foreign_key: "albumid"
125
-
126
- # `tracks` also has a `singerid` column should be used to associate a Track with a Singer.
127
- belongs_to :singer, foreign_key: "singerid"
128
-
129
- # Override the default initialize method to automatically set the singer attribute when an album is given.
130
- def initialize attributes = nil
131
- super
132
- self.singer ||= album&.singer
133
- end
134
-
135
- def album=value
136
- super
137
- # Ensure the singer of this track is equal to the singer of the album that is set.
138
- self.singer = value&.singer
139
- end
140
- end
141
- ```
142
-
143
- ## Running the Sample
144
-
145
- The sample will automatically start a Spanner Emulator in a docker container and execute the sample
146
- against that emulator. The emulator will automatically be stopped when the application finishes.
147
-
148
- Run the application with the command
149
-
150
- ```bash
151
- bundle exec rake run
152
- ```
@@ -1,109 +0,0 @@
1
- # Copyright 2021 Google LLC
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
-
7
- require "io/console"
8
- require_relative "../config/environment"
9
- require_relative "models/singer"
10
- require_relative "models/album"
11
- require_relative "models/track"
12
-
13
- class Application
14
- def self.run
15
- # List all singers, albums and tracks.
16
- list_singers_albums_tracks
17
-
18
- # Create a new album with some tracks.
19
- create_new_album
20
-
21
- # Try to update the singer of an album. This is not possible as albums are interleaved in singers.
22
- update_singer_of_album
23
-
24
- # Try to delete a singer that has at least one album. This is NOT possible as albums is NOT marked with
25
- # ON DELETE CASCADE.
26
- delete_singer_with_albums
27
-
28
- # Try to delete an album that has at least one track. This IS possible as tracks IS marked with
29
- # ON DELETE CASCADE.
30
- delete_album_with_tracks
31
-
32
- puts ""
33
- puts "Press any key to end the application"
34
- STDIN.getch
35
- end
36
-
37
- def self.list_singers_albums_tracks
38
- puts ""
39
- puts "Listing all singers with corresponding albums and tracks"
40
- Singer.all.order("last_name, first_name").each do |singer|
41
- puts "#{singer.first_name} #{singer.last_name} has #{singer.albums.count} albums:"
42
- singer.albums.order("title").each do |album|
43
- puts " #{album.title} has #{album.tracks.count} tracks:"
44
- album.tracks.each do |track|
45
- puts " #{track.title}"
46
- end
47
- end
48
- end
49
- end
50
-
51
- def self.create_new_album
52
- # Create a new album with some tracks.
53
- puts ""
54
- singer = Singer.all.sample
55
- puts "Creating a new album for #{singer.first_name} #{singer.last_name}"
56
- album = singer.albums.build title: "New Title"
57
- album.tracks.build title: "Track 1", duration: 3.5, singer: singer
58
- album.tracks.build title: "Track 2", duration: 3.6, singer: singer
59
- # This will save the album and corresponding tracks in one transaction.
60
- album.save!
61
-
62
- album.reload
63
- puts "Album #{album.title} has #{album.tracks.count} tracks:"
64
- album.tracks.order("title").each do |track|
65
- puts " #{track.title} with duration #{track.duration}"
66
- end
67
- end
68
-
69
- def self.update_singer_of_album
70
- # It is not possible to change the singer of an album or the album of a track. This is because the associations
71
- # between these are not traditional foreign keys, but an immutable parent-child relationship.
72
- album = Album.all.sample
73
- new_singer = Singer.all.except(album.singer).sample
74
- # This will fail as we cannot assign a new singer to an album as it is an INTERLEAVE IN PARENT relationship.
75
- begin
76
- album.update! singer: new_singer
77
- raise StandardError, "Unexpected error: Updating the singer of an album should not be possible."
78
- rescue ActiveRecord::StatementInvalid
79
- puts ""
80
- puts "Failed to update the singer of an album. This is expected."
81
- end
82
- end
83
-
84
- def self.delete_singer_with_albums
85
- # Deleting a singer that has albums is not possible, as the INTERLEAVE IN PARENT of albums is not marked with
86
- # ON DELETE CASCADE.
87
- singer = Album.all.sample.singer
88
- begin
89
- singer.delete
90
- raise StandardError, "Unexpected error: Updating the singer of an album should not be possible."
91
- rescue ActiveRecord::StatementInvalid
92
- puts ""
93
- puts "Failed to delete a singer that has #{singer.albums.count} albums. This is expected."
94
- end
95
- end
96
-
97
- def self.delete_album_with_tracks
98
- # Deleting an album with tracks is supported, as the INTERLEAVE IN PARENT relationship between tracks and albums is
99
- # marked with ON DELETE CASCADE.
100
- puts ""
101
- puts "Total track count: #{Track.count}"
102
- album = Track.all.sample.album
103
- puts "Deleting album #{album.title} with #{album.tracks.count} tracks"
104
- album.delete
105
- puts "Total track count after deletion: #{Track.count}"
106
- end
107
- end
108
-
109
- Application.run
@@ -1,44 +0,0 @@
1
- # Copyright 2021 Google LLC
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
-
7
- class CreateTables < ActiveRecord::Migration[6.0]
8
- def change
9
- # Execute the entire migration as one DDL batch.
10
- connection.ddl_batch do
11
- create_table :singers, id: false do |t|
12
- # Explicitly define the primary key with a custom name to prevent all primary key columns from being named `id`.
13
- t.primary_key :singerid
14
- t.string :first_name
15
- t.string :last_name
16
- end
17
-
18
- create_table :albums, id: false do |t|
19
- # Interleave the `albums` table in the parent table `singers`.
20
- t.interleave_in :singers
21
- t.primary_key :albumid
22
- # `singerid` is defined as a `parent_key` which makes it a part of the primary key in the table definition, but
23
- # it is not presented to ActiveRecord as part of the primary key, to prevent ActiveRecord from considering this
24
- # to be an entity with a composite primary key (which is not supported by ActiveRecord).
25
- t.parent_key :singerid
26
- t.string :title
27
- end
28
-
29
- create_table :tracks, id: false do |t|
30
- # Interleave the `tracks` table in the parent table `albums` and cascade delete all tracks that belong to an
31
- # album when an album is deleted.
32
- t.interleave_in :albums, :cascade
33
- # `trackid` is considered the only primary key column by ActiveRecord.
34
- t.primary_key :trackid
35
- # `singerid` and `albumid` form the parent key of `tracks`. These are part of the primary key definition in the
36
- # database, but are presented as parent keys to ActiveRecord.
37
- t.parent_key :singerid
38
- t.parent_key :albumid
39
- t.string :title
40
- t.numeric :duration
41
- end
42
- end
43
- end
44
- end
@@ -1,15 +0,0 @@
1
- # Copyright 2021 Google LLC
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
-
7
- class Album < ActiveRecord::Base
8
- # `albums` is defined as INTERLEAVE IN PARENT `singers`. The primary key of `singers` is `singerid`.
9
- belongs_to :singer, foreign_key: "singerid"
10
-
11
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `albums` is (`singerid`, `albumid`), but
12
- # only `albumid` is used by ActiveRecord as the primary key. The `singerid` column is defined as a `parent_key` of
13
- # `albums` (see also the `db/migrate/01_create_tables.rb` file).
14
- has_many :tracks, foreign_key: "albumid"
15
- end
@@ -1,20 +0,0 @@
1
- # Copyright 2021 Google LLC
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
-
7
- class Singer < ActiveRecord::Base
8
- # `albums` is defined as INTERLEAVE IN PARENT `singers`. The primary key of `albums` is (`singerid`, `albumid`), but
9
- # only `albumid` is used by ActiveRecord as the primary key. The `singerid` column is defined as a `parent_key` of
10
- # `albums` (see also the `db/migrate/01_create_tables.rb` file).
11
- has_many :albums, foreign_key: "singerid"
12
-
13
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `tracks` is
14
- # (`singerid`, `albumid`, `trackid`), but only `trackid` is used by ActiveRecord as the primary key. The `singerid`
15
- # and `albumid` columns are defined as `parent_key` of `tracks` (see also the `db/migrate/01_create_tables.rb` file).
16
- # The `singerid` column can therefore be used to associate tracks with a singer without the need to go through albums.
17
- # Note also that the inclusion of `singerid` as a column in `tracks` is required in order to make `tracks` a child
18
- # table of `albums` which has primary key (`singerid`, `albumid`).
19
- has_many :tracks, foreign_key: "singerid"
20
- end
@@ -1,25 +0,0 @@
1
- # Copyright 2021 Google LLC
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
-
7
- class Track < ActiveRecord::Base
8
- # `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `albums` is ()`singerid`, `albumid`).
9
- belongs_to :album, foreign_key: "albumid"
10
-
11
- # `tracks` also has a `singerid` column should be used to associate a Track with a Singer.
12
- belongs_to :singer, foreign_key: "singerid"
13
-
14
- # Override the default initialize method to automatically set the singer attribute when an album is given.
15
- def initialize attributes = nil
16
- super
17
- self.singer ||= album&.singer
18
- end
19
-
20
- def album=value
21
- super
22
- # Ensure the singer of this track is equal to the singer of the album that is set.
23
- self.singer = value&.singer
24
- end
25
- end