module Treequel::SortedResultsControl

A Treequel::Control module that implements the "LDAP Control Extension for Server Side Sorting of Search Results" (RFC 2891).

Usage

As with all Controls, you must first register the control with the Treequel::Directory object you're intending to search:

dir = Treequel.directory( 'ldap://ldap.acme.com/dc=acme,dc=com' )
dir.register_controls( Treequel::SortedResultsControl )

Once that's done, any Treequel::Branchset you create will have the #order method that will allow you to specify one or more attributes by which the server should sort results before returning them:

# Fetch people sorted by their last name, then first name, then
# by employeeNumber
people = dir.ou( :People )
sorted_people = people.filter( :objectClass => :person ).
    order( :sn, :givenName, :employeeNumber )

Constants

Criterion

A struct for tracking sorting criteria

OID

The control's request OID

RESPONSE_OID

The control's response OID

RESPONSE_RESULT_DESCRIPTIONS

Descriptions of result codes

Attributes

sort_order_criteria[RW]

The ordering criteria, if any

Public Class Methods

new() click to toggle source

Add the requisite instance variables to including Branchsets.

# File lib/treequel/controls/sortedresults.rb, line 89
def initialize
        self.log.debug "initializing %p" % [ self ]
        @sort_order_criteria = []
end

Public Instance Methods

each( &block ) click to toggle source

Override the Enumerable method to update the cookie value each time a page is fetched.

# File lib/treequel/controls/sortedresults.rb, line 153
def each( &block )
        super do |branch|
                if sorted_control = branch.controls.find {|control| control.oid == RESPONSE_OID }
                        sortResult, attributeType = sorted_control.decode
                        if sortResult.nonzero?
                                self.log.error "got non-zero response code for sort: %d (%s)" %
                                        [ sortResult, RESPONSE_RESULT_DESCRIPTIONS[sortResult] ]
                                raise Treequel::ControlError, RESPONSE_RESULT_DESCRIPTIONS[sortResult]
                        else
                                self.log.debug "got 'success' sort response code."
                        end
                end

                block.call( branch )
        end
end
order( *attributes ) click to toggle source

Clone the Branchset with a server-side sorted results control added and return it.

# File lib/treequel/controls/sortedresults.rb, line 104
def order( *attributes )
        self.log.warn "This control will likely not work in ruby-ldap versions " +
                " <= 0.9.9. See http://code.google.com/p/ruby-activeldap/issues/" +
                "detail?id=38 for details." if LDAP::PATCH_VERSION < 10

        if attributes.flatten.empty?
                self.log.debug "cloning %p with no order" % [ self ]
                return self.unordered
        else
                criteria = attributes.collect do |attrspec|
                        case attrspec
                        when Symbol
                                Criterion.new( attrspec.to_s )

                        when Sequel::SQL::Expression
                                Criterion.new( attrspec.expression.to_s, nil, attrspec.descending )

                        else
                                raise ArgumentError,
                                        "unsupported order specification type %s" % [ attrspec.class.name ]
                        end
                end

                self.log.debug "cloning %p with order criteria: %p" % [ self, criteria ]
                copy = self.clone
                copy.sort_order_criteria += criteria

                return copy
        end
end
unordered() click to toggle source

Clone the Branchset without a server-side sorted results control and return it.

# File lib/treequel/controls/sortedresults.rb, line 137
def unordered
        copy = self.clone
        copy.unordered!
        return copy
end
unordered!() click to toggle source

Remove any server-side sorted results control associated with the receiving Branchset, returning any removed criteria as an Array.

# File lib/treequel/controls/sortedresults.rb, line 146
def unordered!
        return self.sort_order_criteria.slice!( 0..-1 )
end

Protected Instance Methods

get_server_controls() click to toggle source

Treequel::Control API -- Get a configured LDAP::Control object for this Branchset.

# File lib/treequel/controls/sortedresults.rb, line 198
def get_server_controls
        controls = super
        criteria = self.sort_order_criteria

        if criteria.empty?
                self.log.debug "No sort order criteria; skipping the server-side sort control"
        else
                self.log.debug "Found %d sort order criteria; generating a server-side sort control" %
                        [ criteria.length ]
                asn1_string = self.make_sorted_control_value( criteria )
                controls << LDAP::Control.new( OID, asn1_string, true )
        end

        return controls
end
make_sorted_control_value( sort_criteria ) click to toggle source

Make the ASN.1 string for the control value out of the given mode, cookie, reload_hint.

# File lib/treequel/controls/sortedresults.rb, line 177
def make_sorted_control_value( sort_criteria )

        ### (http://tools.ietf.org/html/rfc2891#section-1.1):
    # SortKeyList ::= SEQUENCE OF SEQUENCE {
    #              attributeType   AttributeDescription,
    #              orderingRule    [0] MatchingRuleId OPTIONAL,
    #              reverseOrder    [1] BOOLEAN DEFAULT FALSE }
        encoded_vals = sort_criteria.collect do |criterion|
                seq = []
                seq << OpenSSL::ASN1::OctetString.new( criterion.type )
                seq << OpenSSL::ASN1::ObjectId.new( criterion.ordering_rule ) if criterion.ordering_rule
                seq << OpenSSL::ASN1::Boolean.new( true ) if criterion.reverse_order
                OpenSSL::ASN1::Sequence.new( seq )
        end

        return OpenSSL::ASN1::Sequence.new( encoded_vals ).to_der
end