activerecord-spanner-adapter 0.3.0 → 0.5.0

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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/sync-repo-settings.yaml +16 -0
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
  5. data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
  6. data/.github/workflows/ci.yaml +33 -0
  7. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
  8. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
  9. data/.github/workflows/nightly-unit-tests.yaml +40 -0
  10. data/.github/workflows/release-please-label.yml +25 -0
  11. data/.github/workflows/release-please.yml +39 -0
  12. data/.github/workflows/rubocop.yaml +31 -0
  13. data/.gitignore +67 -5
  14. data/.kokoro/populate-secrets.sh +77 -0
  15. data/.kokoro/release.cfg +33 -0
  16. data/.kokoro/release.sh +15 -0
  17. data/.kokoro/trampoline_v2.sh +489 -0
  18. data/.rubocop.yml +46 -0
  19. data/.toys/release.rb +18 -0
  20. data/.trampolinerc +48 -0
  21. data/.yardopts +11 -0
  22. data/CHANGELOG.md +26 -0
  23. data/CODE_OF_CONDUCT.md +40 -0
  24. data/CONTRIBUTING.md +79 -0
  25. data/Gemfile +9 -4
  26. data/LICENSE +6 -6
  27. data/README.md +67 -30
  28. data/Rakefile +79 -3
  29. data/SECURITY.md +7 -0
  30. data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
  31. data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
  32. data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
  33. data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
  34. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
  35. data/acceptance/cases/migration/change_schema_test.rb +433 -0
  36. data/acceptance/cases/migration/change_table_test.rb +115 -0
  37. data/acceptance/cases/migration/column_attributes_test.rb +122 -0
  38. data/acceptance/cases/migration/column_positioning_test.rb +48 -0
  39. data/acceptance/cases/migration/columns_test.rb +201 -0
  40. data/acceptance/cases/migration/command_recorder_test.rb +406 -0
  41. data/acceptance/cases/migration/create_join_table_test.rb +216 -0
  42. data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
  43. data/acceptance/cases/migration/foreign_key_test.rb +297 -0
  44. data/acceptance/cases/migration/index_test.rb +211 -0
  45. data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
  46. data/acceptance/cases/migration/references_index_test.rb +135 -0
  47. data/acceptance/cases/migration/references_statements_test.rb +166 -0
  48. data/acceptance/cases/migration/rename_column_test.rb +96 -0
  49. data/acceptance/cases/models/calculation_query_test.rb +128 -0
  50. data/acceptance/cases/models/generated_column_test.rb +126 -0
  51. data/acceptance/cases/models/mutation_test.rb +122 -0
  52. data/acceptance/cases/models/query_test.rb +147 -0
  53. data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
  54. data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
  55. data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +152 -0
  58. data/acceptance/cases/type/binary_test.rb +59 -0
  59. data/acceptance/cases/type/boolean_test.rb +31 -0
  60. data/acceptance/cases/type/date_test.rb +32 -0
  61. data/acceptance/cases/type/date_time_test.rb +30 -0
  62. data/acceptance/cases/type/float_test.rb +27 -0
  63. data/acceptance/cases/type/integer_test.rb +44 -0
  64. data/acceptance/cases/type/numeric_test.rb +27 -0
  65. data/acceptance/cases/type/string_test.rb +79 -0
  66. data/acceptance/cases/type/text_test.rb +30 -0
  67. data/acceptance/cases/type/time_test.rb +87 -0
  68. data/acceptance/models/account.rb +13 -0
  69. data/acceptance/models/address.rb +9 -0
  70. data/acceptance/models/album.rb +12 -0
  71. data/acceptance/models/all_types.rb +8 -0
  72. data/acceptance/models/author.rb +11 -0
  73. data/acceptance/models/club.rb +12 -0
  74. data/acceptance/models/comment.rb +9 -0
  75. data/acceptance/models/customer.rb +9 -0
  76. data/acceptance/models/department.rb +9 -0
  77. data/acceptance/models/firm.rb +10 -0
  78. data/acceptance/models/member.rb +13 -0
  79. data/acceptance/models/member_type.rb +9 -0
  80. data/acceptance/models/membership.rb +10 -0
  81. data/acceptance/models/organization.rb +9 -0
  82. data/acceptance/models/post.rb +10 -0
  83. data/acceptance/models/singer.rb +10 -0
  84. data/acceptance/models/track.rb +20 -0
  85. data/acceptance/models/transaction.rb +9 -0
  86. data/acceptance/schema/schema.rb +143 -0
  87. data/acceptance/test_helper.rb +260 -0
  88. data/activerecord-spanner-adapter.gemspec +32 -17
  89. data/assets/solidus-db.png +0 -0
  90. data/benchmarks/README.md +17 -0
  91. data/benchmarks/Rakefile +14 -0
  92. data/benchmarks/application.rb +308 -0
  93. data/benchmarks/config/database.yml +8 -0
  94. data/benchmarks/config/environment.rb +12 -0
  95. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  96. data/benchmarks/db/schema.rb +29 -0
  97. data/benchmarks/models/album.rb +9 -0
  98. data/benchmarks/models/singer.rb +9 -0
  99. data/bin/console +6 -7
  100. data/examples/rails/README.md +262 -0
  101. data/examples/snippets/README.md +29 -0
  102. data/examples/snippets/Rakefile +57 -0
  103. data/examples/snippets/array-data-type/README.md +45 -0
  104. data/examples/snippets/array-data-type/Rakefile +13 -0
  105. data/examples/snippets/array-data-type/application.rb +45 -0
  106. data/examples/snippets/array-data-type/config/database.yml +8 -0
  107. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  108. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  109. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  110. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  111. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  112. data/examples/snippets/bulk-insert/README.md +21 -0
  113. data/examples/snippets/bulk-insert/Rakefile +13 -0
  114. data/examples/snippets/bulk-insert/application.rb +64 -0
  115. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  116. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  117. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  118. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  119. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  120. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  121. data/examples/snippets/commit-timestamp/README.md +18 -0
  122. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  123. data/examples/snippets/commit-timestamp/application.rb +53 -0
  124. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  125. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  126. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  127. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  128. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  129. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  130. data/examples/snippets/config/environment.rb +21 -0
  131. data/examples/snippets/create-records/README.md +12 -0
  132. data/examples/snippets/create-records/Rakefile +13 -0
  133. data/examples/snippets/create-records/application.rb +42 -0
  134. data/examples/snippets/create-records/config/database.yml +8 -0
  135. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  136. data/examples/snippets/create-records/db/schema.rb +26 -0
  137. data/examples/snippets/create-records/db/seeds.rb +5 -0
  138. data/examples/snippets/create-records/models/album.rb +9 -0
  139. data/examples/snippets/create-records/models/singer.rb +9 -0
  140. data/examples/snippets/date-data-type/README.md +19 -0
  141. data/examples/snippets/date-data-type/Rakefile +13 -0
  142. data/examples/snippets/date-data-type/application.rb +35 -0
  143. data/examples/snippets/date-data-type/config/database.yml +8 -0
  144. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  145. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  146. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  147. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  148. data/examples/snippets/generated-column/README.md +41 -0
  149. data/examples/snippets/generated-column/Rakefile +13 -0
  150. data/examples/snippets/generated-column/application.rb +37 -0
  151. data/examples/snippets/generated-column/config/database.yml +8 -0
  152. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  153. data/examples/snippets/generated-column/db/schema.rb +21 -0
  154. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  155. data/examples/snippets/generated-column/models/singer.rb +8 -0
  156. data/examples/snippets/interleaved-tables/README.md +152 -0
  157. data/examples/snippets/interleaved-tables/Rakefile +13 -0
  158. data/examples/snippets/interleaved-tables/application.rb +109 -0
  159. data/examples/snippets/interleaved-tables/config/database.yml +8 -0
  160. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
  161. data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
  162. data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
  163. data/examples/snippets/interleaved-tables/models/album.rb +15 -0
  164. data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
  165. data/examples/snippets/interleaved-tables/models/track.rb +25 -0
  166. data/examples/snippets/migrations/README.md +43 -0
  167. data/examples/snippets/migrations/Rakefile +13 -0
  168. data/examples/snippets/migrations/application.rb +26 -0
  169. data/examples/snippets/migrations/config/database.yml +8 -0
  170. data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
  171. data/examples/snippets/migrations/db/schema.rb +33 -0
  172. data/examples/snippets/migrations/db/seeds.rb +5 -0
  173. data/examples/snippets/migrations/models/album.rb +10 -0
  174. data/examples/snippets/migrations/models/singer.rb +10 -0
  175. data/examples/snippets/migrations/models/track.rb +9 -0
  176. data/examples/snippets/mutations/README.md +34 -0
  177. data/examples/snippets/mutations/Rakefile +13 -0
  178. data/examples/snippets/mutations/application.rb +47 -0
  179. data/examples/snippets/mutations/config/database.yml +8 -0
  180. data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
  181. data/examples/snippets/mutations/db/schema.rb +27 -0
  182. data/examples/snippets/mutations/db/seeds.rb +25 -0
  183. data/examples/snippets/mutations/models/album.rb +9 -0
  184. data/examples/snippets/mutations/models/singer.rb +9 -0
  185. data/examples/snippets/optimistic-locking/README.md +12 -0
  186. data/examples/snippets/optimistic-locking/Rakefile +13 -0
  187. data/examples/snippets/optimistic-locking/application.rb +48 -0
  188. data/examples/snippets/optimistic-locking/config/database.yml +8 -0
  189. data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
  190. data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
  191. data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
  192. data/examples/snippets/optimistic-locking/models/album.rb +9 -0
  193. data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
  194. data/examples/snippets/quickstart/README.md +26 -0
  195. data/examples/snippets/quickstart/Rakefile +13 -0
  196. data/examples/snippets/quickstart/application.rb +51 -0
  197. data/examples/snippets/quickstart/config/database.yml +8 -0
  198. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/quickstart/db/schema.rb +26 -0
  200. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  201. data/examples/snippets/quickstart/models/album.rb +9 -0
  202. data/examples/snippets/quickstart/models/singer.rb +9 -0
  203. data/examples/snippets/read-only-transactions/README.md +13 -0
  204. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  205. data/examples/snippets/read-only-transactions/application.rb +49 -0
  206. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  207. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  209. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  210. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  211. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  212. data/examples/snippets/read-write-transactions/README.md +12 -0
  213. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-write-transactions/application.rb +39 -0
  215. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  217. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  218. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  219. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/timestamp-data-type/README.md +17 -0
  222. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  223. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  224. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  225. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  226. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  227. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  228. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  229. data/examples/solidus/README.md +172 -0
  230. data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
  231. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  232. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  233. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  234. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  235. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  236. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  237. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  238. data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
  239. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  240. data/lib/active_record/type/spanner/array.rb +32 -0
  241. data/lib/active_record/type/spanner/bytes.rb +26 -0
  242. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
  243. data/lib/active_record/type/spanner/time.rb +37 -0
  244. data/lib/activerecord-spanner-adapter.rb +23 -0
  245. data/lib/activerecord_spanner_adapter/base.rb +217 -0
  246. data/lib/activerecord_spanner_adapter/connection.rb +324 -0
  247. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  248. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  249. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  250. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  251. data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
  252. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  253. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  254. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  255. data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
  256. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  257. data/lib/arel/visitors/spanner.rb +35 -0
  258. data/lib/spanner_client_ext.rb +82 -0
  259. data/renovate.json +5 -0
  260. metadata +387 -34
  261. data/.travis.yml +0 -5
  262. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  263. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  264. data/lib/activerecord-spanner-adapter/version.rb +0 -3
