chrono_model 1.2.2 → 2.0.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -20
  3. data/README.md +62 -40
  4. data/lib/active_record/connection_adapters/chronomodel_adapter.rb +17 -11
  5. data/lib/active_record/tasks/chronomodel_database_tasks.rb +64 -23
  6. data/lib/chrono_model/adapter/ddl.rb +168 -153
  7. data/lib/chrono_model/adapter/indexes.rb +99 -94
  8. data/lib/chrono_model/adapter/migrations.rb +81 -104
  9. data/lib/chrono_model/adapter/migrations_modules/legacy.rb +41 -0
  10. data/lib/chrono_model/adapter/migrations_modules/stable.rb +41 -0
  11. data/lib/chrono_model/adapter/tsrange.rb +20 -5
  12. data/lib/chrono_model/adapter/upgrade.rb +89 -91
  13. data/lib/chrono_model/adapter.rb +64 -31
  14. data/lib/chrono_model/chrono.rb +17 -0
  15. data/lib/chrono_model/conversions.rb +15 -9
  16. data/lib/chrono_model/db_console.rb +9 -0
  17. data/lib/chrono_model/json.rb +9 -6
  18. data/lib/chrono_model/patches/as_of_time_holder.rb +2 -2
  19. data/lib/chrono_model/patches/as_of_time_relation.rb +2 -2
  20. data/lib/chrono_model/patches/association.rb +15 -12
  21. data/lib/chrono_model/patches/batches.rb +17 -0
  22. data/lib/chrono_model/patches/db_console.rb +20 -4
  23. data/lib/chrono_model/patches/join_node.rb +4 -4
  24. data/lib/chrono_model/patches/preloader.rb +41 -11
  25. data/lib/chrono_model/patches/relation.rb +53 -8
  26. data/lib/chrono_model/patches.rb +3 -1
  27. data/lib/chrono_model/railtie.rb +29 -24
  28. data/lib/chrono_model/time_gate.rb +3 -3
  29. data/lib/chrono_model/time_machine/history_model.rb +65 -31
  30. data/lib/chrono_model/time_machine/time_query.rb +65 -49
  31. data/lib/chrono_model/time_machine/timeline.rb +52 -28
  32. data/lib/chrono_model/time_machine.rb +66 -25
  33. data/lib/chrono_model/utilities.rb +3 -3
  34. data/lib/chrono_model/version.rb +3 -1
  35. data/lib/chrono_model.rb +31 -36
  36. metadata +39 -136
  37. data/.gitignore +0 -21
  38. data/.rspec +0 -2
  39. data/.travis.yml +0 -41
  40. data/Gemfile +0 -4
  41. data/README.sql +0 -161
  42. data/Rakefile +0 -25
  43. data/chrono_model.gemspec +0 -33
  44. data/gemfiles/rails_5.0.gemfile +0 -6
  45. data/gemfiles/rails_5.1.gemfile +0 -6
  46. data/gemfiles/rails_5.2.gemfile +0 -6
  47. data/spec/aruba/dbconsole_spec.rb +0 -25
  48. data/spec/aruba/fixtures/database_with_default_username_and_password.yml +0 -14
  49. data/spec/aruba/fixtures/database_without_username_and_password.yml +0 -11
  50. data/spec/aruba/fixtures/empty_structure.sql +0 -27
  51. data/spec/aruba/fixtures/migrations/56/20160812190335_create_impressions.rb +0 -10
  52. data/spec/aruba/fixtures/migrations/56/20171115195229_add_temporal_extension_to_impressions.rb +0 -10
  53. data/spec/aruba/fixtures/railsapp/config/application.rb +0 -17
  54. data/spec/aruba/fixtures/railsapp/config/boot.rb +0 -5
  55. data/spec/aruba/fixtures/railsapp/config/environments/development.rb +0 -38
  56. data/spec/aruba/migrations_spec.rb +0 -48
  57. data/spec/aruba/rake_task_spec.rb +0 -71
  58. data/spec/chrono_model/adapter/base_spec.rb +0 -157
  59. data/spec/chrono_model/adapter/ddl_spec.rb +0 -243
  60. data/spec/chrono_model/adapter/indexes_spec.rb +0 -72
  61. data/spec/chrono_model/adapter/migrations_spec.rb +0 -312
  62. data/spec/chrono_model/conversions_spec.rb +0 -43
  63. data/spec/chrono_model/history_models_spec.rb +0 -32
  64. data/spec/chrono_model/json_ops_spec.rb +0 -59
  65. data/spec/chrono_model/time_machine/as_of_spec.rb +0 -188
  66. data/spec/chrono_model/time_machine/changes_spec.rb +0 -50
  67. data/spec/chrono_model/time_machine/counter_cache_race_spec.rb +0 -46
  68. data/spec/chrono_model/time_machine/default_scope_spec.rb +0 -37
  69. data/spec/chrono_model/time_machine/history_spec.rb +0 -104
  70. data/spec/chrono_model/time_machine/keep_cool_spec.rb +0 -27
  71. data/spec/chrono_model/time_machine/manipulations_spec.rb +0 -84
  72. data/spec/chrono_model/time_machine/model_identification_spec.rb +0 -46
  73. data/spec/chrono_model/time_machine/sequence_spec.rb +0 -74
  74. data/spec/chrono_model/time_machine/sti_spec.rb +0 -100
  75. data/spec/chrono_model/time_machine/time_query_spec.rb +0 -261
  76. data/spec/chrono_model/time_machine/timeline_spec.rb +0 -63
  77. data/spec/chrono_model/time_machine/timestamps_spec.rb +0 -43
  78. data/spec/chrono_model/time_machine/transactions_spec.rb +0 -69
  79. data/spec/config.travis.yml +0 -5
  80. data/spec/config.yml.example +0 -9
  81. data/spec/spec_helper.rb +0 -33
  82. data/spec/support/adapter/helpers.rb +0 -53
  83. data/spec/support/adapter/structure.rb +0 -44
  84. data/spec/support/aruba.rb +0 -44
  85. data/spec/support/connection.rb +0 -70
  86. data/spec/support/matchers/base.rb +0 -56
  87. data/spec/support/matchers/column.rb +0 -99
  88. data/spec/support/matchers/function.rb +0 -79
  89. data/spec/support/matchers/index.rb +0 -69
  90. data/spec/support/matchers/schema.rb +0 -39
  91. data/spec/support/matchers/table.rb +0 -275
  92. data/spec/support/time_machine/helpers.rb +0 -47
  93. data/spec/support/time_machine/structure.rb +0 -111
  94. data/sql/json_ops.sql +0 -56
  95. data/sql/uninstall-json_ops.sql +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa9d6f8ee80241092713836e372cf3cb568639e403511ba2c48ba33275f6c1a8
