lib/sequel/plugins/bitemporal.rb in sequel_bitemporal-0.9.0 vs lib/sequel/plugins/bitemporal.rb in sequel_bitemporal-0.9.1

- old
+ new

@@ -29,23 +29,29 @@ def self.now Thread.current[THREAD_NOW_KEY] || DateTime.now end - def self.bitemporal_version_columns - @bitemporal_version_columns ||= [:master_id, :valid_from, :valid_to, :created_at, :expired_at] + def self.version_foreign_keys(master = nil) + return :master_id unless master + primary_key = [*master.primary_key] + primary_key.size > 1 ? primary_key : :master_id end - def self.bitemporal_excluded_columns - @bitemporal_excluded_columns ||= [:id, *bitemporal_version_columns] + def self.bitemporal_version_columns(master = nil) + [*version_foreign_keys(master), :valid_from, :valid_to, :created_at, :expired_at] end + def self.bitemporal_excluded_columns(master = nil) + [:id, *bitemporal_version_columns(master)] + end + def self.configure(master, opts = {}) version = opts[:version_class] raise Error, "please specify version class to use for bitemporal plugin" unless version return version.db.log_info("Version table does not exist for #{version.name}") unless version.db.table_exists?(version.table_name) - missing = bitemporal_version_columns - version.columns + missing = bitemporal_version_columns(master) - version.columns raise Error, "bitemporal plugin requires the following missing column#{"s" if missing.size>1} on version class: #{missing.join(", ")}" unless missing.empty? if Sequel::Plugins::Bitemporal.jdbc?(master.db) master.plugin :typecast_on_load, *master.columns end @@ -74,11 +80,11 @@ @current_version_alias = "#{base_alias}_current_version".to_sym @audit_class = opts[:audit_class] @audit_updated_by_method = opts.fetch(:audit_updated_by_method){ :updated_by } @propagate_per_column = opts.fetch(:propagate_per_column, false) @version_uses_string_nilifier = version.plugins.map(&:to_s).include? "Sequel::Plugins::StringNilifier" - @excluded_columns = Sequel::Plugins::Bitemporal.bitemporal_excluded_columns + @excluded_columns = Sequel::Plugins::Bitemporal.bitemporal_excluded_columns(master) @excluded_columns += Array opts[:excluded_columns] if opts[:excluded_columns] @use_ranges = if opts[:ranges] db = self.db unless db.database_type==:postgres && db.server_version >= 90200 raise "Ranges require PostgreSQL 9.2" @@ -98,12 +104,12 @@ (valid_from <= n) & (valid_to > n) end end end - master.one_to_many :versions, class: version, key: :master_id, graph_alias_base: master.versions_alias - master.one_to_one :current_version, class: version, key: :master_id, graph_alias_base: master.current_version_alias, :graph_block=>(proc do |j, lj, js| + master.one_to_many :versions, class: version, key: version_foreign_keys(master), graph_alias_base: master.versions_alias + master.one_to_one :current_version, class: version, key: version_foreign_keys(master), graph_alias_base: master.current_version_alias, :graph_block=>(proc do |j, lj, js| t = Sequel.delay{ ::Sequel::Plugins::Bitemporal.point_in_time } n = Sequel.delay{ ::Sequel::Plugins::Bitemporal.now } if master.use_ranges master.existence_range_contains(t, j) & master.validity_range_contains(n, j) @@ -133,11 +139,11 @@ Sequel.negate( Sequel.qualify(model.current_version_alias, :id) => nil ) ) end - master.one_to_many :current_or_future_versions, class: version, key: :master_id, :graph_block=>(proc do |j, lj, js| + master.one_to_many :current_or_future_versions, class: version, key: version_foreign_keys(master), :graph_block=>(proc do |j, lj, js| t = Sequel.delay{ ::Sequel::Plugins::Bitemporal.point_in_time } n = Sequel.delay{ ::Sequel::Plugins::Bitemporal.now } if master.use_ranges master.existence_range_contains(t, j) & (Sequel.qualify(j, :valid_to) > n) & @@ -167,11 +173,11 @@ master.def_dataset_method :with_current_or_future_versions do eager_graph(:current_or_future_versions).where( Sequel.negate(Sequel.qualify(:current_or_future_versions, :id) => nil) ) end - version.many_to_one :master, class: master, key: :master_id + version.many_to_one :master, class: master, key: version_foreign_keys(master) version.class_eval do if Sequel::Plugins::Bitemporal.jdbc?(master.db) plugin :typecast_on_load, *columns end @@ -317,20 +323,30 @@ end end def attributes=(attributes) @pending_version ||= begin - current_attributes = {master_id: id} + current_attributes = + if composite_primary_key? + version_values + else + { master_id: id } + end + current_version.keys.each do |key| next if excluded_columns.include? key current_attributes[key] = current_version.send key end if current_version? model.version_class.new current_attributes end pending_version.set_all attributes end + def version_values + version_foreign_keys.map { |k| [k, public_send(k)] }.to_h + end + def update_attributes(attributes={}) self.attributes = attributes if save raise_on_failure: false self else @@ -362,10 +378,14 @@ def after_save super _refresh_set_values @values end + def composite_primary_key? + [*primary_key].size > 1 + end + def destroy point_in_time = ::Sequel::Plugins::Bitemporal.point_in_time versions_dataset.where( expired_at: nil ).where( @@ -469,10 +489,14 @@ def propagated_during_last_save @propagated_during_last_save ||= [] end + def version_foreign_keys + composite_primary_key? ? primary_key : :master_id + end + private def prepare_pending_version return unless pending_version_holds_changes? now = ::Sequel::Plugins::Bitemporal.now @@ -606,10 +630,10 @@ @current_version_values = current_version.values columns = pending_version.columns - excluded_columns_for_changes columns.detect do |column| new_value = pending_version.send column case column - when :id, :master_id, :created_at, :expired_at + when :id, :created_at, :expired_at, *version_foreign_keys false when :valid_from pending_version.values.has_key?(:valid_from) && ( new_value<current_version.valid_from || (