module Historiographer module History module InstanceMethods def destroy false end def destroy! false end def save(*args, **kwargs) if persisted? && (changes.keys - %w(history_ended_at snapshot_id)).any? false else super(*args, **kwargs) end end def save!(*args, **kwargs) if persisted? && (changes.keys - %w(history_ended_at snapshot_id)).any? false else super(*args, **kwargs) end end def snapshot raise "Cannot snapshot a history model!" end def original_class self.class.original_class end private def dummy_instance return @dummy_instance if @dummy_instance cannot_keep_cols = %w(history_started_at history_ended_at history_user_id snapshot_id) cannot_keep_cols += [self.class.inheritance_column.to_sym] if self.original_class.sti_enabled? cannot_keep_cols += [self.class.history_foreign_key] cannot_keep_cols.map!(&:to_s) attrs = attributes.clone attrs[original_class.primary_key] = attrs[self.class.history_foreign_key] instance = original_class.find_or_initialize_by(original_class.primary_key => attrs[original_class.primary_key]) instance.assign_attributes(attrs.except(*cannot_keep_cols)) # Create a module to hold methods from the history class history_methods_module = Module.new # Get methods defined directly in the history class, excluding baseline methods history_methods = self.class.instance_methods(false) - baseline_methods history_methods.each do |method_name| next if instance.singleton_class.method_defined?(method_name) method = self.class.instance_method(method_name) history_methods_module.define_method(method_name) do |*args, &block| method.bind(self.instance_variable_get(:@_history_instance)).call(*args, &block) end end instance.singleton_class.prepend(history_methods_module) self.class.reflect_on_all_associations.each do |reflection| instance.singleton_class.class_eval do define_method(reflection.name) do |*args, &block| history_instance = instance.instance_variable_get(:@_history_instance) history_instance.send(reflection.name, *args, &block) end end end instance.singleton_class.class_eval do define_method(:class) do history_instance = instance.instance_variable_get(:@_history_instance) history_instance.class end end instance.instance_variable_set(:@_history_instance, self) @dummy_instance = instance end def forward_method(method_name, *args, &block) if method_name == :class || method_name == 'class' self.class else dummy_instance.send(method_name, *args, &block) end end end end end