chrono_model 0.8.2 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9e2f6c55882d65be9f8fb0ff2ef4bbc2260d7d0a
4
- data.tar.gz: f1d2aa3879ea83b657a7c3954ad1335817c8f8ce
3
+ metadata.gz: 46c5c3d3c7a91b99e025dbf9dbd961962cc36914
4
+ data.tar.gz: 0d120a3f68b711f77031e3e3df5d203efd812a23
5
5
  SHA512:
6
- metadata.gz: 3b5c36f3ce8e4947038141179a04361f0b3eec0d36a7f50334a9b86903d904a852434ddfad69fa957cc6e2164e0d7dee89aa91593be4b8d72cafdfbcf7cce4d6
7
- data.tar.gz: e1e2f4938157129ff3eaea888b67716ac1e24a9ee45bd0345c6aa1b1c381f309635f1f855eeccfb3b40d5b8008ca7ede2e66460a630306adebcd18ff4b16b2e9
6
+ metadata.gz: 17f0b8f514b94579d9cd5f4b32fad9c5d8bed4d95282c361acf10f930d7f1fd64d503139b6eb6f5bde6e9c2c51134f3bcfafdb5034b0dde95af1814bdae563c4
7
+ data.tar.gz: 4f7d2ee5109d774d3217cec430bf74964b269e9fff6c8b790a6a328fd7bcb043c3e18c87defc3601a4c85e2c2a597bf314c4e0a0251054ca923d4250cc89b6c2
data/.travis.yml CHANGED
@@ -1,20 +1,21 @@
1
1
  rvm:
2
2
  - 1.9.3
3
3
  - 2.0.0
4
- - 2.1.1
4
+ - 2.1.7
5
+ - 2.2.3
5
6
 
6
- services:
7
- - postgresql
7
+ sudo: false
8
8
 
9
- bundler_args: --without development
9
+ language: ruby
10
+ cache: bundler
10
11
 
11
- before_install:
12
- - sudo service postgresql stop
13
- - sudo apt-get update
14
- - sudo apt-get install postgresql-9.3 postgresql-plpython-9.3
15
- - sudo service postgresql start 9.3
12
+ addons:
13
+ postgresql: "9.4"
14
+ apt:
15
+ packages: postgresql-plpython-9.4
16
16
 
17
17
  before_script:
18
18
  - psql -c "CREATE DATABASE chronomodel;" -U postgres
19
19
 
20
- script: bundle exec rake TEST_CONFIG=./spec/config.travis.yml
20
+ script:
21
+ - bundle exec rake TEST_CONFIG=./spec/config.travis.yml CODECLIMATE_REPO_TOKEN=dedfb7472ee410eec459bff3681d9a8fd8dd237e9bd7e8675a7c8eb7e253bba9
data/Gemfile CHANGED
@@ -2,22 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in chrono_model.gemspec
4
4
  gemspec
5
-
6
- group :development do
7
- gem 'pry'
8
- gem 'hirb'
9
-
10
- gem(
11
- case RUBY_VERSION.to_f
12
- when 1.8 then 'ruby-debug'
13
- when 1.9 then 'debugger'
14
- else 'byebug'
15
- end
16
- )
17
- end
18
-
19
- group :test do
20
- gem 'rspec'
21
- gem 'rake'
22
- gem 'fuubar'
23
- end
data/README.md CHANGED
@@ -1,6 +1,17 @@
1
- # Temporal database system on PostgreSQL using [updatable views][], [table inheritance][] and [INSTEAD OF triggers][]. [![Build Status][build-status-badge]][build-status] [![Dependency Status][deps-status-badge]][deps-status] [![Code Climate][code-analysis-badge]][code-analysis]
1
+ # Temporal database system on PostgreSQL using [updatable views][], [table inheritance][] and [INSTEAD OF triggers][].
2
2
 
