Class: Treequel::Filter
- Inherits:
-
Object
- Object
- Treequel::Filter
- Extended by:
- Loggability
- Includes:
- Constants::Patterns
- Defined in:
- lib/treequel/filter.rb
Overview
This is an object that is used to build an LDAP filter for Treequel::Branchsets.
Grammar (from RFC 2254) ==
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]
Defined Under Namespace
Classes: AndComponent, Component, FilterList, ItemComponent, NotComponent, OrComponent, PresentItemComponent, SimpleItemComponent, SubstringItemComponent
Constant Summary collapse
- DEFAULT_EXPRESSION =
The default filter expression to use when searching if none is specified
[ :objectClass ]
- LOGICAL_COMPONENTS =
The mapping of leftmost symbols in a boolean expression and the corresponding FilterComponent class.
{ :or => OrComponent, :| => OrComponent, :and => AndComponent, :& => AndComponent, :not => NotComponent, :"!" => NotComponent, }
- SEQUEL_FILTERTYPE_EQUIVALENTS =
An equivalence mapping of operation names from Sequel expressions into Treequel equivalents
{ :like => :equal, :>= => :greater, :<= => :less, }
- UNSUPPORTED_SEQUEL_FILTERTYPES =
A list of filtertypes that come in as Sequel::Expressions; these generated nicer exception messages that just ‘unknown filtertype’
{ :'~*' => %{LDAP doesn't support Regex filters}, :'~' => %{LDAP doesn't support Regex filters}, :> => %{LDAP doesn't support "greater-than"; use "greater-than-or-equal-to" (>=) instead}, :< => %{LDAP doesn't support "less-than"; use "less-than-or-equal-to" (<=) instead}, }
Constants included from Constants::Patterns
Constants::Patterns::ALPHA, Constants::Patterns::AMPERSAND, Constants::Patterns::ASSERTIONVALUE, Constants::Patterns::ASTERISK, Constants::Patterns::ATTRIBUTE_TYPE, Constants::Patterns::ATTRIBUTE_TYPE_AND_VALUE, Constants::Patterns::ATTRIBUTE_VALUE, Constants::Patterns::BASE64_CHAR, Constants::Patterns::BASE64_STRING, Constants::Patterns::COLON, Constants::Patterns::COMMA, Constants::Patterns::DESCR, Constants::Patterns::DIGIT, Constants::Patterns::DISTINGUISHED_NAME, Constants::Patterns::DN_ESCAPED, Constants::Patterns::DOLLAR, Constants::Patterns::DOT, Constants::Patterns::DQUOTE, Constants::Patterns::DSTRING, Constants::Patterns::EQUALS, Constants::Patterns::ESC, Constants::Patterns::ESCAPED, Constants::Patterns::EXCLAMATION, Constants::Patterns::EXTENSIONS, Constants::Patterns::FILL, Constants::Patterns::FOLD, Constants::Patterns::HEX, Constants::Patterns::HEXPAIR, Constants::Patterns::HEXSTRING, Constants::Patterns::HYPHEN, Constants::Patterns::KEYCHAR, Constants::Patterns::KEYSTRING, Constants::Patterns::KIND, Constants::Patterns::LANGLE, Constants::Patterns::LCURLY, Constants::Patterns::LDAP_ATTRIBUTE_DESCRIPTION, Constants::Patterns::LDAP_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDAP_MATCHING_RULE_DESCRIPTION, Constants::Patterns::LDAP_MATCHING_RULE_USE_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_DESC_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_KIND_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_SYNTAX_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDAP_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_SUBSTRING_FILTER, Constants::Patterns::LDAP_SUBSTRING_FILTER_VALUE, Constants::Patterns::LDAP_SYNTAX_DESCRIPTION, Constants::Patterns::LDAP_TRAILING_KIND_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_UNESCAPE_SQUOTE_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDIF_ATTRIBUTE_DESCRIPTION, Constants::Patterns::LDIF_ATTRIBUTE_TYPE, Constants::Patterns::LDIF_ATTRTYPE_OPTION, Constants::Patterns::LDIF_ATTRTYPE_OPTIONS, Constants::Patterns::LDIF_ATTRVAL_SPEC, Constants::Patterns::LDIF_ATTR_TYPE_CHARS, Constants::Patterns::LDIF_OPT_CHAR, Constants::Patterns::LDIF_SAFE_CHAR, Constants::Patterns::LDIF_SAFE_INIT_CHAR, Constants::Patterns::LDIF_SAFE_STRING, Constants::Patterns::LDIF_VALUE_SPEC, Constants::Patterns::LDIGIT, Constants::Patterns::LEADCHAR, Constants::Patterns::LEADKEYCHAR, Constants::Patterns::LEN, Constants::Patterns::LPAREN, Constants::Patterns::LUTF1, Constants::Patterns::MALFORMED_DSTRING, Constants::Patterns::MALFORMED_QDSTRING, Constants::Patterns::NOIDLEN, Constants::Patterns::NORMAL, Constants::Patterns::NUL, Constants::Patterns::NUMBER, Constants::Patterns::NUMERICOID, Constants::Patterns::OCTET, Constants::Patterns::OID, Constants::Patterns::OIDLIST, Constants::Patterns::OIDS, Constants::Patterns::PAIR, Constants::Patterns::PLUS, Constants::Patterns::QDESCR, Constants::Patterns::QDESCRLIST, Constants::Patterns::QDESCRS, Constants::Patterns::QDSTRING, Constants::Patterns::QDSTRINGLIST, Constants::Patterns::QDSTRINGS, Constants::Patterns::QQ, Constants::Patterns::QS, Constants::Patterns::QUOTED_DESCR, Constants::Patterns::QUOTED_NUMERICOID, Constants::Patterns::QUTF1, Constants::Patterns::QUTF8, Constants::Patterns::RANGLE, Constants::Patterns::RCURLY, Constants::Patterns::RELATIVE_DISTINGUISHED_NAME, Constants::Patterns::RPAREN, Constants::Patterns::SEMI, Constants::Patterns::SEP, Constants::Patterns::SHARP, Constants::Patterns::SP, Constants::Patterns::SPACE, Constants::Patterns::SPECIAL, Constants::Patterns::SQUOTE, Constants::Patterns::STRING, Constants::Patterns::STRINGCHAR, Constants::Patterns::SUTF1, Constants::Patterns::TILDE, Constants::Patterns::TRAILCHAR, Constants::Patterns::TUTF1, Constants::Patterns::UNESCAPED, Constants::Patterns::URI_REF, Constants::Patterns::USAGE, Constants::Patterns::USCORE, Constants::Patterns::UTF0, Constants::Patterns::UTF1, Constants::Patterns::UTF1SUBSET, Constants::Patterns::UTF2, Constants::Patterns::UTF3, Constants::Patterns::UTF4, Constants::Patterns::UTF8, Constants::Patterns::UTFMB, Constants::Patterns::VALUEENCODING, Constants::Patterns::VERTBAR, Constants::Patterns::WSP, Constants::Patterns::XSTRING
Instance Attribute Summary collapse
-
#component ⇒ Object
The filtercomp part of the filter.
Class Method Summary collapse
-
.parse_array_expression(expression) ⇒ Object
Turn the specified expression Array into a Treequel::Filter::Component object and return it.
-
.parse_expression(expression) ⇒ Object
Turn the specified filter
expressioninto a Treequel::Filter::Component object and return it. -
.parse_hash_expression(expression) ⇒ Object
Parse one or more tuples contained in a Hash into an ANDed set of Treequel::Filter::Components and return it.
-
.parse_item_component(attribute, value) ⇒ Object
Parse an item component from the specified
attributeandvalue. -
.parse_logical_array_expression(op, *components) ⇒ Object
Break down the given
expressionas a logical (AND, OR, or NOT) filter component and return it. -
.parse_sequel_expression(expression) ⇒ Object
Parse a Sequel::SQL::Expression as a Treequel::Filter::Component and return it.
-
.parse_tuple_array_expression(expression) ⇒ Object
Parse a tuple of the form: [ Symbol, Object ] into a Treequel::Filter::Component and return it.
Instance Method Summary collapse
-
#&(other_filter) ⇒ Object
(also: #+)
Return a new Filter that is the AND filter of the receiver with
other_filter. -
#==(other_filter) ⇒ Object
Equality operator – returns
trueifother_filteris equivalent to the receiver. -
#initialize(*expression_parts) ⇒ Filter
constructor
Create a new Treequel::Branchset::Filter with the specified
expression. -
#inspect ⇒ Object
Return a human-readable string representation of the filter suitable for debugging.
-
#promiscuous? ⇒ Boolean
(also: #is_promiscuous?)
Returns
trueif the filter contains a single ‘present’ component for the objectClass attribute (which will match every entry). -
#to_s ⇒ Object
Return the Treequel::Branchset::Filter as a String.
-
#|(other_filter) ⇒ Object
Return a new Filter that is the OR filter of the receiver with
other_filter.
Constructor Details
#initialize(*expression_parts) ⇒ Filter
Create a new Treequel::Branchset::Filter with the specified expression.
675 676 677 678 679 680 681 |
# File 'lib/treequel/filter.rb', line 675 def initialize( *expression_parts ) self.log.debug "New filter for expression: %p" % [ expression_parts ] @component = self.class.parse_expression( expression_parts ) self.log.debug " expression parsed into component: %p" % [ @component ] super() end |
Instance Attribute Details
#component ⇒ Object
The filtercomp part of the filter
689 690 691 |
# File 'lib/treequel/filter.rb', line 689 def component @component end |
Class Method Details
.parse_array_expression(expression) ⇒ Object
Turn the specified expression Array into a Treequel::Filter::Component object and return it.
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
# File 'lib/treequel/filter.rb', line 462 def self::parse_array_expression( expression ) self.log.debug "Parsing Array expression %p" % [ expression ] case # [ ] := '(objectClass=*)' when expression.empty? self.log.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) } self.log.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 |
.parse_expression(expression) ⇒ Object
Turn the specified filter expression into a Treequel::Filter::Component object and return it.
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/treequel/filter.rb', line 423 def self::parse_expression( expression ) self.log.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_hash_expression(expression) ⇒ Object
Parse one or more tuples contained in a Hash into an ANDed set of Treequel::Filter::Components and return it.
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/treequel/filter.rb', line 509 def self::parse_hash_expression( expression ) self.log.debug "Parsing Hash expression %p" % [ expression ] filterlist = expression.collect do |key, expr| self.log.debug " adding %p => %p to the filter list" % [ key, expr ] if expr.respond_to?( :fetch ) if expr.respond_to?( :length ) && expr.length > 1 self.log.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 self.log.debug " unwrapping singular subfilter" Treequel::Filter.new([ key.to_sym, expr.first ]) end else self.log.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_item_component(attribute, value) ⇒ Object
Parse an item component from the specified attribute and value
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/treequel/filter.rb', line 587 def self::parse_item_component( attribute, value ) self.log.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 |
.parse_logical_array_expression(op, *components) ⇒ Object
Break down the given expression as a logical (AND, OR, or NOT) filter component and return it.
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
# File 'lib/treequel/filter.rb', line 569 def self::parse_logical_array_expression( op, *components ) self.log.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| self.log.debug " making %p into a component" % [ filterexp ] Treequel::Filter.new( filterexp ) end.flatten return compclass.new( *filterlist ) end |
.parse_sequel_expression(expression) ⇒ Object
Parse a Sequel::SQL::Expression as a Treequel::Filter::Component and return it.
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
# File 'lib/treequel/filter.rb', line 605 def self::parse_sequel_expression( expression ) self.log.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( '*' ) self.log.debug \ " turning a LIKE expression with an asterisk into a substring filter" return Treequel::Filter::SubstringItemComponent.new( attribute, value ) else self.log.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' self.log.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_tuple_array_expression(expression) ⇒ Object
Parse a tuple of the form: [ Symbol, Object ] into a Treequel::Filter::Component and return it.
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
# File 'lib/treequel/filter.rb', line 540 def self::parse_tuple_array_expression( expression ) self.log.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 self.log.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 self.log.debug " item expression from a %p" % [ expression[1].class ] return self.parse_item_component( *expression ) end end |
Instance Method Details
#&(other_filter) ⇒ Object Also known as: +
Return a new Filter that is the AND filter of the receiver with other_filter.
731 732 733 734 735 |
# File 'lib/treequel/filter.rb', line 731 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 |
#==(other_filter) ⇒ Object
Equality operator – returns true if other_filter is equivalent to the receiver.
725 726 727 |
# File 'lib/treequel/filter.rb', line 725 def ==( other_filter ) return ( self.component == other_filter.component ) end |
#inspect ⇒ Object
Return a human-readable string representation of the filter suitable for debugging.
706 707 708 709 710 711 712 |
# File 'lib/treequel/filter.rb', line 706 def inspect return %{#<%s:0x%0x (%s)>} % [ self.class.name, self.object_id * 2, self.component, ] end |
#promiscuous? ⇒ Boolean Also known as: is_promiscuous?
Returns true if the filter contains a single ‘present’ component for the objectClass attribute (which will match every entry)
717 718 719 |
# File 'lib/treequel/filter.rb', line 717 def promiscuous? return self.component.promiscuous? end |
#to_s ⇒ Object
Return the Treequel::Branchset::Filter as a String.
693 694 695 696 697 698 699 700 701 |
# File 'lib/treequel/filter.rb', line 693 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 |
#|(other_filter) ⇒ Object
Return a new Filter that is the OR filter of the receiver with other_filter.
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
# File 'lib/treequel/filter.rb', line 740 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 |