Module: ChronoModel::TimeMachine::HistoryMethods
- Includes:
- TimeQuery
- Defined in:
- lib/chrono_model/time_machine.rb
Overview
Methods that make up the history interface of the companion History model, automatically built for each Model that includes TimeMachine
Defined Under Namespace
Modules: HistorySelect
Instance Method Summary collapse
-
#as_of(time, scope = nil) ⇒ Object
Fetches as of
timerecords. -
#at(time) ⇒ Object
Fetches history record at the given time.
-
#force_history_fields ⇒ Object
HACK FIXME.
-
#history? ⇒ Boolean
To identify this class as the History subclass.
-
#non_history_superclass(klass = self) ⇒ Object
Getting the correct quoted_table_name can be tricky when STI is involved.
-
#of(object) ⇒ Object
Fetches the given
objecthistory, sorted by history record time by default. - #past ⇒ Object
- #quoted_history_fields ⇒ Object
-
#sorted ⇒ Object
Returns the history sorted by recorded_at.
-
#time_query(match, time, options = {}) ⇒ Object
In the History context, pre-fill the :on options with the validity interval.
- #virtual_table_at(time, name = nil) ⇒ Object
Instance Method Details
#as_of(time, scope = nil) ⇒ Object
Fetches as of time records.
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/chrono_model/time_machine.rb', line 458 def as_of(time, scope = nil) as_of = non_history_superclass.unscoped.from(virtual_table_at(time)) # Add default scopes back if we're passed nil or a # specific scope, because we're .unscopeing above. # scopes = !scope.nil? ? [scope] : ( superclass.default_scopes.map do |s| s.respond_to?(:call) ? s.call : s end) scopes.each do |s| s.order_values.each {|clause| as_of = as_of.order(clause)} s.where_values.each {|clause| as_of = as_of.where(clause)} end as_of.instance_variable_set(:@temporal, time) return as_of end |
#at(time) ⇒ Object
Fetches history record at the given time
488 489 490 491 492 493 494 |
# File 'lib/chrono_model/time_machine.rb', line 488 def at(time) time = Conversions.time_to_utc_string(time.utc) if time.kind_of?(Time) && !time.utc? unscoped. select("#{quoted_table_name}.*, #{connection.quote(time)}::timestamp AS as_of_time"). time_query(:at, time) end |
#force_history_fields ⇒ Object
HACK FIXME. When querying history, ChronoModel does not add his timestamps and sorting if there is an aggregate function in the select list - as it is likely what you’ll want. However, if you have a query that performs an aggregate in a subquery, the code below will do the wrong thing - and you’ll have to forcibly add back the history fields yourself.
The obvious solution is to use a VIEW on the history containing the added history fields, and remove all this crap from here… but it is not easily feasible. So we’re going with a workaround for now.
- vjt Wed Apr 2 19:56:35 CEST 2014
525 526 527 |
# File 'lib/chrono_model/time_machine.rb', line 525 def force_history_fields select(HistorySelect::SELECT_VALUES).order(HistorySelect::ORDER_VALUES[quoted_table_name]) end |
#history? ⇒ Boolean
To identify this class as the History subclass
437 438 439 |
# File 'lib/chrono_model/time_machine.rb', line 437 def history? true end |
#non_history_superclass(klass = self) ⇒ Object
Getting the correct quoted_table_name can be tricky when STI is involved. If Orange < Fruit, then Orange::History < Fruit::History (see define_inherited_history_model_for). This means that the superclass method returns Fruit::History, which will give us the wrong table name. What we actually want is the superclass of Fruit::History, which is Fruit. So, we use non_history_superclass instead. -npj
448 449 450 451 452 453 454 |
# File 'lib/chrono_model/time_machine.rb', line 448 def non_history_superclass(klass = self) if klass.superclass.respond_to?(:history?) && klass.superclass.history? non_history_superclass(klass.superclass) else klass.superclass end end |
#of(object) ⇒ Object
Fetches the given object history, sorted by history record time by default. Always includes an “as_of_time” column that is either the upper bound of the validity range or now() if history validity is maximum.
507 508 509 |
# File 'lib/chrono_model/time_machine.rb', line 507 def of(object) where(:id => object).extend(HistorySelect) end |
#past ⇒ Object
432 433 434 |
# File 'lib/chrono_model/time_machine.rb', line 432 def past time_query(:before, :now).where('NOT upper_inf(validity)') end |
#quoted_history_fields ⇒ Object
631 632 633 634 635 636 637 638 639 640 |
# File 'lib/chrono_model/time_machine.rb', line 631 def quoted_history_fields @quoted_history_fields ||= begin validity = [connection.quote_table_name(table_name), connection.quote_column_name('validity') ].join('.') [:lower, :upper].map! {|func| "#{func}(#{validity})"} end end |
#sorted ⇒ Object
Returns the history sorted by recorded_at
498 499 500 |
# File 'lib/chrono_model/time_machine.rb', line 498 def sorted all.order(%[ #{quoted_table_name}."recorded_at", #{quoted_table_name}."hid" ]) end |
#time_query(match, time, options = {}) ⇒ Object
In the History context, pre-fill the :on options with the validity interval.
427 428 429 430 |
# File 'lib/chrono_model/time_machine.rb', line 427 def time_query(match, time, = {}) [:on] ||= :validity super end |
#virtual_table_at(time, name = nil) ⇒ Object
479 480 481 482 483 484 |
# File 'lib/chrono_model/time_machine.rb', line 479 def virtual_table_at(time, name = nil) name = name ? connection.quote_table_name(name) : non_history_superclass.quoted_table_name "(#{at(time).to_sql}) #{name}" end |