3
- ChronoModel does what Oracle sells as "Flashback Queries", but with standard
3
+ [![Build Status][build-status-badge]][build-status]
4
+ [![Dependency Status][deps-status-badge]][deps-status]
5
+ [![Code Climate][code-analysis-badge]][code-analysis]
6
+ [![Test Coverage][test-coverage-badge]][test-coverage]
7
+ [![Inlinedocs][docs-analysis-badge]][docs-analysis]
8
+
9
+ ![{chronos - greek god of time}][chronos-image]
10
+
11
+ > Chronos, the greek god of time.
12
+ > Courtesy of [REBELLE SOCIETY][rebelle-society]
13
+
14
+ ChronoModel implements what Oracle sells as "Flashback Queries", with standard
4
15
  SQL on free PostgreSQL. Academically speaking, ChronoModel implements a
5
16
  [Type-2 Slowly-Changing Dimension][wp-scd-2] with [history tables][wp-scd-4].
6
17
 
@@ -52,10 +63,17 @@ All timestamps are _forcibly_ stored in as UTC, bypassing the
52
63
  ## Requirements
53
64
 
54
65
  * Ruby >= 2.0 (1.9 is still supported, but support will be dropped soon).
55
- * Active Record = 4.0
66
+ * Active Record = 4.2
56
67
  * PostgreSQL >= 9.3
57
- * The `btree_gist` PostgreSQL extension
68
+ * The `btree_gist` and `plpython` PostgreSQL extensions:
69
+
70
+ With Homebrew:
58
71
 
72
+ brew install --with-python postgres
73
+
74
+ With Apt:
75
+
76
+ apt-get install postgresql-plpython
59
77
 
60
78
  ## Installation
61
79
 
@@ -236,7 +254,7 @@ History objects can be changed and `.save`d just like any other record.
236
254
 
237
255
  ## Running tests
238
256
 
239
- You need a running PostgreSQL 9.3 instance. Create `spec/config.yml` with the
257
+ You need a running PostgreSQL >= 9.3 instance. Create `spec/config.yml` with the
240
258
  connection authentication details (use `spec/config.yml.example` as template).
241
259
 
242
260
  You need to connect as a database superuser, because specs need to create the
@@ -257,6 +275,10 @@ comparison of JSON objects [implemented in pl/python][json-opclass].
257
275
  To load the opclass you can use the `ChronoModel::Json.create`
258
276
  convenience method. If you don't use JSON don't bother doing this.
259
277
 
278
+ If you are on Postgres 9.4, you are strongly encouraged to use JSONB,
279
+ that has an equality operator built-in, it's faster and stricter, and
280
+ offers many more indexing abilities and better performance than JSON.
281
+
260
282
  ## Caveats
261
283
 
262
284
  * Rails 4 support requires disabling tsrange parsing support, as it
@@ -264,7 +286,7 @@ convenience method. If you don't use JSON don't bother doing this.
264
286
  as of now, mainly due to a [design clash with ruby][pg-tsrange-and-ruby].
265
287
 
266
288
  * There is (yet) no upgrade path from [v0.5][chronomodel-0.5],
