Branchset

class
Superclass
Object
Included Modules
Enumerable
Treequel::Constants
Treequel::Control
Extended With
Loggability

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

anchor
new( branch, options={} )

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

anchor
+( other )

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
anchor
-( other_object )

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
anchor
as( branchclass )

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
anchor
base_dn()

Returns the DN of the Branchset's branch.

# File lib/treequel/branchset.rb, line 124
def base_dn
        return self.branch.dn
end
anchor
clone( options={} )

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
anchor
collection()

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
anchor
each( &block )

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
anchor
empty?()

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
anchor
extend( *modules )

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
anchor
filter( *filterspec )

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
anchor
filter_string()

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
anchor
first( n=nil )

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
anchor
from( other_dn )

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
anchor
inspect()

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
anchor
limit( new_limit=nil )

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
anchor
map( attribute=nil ) { |branch or attribute| ... }

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
anchor
not( *filterspec )

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
anchor
or( *filterspec )

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
anchor
scope( new_scope=nil )

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
anchor
select( *attributes )

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
anchor
select_all()

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
anchor
select_more( *attributes )

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
anchor
timeout( seconds=nil )

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
anchor
to_hash( keyattr, valueattr=nil )

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
anchor
to_s()

Return the Branchset as a stringified URI.

# File lib/treequel/branchset.rb, line 158
def to_s
        return "%s/%s" % [ self.branch.dn, self.filter_string ]
end
anchor
uri()

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
anchor
with_operational_attributes()

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
anchor
without_limit()

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
anchor
without_timeout()

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