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
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 )
The ordering criteria, if any
Add the requisite instance variables to including Branchsets.
# File lib/treequel/controls/sortedresults.rb, line 91
def initialize
self.log.debug "initializing %p" % [ self ]
@sort_order_criteria = []
end
Override the Enumerable method to update the cookie value each time a page is fetched.
# File lib/treequel/controls/sortedresults.rb, line 155
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
Clone the Branchset with a server-side sorted results control added and return it.
# File lib/treequel/controls/sortedresults.rb, line 106
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
Clone the Branchset without a server-side sorted results control and return it.
# File lib/treequel/controls/sortedresults.rb, line 139
def unordered
copy = self.clone
copy.unordered!
return copy
end
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 148
def unordered!
return self.sort_order_criteria.slice!( 0..-1 )
end
Treequel::Control API – Get a configured LDAP::Control object for this Branchset.
# File lib/treequel/controls/sortedresults.rb, line 200
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 the ASN.1 string for the control value out of the given
mode
, cookie
, reload_hint
.
# File lib/treequel/controls/sortedresults.rb, line 179
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