class DhEasy::Core::SmartCollection
Smart collection capable to avoid duplicates on insert by matching id
defined fields along events.
Constants
- EVENTS
Implemented event list.
Attributes
Default fields values. Apply to missing fields and null values.
Key fields, analog to primary keys.
Public Class Methods
Initialize collection
@param [Array] key_fields
Key fields, analog to primary keys. @param [Hash] opts ({}) Configuration options. @option opts [Array] :values ([]) Initial values; will avoid duplicates on insert. @option opts [Hash] :defaults ({}) Default values. `proc` values will be executed to get default value.
@example With default values.
count = 0 defaults = { 'id' => lambda{|item| count += 1}, 'aaa' => 111, 'bbb' => proc{|item| item['ccc'].nil? ? 'No ccc' : 'Has ccc'} } values = [ {'aaa' => 'Defaults apply on nil values only', 'bbb' => nil}, {'ccc' => 'ddd'}, {'id' => 'abc123'} ] new_item = {'bbb' => 'Look mom! no ccc'} collection = SmartCollection.new ['id'], defaults: defaults collection << new_item collection # => [ # {'id' => 1, 'aaa' => 'Defaults apply on nil values only', 'bbb' => 'No ccc'}, # {'id' => 2, 'aaa' => 111, 'bbb' => 'Has ccc', 'ccc' => 'ddd'}, # {'id' => 'abc123', 'aaa' => 111, 'bbb' => 'No ccc'}, # {'id' => 3, 'aaa' => 111, 'bbb' => 'Look mom! no ccc'} # ]
@note Defaults will apply to missing fields and null values only.
# File lib/dh_easy/core/smart_collection.rb, line 50 def initialize key_fields, opts = {} @key_fields = key_fields || [] @defaults = opts[:defaults] || {} super 0 (opts[:values] || []).each{|item| self << item} end
Public Instance Methods
Add/remplace an item avoiding duplicates
# File lib/dh_easy/core/smart_collection.rb, line 224 def << item item = call_event :before_defaults, item, item apply_defaults item item = call_event :before_match, item, item match = find_match item item = call_event :before_insert, item, item, match delete match unless match.nil? result = super(item) call_event :after_insert, result, item, match end
Apply default values into item. @private
@param [Hash] item Item to apply defaults.
@return [Hash] Item
# File lib/dh_easy/core/smart_collection.rb, line 203 def apply_defaults item defaults.each do |key, value| next unless item[key].nil? item[key] = value.respond_to?(:call) ? value.call(item) : value end end
Add event binding by key and block.
@param [Symbol] key Event name.
@raise [ArgumentError] When unknown event key.
@example before_defaults
defaults = {'aaa' => 111} collection = SmartCollection.new [], defaults: defaults collection.bind_event(:before_defaults) do |collection, item| puts collection # => [] puts item # => {'bbb' => 222} # Sending the item back is required, or a new one # in case you want to replace item to insert. item end data << {'bbb' => 222} data # => [{'aaa' => 111, 'bbb' => 222}]
@example before_match
keys = ['id'] defaults = {'aaa' => 111} values = [ {'id' => 1, 'ccc' => 333} ] collection = SmartCollection.new keys, defaults: defaults values: values collection.bind_event(:before_match) do |collection, item| puts collection # => [{'id' => 1, 'aaa' => 111, 'ccc' => 333}] puts item # => {'id' => 1, 'aaa' => 111, 'bbb' => 222} # Sending the item back is required, or a new one # in case you want to replace item to insert. item end data << {'id' => 1, 'bbb' => 222} data # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
@example before_insert
keys = ['id'] defaults = {'aaa' => 111} values = [ {'id' => 1, 'ccc' => 333} ] collection = SmartCollection.new keys, defaults: defaults values: values collection.bind_event(:before_insert) do |collection, item, match| puts collection # => [{'id' => 1, 'aaa' => 111, 'ccc' => 333}] puts item # => {'id' => 1, 'aaa' => 111, 'bbb' => 222} puts match # => {'id' => 1, 'aaa' => 111, 'ccc' => 333} # Sending the item back is required, or a new one # in case you want to replace item to insert. item end data << {'id' => 1, 'bbb' => 222} data # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
@example after_insert
keys = ['id'] defaults = {'aaa' => 111} values = [ {'id' => 1, 'ccc' => 333} ] collection = SmartCollection.new keys, defaults: defaults values: values collection.bind_event(:after_insert) do |collection, item, match| puts collection # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}] puts item # => {'id' => 1, 'aaa' => 111, 'bbb' => 222} puts match # => {'id' => 1, 'aaa' => 111, 'ccc' => 333} # No need to send item back since it is already inserted end data << {'id' => 1, 'bbb' => 222} data # => [{'id' => 1, 'aaa' => 111, 'bbb' => 222}]
@note Some events will expect a return value to replace item on insertion:
* `before_match` * `before_defaults` * `before_insert`
# File lib/dh_easy/core/smart_collection.rb, line 161 def bind_event key, &block unless EVENTS.include? key raise ArgumentError.new("Unknown event '#{key}'") end (events[key] ||= []) << block end
Call an event @private
@param [Symbol] key Event name. @param default Detault return value when event's return nil. @param args event arguments.
# File lib/dh_easy/core/smart_collection.rb, line 174 def call_event key, default = nil, *args return default if events[key].nil? result = nil events[key].each{|event| result = event.call self, *args} result.nil? ? default : result end
Asigned events. @private
# File lib/dh_easy/core/smart_collection.rb, line 59 def events @events ||= {} end
Find an item by matching filter keys
@param [Hash] filter
@return [Hash,nil] First existing item match or nil when no match.
@note Warning: It uses table scan to filter and will be slow.
# File lib/dh_easy/core/smart_collection.rb, line 217 def find_match filter self.find do |item| match_keys? item, filter end end
Check whenever two items keys match.
@param [Hash] item_a Item to match. @param [Hash] item_b Item to match.
@return [Boolean]
# File lib/dh_easy/core/smart_collection.rb, line 187 def match_keys? item_a, item_b return false if key_fields.nil? || key_fields.count < 1 return true if item_a.nil? && item_b.nil? return false if item_a.nil? || item_b.nil? key_fields.each do |key| return false if item_a[key] != item_b[key] end true end