class Ransack::Nodes::Condition

Experimental RBS support (using type sampling data from the type_fusion project).

# sig/ransack/nodes/condition.rbs

class Ransack::Nodes::Condition < Ransack::Nodes::Node
  def formatted_values_for_attribute: (Ransack::Nodes::Attribute attr) -> String
end

def arel_predicate

def arel_predicate
  raise "not implemented"
end

def arel_predicate

def arel_predicate
  predicate = attributes.map { |attribute|
    association = attribute.parent
    if negative? && attribute.associated_collection?
      query = context.build_correlated_subquery(association)
      context.remove_association(association)
      if self.predicate_name == 'not_null' && self.value
        query.where(format_predicate(attribute))
        Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
      else
        query.where(format_predicate(attribute).not)
        Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
      end
    else
      format_predicate(attribute)
    end
  }.reduce(combinator_method)
  if replace_right_node?(predicate)
    # Replace right node object to plain integer value in order to avoid
    # ActiveModel::RangeError from Arel::Node::Casted.
    # The error can be ignored here because RDBMSs accept large numbers
    # in condition clauses.
    plain_value = predicate.right.value
    predicate.right = plain_value
  end
  predicate
end

def arel_predicate_for_attribute(attr)

def arel_predicate_for_attribute(attr)
  if predicate.arel_predicate === Proc
    values = casted_values_for_attribute(attr)
    unless predicate.wants_array
      values = values.first
    end
    predicate.arel_predicate.call(values)
  else
    predicate.arel_predicate
  end
end

def attr_value_for_attribute(attr)

def attr_value_for_attribute(attr)
  return attr.attr if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
  predicate.case_insensitive ? attr.attr.lower : attr.attr
rescue
  attr.attr
end

def attributes

def attributes
  @attributes ||= []
end

def attributes=(args)

def attributes=(args)
  case args
  when Array
    args.each do |name|
      build_attribute(name)
    end
  when Hash
    args.each do |index, attrs|
      build_attribute(attrs[:name], attrs[:ransacker_args])
    end
  else
    raise ArgumentError,
      "Invalid argument (#{args.class}) supplied to attributes="
  end
end

def build(params)

def build(params)
  params.with_indifferent_access.each do |key, value|
    if key.match(/^(a|v|p|m)$/)
      self.send("#{key}=", value)
    end
  end
  self
end

def build_attribute(name = nil, ransacker_args = [])


isn't fixing issue #701 by introducing untested regressions.
TODO: Add test coverage for this behavior and ensure that `name.nil?`

built. The `name.nil?` conditional below currently does this.
#valid? conditional needs to be bypassed, otherwise nothing is
2. Nodes::Grouping#new_condition without arguments. In this case, the

and +ransacker_args+. Attributes are included only if #valid?.
1. Nodes::Condition#attributes=, with +name+ argument passed or +name+

After refactoring in 235eae3, it is now called from 2 places:

grouping condition.
only, without arguments, without #valid? checking, to build a new
This method was originally called from Nodes::Grouping#new_condition

== build_attribute
def build_attribute(name = nil, ransacker_args = [])
  Attribute.new(@context, name, ransacker_args).tap do |attribute|
    @context.bind(attribute, attribute.name)
    self.attributes << attribute if name.nil? || attribute.valid?
    if predicate && !negative?
      @context.lock_association(attribute.parent)
    end
  end
end

def build_value(val = nil)

def build_value(val = nil)
  Value.new(@context, val).tap do |value|
    self.values << value
  end
end

def casted_array?(predicate)

def casted_array?(predicate)
  predicate.value.is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
end

def casted_values_for_attribute(attr)

def casted_values_for_attribute(attr)
  validated_values.map { |v| v.cast(predicate.type || attr.type) }
end

def combinator

def combinator
  @attributes.size > 1 ? @combinator : nil
end

def combinator=(val)

def combinator=(val)
  @combinator = Constants::AND_OR.detect { |v| v == val.to_s } || nil
end

def combinator_method

def combinator_method
  combinator === Constants::OR ? :or : :and
end

def default_type

def default_type
  predicate.type || (attributes.first && attributes.first.type)
end

def eql?(other)

def eql?(other)
  self.class == other.class &&
  self.attributes == other.attributes &&
  self.predicate == other.predicate &&
  self.values == other.values &&
  self.combinator == other.combinator
end

def extract(context, key, values)