4
- data.tar.gz: '055794bce8014501e3eea0a180abe04278f1aba62015d3f5b98d75499c6faba1'
3
+ metadata.gz: e9dc931f360178771e212906ec3a02d1ff49ffc2393b457a7f531b101bc1e8ce
4
+ data.tar.gz: 7606b75d5eaf453e0d0f90c760607af50bdf862744f7048cc2c8061f41018bc5
5
5
  SHA512:
6
- metadata.gz: 380cbab091ab178aea031fee4e8343797a5e461e98b233a8c732b7cc0f9bd584497fa3b1bcc8703f6d8b74b972aeed2a257f6aba9ca7a2d932b4e72e7c211506
7
- data.tar.gz: c601c91ce05f426aef3c75b37ba9920170666523abff9b219ab09816e6c17f42146131d5ffcc89d233d5d7ec94d6ac5592e6a8160dd8207b9a48c3ff51feb629
6
+ metadata.gz: e64bea647172199e03703f0f4be8538d48276a8cb02fedb2309a68913ed6dad910d01f9ed4498fe345119ff1a12e25a0dda16f2b5976a0cb1b587dc4548eef4a
7
+ data.tar.gz: e326f5fce5a0fc94df9e23618840edb6baeaf307e85bbb3449fad7540cd85863be533caca5e30d073fc880a2759ce21ceed74f471dbba4959ead30e9ce43e5b5
data/LICENSE CHANGED
@@ -1,24 +1,23 @@
1
- Copyright (c) 2012-2014 Marcello Barnaba <m.barnaba@ifad.org>
2
- Copyright (c) 2012-2014 Peter J. Brindisi <p.brindisi@ifad.org>
3
- Copyright (c) 2012-2014 IFAD
4
-
5
1
  MIT License
6
2
 
