chrono_model 0.8.2 → 0.9.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
  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