267
- (PG 9.0-compatible, `box()` and hacks) to v0.6 and up (9.3-only, `tsrange`
289
+ (PG 9.0-compatible, `box()` and hacks) to v0.6 and up (>=9.3-only, `tsrange`
268
290
  and _less_ hacks).
269
291
 
270
292
  * The triggers and temporal indexes cannot be saved in schema.rb. The AR
@@ -296,40 +318,47 @@ convenience method. If you don't use JSON don't bother doing this.
296
318
 
297
319
 
298
320
  [build-status]: https://travis-ci.org/ifad/chronomodel
299
- [build-status-badge]: https://travis-ci.org/ifad/chronomodel.png?branch=master
321
+ [build-status-badge]: https://travis-ci.org/ifad/chronomodel.svg
300
322
  [deps-status]: https://gemnasium.com/ifad/chronomodel
301
- [deps-status-badge]: https://gemnasium.com/ifad/chronomodel.png
323
+ [deps-status-badge]: https://gemnasium.com/ifad/chronomodel.svg
302
324
  [code-analysis]: https://codeclimate.com/github/ifad/chronomodel
303
- [code-analysis-badge]: https://codeclimate.com/github/ifad/chronomodel.png
304
-
305
- [updatable views]: http://www.postgresql.org/docs/9.3/static/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS
306
- [table inheritance]: http://www.postgresql.org/docs/9.3/static/ddl-inherit.html
307
- [INSTEAD OF triggers]: http://www.postgresql.org/docs/9.3/static/sql-createtrigger.html
325
+ [code-analysis-badge]: https://codeclimate.com/github/ifad/chronomodel.svg
326
+ [docs-analysis]: http://inch-ci.org/github/ifad/chronomodel
327
+ [docs-analysis-badge]: http://inch-ci.org/github/ifad/chronomodel.svg?branch=master
328
+ [test-coverage]: https://codeclimate.com/github/ifad/chronomodel
329
+ [test-coverage-badge]: https://codeclimate.com/github/ifad/chronomodel/badges/coverage.svg
330
+
331
+ [chronos-image]: http://i.imgur.com/8NObYiZl.jpg
332
+ [rebelle-society]: http://www.rebellesociety.com/2012/10/11/the-writers-way-week-two-facing-procrastination/chronos_oeuvre_grand1/
333
+
334
+ [updatable views]: http://www.postgresql.org/docs/9.4/static/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS
335
+ [table inheritance]: http://www.postgresql.org/docs/9.4/static/ddl-inherit.html
336
+ [INSTEAD OF triggers]: http://www.postgresql.org/docs/9.4/static/sql-createtrigger.html
308
337
  [wp-scd-2]: http://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_2
309
338
  [wp-scd-4]: http://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_4
310
- [triggers]: http://www.postgresql.org/docs/9.3/static/trigger-definition.html
311
- [schema]: http://www.postgresql.org/docs/9.3/static/ddl-schemas.html
312
- [inherits]: http://www.postgresql.org/docs/9.3/static/ddl-inherit.html
313
- [`current_timestamp`]: http://www.postgresql.org/docs/9.3/interactive/functions-datetime.html#FUNCTIONS-DATETIME-TABLE
339
+ [triggers]: http://www.postgresql.org/docs/9.4/static/trigger-definition.html
340
+ [schema]: http://www.postgresql.org/docs/9.4/static/ddl-schemas.html
341
+ [inherits]: http://www.postgresql.org/docs/9.4/static/ddl-inherit.html
342
+ [`current_timestamp`]: http://www.postgresql.org/docs/9.4/interactive/functions-datetime.html#FUNCTIONS-DATETIME-TABLE
314
343
 
315
- [Partitioning]: http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html)
316
- [partitioning-excl-constraints]: http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION
344
+ [Partitioning]: http://www.postgresql.org/docs/9.4/static/ddl-partitioning.html)
345
+ [partitioning-excl-constraints]: http://www.postgresql.org/docs/9.4/static/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION
317
346
  [README.sql]: https://github.com/ifad/chronomodel/blob/master/README.sql
318
- [GiST indexes]: http://www.postgresql.org/docs/9.3/static/gist.html
319
- [exclusion constraints]: http://www.postgresql.org/docs/9.3/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
320
- [btree_gist]: http://www.postgresql.org/docs/9.3/static/btree-gist.html
321
- [COMMENT]: http://www.postgresql.org/docs/9.3/static/sql-comment.html
347
+ [GiST indexes]: http://www.postgresql.org/docs/9.4/static/gist.html
348
+ [exclusion constraints]: http://www.postgresql.org/docs/9.4/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
349
+ [btree_gist]: http://www.postgresql.org/docs/9.4/static/btree-gist.html
350
+ [COMMENT]: http://www.postgresql.org/docs/9.4/static/sql-comment.html
322
351
  [TimeMachine]: https://github.com/ifad/chronomodel/blob/master/lib/chrono_model/time_machine.rb
323
352
 
324
353
  [r4-tsrange-broken]: https://github.com/rails/rails/pull/13793#issuecomment-34608093
325
354
  [r4-tsrange-incomplete]: https://github.com/rails/rails/issues/14010)
326
355
  [pg-tsrange-and-ruby]: https://bugs.ruby-lang.org/issues/6864
327
356
  [chronomodel-0.5]: https://github.com/ifad/chronomodel/tree/c2daa0f
