class Treequel::Branchset
A branchset represents an abstract set of LDAP
records returned by a search in a directory. It can be used to create, retrieve, update, and delete records.
Search results are fetched on demand, so a branchset can be kept around and reused indefinitely (branchsets never cache results):
people = directory.ou( :people ) davids = people.filter(:firstName => 'david') # no records are retrieved davids.all # records are retrieved davids.all # records are retrieved again
Most branchset methods return modified copies of the branchset (functional style), so you can reuse different branchsets to access data:
# (employeeId < 2000) veteran_davids = davids.filter( :employeeId < 2000 ) # (&(employeeId < 2000)(|(deactivated >= '2008-12-22')(!(deactivated=*)))) active_veteran_davids = veteran_davids.filter([:or, ['deactivated >= ?', Date.today], [:not, [:deactivated]] ]) # (&(employeeId < 2000)(|(deactivated >= '2008-12-22')(!(deactivated=*)))(mobileNumber=*)) active_veteran_davids_with_cellphones = active_veteran_davids.filter( [:mobileNumber] )
Branchsets are Enumerable objects, so they can be manipulated using any of the Enumerable methods, such as map, inject, etc.
Constants
- DEFAULT_FILTER
The default filter to use when searching if non is specified
- DEFAULT_OPTIONS
The default options hash for new Branchsets
- DEFAULT_SCOPE
The default scope to use when searching if none is specified
Attributes
The branchset's base branch that will be used when searching as the basedn
The branchset's search options hash
Public Class Methods
Create a new Branchset
for a search from the DN of the specified branch
(a Treequel::Branch
), with the given options
.
# File lib/treequel/branchset.rb, line 80 def initialize( branch, options={} ) @branch = branch @options = DEFAULT_OPTIONS.merge( options ) self.log.debug "Setting up %p for branch %p with options: %p" % [ self.class, @branch, @options ] if @branch.directory.registered_controls.empty? self.log.debug " no registered controls." else @branch.directory.registered_controls.each do |control| self.log.debug " extending with %p" % [ control ] self.extend( control ) end end super() end
Public Instance Methods
If given another Branchset
or a BranchCollection, return a BranchCollection that includes them both. If given anything else, execute the search and return the results plus other
in an Array.
# File lib/treequel/branchset.rb, line 193 def +( other ) if other.is_a?( Treequel::BranchCollection ) || other.is_a?( Treequel::Branchset ) return Treequel::BranchCollection.new( self, other ) else return self.all + Array( other ) end end
Return the results of executing the search without the other_object
.
# File lib/treequel/branchset.rb, line 203 def -( other_object ) other_dn = other_object.dn return self.reject {|branch| branch.dn.downcase == other_dn.downcase } end
Return a clone of the receiving Branchset
that will return instances of the give branchclass
instead of Treequel::Branch
objects. This may be a subclass of Treequel::Branch
, but it doesn't need to be as long as they duck-type the same.
# File lib/treequel/branchset.rb, line 432 def as( branchclass ) newset = self.clone newset.branch = branchclass.new( self.branch.directory, self.branch.dn ) return newset end
Returns the DN of the Branchset's branch.
# File lib/treequel/branchset.rb, line 124 def base_dn return self.branch.dn end
Override the default clone method to support cloning with different options.
# File lib/treequel/branchset.rb, line 130 def clone( options={} ) self.log.debug "cloning %p with options = %p" % [ self, options ] newset = super() newset.options = @options.merge( options ) return newset end
Create a BranchCollection from the results of the Branchset
and return it.
# File lib/treequel/branchset.rb, line 185 def collection return Treequel::BranchCollection.new( self.all ) end
Iterate over the entries which match the current criteria and yield each of them as Treequel::Branch
objects to the supplied block.
# File lib/treequel/branchset.rb, line 211 def each( &block ) raise LocalJumpError, "no block given" unless block self.branch.search( self.scope, self.filter, :selectattrs => self.select, :timeout => self.timeout, # :sortby => self.order, :limit => self.limit, :client_controls => self.get_client_controls, :server_controls => self.get_server_controls, &block ) end
Return true
if no entries match the Branchset's current criteria.
# File lib/treequel/branchset.rb, line 248 def empty? return self.first.nil? ? true : false end
Extend the Branchset
with one or more modules. Overridden to also call the modules' initializers if they have them.
# File lib/treequel/branchset.rb, line 114 def extend( *modules ) super modules.each do |mod| mod.instance_method( :initialize ).bind( self ).call if mod.private_instance_methods.map( &:to_sym ).include?( :initialize ) end end
Returns a clone of the receiving Branchset
with the given filterspec
added to it.
# File lib/treequel/branchset.rb, line 302 def filter( *filterspec ) if filterspec.empty? opts = self.options opts[:filter] = Treequel::Filter.new(opts[:filter]) unless opts[:filter].is_a?( Treequel::Filter ) return opts[:filter] else self.log.debug "cloning %p with filterspec: %p" % [ self, filterspec ] newfilter = Treequel::Filter.new( *filterspec ) return self.clone( :filter => self.filter + newfilter ) end end
Return an LDAP
filter string made up of the current filter components.
# File lib/treequel/branchset.rb, line 179 def filter_string return self.filter.to_s end
Fetch the first n
entries which matches the current criteria and return them as instances of the object that is set as the branch
(e.g., Treequel::Branch
). If n
is nil
, returns just the first object in the Array.
# File lib/treequel/branchset.rb, line 229 def first( n=nil ) results = self.branch.search( self.scope, self.filter, :selectattrs => self.select, :timeout => self.timeout, # :sortby => self.order, :client_controls => self.get_client_controls, :server_controls => self.get_server_controls, :limit => n || 1 ) if n return results.first( n ) else return results.first end end
Return a clone of the receiving Branchset
that will perform its search from other_dn
instead of its own.
# File lib/treequel/branchset.rb, line 441 def from( other_dn ) newset = self.clone other_dn = other_dn.dn if other_dn.respond_to?( :dn ) newset.branch = newset.branch.class.new( self.branch.directory, other_dn ) return newset end
Return a human-readable string representation of the object suitable for debugging.
# File lib/treequel/branchset.rb, line 164 def inspect "#<%s:0x%0x base_dn='%s', filter=%s, scope=%s, select=%s, limit=%d, timeout=%0.3f>" % [ self.class.name, self.object_id * 2, self.base_dn, self.filter_string, self.scope, self.select.empty? ? '*' : self.select.join(','), self.limit, self.timeout, ] end
If called with a new_limit
, returns a clone of the receiving Branchset
that will fetch (at most) new_limit
Branches. If no new_limit
argument is specified, returns the Branchset's current limit. A limit of '0' means that all Branches will be fetched.
# File lib/treequel/branchset.rb, line 393 def limit( new_limit=nil ) if new_limit.nil? return self.options[:limit] else self.log.debug "cloning %p with new limit: %p" % [ self, new_limit ] return self.clone( :limit => Integer(new_limit) ) end end
Either maps entries which match the current criteria into an Array of the given attribute
, or falls back to the block form if no attribute
is specified. If both an attribute
and a block
are given, the block
is called once for each attribute
value instead of with each Branch.
# File lib/treequel/branchset.rb, line 257 def map( attribute=nil, &block ) # :yields: branch or attribute if attribute if block super() {|branch| block.call(branch[attribute]) } else super() {|branch| branch[attribute] } end else super( &block ) end end
Add a clause made from a negated filterspec
to an existing filter.
# File lib/treequel/branchset.rb, line 331 def not( *filterspec ) self.log.debug "cloning %p with negated filterspec: %p" % [ self, filterspec ] notfilter = Treequel::Filter.new( :not, *filterspec ) return self.clone( :filter => self.filter + notfilter ) end
Add an alternate filter to an existing filter by ORing it with filterspec
.
# File lib/treequel/branchset.rb, line 317 def or( *filterspec ) opts = self.options existing_filter = self.filter raise Treequel::ExpressionError, "no existing filter" if existing_filter.promiscuous? newfilter = Treequel::Filter.new( *filterspec ) self.log.debug "cloning %p with alternative filterspec: %p" % [ self, filterspec ] return self.clone( :filter => (self.filter | newfilter) ) end
If called with no argument, returns the current scope of the Branchset
. If called with an argument (which should be one of the keys of Treequel::Constants::SCOPE), returns a clone of the receiving Branchset
with the new_scope
.
# File lib/treequel/branchset.rb, line 342 def scope( new_scope=nil ) if new_scope self.log.debug "cloning %p with new scope: %p" % [ self, new_scope ] return self.clone( :scope => new_scope.to_sym ) else return @options[:scope] end end
If called with one or more attributes
, returns a clone of the receiving Branchset
that will only fetch the attributes
specified. If no attributes
are specified, return the list of attributes that will be fetched by the receiving Branchset
. An empty Array means that it should fetch all attributes, which is the default.
# File lib/treequel/branchset.rb, line 357 def select( *attributes ) if attributes.empty? return self.options[:select].collect {|attribute| attribute.to_s } else self.log.debug "cloning %p with new selection: %p" % [ self, attributes ] return self.clone( :select => attributes ) end end
Returns a clone of the receiving Branchset
that will fetch all attributes.
# File lib/treequel/branchset.rb, line 368 def select_all return self.clone( :select => [] ) end
Return a clone of the receiving Branchset
that will fetch the specified attributes
in addition to its own.
# File lib/treequel/branchset.rb, line 375 def select_more( *attributes ) return self.select( *(Array(@options[:select]) | attributes) ) end
Return a clone of the receiving Branchset
that will search with its timeout set to seconds
, which is in floating-point seconds.
# File lib/treequel/branchset.rb, line 412 def timeout( seconds=nil ) if seconds return self.clone( :timeout => seconds ) else return @options[:timeout] end end
Map the results returned by the search into a hash keyed by the first value of keyattr
in the entry. If the optional valueattr
argument is given, the values will be the first corresponding attribute, else the value will be the whole entry.
# File lib/treequel/branchset.rb, line 273 def to_hash( keyattr, valueattr=nil ) return self.inject({}) do |hash, branch| key = branch[ keyattr ] key = key.first if key.respond_to?( :first ) # Extract either the valueattr, or the whole entry hash if no valueattr was given. if valueattr self.log.debug " extracting value for attribute %p" % [ valueattr ] if branch[ valueattr ].respond_to?( :first ) hash[ key ] = branch[ valueattr ].first else hash[ key ] = branch[ valueattr ] end else self.log.debug " using the whole entry hash (%p)" hash[ key ] = branch.entry end hash end end
Return a string representation of the Branchset's filter
# File lib/treequel/branchset.rb, line 139 def uri # :scheme, # :host, :port, # :dn, # :attributes, # :scope, # :filter, # :extensions, uri = self.branch.uri uri.attributes = self.select.join(',') uri.scope = SCOPE_NAME[ self.scope ] uri.filter = self.filter_string # :TODO: Add extensions? Support extensions in Branchset? return uri end
Return a clone of the receiving Branchset
that will fetch any operational attributes in addition to its own. This is exactly equivalent to:
branchset.select( :+ ).
# File lib/treequel/branchset.rb, line 384 def with_operational_attributes return self.select( :+ ) end
Return a clone of the receiving Branchset
that has no restriction on the number of Branches that will be fetched.
# File lib/treequel/branchset.rb, line 405 def without_limit return self.clone( :limit => 0 ) end
Return a clone of the receiving Branchset
that will not use a timeout when searching.
# File lib/treequel/branchset.rb, line 423 def without_timeout return self.clone( :timeout => 0 ) end