lib/dcmgr/models/base_new.rb in wakame-vdc-agents-10.11.0 vs lib/dcmgr/models/base_new.rb in wakame-vdc-agents-10.12.0
- old
+ new
@@ -48,10 +48,11 @@
unless has_column?(:uuid)
# add :uuid column with unique index constraint.
column(:uuid, String, :size=>8, :null=>false, :fixed=>true, :unique=>true)
end
}
+ model.many_to_many :tags, :dataset=>lambda { Tag.join(TagMapping.table_name, :tag_id=>:id, :uuid=>self.canonical_uuid); }
end
module InstanceMethods
# read-only instance method to retrieve @uuid_prefix class
# variable.
@@ -63,49 +64,75 @@
super
# set random generated uuid value
self[:uuid] ||= Array.new(8) do UUID_TABLE[rand(UUID_TABLE.size)]; end.join
end
+ # model hook
+ def after_destroy
+ super
+ TagMapping.filter(:uuid=>self.canonical_uuid).delete
+ end
+
# Returns canonicalized uuid which has the form of
# "{uuid_prefix}-{uuid}".
def canonical_uuid
"#{self.uuid_prefix}-#{self[:uuid]}"
end
alias_method :cuuid, :canonical_uuid
# Put the tag on the object.
#
- # This method just delegates the method call of Tag#label().
- # @params [Models::Tag,String] tag_or_tag_uuid 'tag-xxxx' is expected when the type is string
- def label_tag(tag_or_tag_uuid)
- tag = case tag_or_tag_uuid
+ # @params [Models::Tag,String,Symbol] arg1
+ # @params [String,NilClass] arg2
+ # @params [String,NilClass] arg3
+ #
+ # @example
+ # lable_tag('tag-xxxxx')
+ # t = Tag['tag-xxxx']
+ # label_tag(t)
+ # label_tag(:NetworkPool, 'newname1', 'account_id')
+ def label_tag(arg1, arg2=nil, arg3=nil)
+ tag = case arg1
when String
- Tag[tag_or_tag_uuid]
+ Tag[arg1]
+ when Symbol
+ acctid = arg3 || self.respond_to?(:account_id) ? self.account_id : raise("Unknown Account ID")
+ Dcmgr::Tags.const_get(arg1).find_or_create(:account_id=>acctid, :name=>arg2)
when Tag
- tag_or_tag_uuid
+ arg1
else
- raise ArgumentError, "Invalid type: #{tag_or_tag_uuid.class}"
+ raise ArgumentError, "Invalid type: #{arg1.class}"
end
-
- tag.label(self.uuid)
+ raise "Root Tag class can not be used" unless tag.class < Tag
+ tag.label(self.canonical_uuid)
end
# Remove the labeled tag from the object
#
- # This method just delegates the method call of Tag#unlabel().
- # @params [Models::Tag,String] tag_or_tag_uuid 'tag-xxxx' is expected when the type is string
- def unlabel_tag(tag_or_tag_uuid)
- tag = case tag_or_tag_uuid
+ # @params [Models::Tag,String,Symbol] arg1
+ # @params [String,NilClass] arg2
+ # @params [String,NilClass] arg3
+ #
+ # @example
+ # unlable_tag('tag-xxxxx')
+ # t = Tag['tag-xxxx']
+ # unlabel_tag(t)
+ # unlabel_tag(:NetworkPool, 'newname1', 'account_id')
+ def unlabel_tag(arg1, arg2=nil, arg3=nil)
+ tag = case arg1
when String
- Tag[tag_or_tag_uuid]
+ Tag[arg1]
+ when Symbol
+ acctid = arg3 || self.respond_to?(:account_id) ? self.account_id : raise("Unknown Account ID")
+ Dcmgr::Tags.const_get(arg1).find(:account_id=>acctid, :name=>arg2)
when Tag
- tag_or_tag_uuid
+ arg1
else
- raise ArgumentError, "Invalid type: #{tag_or_tag_uuid.class}"
+ raise ArgumentError, "Invalid type: #{arg1.class}"
end
- tag.unlabel(self.uuid)
+ tag.unlabel(self.canonical_uuid)
end
def to_hash()
self.values.dup.merge({:id=>canonical_uuid, :uuid=>canonical_uuid})
end
@@ -254,10 +281,119 @@
self.schema_builders << blk
end
end
end
-
+
+ # This plugin is to archive the changes on each column of the model
+ # to a history table.
+ #
+ # plugin ArchiveChangedColumn, :your_history_table
+ # or
+ # plugin ArchiveChangedColumn
+ # history_dataset = DB[:history_table]
+ #
+ # The history table should have the schema below:
+ # schema do
+ # Fixnum :id, :null=>false, :primary_key=>true
+ # String :uuid, :size=>50, :null=>false
+ # String :attr, :null=>false
+ # String :vchar_value, :null=>true
+ # String :blob_value, :null=>true, :text=>true
+ # Time :created_at, :null=>false
+ # index [:uuid, :created_at]
+ # index [:uuid, :attr]
+ # end
+ module ArchiveChangedColumn
+ def self.configure(model, history_table=nil)
+ model.history_dataset = case history_table
+ when NilClass
+ nil
+ when String,Symbol
+ model.db.from(history_table)
+ when Class
+ raise "Unknown type" unless history_table < Sequel::Model
+ history_table.dataset
+ when Sequel::Dataset
+ history_table
+ else
+ raise "Unknown type"
+ end
+ end
+
+ module ClassMethods
+ def history_dataset=(ds)
+ @history_ds = ds
+ end
+
+ def history_dataset
+ @history_ds
+ end
+ end
+
+ module InstanceMethods
+ def history_snapshot(at)
+ raise TypeError unless at.is_a?(Time)
+
+ if self.created_at > at || (!self.terminated_at.nil? && self.terminated_at < at)
+ raise "#{at} is not in the range of the object's life span."
+ end
+
+ ss = self.dup
+ # SELECT * FROM (SELECT * FROM `instance_histories` WHERE
+ # (`uuid` = 'i-ezsrs132') AND created_at <= '2010-11-30 23:08:05'
+ # ORDER BY created_at DESC) AS a GROUP BY a.attr;
+ ds = self.class.history_dataset.filter('uuid=? AND created_at <= ?', self.canonical_uuid, at).order(:created_at.desc)
+ ds = ds.from_self.group_by(:attr)
+ ds.all.each { |h|
+ if !h[:blob_value].nil?
+ ss.send("#{h[:attr]}=", typecast_value(h[:attr], h[:blob_value]))
+ else
+ ss.send("#{h[:attr]}=", typecast_value(h[:attr], h[:vchar_value]))
+ end
+ }
+ # take care for serialized columns by serialization plugin.
+ ss.deserialized_values.clear if ss.respond_to?(:deserialized_values)
+
+ ss
+ end
+
+ def before_create
+ return false if super == false
+ store_changes(self.columns)
+ true
+ end
+
+ def before_update
+ return false if super == false
+ store_changes(self.changed_columns)
+ true
+ end
+
+ private
+ def store_changes(cols_stored)
+ return if cols_stored.nil? || cols_stored.empty?
+ common_rec = {
+ :uuid=>self.canonical_uuid,
+ :created_at => Time.now,
+ }
+
+ cols_stored.each { |c|
+ hist_rec = common_rec.dup
+ hist_rec[:attr] = c.to_s
+
+ coldef = self.class.db_schema[c]
+ case coldef[:type]
+ when :text,:blob
+ hist_rec[:blob_value]= (new? ? (self[c] || coldef[:default]) : self[c])
+ else
+ hist_rec[:vchar_value]=(new? ? (self[c] || coldef[:default]) : self[c])
+ end
+ self.class.history_dataset.insert(hist_rec)
+ }
+ end
+ end
+ end
class BaseNew < Sequel::Model
LOCK_TABLES_KEY='__locked_tables'