chrono_model 3.0.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 222db0e6673e7171d44dac2163b5048ebf6b459c18fa2a2609a3051fce8c6591
4
- data.tar.gz: 871fdf28717c85267e6b783f1f2d6ba3a24b1261e974e53a25de28d745bc8579
3
+ metadata.gz: b8e1a4cdc29f1fa0435f82c9277674a7085e6caf445ea45673995864038d8a1b
4
+ data.tar.gz: 60df7e87f659a392cf22e8a754b608d7d1fc030d4f63a30dde4af1644ab7917d
5
5
  SHA512:
6
- metadata.gz: 0bf942259052f134ea38de898307a65f20a6b448d8e8ff99a09ae06cd751f9834e1eb22abd4498c880005b8fc91f44ecebea4702be8c174570c50027dce13a75
7
- data.tar.gz: 2b894f82a0cb9687d59dca9c3427a854cf6dc02191a08df71a215dfc577a8d1dd58632d44eb179395f097aa7d2391a7c738968d0f4c68f16cc14fffd2d18e034
6
+ metadata.gz: 22333be09e65a5347f3413976eea41581b76c7ad94dc138924cd7e5e8f6cb36cd572567f6b195c896a14262d96a3623f7b49116ed2856dcf58441ad4f43ceff2
7
+ data.tar.gz: 9d96851d9dc3947e485a1c09c89eb06c0e5b07a0ddf9c43e75bdd1c3bb13ca4df599e7f4e8050268ba30c00de7878d1a6c496db1f70b2e3e9c76f0bb10d3d63c
data/README.md CHANGED
@@ -67,24 +67,19 @@ All timestamps are _forcibly_ stored in as UTC, bypassing the
67
67
  * PostgreSQL >= 9.4
68
68
  * The `btree_gist` PostgreSQL extension
69
69
 
70
- With Homebrew:
71
-
72
- brew install postgres
73
-
74
- With apt:
75
-
76
- apt-get install postgresql-11
77
-
78
70
  ## Installation
79
71
 
80
72
  Add this line to your application's Gemfile:
81
73
 
82
- gem 'chrono_model'
74
+ ```ruby
75
+ gem 'chrono_model'
76
+ ```
83
77
 
84
78
  And then execute:
85
79
 
86
- $ bundle
87
-
80
+ ```sh
81
+ $ bundle
82
+ ```
88
83
 
89
84
  ## Configuration
90
85
 
@@ -99,7 +94,7 @@ development:
99
94
  Configure Active Record in your `config/application.rb` to use the `:sql` schema
100
95
  format:
101
96
 
102
- ```rb
97
+ ```ruby
103
98
  config.active_record.schema_format = :sql