328
- [Common Table Expressions]: http://www.postgresql.org/docs/9.3/static/queries-with.html
357
+ [Common Table Expressions]: http://www.postgresql.org/docs/9.4/static/queries-with.html
329
358
  [cte-optimization-fence]: http://archives.postgresql.org/pgsql-hackers/2012-09/msg00700.php
330
359
  [cte-opt-out-fence]: http://archives.postgresql.org/pgsql-hackers/2012-10/msg00024.php
331
360
  [chronomodel-cte-impl]: https://github.com/ifad/chronomodel/commit/18f4c4b
332
361
 
333
- [json-type]: http://www.postgresql.org/docs/9.3/static/datatype-json.html
334
- [json-func]: http://www.postgresql.org/docs/9.3/static/functions-json.html
362
+ [json-type]: http://www.postgresql.org/docs/9.4/static/datatype-json.html
363
+ [json-func]: http://www.postgresql.org/docs/9.4/static/functions-json.html
335
364
  [json-opclass]: https://github.com/ifad/chronomodel/blob/master/sql/json_ops.sql
data/chrono_model.gemspec CHANGED
@@ -4,9 +4,9 @@ require File.expand_path('../lib/chrono_model/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ['Marcello Barnaba', 'Peter Joseph Brindisi']
6
6
  gem.email = ['vjt@openssl.it', 'p.brindisi@ifad.org']
7
- gem.description = %q{Give your models as-of date temporal extensions. Built entirely for PostgreSQL >= 9.0}
7
+ gem.description = %q{Give your models as-of date temporal extensions. Built entirely for PostgreSQL >= 9.3}
8
8
  gem.summary = %q{Temporal extensions (SCD Type II) for Active Record}
9
- gem.homepage = 'http://github.com/ifad/chronomodel'
9
+ gem.homepage = 'https://github.com/ifad/chronomodel'
10
10
 
11
11
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
12
  gem.files = `git ls-files`.split("\n")
@@ -15,7 +15,15 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = ChronoModel::VERSION
17
17
 
18
- gem.add_dependency "activerecord", "~> 4.0.0"
18
+ gem.add_dependency "activerecord", "~> 4.2.0"
19
19
  gem.add_dependency "pg"
20
20
  gem.add_dependency "multi_json"
21
+
22
+ gem.add_development_dependency 'pry'
23
+ gem.add_development_dependency 'hirb'
24
+ gem.add_development_dependency RUBY_VERSION.to_f == 1.9 ? 'debugger' : 'byebug'
25
+ gem.add_development_dependency 'rspec'
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'fuubar'
28
+ gem.add_development_dependency 'codeclimate-test-reporter'
21
29
  end
data/lib/chrono_model.rb CHANGED
@@ -23,6 +23,10 @@ if RUBY_VERSION.to_i >= 2
23
23
  ActiveRecord::Associations::Association.instance_eval do
24
24
  prepend ChronoModel::Patches::Association
25
25
  end
26
+
27
+ ActiveRecord::Relation.instance_eval do
28
+ prepend ChronoModel::Patches::Relation
29
+ end
26
30
  else
27
31
  ActiveSupport::Deprecation.warn 'Ruby 1.9 is deprecated. Please update your Ruby <3'
28
32
 
@@ -32,5 +36,11 @@ else
32
36
  end
33
37
 
34
38
  ActiveRecord::Associations::Association = ChronoModel::Patches::AssociationPatch
39
+
40
+ class ChronoModel::Patches::RelationPatch < ActiveRecord::Relation
41
+ include ChronoModel::Patches::Relation
42
+ end
43
+
44
+ ActiveRecord::Relation = ChronoModel::Patches::RelationPatch
35
45
  end
36
46
  end
@@ -1,6 +1,8 @@
1
1
  require 'active_record'
2
2
  require 'active_record/connection_adapters/postgresql_adapter'
3
3
 
4
+ require 'multi_json'
5
+
4
6
  module ChronoModel
5
7
 
6
8
  # This class implements all ActiveRecord::ConnectionAdapters::SchemaStatements
@@ -285,7 +287,19 @@ module ChronoModel
285
287
  chrono_alter(table_name) { super }
286
288
  end
287
289
 
288
- # Runs column_definitions, primary_key and indexes in the temporal schema,
290
+ # Runs column_definitions in the temporal schema, as the table there
291
+ # defined is the source for this information.
292
+ #
293
+ # The default search path is included however, since the table
294
+ # may reference types defined in other schemas, which result in their
295
+ # names becoming schema qualified, which will cause type resolutions to fail.
296
+ #
297
+ define_method(:column_definitions) do |table_name|
298
+ return super(table_name) unless is_chrono?(table_name)
299
+ on_schema(TEMPORAL_SCHEMA + ',' + self.schema_search_path, false) { super(table_name) }
300
+ end
301
+
302
+ # Runs primary_key, indexes and default_sequence_name in the temporal schema,
289
303
  # as the table there defined is the source for this information.
290
304
  #
291
305
  # Moreover, the PostgreSQLAdapter +indexes+ method uses current_schema(),
@@ -294,10 +308,11 @@ module ChronoModel
294
308
  # Schema nesting is disabled on these calls, make sure to fetch metadata
295
309
  # from the first caller's selected schema and not from the current one.
296
310
  #
297
- [:column_definitions, :primary_key, :indexes].each do |method|
298
- define_method(method) do |table_name|
299
- return super(table_name) unless is_chrono?(table_name)
300
- _on_temporal_schema(false) { super(table_name) }
311
+ [:primary_key, :indexes, :default_sequence_name].each do |method|
312
+ define_method(method) do |*args|
313
+ table_name = args.first
314
+ return super(*args) unless is_chrono?(table_name)
315
+ _on_temporal_schema(false) { super(*args) }
301
316
  end
302
317
  end
303
318
 
@@ -454,6 +469,12 @@ module ChronoModel
454
469
  end
455
470
  end
456
471
 
472
+ def chrono_setup!
473
+ chrono_create_schemas
474
+
475
+ chrono_upgrade_structure!
476
+ end
477
+
457
478
  # HACK: Redefine tsrange parsing support, as it is broken currently.
458
479
  #
459
480
  # This self-made API is here because currently AR4 does not support
@@ -468,18 +489,13 @@ module ChronoModel
468
489
  #
469
490
  # so, for now, we are implementing our own.
470
491
  #
471
- class TSRange < OID::Type
472
- def extract_bounds(value)
473
- from, to = value[1..-2].split(',')
474
- {
475
- from: (value[1] == ',' || from == '-infinity') ? nil : from[1..-2],
476
- to: (value[-2] == ',' || to == 'infinity') ? nil : to[1..-2],
477
- #exclude_start: (value[0] == '('),
478
- #exclude_end: (value[-1] == ')')
479
- }
480
- end
492
+ class TSRange < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range
493
+ OID = 3908
494
+
495
+ def cast_value(value)
496
+ return if value == 'empty'
497
+ return value if value.is_a?(::Array)
481
498
 
482
- def type_cast(value)
483
499
  extracted = extract_bounds(value)
484
500
 
485
501
  from = Conversions.string_to_utc_time extracted[:from]
@@ -487,13 +503,23 @@ module ChronoModel
487
503
 
488
504
  [from, to]
489
505
  end
506
+
507
+ def extract_bounds(value)
508
+ from, to = value[1..-2].split(',')
509
+ {
510
+ from: (value[1] == ',' || from == '-infinity') ? nil : from[1..-2],
511
+ to: (value[-2] == ',' || to == 'infinity') ? nil : to[1..-2],
512
+ }
513
+ end
490
514
  end
491
515
 
492
- def chrono_setup!
493
- chrono_create_schemas
494
- chrono_setup_type_map
516
+ def initialize_type_map(type_map)
517
+ super.tap do
518
+ ar_type = type_map.fetch(TSRange::OID)
519
+ cm_type = TSRange.new(ar_type.subtype, ar_type.type)
495
520
 
496
- chrono_upgrade_structure!
521
+ type_map.register_type TSRange::OID, cm_type
522
+ end
497
523
  end
498
524
 
499
525
  # Copy the indexes from the temporal table to the history table if the indexes
@@ -529,12 +555,6 @@ module ChronoModel
529
555
  end
530
556
  end
531
557
 
532
- # Adds the above TSRange class to the PG Adapter OID::TYPE_MAP
533
- #
534
- def chrono_setup_type_map
535
- OID::TYPE_MAP[3908] = TSRange.new
536
- end
537
-
538
558
  # Upgrades existing structure for each table, if required.
539
559
  # TODO: allow upgrades from pre-0.6 structure with box() and stuff.
540
560
  #
@@ -3,6 +3,60 @@ require 'active_record'
3
3
  module ChronoModel
4
4
  module Patches
5
5
 
6
+ module AsOfTimeHolder
7
+ # Sets the virtual 'as_of_time' attribute to the given time, converting to UTC.
8
+ #
9
+ # See ChronoModel::Patches::AsOfTimeHolder
10
+ #
11
+ def as_of_time!(time)
12
+ @_as_of_time = time.utc
13
+
14
+ self
15
+ end
16
+
17
+ # Reads the virtual 'as_of_time' attribute
18
+ #
19
+ # See ChronoModel::Patches::AsOfTimeHolder
20
+ #
21
+ def as_of_time
22
+ @_as_of_time
23
+ end
24
+ end
25
+
26
+ module Relation
27
+ include AsOfTimeHolder
28
+
29
+ def load
30
+ return super unless @_as_of_time && !loaded?
31
+
32
+ super.each {|record| record.as_of_time!(@_as_of_time) }
33
+ end
34
+
35
+ def merge(*)
36
+ return super unless @_as_of_time
37
+
38
+ super.as_of_time!(@_as_of_time)
39
+ end
40
+
41
+ def build_arel
42
+ return super unless @_as_of_time
43
+
44
+ super.tap do |arel|
45
+
46
+ arel.join_sources.each do |join|
47
+ model = TimeMachine.chrono_models[join.left.table_name]
48
+ next unless model
49
+
50
+ join.left = Arel::Nodes::SqlLiteral.new(
51
+ model.history.virtual_table_at(@_as_of_time,
52
+ join.left.table_alias || join.left.table_name)
53
+ )
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+
6
60
  # Patches ActiveRecord::Associations::Association to add support for
7
61
  # temporal associations.
8
62
  #
@@ -12,6 +66,9 @@ module ChronoModel
12
66
  # on the join model's (:through association) one.
13
67
  #
14
68
  module Association
69
+ def skip_statement_cache?
70
+ super || _chrono_target?
71
+ end
15
72
 
16
73
  # If the association class or the through association are ChronoModels,
17
74
  # then fetches the records from a virtual table using a subquery scope
@@ -20,11 +77,7 @@ module ChronoModel
20
77
  scope = super
21
78
  return scope unless _chrono_record?
22
79
 
23
- klass = reflection.options[:polymorphic] ?
24
- owner.public_send(reflection.foreign_type).constantize :
25
- reflection.klass
26
-
27
- if klass.chrono?
80
+ if _chrono_target?
28
81
  # For standard associations, replace the table name with the virtual
29
82
  # as-of table name at the owner's as-of-time
30
83
  #
@@ -54,9 +107,10 @@ module ChronoModel
54
107
  join.left.table_alias = table_alias
55
108
  end
56
109
  end
57
-
58
110
  end
59
111
 
112
+ scope.as_of_time!(owner.as_of_time)
113
+
60
114
  return scope
61
115
  end
62
116
 
@@ -64,6 +118,15 @@ module ChronoModel
64
118
  def _chrono_record?
65
119
  owner.respond_to?(:as_of_time) && owner.as_of_time.present?
66
120
  end
121
+
122
+ def _chrono_target?
123
+ @_target_klass ||= reflection.options[:polymorphic] ?
124
+ owner.public_send(reflection.foreign_type).constantize :
125
+ reflection.klass
126
+
127
+ @_target_klass.chrono?
128
+ end
129
+
67
130
  end
68
131
 
69
132
  end