@@ -0,0 +1,8 @@
1
+ development:
2
+ adapter: spanner
3
+ emulator_host: localhost:9010
4
+ project: test-project
5
+ instance: test-instance
6
+ database: testdb
7
+ pool: 5
8
+ timeout: 5000
@@ -0,0 +1,22 @@
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
+ connection.ddl_batch do
10
+ create_table :singers do |t|
11
+ t.string :first_name
12
+ t.string :last_name
13
+ end
14
+
15
+ create_table :albums do |t|
16
+ t.string :title
17
+ t.numeric :marketing_budget
18
+ t.references :singer, index: false, foreign_key: true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `rails
6
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 1) do
14
+
15
+ create_table "albums", force: :cascade do |t|
16
+ t.string "title"
17
+ t.decimal "marketing_budget"
18
+ t.integer "singer_id", limit: 8
19
+ end
20
+
21
+ create_table "singers", force: :cascade do |t|
22
+ t.string "first_name"
23
+ t.string "last_name"
24
+ end
25
+
26
+ add_foreign_key "albums", "singers"
27
+ end
@@ -0,0 +1,25 @@
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_relative "../../config/environment.rb"
8
+ require_relative "../models/singer"
9
+ require_relative "../models/album"
10
+
11
+ first_names = ["Pete", "Alice", "John", "Ethel", "Trudy", "Naomi", "Wendy", "Ruben", "Thomas", "Elly"]
12
+ last_names = ["Wendelson", "Allison", "Peterson", "Johnson", "Henderson", "Ericsson", "Aronson", "Tennet", "Courtou"]
13
+
14
+ adjectives = ["daily", "happy", "blue", "generous", "cooked", "bad", "open"]
15
+ nouns = ["windows", "potatoes", "bank", "street", "tree", "glass", "bottle"]
16
+ budgets = [15000, 25000, 10000, 20000, 30000, 12000, 13000]
17
+
18
+ 5.times do
19
+ Singer.create first_name: first_names.sample, last_name: last_names.sample
20
+ end
21
+
22
+ 20.times do
23
+ singer_id = Singer.all.sample.id
24
+ Album.create title: "#{adjectives.sample} #{nouns.sample}", marketing_budget: budgets.sample, singer_id: singer_id
25
+ end
@@ -0,0 +1,9 @@
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
+ belongs_to :singer
9
+ end
@@ -0,0 +1,9 @@
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
+ has_many :albums
9
+ end
@@ -0,0 +1,12 @@
1
+ # Sample - Optimistic Locking
2
+
3
+ This example shows how to use optimistic locking with the Spanner ActiveRecord adapter.
4
+
5
+ The sample will automatically start a Spanner Emulator in a docker container and execute the sample
6
+ against that emulator. The emulator will automatically be stopped when the application finishes.
7
+
8
+ Run the application with the command
9
+
10
+ ```bash
11
+ bundle exec rake run
12
+ ```
@@ -0,0 +1,13 @@
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_relative "../config/environment"
8
+ require "sinatra/activerecord/rake"
9
+
10
+ desc "Sample showing how to use optimistic locking with the Cloud Spanner ActiveRecord adapter."
11
+ task :run do
12
+ Dir.chdir("..") { sh "bundle exec rake run[optimistic-locking]" }
13
+ end
@@ -0,0 +1,48 @@
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
+
12
+ class Application
13
+ def self.run
14
+ # Get a random singer and then update the singer in a separate thread.
15
+ # This simulates a concurrent update of the same record by two different processes.
16
+ singer = Singer.all.sample
17
+
18
+ puts ""
19
+ puts "Singer #{singer.first_name} #{singer.last_name} with version #{singer.lock_version} loaded"
20
+
21
+ t = Thread.new do
22
+ # Load the singer in the separate thread into a separate variable.
23
+ singer2 = Singer.find singer.id
24
+ singer2.update last_name: "Rashford"
25
+ puts ""
26
+ puts "Updated the last name of the singer to #{singer2.last_name}"
27
+ end
28
+ t.join
29
+
30
+ # Now try to update the singer in the main thread. This will fail, as the lock_version number has been increased
31
+ # by the update in the separate thread.
32
+ begin
33
+ singer.update last_name: "Drake"
34
+ rescue ActiveRecord::StaleObjectError
35
+ puts ""
36
+ puts "Updating the singer in the main thread failed with a StaleObjectError"
37
+ end
38
+
39
+ singer.reload
40
+ puts "Reloaded singer data: #{singer.first_name} #{singer.last_name}, version: #{singer.lock_version}"
41
+
42
+ puts ""
43
+ puts "Press any key to end the application"
44
+ STDIN.getch
45
+ end
46
+ end
47
+
48
+ Application.run
@@ -0,0 +1,8 @@
1
+ development:
2
+ adapter: spanner
3
+ emulator_host: localhost:9010
4
+ project: test-project
5
+ instance: test-instance
6
+ database: testdb
7
+ pool: 5
8
+ timeout: 5000
@@ -0,0 +1,26 @@
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
+ connection.ddl_batch do
10
+ create_table :singers do |t|
11
+ t.string :first_name
12
+ t.string :last_name
13
+ # `lock_version` is the default name for an optimistic lock version column in ActiveRecord.
14
+ t.integer :lock_version
15
+ end
16
+
17
+ create_table :albums do |t|
18
+ t.string :title
19
+ t.numeric :marketing_budget
20
+ t.references :singer, index: false, foreign_key: true
21
+ # `lock_version` is the default name for an optimistic lock version column in ActiveRecord.
22
+ t.integer :lock_version
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `rails
6
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 1) do
14
+
15
+ create_table "albums", force: :cascade do |t|
16
+ t.string "title"
17
+ t.decimal "marketing_budget"
18
+ t.integer "singer_id", limit: 8
19
+ t.integer "lock_version", limit: 8
20
+ end
21
+
22
+ create_table "singers", force: :cascade do |t|
23
+ t.string "first_name"
24
+ t.string "last_name"
25
+ t.integer "lock_version", limit: 8
26
+ end
27
+
28
+ add_foreign_key "albums", "singers"
29
+ end
@@ -0,0 +1,25 @@
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_relative "../../config/environment.rb"
8
+ require_relative "../models/singer"
9
+ require_relative "../models/album"
10
+
11
+ first_names = ["Pete", "Alice", "John", "Ethel", "Trudy", "Naomi", "Wendy", "Ruben", "Thomas", "Elly"]
12
+ last_names = ["Wendelson", "Allison", "Peterson", "Johnson", "Henderson", "Ericsson", "Aronson", "Tennet", "Courtou"]
13
+
14
+ adjectives = ["daily", "happy", "blue", "generous", "cooked", "bad", "open"]
15
+ nouns = ["windows", "potatoes", "bank", "street", "tree", "glass", "bottle"]
16
+ budgets = [15000, 25000, 10000, 20000, 30000, 12000, 13000]
17
+
18
+ 5.times do
19
+ Singer.create first_name: first_names.sample, last_name: last_names.sample
20
+ end
21
+
22
+ 20.times do
23
+ singer_id = Singer.all.sample.id
24
+ Album.create title: "#{adjectives.sample} #{nouns.sample}", marketing_budget: budgets.sample, singer_id: singer_id
25
+ end
@@ -0,0 +1,9 @@
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
+ belongs_to :singer
9
+ end
@@ -0,0 +1,9 @@
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
+ has_many :albums
9
+ end
@@ -0,0 +1,26 @@
1
+ # Sample - Quickstart
2
+
3
+ This sample shows how to create and use a very simple Cloud Spanner database with ActiveRecord.
4
+ The database consists of two tables:
5
+ - Singers
6
+ - Albums
7
+
8
+ Albums references Singers with a [foreign key](https://cloud.google.com/spanner/docs/foreign-keys/overview).
9
+
10
+ The sample will automatically start a Spanner Emulator in a docker container and execute the sample
11
+ against that emulator. The emulator will automatically be stopped when the application finishes.
12
+
13
+ This sample will:
14
+ 1. Create the sample database by calling `rake db:migrate`. This will execute the migrations in the folder `db/migrate`.
15
+ 2. Fill the sample database with some data by calling `rake db:seed`. This will execute the script in `db/seeds.rb`.
16
+ The seed script fills the database with 10 random singers and 30 random albums.
17
+ 3. Run the `application.rb` file. This application will:
18
+ 1. List all known singers and albums.
19
+ 2. Select a random singer and update the name of this singer.
20
+ 3. Select all singers whose last names start with an 'A'.
21
+
22
+ Run the application with the command
23
+
24
+ ```bash
25
+ bundle exec rake run
26
+ ```
@@ -0,0 +1,13 @@
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_relative "../config/environment"
8
+ require "sinatra/activerecord/rake"
9
+
10
+ desc "Runs a simple ActiveRecord tutorial application on a Spanner emulator."
11
+ task :run do
12
+ Dir.chdir("..") { sh "bundle exec rake run[quickstart]" }
13
+ end
@@ -0,0 +1,51 @@
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
+
12
+ class Application
13
+ def self.run # rubocop:disable Metrics/AbcSize
14
+ # Fetch all singers and albums from the database. The database has been pre-filled by the `db/seeds.rb` script.
15
+ puts "Known singers and their albums:"
16
+ puts ""
17
+ Singer.all.each do |singer|
18
+ puts "#{singer.first_name} #{singer.last_name}"
19
+ singer.albums.each do |album|
20
+ puts " #{album.title}"
21
+ end
22
+ end
23
+
24
+ # Select a random singer and update the name of this singer.
25
+ puts ""
26
+ singer = Singer.all.sample
27
+ puts "Current name of singer #{singer.id} is '#{singer.first_name} #{singer.last_name}'"
28
+ puts "Updating name to 'Dave Anderson'"
29
+ singer.first_name = "Dave"
30
+ singer.last_name = "Anderson"
31
+ singer.save!
32
+ singer.reload
33
+ puts "New name of singer #{singer.id}: #{singer.first_name} #{singer.last_name}"
34
+
35
+ # Select all singers whose last name start with 'A'. This should include at least the singer that was updated
36
+ # in the previous step, but probably also a number of other singers.
37
+ puts ""
38
+ puts "Getting all singers with a last name that starts with 'A'"
39
+
40
+ last_name = Singer.arel_table["last_name"]
41
+ Singer.where(last_name.matches("A%")).each do |s|
42
+ puts "#{s.first_name} #{s.last_name}"
43
+ end
44
+
45
+ puts ""
46
+ puts "Press any key to end the application"
47
+ STDIN.getch
48
+ end
49
+ end
50
+
51
+ Application.run
@@ -0,0 +1,8 @@
1
+ development:
2
+ adapter: spanner
3
+ emulator_host: localhost:9010
4
+ project: test-project
5
+ instance: test-instance
6
+ database: testdb
7
+ pool: 5
8
+ timeout: 5000
@@ -0,0 +1,21 @@
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
+ connection.ddl_batch do
10
+ create_table :singers do |t|
11
+ t.string :first_name
12
+ t.string :last_name
13
+ end
14
+
15
+ create_table :albums do |t|
16
+ t.string :title
17
+ t.references :singer, index: false, foreign_key: true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `rails
6
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 1) do
14
+
15
+ create_table "albums", force: :cascade do |t|
16
+ t.string "title"
17
+ t.integer "singer_id", limit: 8
18
+ end
19
+
20
+ create_table "singers", force: :cascade do |t|
21
+ t.string "first_name"
22
+ t.string "last_name"
23
+ end
24
+
25
+ add_foreign_key "albums", "singers"
26
+ end