104
99
  ```
105
100
 
@@ -121,7 +116,7 @@ view and all the trigger machinery. Every other housekeeping of the temporal
121
116
  structure is handled behind the scenes by the other schema statements. E.g.:
122
117
 
123
118
  * `rename_table` - renames tables, views, sequences, indexes and triggers
124
- * `drop_table` - drops the temporal table and all dependant objects
119
+ * `drop_table` - drops the temporal table and all dependent objects
125
120
  * `add_column` - adds the column to the current table and updates triggers
126
121
  * `rename_column` - renames the current table column and updates the triggers
127
122
  * `remove_column` - removes the current table column and updates the triggers
@@ -192,10 +187,12 @@ occur (see https://github.com/ifad/chronomodel/issues/71).
192
187
  In such cases, ensure to add `no_journal: %w( your_counter_cache_column_name )`
193
188
  to your `create_table`. Example:
194
189
 
195
- create_table 'sections', temporal: true, no_journal: %w( articles_count ) do |t|
196
- t.string :name
197
- t.integer :articles_count, default: 0
198
- end
190
+ ```ruby
191
+ create_table 'sections', temporal: true, no_journal: %w[articles_count] do |t|
192
+ t.string :name
193
+ t.integer :articles_count, default: 0
194
+ end
195
+ ```
199
196
 
200
197
  ## Data querying
201
198
 
@@ -282,12 +279,12 @@ cannot be deleted.
282
279
 
283
280
  ChronoModel currently performs upgrades by dropping and re-creating the views
284
281
  that give access to current data. If you have built other database objects on
285
- these views, the upgrade cannot be performed automatically as the dependant
282
+ these views, the upgrade cannot be performed automatically as the dependent
286
283
  objects must be dropped first.
287
284
 
288
285
  When booting, ChronoModel will issue a warning in your logs about the need of
289
286
  a structure upgrade. Structure usually changes across versions. In this case,
290
- you need to set up a rake task that drops your dependant objects, runs
287
+ you need to set up a rake task that drops your dependent objects, runs
291
288
  ChronoModel.upgrade! and then re-creates them.
292
289
 
293
290
  A migration system should be introduced, but it is seen as overkill for now,
@@ -304,7 +301,9 @@ You need to connect as a database superuser, because specs need to create the
304
301
 
305
302
  To run the full test suite, use
306
303
 
307
- rake
304
+ ```sh
305
+ $ rake
306
+ ```
308
307
 
309
308
  SQL queries are logged to `spec/debug.log`. If you want to see them in your
310
309
  output, set the `VERBOSE=true` environment variable.
@@ -313,7 +312,9 @@ Some tests check the nominal execution of rake tasks within a test Rails app,
313
312
  and those are quite time consuming. You can run the full ChronoModel tests
314
313
  only against ActiveRecord by using
315
314
 
316
- rspec spec/chrono_model
315
+ ```sh
316
+ $ rspec spec/chrono_model
317
+ ```
317
318
 
318
319
  Ensure to run the full test suite before pushing.
319
320
 
@@ -328,10 +329,6 @@ Ensure to run the full test suite before pushing.
328
329
  of an object at a specific point in time within the application could
329
330
  lead to inaccuracies.
330
331
 
331
- * Rails 4+ support requires disabling tsrange parsing support, as it
332
- [is broken][r4-tsrange-broken] and [incomplete][r4-tsrange-incomplete]
333
- as of now, mainly due to a [design clash with ruby][pg-tsrange-and-ruby].
334
-
335
332
  * The triggers and temporal indexes cannot be saved in schema.rb. The AR
336
333
  schema dumper is quite basic, and it isn't (currently) extensible.
337
334
  As we're using many database-specific features, Chronomodel forces the
@@ -351,7 +348,7 @@ Ensure to run the full test suite before pushing.
351
348
  * Foreign keys are not supported. [See issue #174][gh-issue-174]
352
349
 
353
350
  * There may be unexpected results when combining eager loading and joins.
354
- [See issue #186][gh-issue-186]
351
+ [See issue #186][gh-issue-186]
355
352
 
356
353
  * Global ID ignores historical objects. [See issue #192][gh-issue-192]
357
354
 
@@ -412,7 +409,6 @@ This software is Made in Italy :it: :smile:.
412
409
  [pg-exclusion-constraints]: https://www.postgresql.org/docs/9.4/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
413
410
  [pg-btree-gist]: https://www.postgresql.org/docs/9.4/btree-gist.html
414
411
  [pg-comment]: https://www.postgresql.org/docs/9.4/sql-comment.html
415
- [pg-tsrange-and-ruby]: https://bugs.ruby-lang.org/issues/6864
416
412
  [pg-ctes]: https://www.postgresql.org/docs/9.4/queries-with.html
417
413
  [pg-cte-optimization-fence]: https://www.postgresql.org/message-id/201209191305.44674.db@kavod.com
418
414
  [pg-cte-opt-out-fence]: https://www.postgresql.org/message-id/CAHyXU0zpM5+Dsb_pKxDmm-ZoWUAt=SkHHaiK_DBqcmtxTas6Nw@mail.gmail.com
@@ -420,9 +416,6 @@ This software is Made in Italy :it: :smile:.
420
416
  [pg-json-func]: https://www.postgresql.org/docs/9.4/functions-json.html
421
417
  [pg-json-opclass]: https://github.com/ifad/chronomodel/blob/master/sql/json_ops.sql
422
418
 
423
- [r4-tsrange-broken]: https://github.com/rails/rails/pull/13793#issuecomment-34608093
424
- [r4-tsrange-incomplete]: https://github.com/rails/rails/issues/14010
425
-
426
419
  [cm-readme-sql]: https://github.com/ifad/chronomodel/blob/master/README.sql
427
420
  [cm-timemachine]: https://github.com/ifad/chronomodel/blob/master/lib/chrono_model/time_machine.rb
428
421
  [cm-cte-impl]: https://github.com/ifad/chronomodel/commit/18f4c4b
@@ -42,7 +42,7 @@ module ChronoModel
42
42
  return if upgrade.empty?
43
43
 
44
44
  logger.warn 'ChronoModel: There are tables needing a structure upgrade, and ChronoModel structures need to be recreated.'
45
- logger.warn 'ChronoModel: Please run ChronoModel.upgrade! to attempt the upgrade. If you have dependant database objects'
45
+ logger.warn 'ChronoModel: Please run ChronoModel.upgrade! to attempt the upgrade. If you have dependent database objects'
46
46
  logger.warn 'ChronoModel: the upgrade will fail and you have to drop the dependent objects, run .upgrade! and create them'
47
47
  logger.warn 'ChronoModel: again. Sorry. Some features or the whole library may not work correctly until upgrade is complete.'
48
48
  logger.warn "ChronoModel: Tables pending upgrade: #{upgrade}"
@@ -7,7 +7,6 @@ require 'chrono_model/adapter/migrations_modules/stable'
7
7
 
8
8
  require 'chrono_model/adapter/ddl'
9
9
  require 'chrono_model/adapter/indexes'
10
- require 'chrono_model/adapter/tsrange'
11
10
  require 'chrono_model/adapter/upgrade'
12
11
 
13
12
  module ChronoModel
@@ -19,7 +18,6 @@ module ChronoModel
19
18
  include ChronoModel::Adapter::Migrations
20
19
  include ChronoModel::Adapter::DDL
21
20
  include ChronoModel::Adapter::Indexes
22
- include ChronoModel::Adapter::TSRange
23
21
  include ChronoModel::Adapter::Upgrade
24
22
 
25
23
  # The schema holding current data
@@ -6,19 +6,6 @@ module ChronoModel
6
6
 
7
7
  ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d+))?\z/
8
8
 
9
- # rubocop:disable Style/PerlBackrefs
10
- def string_to_utc_time(string)
11
- return string if string.is_a?(Time)
12
-
13
- return unless string =~ ISO_DATETIME
14
-
15
- # .1 is .100000, not .000001
16
- usec = $7.ljust(6, '0') unless $7.nil?
17
-
18
- Time.utc $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, usec.to_i
19
- end
20
- # rubocop:enable Style/PerlBackrefs
21
-
22
9
  def time_to_utc_string(time)
23
10
  time.to_fs(:db) << '.' << format('%06d', time.usec)
24
11
  end
@@ -107,7 +107,7 @@ module ChronoModel
107
107
  # name has the "::History" suffix but that is never going to be
108
108
  # present in the data.
109
109
  #
110
- # As such it is overriden here to return the same contents that
110
+ # As such it is overridden here to return the same contents that
111
111
  # the parent would have returned.
112
112
  delegate :sti_name, to: :superclass
113
113
 
@@ -132,7 +132,7 @@ module ChronoModel
132
132
  end
133
133
 
134
134
  # The history id is `hid`, but this cannot set as primary key
135
- # or temporal assocations will break. Solutions are welcome.
135
+ # or temporal associations will break. Solutions are welcome.
136
136
  def id
137
137
  hid
138
138
  end
@@ -204,24 +204,24 @@ module ChronoModel
204
204
  self.class.superclass.find(rid)
205
205
  end
206
206
 
207
- def record # :nodoc:
208
- ActiveSupport::Deprecation.warn '.record is deprecated in favour of .current_version'
209
- current_version
210
- end
211
-
207
+ # Return `nil` instead of -Infinity/Infinity to preserve current
208
+ # Chronomodel behaviour and avoid failures with Rails 7.0 and
209
+ # unbounded time ranges
210
+ #
211
+ # Check if `begin` and `end` are `Time` because validity is a `tsrange`
212
+ # column, so it is either `Time`, `nil`, and in some cases Infinity.
213
+ #
214
+ # Ref: rails/rails#45099
215
+ # TODO: consider removing when Rails 7.0 support will be dropped
212
216
  def valid_from
213
- validity.first
217
+ validity.begin if validity.begin.is_a?(Time)
214
218
  end
215
219
 
216
220
  def valid_to
217
- validity.last
221
+ validity.end if validity.end.is_a?(Time)
218
222
  end
219
223
  alias as_of_time valid_to
220
224
 
221
- def recorded_at
222
- ChronoModel::Conversions.string_to_utc_time attributes_before_type_cast['recorded_at']
223
- end
224
-
225
225
  # Starting from Rails 6.0, `.read_attribute` will use the memoized
226
226
  # `primary_key` if it detects that the attribute name is `id`.
227
227
  #
@@ -46,7 +46,8 @@ module ChronoModel
46
46
  end
47
47
 
48
48
  def time_for_time_query(t, column)
49
- if t == :now || t == :today
49
+ case t
50
+ when :now, :today
50
51
  now_for_column(column)
51
52
  else
52
53
  quoted_t = connection.quote(connection.quoted_date(t))
@@ -81,9 +81,7 @@ module ChronoModel
81
81
  sql << " LIMIT #{options[:limit].to_i}" if options.key?(:limit)
82
82
 
83
83
  connection.on_schema(Adapter::HISTORY_SCHEMA) do
84
- connection.select_values(sql, "#{name} periods").map! do |ts|
85
- Conversions.string_to_utc_time ts
86
- end
84
+ connection.select_values(sql, "#{name} periods")
87
85
  end
88
86
  end
89
87
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChronoModel
4
- VERSION = '3.0.1'
4
+ VERSION = '4.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcello Barnaba
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-02-26 00:00:00.000000000 Z
12
+ date: 2024-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -72,7 +72,6 @@ files:
72
72
  - lib/chrono_model/adapter/indexes.rb
73
73
  - lib/chrono_model/adapter/migrations.rb
74
74
  - lib/chrono_model/adapter/migrations_modules/stable.rb
75
- - lib/chrono_model/adapter/tsrange.rb
76
75
  - lib/chrono_model/adapter/upgrade.rb
77
76
  - lib/chrono_model/chrono.rb
78
77
  - lib/chrono_model/conversions.rb
@@ -117,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
116
  - !ruby/object:Gem::Version
118
117
  version: '0'
119
118
  requirements: []
120
- rubygems_version: 3.5.5
119
+ rubygems_version: 3.5.9
121
120
  signing_key:
122
121
  specification_version: 4
123
122
  summary: Temporal extensions (SCD Type II) for Active Record
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ChronoModel
4
- class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
5
- module TSRange
6
- # HACK: Redefine tsrange parsing support, as it is broken currently.
7
- #
8
- # This self-made API is here because currently AR4 does not support
9
- # open-ended ranges. The reasons are poor support in Ruby:
10
- #
11
- # https://bugs.ruby-lang.org/issues/6864
12
- #
13
- # and an instable interface in Active Record:
14
- #
15
- # https://github.com/rails/rails/issues/13793
16
- # https://github.com/rails/rails/issues/14010
17
- #
18
- # so, for now, we are implementing our own.
19
- #
20
- class Type < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range
21
- OID = 3908
22
-
23
- def cast_value(value)
24
- return if value == 'empty'
25
- return value if value.is_a?(::Array)
26
-
27
- extracted = extract_bounds(value)
28
-
29
- from = Conversions.string_to_utc_time extracted[:from]
30
- to = Conversions.string_to_utc_time extracted[:to]
31
-
32
- [from, to]
33
- end
34
-
35
- def extract_bounds(value)
36
- from, to = value[1..-2].split(',')
37
-
38
- from_bound =
39
- if value[1] == ',' || from == '-infinity'
40
- nil
41
- else
42
- from[1..-2]
43
- end
44
-
45
- to_bound =
46
- if value[-2] == ',' || to == 'infinity'
47
- nil
48
- else
49
- to[1..-2]
50
- end
51
-
52
- {
53
- from: from_bound,
54
- to: to_bound
55
- }
56
- end
57
- end
58
-
59
- def initialize_type_map(m = type_map)
60
- super.tap do
61
- typ = ChronoModel::Adapter::TSRange::Type
62
- oid = typ::OID
63
-
64
- ar_type = type_map.fetch(oid)
65
- cm_type = typ.new(ar_type.subtype, ar_type.type)
66
-
67
- type_map.register_type oid, cm_type
68
- end
69
- end
70
- end
71
- end
72
- end