module Braintree class AdvancedSearch class SearchNode def self.operators(*operator_names) operator_names.each do |operator| define_method(operator) do |value| @parent.add_criteria(@node_name, operator => value.to_s) end end end def initialize(name, parent) @node_name, @parent = name, parent end end class IsNode < SearchNode operators :is end class EqualityNode < IsNode operators :is_not end class PartialMatchNode < EqualityNode operators :ends_with, :starts_with end class TextNode < PartialMatchNode operators :contains end class KeyValueNode < SearchNode def is(value) @parent.add_criteria(@node_name, value) end end class MultipleValueNode < SearchNode def in(*values) values.flatten! unless allowed_values.nil? bad_values = values - allowed_values raise ArgumentError.new("Invalid argument(s) for #{@node_name}: #{bad_values.join(", ")}") if bad_values.any? end @parent.add_criteria(@node_name, values) end def initialize(name, parent, options) super(name, parent) @options = options end def allowed_values @options[:allows] end def is(value) self.in(value) end end class EndsWithNode < SearchNode operators :ends_with end class MultipleValueOrTextNode < MultipleValueNode extend Forwardable def_delegators :@text_node, :contains, :ends_with, :is, :is_not, :starts_with def initialize(name, parent, options) super @text_node = TextNode.new(name, parent) end end class RangeNode < SearchNode operators :is def between(min, max) self >= min && self <= max end def >=(min) @parent.add_criteria(@node_name, :min => min) end def <=(max) @parent.add_criteria(@node_name, :max => max) end end def self.text_fields(*fields) _create_field_accessors(fields, TextNode) end def self.equality_fields(*fields) _create_field_accessors(fields, EqualityNode) end def self.is_fields(*fields) _create_field_accessors(fields, IsNode) end def self.partial_match_fields(*fields) _create_field_accessors(fields, PartialMatchNode) end def self.multiple_value_field(field, options={}) define_method(field) do MultipleValueNode.new(field, self, options) end end def self.multiple_value_or_text_field(field, options={}) define_method(field) do MultipleValueOrTextNode.new(field, self, options) end end def self.key_value_fields(*fields) _create_field_accessors(fields, KeyValueNode) end def self.range_fields(*fields) _create_field_accessors(fields, RangeNode) end def self.date_range_fields(*fields) _create_field_accessors(fields, DateRangeNode) end def self.ends_with_fields(*fields) _create_field_accessors(fields, EndsWithNode) end def self._create_field_accessors(fields, node_class) fields.each do |field| define_method(field) do |*args| raise "An operator is required" unless args.empty? node_class.new(field, self) end end end def initialize @criteria = {} end def add_criteria(key, value) if @criteria[key].is_a?(Hash) @criteria[key].merge!(value) else @criteria[key] = value end end def to_hash @criteria end end end