class ActionBlocks::FilterAdapter

Data Engine

Attributes

engine[RW]
filter_reqs[RW]
user[RW]

Public Class Methods

new(engine:, filter_reqs:, user:) click to toggle source
# File lib/action_blocks/data_engine/filter_adapter.rb, line 6
def initialize(engine:, filter_reqs:, user:)
  @engine = engine
  @user = user
  @rls_scheme = filter_reqs
  @model_id = @engine.root_klass.to_s.underscore
end

Public Instance Methods

evaluate(expression) click to toggle source
# File lib/action_blocks/data_engine/filter_adapter.rb, line 41
def evaluate(expression)
  # Convert Symbol to Arel Attribute
  return @arel_attributes[expression] if expression.class == Symbol

  # Convert Proc to it's result
  if expression.class == Proc
    proc_args = {}
    # debug expression.parameters
    if expression.parameters.include?([:keyreq, :user])
      proc_args[:user] = @user
    end
    return expression.call(**proc_args)
  end

  # Convert expression to Arel Predicate
  if expression.class == Array
    fn, *args = expression
    case fn
    when :user
      return @user.send(args[0])
    when :eq
      left, right = args
      return evaluate(left).eq(evaluate(right))
    when :not_eq
      left, right = args
      return evaluate(left).not_eq(evaluate(right))
    when :and
      return args.map {|x| evaluate(x)}.reduce(&:and)
    when :or
      return args.map {|x| evaluate(x)}.reduce(&:or)
    else
      raise "RLS function #{fn.inspect} not recognized"
    end
  end

  return expression
end
get_arel_attributes() click to toggle source

Convert all fields to arel nodes while building up needed @engine.joins

# File lib/action_blocks/data_engine/filter_adapter.rb, line 25
def get_arel_attributes()
  @fields = get_fields(@rls_scheme)
  @arel_attributes = {}
  [@fields].flatten.each do |f|
    f = ActionBlocks.find("field-#{@model_id}-#{f}")
    select_req = f.select_requirements
    if select_req[:type] == :summary
      raise "Summary fields not supported in authorizations"
    end
    field_name = select_req[:field_name]
    node, *rest = select_req[:path]
    @arel_attributes[field_name] = walk_path(@engine.root_klass, node, @engine.root_key, rest)
  end
  return @arel_attributes
end
get_fields(expression) click to toggle source

Extract fields from lisp/scheme

# File lib/action_blocks/data_engine/filter_adapter.rb, line 14
def get_fields(expression)
  if expression.class == Array
    fn, *args = expression
    return [] if fn == :user
    return args.map { |a| get_fields(a) }.flatten.uniq
  end
  return expression if expression.class == Symbol
  return []
end
process() click to toggle source
# File lib/action_blocks/data_engine/filter_adapter.rb, line 79
def process
  if (!@rls_scheme.empty?)
    @arel_attributes = get_arel_attributes()
    @engine.wheres << evaluate(@rls_scheme)
  end
end
walk_path(klass, node, parent_key, col_path) click to toggle source
# File lib/action_blocks/data_engine/filter_adapter.rb, line 86
def walk_path(klass, node, parent_key, col_path)
  key = [parent_key, node].compact.join('_').to_sym
  return node if node.class != Symbol
  return @engine.tables[parent_key][node.to_sym] if col_path.empty?

  # Create Arel Table Alias
  relation = klass.reflections[node.to_s]
  klass = relation.klass
  @engine.tables[key] = klass.arel_table.alias(key) unless @engine.tables[key]
  # Create Join
  fk = relation.join_foreign_key
  pk = relation.join_primary_key
  join_on = @engine.tables[key].create_on(@engine.tables[parent_key][fk].eq(@engine.tables[key][pk]))
  @engine.joins[key] = @engine.tables[parent_key].create_join(@engine.tables[key], join_on, Arel::Nodes::OuterJoin)
  # Recurse
  next_node, *rest = col_path
  return walk_path(klass, next_node, key, rest)
end