lib/treequel/branch.rb in treequel-1.2.2 vs lib/treequel/branch.rb in treequel-1.3.0pre384
- old
+ new
@@ -36,15 +36,21 @@
#################################################################
# [Boolean] Whether or not to include operational attributes by default.
@include_operational_attrs = false
+ # [Boolean] Whether or not to freeze values cached in @values. This helps
+ # prevent you from accidentally doing branch[:attr] << 'value', which
+ # modifies the cached values, but not the entry.
+ @freeze_converted_values = true
+
# Whether or not to include operational attributes when fetching the
# entry for branches.
class << self
extend Treequel::AttributeDeclarations
predicate_attr :include_operational_attrs
+ predicate_attr :freeze_converted_values
end
### Create a new Treequel::Branch from the given +entry+ hash from the specified +directory+.
###
@@ -52,12 +58,16 @@
### @param [Treequel::Directory] directory The directory object the Branch is from.
###
### @return [Treequel::Branch] The new branch object.
def self::new_from_entry( entry, directory )
entry = Treequel::HashUtilities.stringify_keys( entry )
- Treequel.logger.debug "Creating Branch from entry: %p in directory: %p" % [ entry, directory ]
- return self.new( directory, entry['dn'].first, entry )
+ dnvals = entry.delete( 'dn' ) or
+ raise ArgumentError, "no 'dn' attribute for entry"
+
+ Treequel.logger.debug "Creating Branch from entry: %p in directory: %s" %
+ [ dnvals.first, directory ]
+ return self.new( directory, dnvals.first, entry )
end
#################################################################
### I N S T A N C E M E T H O D S
@@ -70,10 +80,11 @@
###
### @param [Treequel::Directory] directory The directory the Branch belongs to.
### @param [String] dn The DN of the entry the Branch is wrapping.
### @param [LDAP::Entry, Hash] entry The entry object if it's already been fetched.
def initialize( directory, dn, entry=nil )
+ raise ArgumentError, "nil DN" unless dn
raise ArgumentError, "invalid DN" unless
dn.match( Patterns::DISTINGUISHED_NAME ) || dn.empty?
raise ArgumentError, "can't cast a %s to an LDAP::Entry" % [entry.class.name] unless
entry.nil? || entry.is_a?( Hash )
@@ -91,11 +102,11 @@
######
public
######
# Delegate some other methods to a new Branchset via the #branchset method
- def_method_delegators :branchset, :filter, :scope, :select, :limit, :timeout, :order
+ def_method_delegators :branchset, :filter, :scope, :select, :limit, :timeout, :order, :as
# Delegate some methods to the Branch's directory via its accessor
def_method_delegators :directory, :controls, :referrals
@@ -202,11 +213,12 @@
### Return the Branch's immediate parent node.
### @return [Treequel::Branch]
def parent
- return self.class.new( self.directory, self.parent_dn )
+ pardn = self.parent_dn or return nil
+ return self.class.new( self.directory, pardn )
end
### Perform a search with the specified +scope+, +filter+, and +parameters+
### using the receiver as the base.
@@ -265,11 +277,11 @@
entry = self.entry || self.valid_attributes_hash
self.log.debug " making LDIF from an entry: %p" % [ entry ]
entry.keys.reject {|k| k == 'dn' }.each do |attribute|
- entry[ attribute ].each do |val|
+ Array( entry[attribute] ).each do |val|
ldif << ldif_for_attr( attribute, val, width )
end
end
return LDAP::LDIF::Entry.new( ldif )
@@ -297,18 +309,20 @@
### Fetch the value/s associated with the given +attrname+ from the underlying entry.
### @return [Array, String]
def []( attrname )
attrsym = attrname.to_sym
- unless @values.key?( attrsym )
+ if @values.key?( attrsym )
+ self.log.debug " value for %p is cached (%p)." % [ attrname, @values[attrsym] ]
+ else
self.log.debug " value for %p is NOT cached." % [ attrsym ]
value = self.get_converted_object( attrsym )
self.log.debug " converted value is: %p" % [ value ]
- value.freeze if value.respond_to?( :freeze )
+ value.freeze if
+ self.class.freeze_converted_values? &&
+ value.respond_to?( :freeze )
@values[ attrsym ] = value
- else
- self.log.debug " value for %p is cached." % [ attrname ]
end
return @values[ attrsym ]
end
@@ -364,27 +378,37 @@
### @example Delete any blank 'description' or 'cn' attributes:
### branch.delete( :description => '', :cn => '' )
###
### @return [TrueClass] if the delete succeeded
def delete( *attributes )
+
+ # If no attributes are given, delete the whole entry
if attributes.empty?
self.log.info "No attributes specified; deleting entire entry for %s" % [ self.dn ]
self.directory.delete( self )
+
+ # Otherwise, gather up the LDAP::Mod objects that will delete the given attributes
else
self.log.debug "Deleting attributes: %p" % [ attributes ]
mods = attributes.flatten.collect do |attribute|
+
+ # Delete particular values of the attribute
if attribute.is_a?( Hash )
attribute.collect do |key,vals|
- vals = Array( vals ).collect {|val| val.to_s }
+ vals = [ vals ] unless vals.is_a?( Array )
+ vals.collect! {|val| self.get_converted_attribute(key, val) }
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, key.to_s, vals )
end
+
+ # Delete all values of the attribute
else
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, attribute.to_s, [] )
end
- end.flatten
- self.directory.modify( self, mods )
+ end
+
+ self.directory.modify( self, mods.flatten )
end
self.clear_caches
return true
@@ -392,13 +416,14 @@
### Create the entry for this Branch with the specified +attributes+. The +attributes+ should,
### at a minimum, contain the pair `:objectClass => :someStructuralObjectClass`.
###
- ### @param [Hash<Symbol,String => Object>] attributes
+ ### @see Treequel::Directory#create
def create( attributes={} )
self.directory.create( self, attributes )
+ self.clear_caches
return self
end
### Copy the entry for this Branch to a new entry with the given +newdn+ and merge in the
@@ -430,11 +455,14 @@
###
### @param [String] rdn
### @param [Hash<String, Symbol => Object>] attributes
def move( rdn )
self.log.debug "Asking the directory to move me to an entry called %p" % [ rdn ]
- return self.directory.move( self, rdn )
+ self.directory.move( self, rdn )
+ self.clear_caches
+
+ return self
end
### Comparable interface: Returns -1 if other_branch is less than, 0 if other_branch is
### equal to, and +1 if other_branch is greater than the receiving Branch.
@@ -491,22 +519,21 @@
def object_classes( *additional_classes )
self.log.debug "Fetching object classes for %s" % [ self.dn ]
schema = self.directory.schema
oc_oids = self[:objectClass] || []
- self.log.debug " objectClass OIDs are: %p" % [ oc_oids ]
oc_oids |= additional_classes.collect {|str| str.to_sym }
- oc_oids << :top if oc_oids.empty?
+ oc_oids << 'top' if oc_oids.empty?
oclasses = []
oc_oids.each do |oid|
oc = schema.object_classes[ oid.to_sym ] or
raise Treequel::Error, "schema doesn't have a %p objectClass" % [ oid ]
oclasses << oc
end
- self.log.debug " found %d objectClasses: %p" % [ oclasses.length, oclasses ]
+ self.log.debug " found %d objectClasses: %p" % [ oclasses.length, oclasses.map(&:name) ]
return oclasses.uniq
end
### Return the receiver's operational attributes as attributeType schema objects.
@@ -537,11 +564,11 @@
oclasses = self.object_classes( *additional_object_classes )
self.log.debug "Gathering MUST attribute types for objectClasses: %p" %
[ oclasses.map(&:name) ]
oclasses.each do |oc|
- self.log.debug " adding %p from %p" % [ oc.must, oc ]
+ self.log.debug " adding %p from %p" % [ oc.must.map(&:name), oc.name ]
types |= oc.must
end
return types
end
@@ -569,14 +596,14 @@
### @return [Hash<String => String>]
def must_attributes_hash( *additional_object_classes )
attrhash = {}
self.must_attribute_types( *additional_object_classes ).each do |attrtype|
- self.log.debug " adding attrtype %p to the MUST attributes hash" % [ attrtype ]
+ # self.log.debug " adding attrtype %p to the MUST attributes hash" % [ attrtype.name ]
if attrtype.name == :objectClass
- attrhash[ :objectClass ] = [:top] | additional_object_classes
+ attrhash[ :objectClass ] = ['top'] | additional_object_classes
elsif attrtype.single?
attrhash[ attrtype.name ] = ''
else
attrhash[ attrtype.name ] = ['']
end
@@ -623,11 +650,11 @@
def may_attributes_hash( *additional_object_classes )
entry = self.entry
attrhash = {}
self.may_attribute_types( *additional_object_classes ).each do |attrtype|
- self.log.debug " adding attrtype %p to the MAY attributes hash" % [ attrtype ]
+ # self.log.debug " adding attrtype %p to the MAY attributes hash" % [ attrtype.named ]
if attrtype.single?
attrhash[ attrtype.name ] = nil
else
attrhash[ attrtype.name ] = []
@@ -770,19 +797,20 @@
### Get the value associated with +attrsym+, convert it to a Ruby object if the Branch's
### directory has a conversion rule, and return it.
def get_converted_object( attrsym )
- return nil unless self.entry
- value = self.entry[ attrsym.to_s ] or return nil
+ value = self.entry ? self.entry[ attrsym.to_s ] : nil
if attribute = self.directory.schema.attribute_types[ attrsym ]
+ syntax_oid = attribute.syntax.oid
+
if attribute.single?
- value = self.directory.convert_to_object( attribute.syntax.oid, value.first )
+ value = self.directory.convert_to_object( syntax_oid, value.first ) if value
else
- value = value.collect do |raw|
- self.directory.convert_to_object( attribute.syntax.oid, raw )
+ value = Array( value ).collect do |raw|
+ self.directory.convert_to_object( syntax_oid, raw )
end
end
else
self.log.info "no attributeType for %p" % [ attrsym ]
end
@@ -793,11 +821,11 @@
### Convert the specified +object+ according to the Branch's directory's conversion rules,
### and return it.
def get_converted_attribute( attrsym, object )
if attribute = self.directory.schema.attribute_types[ attrsym ]
- self.log.debug "converting %p object to a %p attribute" %
- [ attrsym, attribute.syntax.desc ]
+ self.log.debug "converting %p object (a %p) to a %p attribute" %
+ [ attrsym, object.class, attribute.syntax.desc ]
return self.directory.convert_to_attribute( attribute.syntax.oid, object )
else
self.log.info "no attributeType for %p" % [ attrsym ]
return object.to_s
end