class Eco::API::Session::Batch::BasePolicy

Helper class to build a hierarchical model of policies @example Usage:

class PolicyModel < Eco::API::Session::Batch::BasePolicy
  MODEL = {attr1: ["prop1a", "prop1b", {"prop1c": ["prop1c1"]}]}
  self.model = MODEL
  policy_attrs *model_attrs
end

policies = PolicyModel.new("batch_policy")
policies.attr1c do |attr1c|
  attr1c.prop1c1.max = 30
end

@attr_reader attr [Symbol] the Symbol `name` of the current policy @attr_reader max [Integer] `max` allowed number of occurrences of the property @attr_reader min [Integer] `min` required number of occurrences of the property

Attributes

attr[R]
max[RW]
min[RW]

Public Class Methods

new(attr = nil, _parent: self) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 72
def initialize(attr = nil, _parent: self)
  @_parent  = _parent
  @attr     = attr.to_sym
  @policies = {}
end
policy_attrs(*attrs) click to toggle source

Attributes of this level of the model that should be included @param attrs [Array<Symbol>, Array<String>] each of the subpolicies of the model that should be available

# File lib/eco/api/session/batch/base_policy.rb, line 41
def policy_attrs(*attrs)
  attrs = attrs.map(&:to_sym)

  attrs.each do |attr|
    method = attr.to_s.freeze
    var    = "@#{method}".freeze

    define_method(method) do |&block|
      unless policy = self[attr]
        klass  = self.class.policy_class(attr)
        policy = self[attr] = klass.new(attr, _parent: self)
      end

      if block
        block.call(policy)
        self
      else
        policy
      end
    end

  end
end
policy_class(key) click to toggle source

If the class for `key` exists, it returns it. Otherwise it generates it. @note for this to work, `key` should be one of the submodels of the current class' `model` @return [Eco::API::Session::Batch::BasePolicy] or subclass thereof

# File lib/eco/api/session/batch/base_policy.rb, line 29
def policy_class(key)
  key             = key.to_sym.freeze
  class_name      = to_constant(key)

  new_class(class_name, inherits: Eco::API::Session::Batch::BasePolicy) do |klass|
    klass.model = model[key]
    klass.policy_attrs *klass.model_attrs
  end
end

Public Instance Methods

[](attr) click to toggle source

@param attr [Symbol, String] name of the policy @return [Array<Eco::API::Session::Batch::BasePolicy>] the used subpolicies

# File lib/eco/api/session/batch/base_policy.rb, line 130
def [](attr)
  @policies[attr.to_sym]
end
[]=(attr, value) click to toggle source

@param attr [Symbol, String] name of the policy @param value [Expected object of Eco::API::Session::Batch::BasePolicy] a subpolicy to assign to a name `attr`

# File lib/eco/api/session/batch/base_policy.rb, line 136
def []=(attr, value)
  raise "Expected object of Eco::API::Session::Batch::BasePolicy. Given #{value.class}" unless value.is_a?(Eco::API::Session::Batch::BasePolicy)
  @policies[attr.to_sym] = value
end
active?(attr) click to toggle source

@param attr [Symbol, String] name of the policy @return [Boolean] if `attr` is an active subpolicy

# File lib/eco/api/session/batch/base_policy.rb, line 124
def active?(attr)
  @policies.key?(attr.to_sym)
end
compliant?(model, recurse: true) click to toggle source

@param model [Hash] plain hash (or hashable object) with the stats to check policy compliance against @param recurse [Boolean] to determine if we only check the current policy or also all active subpolicies @return [Boolean] `true` if `model` is compliant with the current policy

# File lib/eco/api/session/batch/base_policy.rb, line 144
def compliant?(model, recurse: true)
  unless hash = model_to_hash(model)
    raise "Expected 'model' to be a Hash (or hashable) object. Given: #{model}"
  end
  value   = model_attr(hash)
  good    = !model_attr?(hash) || (min?(value) && max?(value))
  #pp "batch_policy: '#{attr}' - #{value}: 'min' #{min?(value)}; 'max' #{max?(value)}"
  good  &&= all? {|active| active.compliant?(model, recurse: recurse)} if recurse
  good
