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.
The default filter to use when searching if non is specified
The default options hash for new Branchsets
The default scope to use when searching if none is specified
The branchset's base branch that will be used when searching as the basedn
The branchset's search options hash
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
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