Module: ChronoModel::TimeMachine
- Extended by:
- ActiveSupport::Concern
- Includes:
- Patches::AsOfTimeHolder
- Defined in:
- lib/chrono_model/time_machine.rb,
lib/chrono_model/time_machine/timeline.rb,
lib/chrono_model/time_machine/time_query.rb,
lib/chrono_model/time_machine/history_model.rb
Defined Under Namespace
Modules: ClassMethods, HistoryModel, TimeQuery, Timeline
Class Method Summary collapse
Instance Method Summary collapse
-
#as_of(time) ⇒ Object
Returns a read-only representation of this record as it was
timeago. -
#as_of!(time) ⇒ Object
Returns a read-only representation of this record as it was
timeago. -
#changes_against(ref) ⇒ Object
Returns the differences between this record and an arbitrary reference record.
-
#current_version ⇒ Object
Returns the current history version.
-
#destroy ⇒ Object
Inhibit destroy of historical records.
-
#historical? ⇒ Boolean
Returns a boolean indicating whether this record is an history entry.
-
#history ⇒ Object
Return the complete read-only history of this instance.
-
#last_changes ⇒ Object
Returns the differences between this entry and the previous history one.
-
#pred(options = {}) ⇒ Object
Returns the previous record in the history, or nil if this is the only recorded entry.
-
#pred_timestamp(options = {}) ⇒ Object
Returns the previous timestamp in this record’s timeline.
-
#succ ⇒ Object
This is a current record, so its next instance is always nil.
-
#timeline(options = {}) ⇒ Object
Returns an Array of timestamps for which this instance has an history record.
Methods included from Patches::AsOfTimeHolder
Class Method Details
.define_history_model_for(model) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/chrono_model/time_machine.rb', line 47 def self.define_history_model_for(model) history = Class.new(model) { include ChronoModel::TimeMachine::HistoryModel } model.singleton_class.instance_eval do define_method(:history) { history } end history.singleton_class.instance_eval do define_method(:sti_name) { model.sti_name } end model.const_set :History, history return history end |
.define_inherited_history_model_for(subclass) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/chrono_model/time_machine.rb', line 63 def self.define_inherited_history_model_for(subclass) # Define history model for the subclass history = Class.new(subclass.superclass.history) history.table_name = subclass.superclass.history.table_name # Override the STI name on the history subclass history.singleton_class.instance_eval do define_method(:sti_name) { subclass.sti_name } end # Return the subclass history via the .history method subclass.singleton_class.instance_eval do define_method(:history) { history } end # Define the History constant inside the subclass subclass.const_set :History, history history.instance_eval do # Monkey patch of ActiveRecord::Inheritance. # STI fails when a Foo::History record has Foo as type in the # inheritance column; AR expects the type to be an instance of the # current class or a descendant (or self). def find_sti_class(type_name) super(type_name + "::History") end end end |
Instance Method Details
#as_of(time) ⇒ Object
Returns a read-only representation of this record as it was time ago. Returns nil if no record is found.
126 127 128 |
# File 'lib/chrono_model/time_machine.rb', line 126 def as_of(time) _as_of(time).first end |
#as_of!(time) ⇒ Object
Returns a read-only representation of this record as it was time ago. Raises ActiveRecord::RecordNotFound if no record is found.
133 134 135 |
# File 'lib/chrono_model/time_machine.rb', line 133 def as_of!(time) _as_of(time).first! end |
#changes_against(ref) ⇒ Object
Returns the differences between this record and an arbitrary reference record. The changes representation is an hash keyed by attribute whose values are arrays containing previous and current attributes values - the same format used by ActiveModel::Dirty.
224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/chrono_model/time_machine.rb', line 224 def changes_against(ref) self.class.attribute_names_for_history_changes.inject({}) do |changes, attr| old, new = ref.public_send(attr), self.public_send(attr) changes.tap do |c| changed = old.respond_to?(:history_eql?) ? !old.history_eql?(new) : old != new c[attr] = [old, new] if changed end end end |
#current_version ⇒ Object
Returns the current history version
207 208 209 |
# File 'lib/chrono_model/time_machine.rb', line 207 def current_version self.historical? ? self.class.find(self.id) : self end |
#destroy ⇒ Object
Inhibit destroy of historical records
167 168 169 170 |
# File 'lib/chrono_model/time_machine.rb', line 167 def destroy raise ActiveRecord::ReadOnlyRecord, 'Cannot delete historical records' if historical? super end |
#historical? ⇒ Boolean
Returns a boolean indicating whether this record is an history entry.
161 162 163 |
# File 'lib/chrono_model/time_machine.rb', line 161 def historical? self.as_of_time.present? || self.kind_of?(self.class.history) end |
#history ⇒ Object
Return the complete read-only history of this instance.
148 149 150 |
# File 'lib/chrono_model/time_machine.rb', line 148 def history self.class.history.chronological.of(self) end |
#last_changes ⇒ Object
Returns the differences between this entry and the previous history one. See: changes_against.
214 215 216 217 |
# File 'lib/chrono_model/time_machine.rb', line 214 def last_changes pred = self.pred changes_against(pred) if pred end |
#pred(options = {}) ⇒ Object
Returns the previous record in the history, or nil if this is the only recorded entry.
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/chrono_model/time_machine.rb', line 175 def pred( = {}) if self.class.timeline_associations.empty? history.order(Arel.sql('upper(validity) DESC')).offset(1).first else return nil unless (ts = ()) order_clause = Arel.sql %[ LOWER(#{options[:table] || self.class.quoted_table_name}."validity") DESC ] self.class.as_of(ts).order(order_clause).find([:id] || id) end end |
#pred_timestamp(options = {}) ⇒ Object
Returns the previous timestamp in this record’s timeline. Includes temporal associations.
190 191 192 193 194 195 196 197 |
# File 'lib/chrono_model/time_machine.rb', line 190 def ( = {}) if historical? [:before] ||= as_of_time timeline(.merge(limit: 1, reverse: true)).first else timeline(.merge(limit: 2, reverse: true)).second end end |
#succ ⇒ Object
This is a current record, so its next instance is always nil.
201 202 203 |
# File 'lib/chrono_model/time_machine.rb', line 201 def succ nil end |
#timeline(options = {}) ⇒ Object
Returns an Array of timestamps for which this instance has an history record. Takes temporal associations into account.
155 156 157 |
# File 'lib/chrono_model/time_machine.rb', line 155 def timeline( = {}) self.class.history.timeline(self, ) end |