7
- Permission is hereby granted, free of charge, to any person obtaining
8
- a copy of this software and associated documentation files (the
9
- "Software"), to deal in the Software without restriction, including
10
- without limitation the rights to use, copy, modify, merge, publish,
11
- distribute, sublicense, and/or sell copies of the Software, and to
12
- permit persons to whom the Software is furnished to do so, subject to
13
- the following conditions:
3
+ Copyright (c) 2012-2022 Marcello Barnaba <m.barnaba@ifad.org>
4
+ Copyright (c) 2012-2022 Peter J. Brindisi <p.brindisi@ifad.org>
5
+ Copyright (c) 2012-2022 IFAD
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
14
13
 
15
- The above copyright notice and this permission notice shall be
16
- included in all copies or substantial portions of the Software.
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
17
16
 
18
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # Temporal database system on PostgreSQL using [updatable views][pg-updatable-views], [table inheritance][pg-table-inheritance] and [INSTEAD OF triggers][pg-instead-of-triggers].
2
2
 
3
3
  [![Build Status][build-status-badge]][build-status]
4
+ [![Legacy Build Status][legacy-build-status-badge]][build-status]
4
5
  [![Code Climate][code-analysis-badge]][code-analysis]
5
6
  [![Test Coverage][test-coverage-badge]][test-coverage]
6
7
  [![Gem Version][gem-version-badge]][gem-version]
7
8
  [![Inlinedocs][docs-analysis-badge]][docs-analysis]
8
9
 
9
- ![{A Delorean that we all love}][delorean-image]
10
+ ![A Delorean that we all love][delorean-image]
10
11
 
11
12
  ChronoModel implements what Oracle sells as "Flashback Queries", with standard
12
13
  SQL on free PostgreSQL. Academically speaking, ChronoModel implements a
@@ -62,8 +63,8 @@ All timestamps are _forcibly_ stored in as UTC, bypassing the
62
63
 
63
64
  ## Requirements
64
65
 
