module Eco module Language module Models class Collection include Enumerable ATTR_PRESENCE_METHODS = ["present", "empty", "present_all?", "present_some?"] ATTR_COLLECTION_METHODS = ["exclude", "remove", "attr", "attr?", "attrs", "unique_attrs", "contains"] + ATTR_PRESENCE_METHODS class << self def attr_collection (*attrs) block = ->(method) { attrs_create_method(attrs, method) } ATTR_COLLECTION_METHODS.each(&block) end def attr_presence (*attrs) block = ->(method) { attrs_create_method(attrs, method) } ATTR_PRESENCE_METHODS.each(&block) end def attrs_create_method(attrs, method) attrs.each do |attr| attr = attr.to_s if method.include?("attr") attr_method = method.sub("attr", attr) else attr_method = "#{attr}_#{method}" end define_method attr_method do |*args| send(method, attr, *args) end end end end #attr_reader :items def initialize(data = [], klass:, factory: nil, handy: Eco::Assets::Language.new) raise "Raise klass required, given: #{klass}" if !klass @klass = klass @factory = factory @handy = handy @items = to_klass(data) end def newFrom(data) self.class.new(data, klass: @klass, factory: @factory) end def merge(data) data = data.to_a unless data.is_a?(Array) newFrom to_a + data end def to_c Collection.new(self, klass: @klass, factory: @factory) end def new self.class.new(to_a, klass: @klass, factory: @factory) end def length count end def empty? count == 0 end def each(params: {}, &block) return to_enum(:each) unless block @items.each(&block) end def <(value) @items.clear self << value end def <<(value) @items.concat(into_a(value)) on_change self end def update(&block) newFrom self.map(&block) end def delete!(value) self < @items - into_a(value) end # attr dependant methods def exclude(attr, value, modifier = Language::MatchModifier.new) newFrom @items - self.attr(attr, value, modifier) end def remove(attr, value, modifier = Language::MatchModifier.new) self < exclude(attr, value, modifier) end def attr(attr, value = true, modifier = Language::MatchModifier.new) if !!value == value # boolean? present(attr, value) else return newFrom select { |object| attr_val = fetch_attr(object, attr) match?(attr_val, value, modifier) } end end def attr?(attr, value = true, modifier = Language::MatchModifier.new) modifier = modifier.new.reverse if !!value == value # boolean? present(attr, value).length == length else obj_vals = attrs(attr) return match?(obj_vals, value, modifier) end end def attrs(attr) map { |object| fetch_attr(object, attr) } end def unique_attrs(attr) to_h(attr).keys end def present(attr, flag = true) block = ->(o) { !!fetch_attr(o, attr) == !!flag } newFrom select(&block) end def empty(attr, flag = true) present(attr, !flag) end def present_all?(attr, flag = true) present(attr, flag).length == length end def present_some?(attr, flag = true) present(attr, flag).length > 0 end def contains(attr, value, modifier = Language::MatchModifier.new) modifier = modifier.new.pattern self.attr(attr, value, modifier) end def group_by(attr) to_h(attr) #return {} if !attr #self.to_a.group_by { |object| object.method(attr).call } end def to_h(attr) return {} if !attr to_a.group_by { |object| object.method(attr).call } end protected def on_change # function to be overriden by children classes end def into_a(value) value = [].push(value) unless value.is_a?(Enumerable) value.to_a end private def match?(*args) @handy.match?(*args) end def to_klass(list) into_a(list).map do |v| v.is_a?(@klass) ? v : @factory&.new(v) || @klass.new(v) end end def fetch_attr(object,attr) object.method(attr).call end end end end end