+ Spec for Net::LDAP::Entry

Pretty much rewrote the code. It now also responds to respond_to?
correctly. Most of that symbol manipulation is now in just one place.
This commit is contained in:
Kaspar Schiess 2010-03-18 10:08:59 +01:00
parent c01dc9ee89
commit cfd9dbc922
2 changed files with 115 additions and 56 deletions

View file

@ -71,14 +71,9 @@ class LDAP
# #
class Entry class Entry
# This constructor is not generally called by user code. # This constructor is not generally called by user code.
#-- #
# Originally, myhash took a block so we wouldn't have to
# make sure its elements returned empty arrays when necessary.
# Got rid of that to enable marshalling of Entry objects,
# but that doesn't work anyway, because Entry objects have
# singleton methods. So we define a custom dump and load.
def initialize dn = nil # :nodoc: def initialize dn = nil # :nodoc:
@myhash = {} # originally: Hash.new {|k,v| k[v] = [] } @myhash = {}
@myhash[:dn] = [dn] @myhash[:dn] = [dn]
end end
@ -95,20 +90,18 @@ class LDAP
#-- #--
# Discovered bug, 26Aug06: I noticed that we're not converting the # Discovered bug, 26Aug06: I noticed that we're not converting the
# incoming value to an array if it isn't already one. # incoming value to an array if it isn't already one.
def []= name, value # :nodoc: def []=(name, value) # :nodoc:
sym = name.to_s.downcase.intern sym = attribute_name(name)
value = [value] unless value.is_a?(Array) value = [value] unless value.is_a?(Array)
@myhash[sym] = value @myhash[sym] = value
end end
#-- #--
# We have to deal with this one as we do with []= # We have to deal with this one as we do with []= because this one and not
# because this one and not the other one gets called # the other one gets called in formulations like entry["CN"] << cn.
# in formulations like entry["CN"] << cn.
# #
def [] name # :nodoc: def [](name) # :nodoc:
name = name.to_s.downcase.intern unless name.is_a?(Symbol) name = attribute_name(name) unless name.is_a?(Symbol)
@myhash[name] || [] @myhash[name] || []
end end
@ -139,8 +132,6 @@ class LDAP
alias_method :each_attribute, :each alias_method :each_attribute, :each
# Converts the Entry to a String, representing the # Converts the Entry to a String, representing the
# Entry's attributes in LDIF format. # Entry's attributes in LDIF format.
#-- #--
@ -166,24 +157,15 @@ class LDAP
#-- #--
# TODO, doesn't support broken lines. # TODO, doesn't support broken lines.
# It generates a SINGLE Entry object from an incoming LDIF stream # It generates a SINGLE Entry object from an incoming LDIF stream which is
# which is of course useless for big LDIF streams that encode # of course useless for big LDIF streams that encode many objects.
# many objects.
# DO NOT DOCUMENT THIS METHOD UNTIL THESE RESTRICTIONS ARE LIFTED.
# As it is, it's useful for unmarshalling objects that we create,
# but not for reading arbitrary LDIF files.
# Eventually, we should have a class method that parses large LDIF
# streams into individual LDIF blocks (delimited by blank lines)
# and passes them here.
# #
# There is one oddity, noticed by Matthias Tarasiewicz: as originally # DO NOT DOCUMENT THIS METHOD UNTIL THESE RESTRICTIONS ARE LIFTED.
# written, this code would return an Entry object in which the DN #
# attribute consisted of a two-element array, and the first element was # As it is, it's useful for unmarshalling objects that we create, but not
# nil. That's because Entry#initialize doesn't like to create an object # for reading arbitrary LDIF files. Eventually, we should have a class
# without a DN attribute so it adds one: nil. The workaround here is # method that parses large LDIF streams into individual LDIF blocks
# to wipe out the nil DN after creating the Entry object, and trust the # (delimited by blank lines) and passes them here.
# LDIF string to fill it in. If it doesn't we return a nil at the end.
# (30Sep06, FCianfrocca)
# #
class << self class << self
def from_single_ldif_string ldif def from_single_ldif_string ldif
@ -202,35 +184,42 @@ class LDAP
entry.dn ? entry : nil entry.dn ? entry : nil
end end
end end
#--
# Part of the support for getter and setter style access to attributes.
#
def respond_to?(sym)
name = attribute_name(sym)
return true if valid_attribute?(name)
return super
end
#-- #--
# Convenience method to convert unknown method names # Supports getter and setter style access for all the attributes that this
# to attribute references. Of course the method name # entry holds.
# comes to us as a symbol, so let's save a little time
# and not bother with the to_s.downcase two-step.
# Of course that means that a method name like mAIL
# won't work, but we shouldn't be encouraging that
# kind of bad behavior in the first place.
# Maybe we should thow something if the caller sends
# arguments or a block...
# #
def method_missing *args, &block # :nodoc: def method_missing sym, *args, &block # :nodoc:
s = args[0].to_s.downcase.intern name = attribute_name(sym)
if attribute_names.include?(s)
self[s] if valid_attribute? name
elsif s.to_s[-1] == 61 and s.to_s.length > 1 if setter?(sym) && args.size == 1
value = args[1] or raise RuntimeError.new( "unable to set value" ) value = args.first
value = [value] unless value.is_a?(Array) value = [value] unless value.instance_of?(Array)
name = s.to_s[0..-2].intern self[name]= value
self[name] = value
else return value
raise NoMethodError.new( "undefined method '#{s}'" ) elsif args.empty?
return self[name]
end
end end
super
end end
def write def write
end end
private
#-- #--
# Internal convenience method. It seems like the standard # Internal convenience method. It seems like the standard
@ -249,8 +238,27 @@ class LDAP
end end
false false
end end
private :is_attribute_value_binary?
# Returns the symbol that can be used to access the attribute that
# sym_or_str designates.
#
def attribute_name(sym_or_str)
str = sym_or_str.to_s.downcase
# Does str match 'something='? Still only returns :something
return str[0...-1].to_sym if str.size>1 && str[-1] == ?=
return str.to_sym
end
# Given a valid attribute symbol, returns true.
#
def valid_attribute?(attr_name)
attribute_names.include?(attr_name)
end
def setter?(sym)
sym.to_s[-1] == ?=
end
end # class Entry end # class Entry

View file

@ -0,0 +1,51 @@
require 'spec_helper'
describe Net::LDAP::Entry do
attr_reader :entry
before(:each) do
@entry = Net::LDAP::Entry.from_single_ldif_string(
%Q{dn: something
foo: foo
barAttribute: bar
}
)
end
describe "entry access" do
it "should always respond to #dn" do
entry.should respond_to(:dn)
end
context "<- #foo" do
it "should respond_to?" do
entry.should respond_to(:foo)
end
it "should return 'foo'" do
entry.foo.should == ['foo']
end
end
context "<- #Foo" do
it "should respond_to?" do
entry.should respond_to(:Foo)
end
it "should return 'foo'" do
entry.foo.should == ['foo']
end
end
context "<- #foo=" do
it "should respond_to?" do
entry.should respond_to(:foo=)
end
it "should set 'foo'" do
entry.foo= 'bar'
entry.foo.should == ['bar']
end
end
context "<- #fOo=" do
it "should return 'foo'" do
entry.fOo= 'bar'
entry.fOo.should == ['bar']
end
end
end
end