This is an object that is used to build an LDAP filter for Treequel::Branchsets.
filter = "(" filtercomp ")" filtercomp = and / or / not / item and = "&" filterlist or = "|" filterlist not = "!" filter filterlist = 1*filter item = simple / present / substring / extensible simple = attr filtertype value filtertype = equal / approx / greater / less equal = "=" approx = "~=" greater = ">=" less = "<=" extensible = attr [":dn"] [":" matchingrule] ":=" value / [":dn"] ":" matchingrule ":=" value present = attr "=*" substring = attr "=" [initial] any [final] initial = value any = "*" *(value "*") final = value attr = AttributeDescription from Section 4.1.5 of [1] matchingrule = MatchingRuleId from Section 4.1.9 of [1] value = AttributeValue from Section 4.1.6 of [1]
The default filter expression to use when searching if none is specified
The mapping of leftmost symbols in a boolean expression and the corresponding FilterComponent class.
An equivalence mapping of operation names from Sequel expressions into Treequel equivalents
A list of filtertypes that come in as Sequel::Expressions; these generated nicer exception messages that just 'unknown filtertype'
The filtercomp part of the filter
Create a new Treequel::Branchset::Filter with the specified
expression
.
# File lib/treequel/filter.rb, line 657 def initialize( *expression_parts ) @component = self.class.parse_expression( expression_parts ) self.log.debug "created a filter with component: %p" % [ @component ] super() end
Turn the specified expression Array into a Treequel::Filter::Component object and return it.
# File lib/treequel/filter.rb, line 449 def self::parse_array_expression( expression ) Treequel.logger.debug "Parsing Array expression %p" % [ expression ] case # [ ] := '(objectClass=*)' when expression.empty? Treequel.logger.debug " empty expression -> objectClass presence item component" return Treequel::Filter::PresentItemComponent.new # Collection of subfilters # [ [:uid, 'mahlon'], [:employeeNumber, 20202] ] when expression.all? {|elem| elem.is_a?(Array) } Treequel.logger.debug " parsing array of subfilters" filters = expression.collect {|exp| Treequel::Filter.new(exp) } if filters.length > 1 return Treequel::Filter::AndComponent.new( filters ) else return filters.first end # Literal filters [ 'uid~=gung', 'l=bangkok' ] := '(uid~=gung)(l=bangkok)' when expression.all? {|item| item.is_a?(String) } filters = expression.collect {|item| Treequel::Filter.new(item) } return Treequel::Filter::FilterList.new( filters ) # Collection of subfilter objects when expression.all? {|elem| elem.is_a?(Treequel::Filter) } return Treequel::Filter::FilterList.new( expression ) # [ :attribute ] := '(attribute=*)' when expression.length == 1 return self.parse_expression( expression[0] ) when expression[0].is_a?( Symbol ) return self.parse_tuple_array_expression( expression ) else raise Treequel::ExpressionError, "don't know how to turn %p into a filter component" % [ expression ] end end
Turn the specified filter expression
into a Treequel::Filter::Component object and
return it.
# File lib/treequel/filter.rb, line 410 def self::parse_expression( expression ) Treequel.logger.debug "Parsing expression %p" % [ expression ] expression = expression[0] if expression.is_a?( Array ) && expression.length == 1 case expression # String-literal filters when String return expression # 'Item' components when Array return self.parse_array_expression( expression ) # Composite item components when Hash return self.parse_hash_expression( expression ) # Unwrapped presence item filter when Symbol return Treequel::Filter::PresentItemComponent.new( expression ) # Support Sequel expressions when Sequel::SQL::Expression return self.parse_sequel_expression( expression ) # Filters and components can already act as components of other filters when Treequel::Filter, Treequel::Filter::Component return expression else raise Treequel::ExpressionError, "don't know how to turn %p into an filter component" % [ expression ] end end
Parse one or more tuples contained in a Hash into an ANDed set of Treequel::Filter::Components and return it.
# File lib/treequel/filter.rb, line 496 def self::parse_hash_expression( expression ) Treequel.logger.debug "Parsing Hash expression %p" % [ expression ] filterlist = expression.collect do |key, expr| Treequel.logger.debug " adding %p => %p to the filter list" % [ key, expr ] if expr.respond_to?( :fetch ) Treequel.logger.debug " ORing together %d subfilters since %p has indices" % [ expr.length, expr ] subfilters = expr.collect {|val| Treequel::Filter.new(key, val) } Treequel::Filter.new( :or, subfilters ) else Treequel.logger.debug " value is a scalar; creating a single filter" Treequel::Filter.new([ key.to_sym, expr ]) end end if filterlist.length > 1 return Treequel::Filter::AndComponent.new( *filterlist ) else return filterlist.first end end
Parse an item component from the specified attribute
and
value
# File lib/treequel/filter.rb, line 569 def self::parse_item_component( attribute, value ) Treequel.logger.debug " tuple expression (%p=%p)-> item component" % [ attribute, value ] case when attribute.to_s.index( ':' ) raise NotImplementedError, "extensible filters are not yet supported" when value == '*' return Treequel::Filter::PresentItemComponent.new( attribute ) when value =~ LDAP_SUBSTRING_FILTER_VALUE return Treequel::Filter::SubstringItemComponent.new( attribute, value ) else return Treequel::Filter::SimpleItemComponent.new( attribute, value ) end end
Break down the given expression
as a logical (AND, OR, or NOT)
filter component and return it.
# File lib/treequel/filter.rb, line 551 def self::parse_logical_array_expression( op, *components ) Treequel.logger.debug "Parsing logical %p expression with components: %p" % [ op, components ] compclass = LOGICAL_COMPONENTS[ op ] or raise "don't know what a %p condition is. I only know about: %p" % [ op, LOGICAL_COMPONENTS.keys ] filterlist = components.collect do |filterexp| Treequel.logger.debug " making %p into a component" % [ filterexp ] Treequel::Filter.new( filterexp ) end.flatten return compclass.new( *filterlist ) end
Parse a Sequel::SQL::Expression as a Treequel::Filter::Component and return it.
# File lib/treequel/filter.rb, line 587 def self::parse_sequel_expression( expression ) Treequel.logger.debug " parsing Sequel expression: %p" % [ expression ] if expression.respond_to?( :op ) op = expression.op.to_s.downcase.to_sym if equivalent = SEQUEL_FILTERTYPE_EQUIVALENTS[ op ] attribute, value = *expression.args # Turn :sn.like( 'bob' ) into (cn~=bob) 'cause it has no asterisks if op == :like if value.index( '*' ) Treequel.logger.debug " turning a LIKE expression with an asterisk into a substring filter" return Treequel::Filter::SubstringItemComponent.new( attribute, value ) else Treequel.logger.debug " turning a LIKE expression with no wildcards into an 'approx' filter" equivalent = :approx end end return Treequel::Filter::SimpleItemComponent.new( attribute, value, equivalent ) elsif op == :'!=' contents = Treequel::Filter.new( expression.args ) return Treequel::Filter::NotComponent.new( contents ) elsif op == :'not like' Treequel.logger.debug " making a NOT LIKE expression out of: %p" % [ expression ] attribute, value = *expression.args component = nil if value.index( '*' ) component = Treequel::Filter::SubstringItemComponent.new( attribute, value ) else component = Treequel::Filter::SimpleItemComponent.new( attribute, value, :approx ) end filter = Treequel::Filter.new( component ) return Treequel::Filter::NotComponent.new( filter ) elsif LOGICAL_COMPONENTS.key?( op ) components = expression.args.collect do |comp| Treequel::Filter.new( comp ) end return self.parse_logical_array_expression( op, components ) elsif msg = UNSUPPORTED_SEQUEL_FILTERTYPES[ op ] raise Treequel::ExpressionError, "unsupported Sequel filter syntax %p: %s" % [ expression, msg ] else raise ScriptError, " unhandled Sequel BooleanExpression: add handling for %p: %p" % [ op, expression ] end else raise Treequel::ExpressionError, "don't know how to turn %p into a component" % [ expression ] end end
Parse a tuple of the form: [ Symbol, Object ] into a Treequel::Filter::Component and return it.
# File lib/treequel/filter.rb, line 522 def self::parse_tuple_array_expression( expression ) Treequel.logger.debug "Parsing tuple Array expression %p" % [ expression ] case expression[1] # [ :and/:or/:not, [:uid, 1] ] := (&/|/!(uid=1)) # [ :and/:or/:not, {:uid => 1} ] := (&/|/!(uid=1)) when Array, Hash return self.parse_logical_array_expression( *expression ) when Range Treequel.logger.debug " two ANDed item expressions from a Range" attribute = expression[0] range = expression[1] left = "#{attribute}>=#{range.begin}" right = "#{attribute}<=#{range.exclude_end? ? range.max : range.end}" return self.parse_logical_array_expression( :and, [left, right] ) # [ :attribute, 'value' ] := '(attribute=value)' # when String, Symbol, Numeric, Time else Treequel.logger.debug " item expression from a %p" % [ expression[1].class ] return self.parse_item_component( *expression ) end end
Return a new Filter that is the AND filter of the
receiver with other_filter
.
# File lib/treequel/filter.rb, line 712 def &( other_filter ) return other_filter if self.promiscuous? return self.dup if other_filter.promiscuous? return self.class.new( :and, [self, other_filter] ) end
Equality operator -- returns true
if other_filter
is equivalent to the receiver.
# File lib/treequel/filter.rb, line 706 def ==( other_filter ) return ( self.component == other_filter.component ) end
Return a human-readable string representation of the filter suitable for debugging.
# File lib/treequel/filter.rb, line 687 def inspect return %Q{#<%s:0x%0x (%s)>} % [ self.class.name, self.object_id * 2, self.component, ] end
Returns true
if the filter contains a single 'present'
component for the objectClass attribute (which will match every entry)
# File lib/treequel/filter.rb, line 698 def promiscuous? return self.component.promiscuous? end
Return the Treequel::Branchset::Filter as a String.
# File lib/treequel/filter.rb, line 674 def to_s # self.log.debug "stringifying filter %p" % [ self ] filtercomp = self.component.to_s if filtercomp[0] == (( return filtercomp else return '(' + filtercomp + ')' end end
Return a new Filter that is the OR filter of the
receiver with other_filter
.
# File lib/treequel/filter.rb, line 721 def |( other_filter ) return other_filter if self.promiscuous? return self.dup if other_filter.promiscuous? # Collapse nested ORs into a single one with an additional alternation # if possible. if self.component.respond_to?( :add_alternation ) self.log.debug "collapsing nested ORs..." newcomp = self.component.dup newcomp.add_alternation( other_filter ) return self.class.new( newcomp ) else return self.class.new( :or, [self, other_filter] ) end end