module Graphoid::ActiveRecordDriver

Public Class Methods

belongs_to?(type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 18
def belongs_to?(type)
  type == ActiveRecord::Reflection::BelongsToReflection
end
class_of(relation) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 56
def class_of(relation)
  {
    ActiveRecord::Reflection::HasAndBelongsToManyReflection => ManyToMany,
    ActiveRecord::Reflection::BelongsToReflection => BelongsTo,
    ActiveRecord::Reflection::ThroughReflection => ManyToMany,
    ActiveRecord::Reflection::HasManyReflection => HasMany,
    ActiveRecord::Reflection::HasOneReflection => HasOne
  }[relation.class] || Relation
end
eager_load(selection, model) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 101
def eager_load(selection, model)
  nodes = selection.ast_node.selections.first.selections
  nodes.select!{ |n| !n.selections.empty? }
  include_array = generate_array(nodes)
  include_array.empty? ? model : model.includes(*include_array)
end
embedded_in?(_type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 34
def embedded_in?(_type)
  false
end
embeds_many?(_type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 30
def embeds_many?(_type)
  false
end
embeds_one?(_type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 26
def embeds_one?(_type)
  false
end
execute_and(scope, parsed) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 108
def execute_and(scope, parsed)
  scope.where(parsed)
end
execute_or(scope, list) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 112
def execute_or(scope, list)
  list.map! do |object|
    Graphoid::Queries::Processor.execute(scope, object)
  end
  list.reduce(:or)
end
fields_of(model) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 70
def fields_of(model)
  model.columns
end
generate_array(nodes) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 86
def generate_array(nodes)
  include_array = []
  nodes.each do |node|
    children = node.selections.select!{ |n| !n.selections.empty? }

    if children.empty?
      include_array.push(node.name.to_sym)
    else
      include_array.push(node.name.to_sym => generate_array(children))
    end
  end

  include_array
end
has_and_belongs_to_many?(type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 10
def has_and_belongs_to_many?(type)
  type == ActiveRecord::Reflection::HasAndBelongsToManyReflection
end
has_many?(type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 14
def has_many?(type)
  type == ActiveRecord::Reflection::HasManyReflection
end
has_one?(type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 22
def has_one?(type)
  type == ActiveRecord::Reflection::HasOneReflection
end
inverse_name_of(relation) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 66
def inverse_name_of(relation)
  relation.inverse_of&.class_name&.underscore
end
parse(attribute, value, operator) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 119
def parse(attribute, value, operator)
  field = attribute.name
  case operator
  when 'not'
    parsed = ["#{field} != ?", value]
    parsed = ["#{field} not like ?", value.to_s] if attribute.type == :string
    parsed = ["#{field} is not null"] if value.nil?
  when 'contains', 'regex'
    parsed = ["#{field} like ?", "%#{value}%"]
  when 'gt', 'gte', 'lt', 'lte', 'not', 'in', 'nin'
    operator = { gt: '>', gte: '>=', lt: '<', lte: '<=', in: 'in', nin: 'not in' }[operator.to_sym]
    parsed = ["#{field} #{operator} (?)", value]
  else
    parsed = ["#{field} = ?", value]
  end
  parsed
end
relate_many(scope, relation, value, operator) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 148
def relate_many(scope, relation, value, operator)
  parsed = {}
  field_name = relation.inverse_name || scope.name.underscore
  target = Graphoid::Queries::Processor.execute(relation.klass, value).to_a

  if relation.many_to_many?
    field_name = field_name.to_s.singularize + '_ids'
    ids = target.map(&field_name.to_sym)
    ids.flatten!.uniq!
  else
    field_name = :"#{field_name}_id"
    ids = target.map(&field_name)
  end

  if operator == 'none'
    parsed = ['id not in (?)', ids] if ids.present?
  elsif operator == 'some'
    parsed = ['id in (?)', ids]
  elsif operator == 'every'

    # the following process is a SQL division
    # the amount of queries it executes is on per row
    # it is the same than doing an iteration process
    # that iteration process would work in mongoid too

    # TODO: check and fix this query for many to many relations

    plural_name = relation.name.pluralize
    conditions = value.map do |_key, _value|
      operation = Operation.new(relation.klass, _key, _value)
      parsed = parse(operation.operand, operation.value, operation.operator)
      val = parsed.last.is_a?(String) ? "'#{parsed.last}'" : parsed.last
      parsed = parsed.first.sub('?', val)
      " AND #{parsed}"
    end.join

    query = "
              SELECT count(id) as total, #{field_name}
              FROM #{plural_name} A
              GROUP BY #{field_name}
              HAVING total = (
                SELECT count(id)
                FROM #{plural_name} B
                WHERE B.#{field_name} = A.#{field_name}
                #{conditions}
              )
            "
    result = ActiveRecord::Base.connection.execute(query)
    ids = result.map { |row| row[field_name.to_s] }

    parsed = ['id in (?)', ids]
  end

  parsed
end
relate_through(scope, relation, value) click to toggle source

TODO: fix this as it is unused

# File lib/graphoid/drivers/active_record.rb, line 138
def relate_through(scope, relation, value)
  # if relation.has_one_through?
  #   ids = Graphoid::Queries::Processor.execute(relation.klass, value).to_a.map(&:id)
  #   through = relation.source.options[:through].to_s.camelize.constantize
  #   ids = through.where(id: ids)
  #   ids = Graphoid::Queries::Processor.execute(relation.klass, value).to_a.map(&:id)
  #   parsed = *["#{field.underscore}_id in (?)", ids]
  # end
end
relation_type(relation) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 82
def relation_type(relation)
  relation.class
end
relations_of(model) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 74
def relations_of(model)
  model.reflections
end
skip(result, skip) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 78
def skip(result, skip)
  result.offset(skip)
end
through?(type) click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 6
def through?(type)
  type == ActiveRecord::Reflection::ThroughReflection
end
types_map() click to toggle source
# File lib/graphoid/drivers/active_record.rb, line 38
def types_map
  {
    binary: GraphQL::Types::Boolean,
    boolean: GraphQL::Types::Boolean,
    float: GraphQL::Types::Float,
    integer: GraphQL::Types::Int,
    string: GraphQL::Types::String,

    datetime: Graphoid::Scalars::DateTime,
    date: Graphoid::Scalars::DateTime,
    time: Graphoid::Scalars::DateTime,
    timestamp: Graphoid::Scalars::DateTime,
    text: Graphoid::Scalars::Text,
    bigint: Graphoid::Scalars::BigInt,
    decimal: Graphoid::Scalars::Decimal
  }
end