65
- * Ruby >= 2.3
66
- * Active Record >= 5.0. See the [detailed supported versions matrix on travis](https://travis-ci.org/ifad/chronomodel)
66
+ * Ruby >= 2.2.2
67
+ * Active Record >= 5.0. See the [detailed supported versions matrix on Ruby GitHub Actions workflows](https://github.com/ifad/chronomodel/blob/master/.github/workflows)
67
68
  * PostgreSQL >= 9.4 (legacy support for 9.3)
68
69
  * The `btree_gist` PostgreSQL extension
69
70
 
@@ -96,6 +97,13 @@ development:
96
97
  username: ...
97
98
  ```
98
99
 
100
+ Configure Active Record in your `config/application.rb` to use the `:sql` schema
101
+ format:
102
+
103
+ ```rb
104
+ config.active_record.schema_format = :sql
105
+ ```
106
+
99
107
  ## Schema creation
100
108
 
101
109
  ChronoModel hooks all `ActiveRecord::Migration` methods to make them temporal
@@ -141,7 +149,7 @@ validity from midnight, January 1st, 1 CE. You can set a specific validity with
141
149
  the `:validity` option:
142
150
 
143
151
  ```ruby
144
- change_table :your_table, :temporal => true, :copy_data => true, :validity => '1977-01-01'
152
+ change_table :your_table, temporal: true, copy_data: true, validity: '1977-01-01'
145
153
  ```
146
154
 
147
155
  Please note that `change_table` requires you to use *old_style* `up` and
@@ -156,9 +164,9 @@ history.
156
164
  You can also choose which fields are to be journaled, passing the following
157
165
  options to `create_table`:
158
166
 
159
- * `:journal => %w( fld1 fld2 .. .. )` - record changes in the history only when changing specified fields
160
- * `:no_journal => %w( fld1 fld2 .. )` - do not record changes to the specified fields
161
- * `:full_journal => true` - record changes to *all* fields, including `updated_at`.
167
+ * `journal: %w( fld1 fld2 .. .. )` - record changes in the history only when changing specified fields
168
+ * `no_journal: %w( fld1 fld2 .. )` - do not record changes to the specified fields
169
+ * `full_journal: true` - record changes to *all* fields, including `updated_at`.
162
170
 
163
171
  These options are stored as JSON in the [COMMENT][pg-comment] area of the
164
172
  public view, alongside with the ChronoModel version that created them.
@@ -195,7 +203,7 @@ to your `create_table`. Example:
195
203
  Include the `ChronoModel::TimeMachine` module in your model.
196
204
 
197
205
  ```ruby
198
- module Country < ActiveRecord::Base
206
+ class Country < ActiveRecord::Base
199
207
  include ChronoModel::TimeMachine
200
208
 
201
209
  has_many :compositions
@@ -334,7 +342,7 @@ ChronoModel::Json.create
334
342
  ## Caveats
335
343
 
336
344
  * Rails 4+ support requires disabling tsrange parsing support, as it
337
- [is broken][r4-tsrange-broken] and [incomplete][r4-tsrange-incomplete]
345
+ [is broken][r4-tsrange-broken] and [incomplete][r4-tsrange-incomplete]
338
346
  as of now, mainly due to a [design clash with ruby][pg-tsrange-and-ruby].
339
347
 
340
348
  * The triggers and temporal indexes cannot be saved in schema.rb. The AR
@@ -345,14 +353,24 @@ ChronoModel::Json.create
345
353
  `db:structure:load`.
346
354
  Two helper tasks are also added, `db:data:dump` and `db:data:load`.
347
355
 
348
- * The choice of using subqueries instead of [Common Table Expressions]
349
- [pg-ctes] was dictated by the fact that CTEs [currently act as an
350
- optimization fence][pg-cte-optimization-fence].
356
+ * The choice of using subqueries instead of [Common Table Expressions][pg-ctes]
357
+ was dictated by the fact that CTEs [currently act as an optimization
358
+ fence][pg-cte-optimization-fence].
351
359
  If it will be possible [to opt-out of the fence][pg-cte-opt-out-fence]
352
360
  in the future, they will be probably be used again as they were [in the
353
361
  past][cm-cte-impl], because the resulting queries were more readable,
354
362
  and do not inhibit using `.from()` on the `AR::Relation`.
355
363
 
364
+ * Foreign keys are not supported. [See issue #174][gh-issue-174]
365
+
366
+ * There may be unexpected results when combining eager loading and joins.
367
+ [See issue #186][gh-issue-186]
368
+
369
+ * Global ID ignores historical objects. [See issue #192][gh-issue-192]
370
+
371
+ * Different historical objects are considered the identical. [See issue
372
+ #206][gh-issue-206]
373
+
356
374
 
357
375
  ## Contributing
358
376
 
@@ -376,41 +394,41 @@ while using it in many important projects.
376
394
  This software is Made in Italy :it: :smile:.
377
395
 
378
396
 
379
- [build-status]: https://travis-ci.org/ifad/chronomodel
380
- [build-status-badge]: https://travis-ci.org/ifad/chronomodel.svg
381
- [code-analysis]: https://codeclimate.com/github/ifad/chronomodel
382
- [code-analysis-badge]: https://codeclimate.com/github/ifad/chronomodel.svg
383
- [docs-analysis]: http://inch-ci.org/github/ifad/chronomodel
384
- [docs-analysis-badge]: http://inch-ci.org/github/ifad/chronomodel.svg?branch=master
397
+ [build-status]: https://github.com/ifad/chronomodel/actions
398
+ [build-status-badge]: https://github.com/ifad/chronomodel/actions/workflows/ruby.yml/badge.svg
399
+ [code-analysis]: https://codeclimate.com/github/ifad/chronomodel/maintainability
400
+ [code-analysis-badge]: https://api.codeclimate.com/v1/badges/cdee7327938dc2eaff99/maintainability
401
+ [docs-analysis]: https://inch-ci.org/github/ifad/chronomodel
402
+ [docs-analysis-badge]: https://inch-ci.org/github/ifad/chronomodel.svg?branch=master
385
403
  [gem-version]: https://rubygems.org/gems/chrono_model
386
404
  [gem-version-badge]: https://badge.fury.io/rb/chrono_model.svg
405
+ [legacy-build-status-badge]: https://github.com/ifad/chronomodel/actions/workflows/legacy_ruby.yml/badge.svg
387
406
  [test-coverage]: https://codeclimate.com/github/ifad/chronomodel
388
407
  [test-coverage-badge]: https://codeclimate.com/github/ifad/chronomodel/badges/coverage.svg
389
408
 
390
409
  [delorean-image]: https://i.imgur.com/DD77F4s.jpg
391
- [rebelle-society]: http://www.rebellesociety.com/2012/10/11/the-writers-way-week-two-facing-procrastination/chronos_oeuvre_grand1/
392
-
393
- [wp-scd-2]: http://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_2
394
- [wp-scd-4]: http://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_4
395
-
396
- [pg-updatable-views]: http://www.postgresql.org/docs/9.4/static/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS
397
- [pg-table-inheritance]: http://www.postgresql.org/docs/9.4/static/ddl-inherit.html
398
- [pg-instead-of-triggers]: http://www.postgresql.org/docs/9.4/static/sql-createtrigger.html
399
- [pg-triggers]: http://www.postgresql.org/docs/9.4/static/trigger-definition.html
400
- [pg-schema]: http://www.postgresql.org/docs/9.4/static/ddl-schemas.html
401
- [pg-current-timestamp]: http://www.postgresql.org/docs/9.4/interactive/functions-datetime.html#FUNCTIONS-DATETIME-TABLE
402
- [pg-partitioning]: http://www.postgresql.org/docs/9.4/static/ddl-partitioning.html
403
- [pg-partitioning-excl-constraints]: http://www.postgresql.org/docs/9.4/static/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION
404
- [pg-gist-indexes]: http://www.postgresql.org/docs/9.4/static/gist.html
405
- [pg-exclusion-constraints]: http://www.postgresql.org/docs/9.4/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
406
- [pg-btree-gist]: http://www.postgresql.org/docs/9.4/static/btree-gist.html
407
- [pg-comment]: http://www.postgresql.org/docs/9.4/static/sql-comment.html
410
+
411
+ [wp-scd-2]: https://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_2
412
+ [wp-scd-4]: https://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_4
413
+
414
+ [pg-updatable-views]: https://www.postgresql.org/docs/9.4/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS
415
+ [pg-table-inheritance]: https://www.postgresql.org/docs/9.4/ddl-inherit.html
416
+ [pg-instead-of-triggers]: https://www.postgresql.org/docs/9.4/sql-createtrigger.html
417
+ [pg-triggers]: https://www.postgresql.org/docs/9.4/trigger-definition.html
418
+ [pg-schema]: https://www.postgresql.org/docs/9.4/ddl-schemas.html
419
+ [pg-current-timestamp]: https://www.postgresql.org/docs/9.4/functions-datetime.html#FUNCTIONS-DATETIME-TABLE
420
+ [pg-partitioning]: https://www.postgresql.org/docs/9.4/ddl-partitioning.html
421
+ [pg-partitioning-excl-constraints]: https://www.postgresql.org/docs/9.4/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION
422
+ [pg-gist-indexes]: https://www.postgresql.org/docs/9.4/gist.html
423
+ [pg-exclusion-constraints]: https://www.postgresql.org/docs/9.4/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
424
+ [pg-btree-gist]: https://www.postgresql.org/docs/9.4/btree-gist.html
425
+ [pg-comment]: https://www.postgresql.org/docs/9.4/sql-comment.html
408
426
  [pg-tsrange-and-ruby]: https://bugs.ruby-lang.org/issues/6864
409
- [pg-ctes]: http://www.postgresql.org/docs/9.4/static/queries-with.html
410
- [pg-cte-optimization-fence]: http://archives.postgresql.org/pgsql-hackers/2012-09/msg00700.php
411
- [pg-cte-opt-out-fence]: http://archives.postgresql.org/pgsql-hackers/2012-10/msg00024.php
412
- [pg-json-type]: http://www.postgresql.org/docs/9.4/static/datatype-json.html
413
- [pg-json-func]: http://www.postgresql.org/docs/9.4/static/functions-json.html
427
+ [pg-ctes]: https://www.postgresql.org/docs/9.4/queries-with.html
428
+ [pg-cte-optimization-fence]: https://www.postgresql.org/message-id/201209191305.44674.db@kavod.com
429
+ [pg-cte-opt-out-fence]: https://www.postgresql.org/message-id/CAHyXU0zpM5+Dsb_pKxDmm-ZoWUAt=SkHHaiK_DBqcmtxTas6Nw@mail.gmail.com
430
+ [pg-json-type]: https://www.postgresql.org/docs/9.4/datatype-json.html
431
+ [pg-json-func]: https://www.postgresql.org/docs/9.4/functions-json.html
414
432
  [pg-json-opclass]: https://github.com/ifad/chronomodel/blob/master/sql/json_ops.sql
415
433
 
416
434
  [r4-tsrange-broken]: https://github.com/rails/rails/pull/13793#issuecomment-34608093
@@ -421,3 +439,7 @@ This software is Made in Italy :it: :smile:.
421
439
  [cm-cte-impl]: https://github.com/ifad/chronomodel/commit/18f4c4b
422
440
 
423
441
  [gh-pzac]: https://github.com/pzac
442
+ [gh-issue-174]: https://github.com/ifad/chronomodel/issues/174
443
+ [gh-issue-186]: https://github.com/ifad/chronomodel/issues/186
444
+ [gh-issue-192]: https://github.com/ifad/chronomodel/issues/192
445
+ [gh-issue-206]: https://github.com/ifad/chronomodel/issues/206
@@ -1,13 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'chrono_model'
2
4
 
3
5
  module ActiveRecord
6
+ # TODO: Remove when dropping Rails < 7.2 compatibility
4
7
  module ConnectionHandling
8
+ def chronomodel_adapter_class
9
+ ChronoModel::Adapter
10
+ end
5
11
 
6
12
  # Install the new adapter in ActiveRecord. This approach is required because
7
13
  # the PG adapter defines +add_column+ itself, thus making impossible to use
8
14
  # super() in overridden Module methods.
9
15
  #
10
16
  def chronomodel_connection(config) # :nodoc:
17
+ return chronomodel_adapter_class.new(config) if ActiveRecord::VERSION::STRING >= '7.1'
18
+
11
19
  conn_params = config.symbolize_keys
12
20
 
13
21
  conn_params.delete_if { |_, v| v.nil? }
@@ -20,24 +28,22 @@ module ActiveRecord
20
28
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
21
29
  conn_params.slice!(*valid_conn_param_keys)
22
30
 
23
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
24
- # so just pass a nil connection object for the time being.
25
- adapter = ChronoModel::Adapter.new(nil, logger, conn_params, config)
31
+ conn = PG.connect(conn_params) if ActiveRecord::VERSION::MAJOR >= 6
32
+
33
+ adapter = ChronoModel::Adapter.new(conn, logger, conn_params, config)
26
34
 
27
35
  unless adapter.chrono_supported?
28
- raise ChronoModel::Error, "Your database server is not supported by ChronoModel. "\
29
- "Currently, only PostgreSQL >= 9.3 is supported."
36
+ raise ChronoModel::Error, 'Your database server is not supported by ChronoModel. ' \
37
+ 'Currently, only PostgreSQL >= 9.3 is supported.'
30
38
  end
31
39
 
32
40
  adapter.chrono_setup!
33
41
 
34
- return adapter
35
- end
42
+ adapter
43
+ rescue ::PG::Error => e
44
+ raise ActiveRecord::NoDatabaseError if e.message.include?(conn_params[:dbname])
36
45
 
37
- module Connectionadapters
38
- ChronoModelAdapter = ::ChronoModel::Adapter
46
+ raise
39
47
  end
40
-
41
48
  end
42
49
  end
43
-
@@ -1,11 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Tasks
3
5
  class ChronomodelDatabaseTasks < PostgreSQLDatabaseTasks
6
+ CHRONOMODEL_SCHEMAS = [
7
+ ChronoModel::Adapter::TEMPORAL_SCHEMA,
8
+ ChronoModel::Adapter::HISTORY_SCHEMA
9
+ ].freeze
4
10
 
5
11
  def structure_dump(*arguments)
6
-
7
- with_chronomodel_schema_search_path do
8
- super(*arguments)
12
+ if schema_search_path.present?
13
+ with_chronomodel_schema_search_path { super }
14
+ else
15
+ super
9
16
  end
10
17
 
11
18
  # The structure.sql includes CREATE SCHEMA statements, but as these are executed
@@ -18,55 +25,82 @@ module ActiveRecord
18
25
  sql = File.read(filename).gsub(/CREATE SCHEMA (?!IF NOT EXISTS)/, '\&IF NOT EXISTS ')
19
26
  File.open(filename, 'w') { |file| file << sql }
20
27
 
21
- remove_sql_header_comments(filename)
28
+ remove_sql_header_comments(filename) if ActiveRecord::VERSION::STRING < '5.1'
22
29
  end
23
30
 
24
31
  def data_dump(target)
25
- set_psql_env
32
+ psql_env
26
33
 
27
- args = ['-c', '-f', target]
28
- args << configuration['database']
34
+ args = ['-c', '-f', target.to_s]
35
+ args << chronomodel_configuration[:database]
29
36
 
30
- run_cmd "pg_dump", args, 'dumping data'
37
+ run_cmd 'pg_dump', args, 'dumping data'
31
38
  end
32
39
 
33
40
  def data_load(source)
34
- set_psql_env
41
+ psql_env
35
42
 
36
43
  args = ['-f', source]
37
- args << configuration['database']
44
+ args << chronomodel_configuration[:database]
38
45
 
39
- run_cmd "psql", args, 'loading data'
46
+ run_cmd 'psql', args, 'loading data'
40
47
  end
41
48
 
42
49
  private
43
50
 
51
+ # In Rails 6.1.x the configuration instance variable is not available
52
+ # and it's been replaced by @configuration_hash (which is frozen).
53
+ def chronomodel_configuration
54
+ @chronomodel_configuration ||=
55
+ if defined?(@configuration_hash)
56
+ @configuration_hash
57
+ else
58
+ configuration.with_indifferent_access
59
+ end
60
+ end
61
+
44
62
  # If a schema search path is defined in the configuration file, it will
45
63
  # be used by the database tasks class to dump only the specified search
46
64
  # path. Here we add also ChronoModel's temporal and history schemas to
47
65
  # the search path and yield.
48
66
  #
49
67
  def with_chronomodel_schema_search_path
50
- original_schema_search_path = configuration['schema_search_path']
51
-
52
- if original_schema_search_path.present?
53
- configuration['schema_search_path'] = original_schema_search_path.split(',').concat([
54
- ChronoModel::Adapter::TEMPORAL_SCHEMA,
55
- ChronoModel::Adapter::HISTORY_SCHEMA
56
- ]).join(',')
57
- end
68
+ patch_configuration!
58
69
 
59
70
  yield
60
-
61
71
  ensure
62
- configuration['schema_search_path'] = original_schema_search_path
72
+ reset_configuration!
63
73
  end
64
74
 
65
- unless method_defined? :remove_sql_header_comments
75
+ def patch_configuration!
76
+ @original_schema_search_path = schema_search_path
77
+
78
+ chronomodel_schema_search_path = "#{schema_search_path},#{CHRONOMODEL_SCHEMAS.join(',')}"
79
+
80
+ if defined?(@configuration_hash)
81
+ @configuration_hash = @configuration_hash.dup
82
+ @configuration_hash[:schema_search_path] = chronomodel_schema_search_path
83
+ @configuration_hash.freeze
84
+ else
85
+ configuration['schema_search_path'] = chronomodel_schema_search_path
86
+ end
87
+ end
88
+
89
+ def reset_configuration!
90
+ if defined?(@configuration_hash)
91
+ @configuration_hash = @configuration_hash.dup
92
+ @configuration_hash[:schema_search_path] = @original_schema_search_path
93
+ @configuration_hash.freeze
94
+ else
95
+ configuration['schema_search_path'] = @original_schema_search_path
96
+ end
97
+ end
98
+
99
+ unless private_instance_methods.include?(:remove_sql_header_comments)
66
100
  def remove_sql_header_comments(filename)
67
101
  sql_comment_begin = '--'
68
102
  removing_comments = true
69
- tempfile = Tempfile.open("uncommented_structure.sql")
103
+ tempfile = Tempfile.open('uncommented_structure.sql')
70
104
  begin
71
105
  File.foreach(filename) do |line|
72
106
  unless removing_comments && (line.start_with?(sql_comment_begin) || line.blank?)
@@ -81,6 +115,13 @@ module ActiveRecord
81
115
  end
82
116
  end
83
117
 
118
+ unless private_instance_methods.include?(:psql_env)
119
+ alias psql_env set_psql_env
120
+ end
121
+
122
+ def schema_search_path
123
+ @schema_search_path ||= chronomodel_configuration[:schema_search_path]
124
+ end
84
125
  end
85
126
  end
86
127
  end