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