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

branch[RW]

The branchset's base branch that will be used when searching as the basedn

options[RW]

The branchset's search options hash

Public Class Methods

new( branch, options={} ) click to toggle source

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 75
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

+( other ) click to toggle source

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 188
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
-( other_object ) click to toggle source

Return the results of executing the search without the other_object.

# File lib/treequel/branchset.rb, line 198
def -( other_object )
        other_dn = other_object.dn
        return self.reject {|branch| branch.dn.downcase == other_dn.downcase }
end
as( branchclass ) click to toggle source

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 427
def as( branchclass )
        newset = self.clone
        newset.branch = branchclass.new( self.branch.directory, self.branch.dn )
        return newset
end
base_dn() click to toggle source

Returns the DN of the Branchset's branch.

# File lib/treequel/branchset.rb, line 119
def base_dn
        return self.branch.dn
end
clone( options={} ) click to toggle source

Override the default clone method to support cloning with different options.

# File lib/treequel/branchset.rb, line 125
def clone( options={} )
        self.log.debug "cloning %p with options = %p" % [ self, options ]
        newset = super()
        newset.options = @options.merge( options )
        return newset
end
collection() click to toggle source

Create a BranchCollection from the results of the Branchset and return it.

# File lib/treequel/branchset.rb, line 180
def collection
        return Treequel::BranchCollection.new( self.all )
end
each( &block ) click to toggle source

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 206
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
empty?() click to toggle source

Return true if no entries match the Branchset's current criteria.

# File lib/treequel/branchset.rb, line 243
def empty?
        return self.first.nil? ? true : false
end
extend( *modules ) click to toggle source

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 109
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
filter( *filterspec ) click to toggle source

Returns a clone of the receiving Branchset with the given filterspec added to it.

# File lib/treequel/branchset.rb, line 297
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
filter_string() click to toggle source

Return an LDAP filter string made up of the current filter components.

# File lib/treequel/branchset.rb, line 174
def filter_string
        return self.filter.to_s
end
first( n=nil ) click to toggle source

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 224
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
from( other_dn ) click to toggle source

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 436
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
inspect() click to toggle source

Return a human-readable string representation of the object suitable for debugging.

# File lib/treequel/branchset.rb, line 159
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
limit( new_limit=nil ) click to toggle source

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 388
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
map( attribute=nil ) { |branch or attribute| ... } click to toggle source

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 252
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
not( *filterspec ) click to toggle source

Add a clause made from a negated filterspec to an existing filter.

# File lib/treequel/branchset.rb, line 326
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
or( *filterspec ) click to toggle source

Add an alternate filter to an existing filter by ORing it with filterspec.

# File lib/treequel/branchset.rb, line 312
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
scope( new_scope=nil ) click to toggle source

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 337
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
select( *attributes ) click to toggle source

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 352
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
select_all() click to toggle source

Returns a clone of the receiving Branchset that will fetch all attributes.

# File lib/treequel/branchset.rb, line 363
def select_all
        return self.clone( :select => [] )
end
select_more( *attributes ) click to toggle source

Return a clone of the receiving Branchset that will fetch the specified attributes in addition to its own.

# File lib/treequel/branchset.rb, line 370
def select_more( *attributes )
        return self.select( *(Array(@options[:select]) | attributes) )
end
timeout( seconds=nil ) click to toggle source

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 407
def timeout( seconds=nil )
        if seconds
                return self.clone( :timeout => seconds )
        else
                return @options[:timeout]
        end
end
to_hash( keyattr, valueattr=nil ) click to toggle source

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 268
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
to_s() click to toggle source

Return the Branchset as a stringified URI.

# File lib/treequel/branchset.rb, line 153
def to_s
        return "%s/%s" % [ self.branch.dn, self.filter_string ]
end
uri() click to toggle source

Return a string representation of the Branchset's filter

# File lib/treequel/branchset.rb, line 134
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
with_operational_attributes() click to toggle source

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 379
def with_operational_attributes
        return self.select( :+ )
end
without_limit() click to toggle source

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 400
def without_limit
        return self.clone( :limit => 0 )
end
without_timeout() click to toggle source

Return a clone of the receiving Branchset that will not use a timeout when searching.

# File lib/treequel/branchset.rb, line 418
def without_timeout
        return self.clone( :timeout => 0 )
end