end
each(&block) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 112
def each(&block)
  return to_enum(:each) unless block
  items.each(&block)
end
empty?() click to toggle source

@return [Boolean] `true` if there are no active subpolicies, `false` otherwise

# File lib/eco/api/session/batch/base_policy.rb, line 103
def empty?
  count == 0
end
items() click to toggle source

@return [Array<Eco::API::Session::Batch::BasePolicy>] the active subpolicies

# File lib/eco/api/session/batch/base_policy.rb, line 118
def items
  @policies.values
end
length() click to toggle source

return [Integer] number of declared subpolicies

# File lib/eco/api/session/batch/base_policy.rb, line 98
def length
  count
end
max?(value) click to toggle source

@note if there's no `max` defined, it always returns `true` @param value [Integer] value to check if it's in the maximum allowed @return [Boolen] `true` if `value` is lesser or equal to `min`

# File lib/eco/api/session/batch/base_policy.rb, line 93
def max?(value)
  !max || !value || (max >= value)
end
min?(value) click to toggle source

@note if there's no `min` defined, it always returns `true` @param value [Integer] value to check if it's in the minimum required @return [Boolen] `true` if `value` is grater or equal to `min`

# File lib/eco/api/session/batch/base_policy.rb, line 86
def min?(value)
  !min || !value|| (min <= value)
end
subpolicies?() click to toggle source

@return [Boolean] `true` if there are active subpolicies, `false` otherwise

# File lib/eco/api/session/batch/base_policy.rb, line 108
def subpolicies?
  !empty?
end
uncompliance(model, recurse: true) click to toggle source

@param model [Hash] plain hash (or hashable object) with the stats to check policy compliance against @param recurse [Boolean] to determine if we only check the current policy or also all active subpolicies @return [String] message with what failed to meet compliance

# File lib/eco/api/session/batch/base_policy.rb, line 175
def uncompliance(model, recurse: true)
  unless hash = model_to_hash(model)
    raise "Expected 'model' to be a Hash (or hashable) object. Given: #{model}"
  end
  msg = ""
  unless compliant?(hash, recurse: false)
    value = model_attr(hash)
    msg += "'#{attr(as_namespace: true)}' fails to meet: "
    msg += " [ min(#{min}) >=  #{value}] " unless min?(value)
    msg += " [ max(#{max}) <=  #{value}] " unless max?(value)
    msg += "\n"
  end

  if recurse
    map do |active|
      active.uncompliance(hash, recurse: true)
    end.compact.tap do |msgs|
      msg += "\n" + msgs.join("\n") unless msgs.empty?
    end
  end
  msg
end
uncompliant(model) click to toggle source

@param model [Hash] plain hash (or hashable object) with the stats to check policy compliance against @return [Array<Eco::API::Session::Batch::BasePolicy>] non-compliant policies for the `model`

# File lib/eco/api/session/batch/base_policy.rb, line 164
def uncompliant(model)
  each_with_object([]) do |active, arr|
    arr.concat(active.uncompliant(model))
  end.tap do |arr|
    arr.unshift(self) unless compliant?(model, recurse: false)
  end
end
validate!(model) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 155
def validate!(model)
  unless compliant?(model)
    msg = self.uncompliance(model)
    raise "Uncompliance Exception\n#{msg}"
  end
end

Protected Instance Methods

model_attr(hash) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 214
def model_attr(hash)
  return hash[self.attr] if hash.key?(self.attr)
  hash[self.attr.to_s]   if model_attr?(hash)
end
model_attr?(hash) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 210
def model_attr?(hash)
  hash.key?(self.attr) || hash.key?(self.attr.to_s)
end
root?() click to toggle source

Internal helper to know if we are at the top/root of the hierarchical model @return [Boolean] `true` if this object is the top `root`, `false` otherwise

# File lib/eco/api/session/batch/base_policy.rb, line 202
def root?
  @_parent == self
end
to_h() click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 206
def to_h
  @policies
end

Private Instance Methods

model_to_hash(model) click to toggle source
# File lib/eco/api/session/batch/base_policy.rb, line 221
def model_to_hash(model)
  return model if model.is_a?(Hash)
  model.to_h if model.respond_to?(:to_h)
end