def extract(context, key, values)
  attributes, predicate, combinator =
    extract_values_for_condition(key, context)
  if attributes.size > 0 && predicate
    condition = self.new(context)
    condition.build(
      a: attributes,
      p: predicate.name,
      m: combinator,
      v: predicate.wants_array ? Array(values) : [values]
    )
    # TODO: Figure out what to do with multiple types of attributes,
    # if anything. Tempted to go with "garbage in, garbage out" here.
    if predicate.validate(condition.values, condition.default_type)
      condition
    else
      nil
    end
  end
end

def extract_values_for_condition(key, context = nil)

def extract_values_for_condition(key, context = nil)
  str = key.dup
  name = Predicate.detect_and_strip_from_string!(str)
  predicate = Predicate.named(name)
  unless predicate || Ransack.options[:ignore_unknown_conditions]
    raise ArgumentError, "No valid predicate for #{key}"
  end
  if context.present?
    str = context.ransackable_alias(str)
  end
  combinator =
  if str.match(/_(or|and)_/)
    $1
  else
    nil
  end
  if context.present? && context.attribute_method?(str)
    attributes = [str]
  else
    attributes = str.split(/_and_|_or_/)
  end
  [attributes, predicate, combinator]
end

def format_predicate(attribute)

def format_predicate(attribute)
  arel_pred = arel_predicate_for_attribute(attribute)
  arel_values = formatted_values_for_attribute(attribute)
  predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
  if in_predicate?(predicate)
    predicate.right = predicate.right.map do |pr|
      casted_array?(pr) ? format_values_for(pr) : pr
    end
  end
  predicate
end

def format_values_for(predicate)

def format_values_for(predicate)
  predicate.value.map do |val|
    val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
  end
end

def formatted_values_for_attribute(attr)

Experimental RBS support (using type sampling data from the type_fusion project).

def formatted_values_for_attribute: (Ransack::Nodes::Attribute attr) -> String

This signature was generated using 1 sample from 1 application.

def formatted_values_for_attribute(attr)
  formatted = casted_values_for_attribute(attr).map do |val|
    if attr.ransacker && attr.ransacker.formatter
      val = attr.ransacker.formatter.call(val)
    end
    val = predicate.format(val)
    val
  end
  if predicate.wants_array
    formatted
  else
    formatted.first
  end
end

def hash

def hash
  [attributes, predicate, values, combinator].hash
end

def in_predicate?(predicate)

def in_predicate?(predicate)
  return unless defined?(Arel::Nodes::Casted)
  predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
end

def inspect

def inspect
  data = [
    ['attributes'.freeze, a.try(:map, &:name)],
    ['predicate'.freeze, p],
    [Constants::COMBINATOR, m],
    ['values'.freeze, v.try(:map, &:value)]
  ]
  .reject { |e| e[1].blank? }
  .map { |v| "#{v[0]}: #{v[1]}" }
  .join(', '.freeze)
  "Condition <#{data}>"
end

def key

def key
  @key ||= attributes.map(&:name).join("_#{combinator}_") +
    "_#{predicate.name}"
end

def negative?

def negative?
  predicate.negative?
end

def persisted?

def persisted?
  false
end

def predicate_name

def predicate_name
  predicate.name if predicate
end

def predicate_name=(name)

def predicate_name=(name)
  self.predicate = Predicate.named(name)
  unless negative?
    attributes.each { |a| context.lock_association(a.parent) }
  end
  @predicate
end

def replace_right_node?(predicate)

def replace_right_node?(predicate)
  return false unless predicate.is_a?(Arel::Nodes::Binary)
  arel_node = predicate.right
  return false unless arel_node.is_a?(Arel::Nodes::Casted)
  relation, name = arel_node.attribute.values
  attribute_type = relation.type_for_attribute(name).type
  attribute_type == :integer && arel_node.value.is_a?(Integer)
end

def valid?

def valid?
  attributes.detect(&:valid?) && predicate && valid_arity? &&
    predicate.validate(values, default_type) && valid_combinator?
end

def valid_arity?

def valid_arity?
  values.size <= 1 || predicate.wants_array
end

def valid_combinator?

def valid_combinator?
  attributes.size < 2 || Constants::AND_OR.include?(combinator)
end

def validated_values

def validated_values
  values.select { |v| predicate.validator.call(v.value) }
end

def value

def value
  if predicate.wants_array
    values.map { |v| v.cast(default_type) }
  else
    values.first.cast(default_type)
  end
end

def values

def values
  @values ||= []
end

def values=(args)

def values=(args)
  case args
  when Array
    args.each do |val|
      val = Value.new(@context, val)
      self.values << val
    end
  when Hash
    args.each do |index, attrs|
      val = Value.new(@context, attrs[:value])
      self.values << val
    end
  else
    raise ArgumentError,
      "Invalid argument (#{args.class}) supplied to values="
  end
end