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.

Calls superclass method
# 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

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

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
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 432
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 124
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.

Calls superclass method
# 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
collection() click to toggle source

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

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( *modules ) click to toggle source

Extend the Branchset with one or more modules. Overridden to also call the modules' initializers if they have them.

Calls superclass method
# 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
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 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
filter_string() click to toggle source

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

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

Calls superclass method
# 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
not( *filterspec ) click to toggle source

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

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

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

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
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 384
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 405
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 423
def without_timeout
        return self.clone( :timeout => 0 )
end