ruby-activeldap 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -75,7 +75,7 @@ module ActiveLDAP
75
75
  key = options[:foreign_key] || association_id.to_s + "_id"
76
76
  local_key = options[:local_key] || ''
77
77
  class_eval <<-"end_eval"
78
- def #{association_id}(objects = false)
78
+ def #{association_id}(objects = true)
79
79
  local_key = "#{local_key}"
80
80
  local_key = dnattr() if local_key.empty?
81
81
  return #{klass}.find_all(:attribute => "#{key}", :value => attribute_method(local_key), :objects => objects)
@@ -100,7 +100,7 @@ module ActiveLDAP
100
100
  key = options[:local_key] || association_id.to_s + "_id"
101
101
  foreign_key = options[:foreign_key] || ''
102
102
  class_eval <<-"end_eval"
103
- def #{association_id}(objects = false)
103
+ def #{association_id}(objects = true)
104
104
  foreign_key = "#{foreign_key}"
105
105
  if foreign_key.empty?
106
106
  foreign_key = dnattr()
@@ -74,6 +74,12 @@ module ActiveLDAP
74
74
  class ConnectionError < RuntimeError
75
75
  end
76
76
 
77
+ # ObjectClassError
78
+ #
79
+ # An exception raised when an objectClass is not defined in the schema
80
+ class ObjectClassError < RuntimeError
81
+ end
82
+
77
83
 
78
84
  # Base
79
85
  #
@@ -189,7 +195,7 @@ module ActiveLDAP
189
195
  # Setup default logger to console
190
196
  if @@logger.nil?
191
197
  @@logger = Log4r::Logger.new('activeldap')
192
- @@logger.level = Log4r::FATAL
198
+ @@logger.level = Log4r::OFF
193
199
  Log4r::StderrOutputter.new 'console'
194
200
  @@logger.add('console')
195
201
  end
@@ -240,9 +246,8 @@ module ActiveLDAP
240
246
  raise ConnectionError, "#{detail.exception} - LDAP connection failure, or server does not support schema queries."
241
247
  end
242
248
 
243
-
244
249
  # Cleanly return
245
- return nil
250
+ return true
246
251
  end # Base.connect
247
252
 
248
253
  # Base.close
@@ -387,7 +392,6 @@ module ActiveLDAP
387
392
  end
388
393
 
389
394
  # Allow a single string argument
390
- attr = dnattr()
391
395
  objects = false
392
396
  val = config
393
397
  # Or a hash
@@ -487,9 +491,6 @@ module ActiveLDAP
487
491
  @ldap_data = {} # original ldap entry data
488
492
  @ldap_data.default = []
489
493
  @attr_methods = {} # list of valid method calls for attributes used for dereferencing
490
- @must = {} # list of schema required attributes
491
- @may = {} # list of schema optional attributes
492
- @sup = {} # list of schema supplemental attributes
493
494
 
494
495
  # Break val apart if it is a dn
495
496
  if val.match(/^#{dnattr()}=([^,=]+),#{base()}$/i)
@@ -504,7 +505,7 @@ module ActiveLDAP
504
505
  @exists = false
505
506
  # Setup what should eb authoritative
506
507
  @dn = "#{dnattr()}=#{val},#{base()}"
507
- send(:objectClass=, required_classes())
508
+ send(:apply_objectclass, required_classes())
508
509
  else # do a search then
509
510
  # Search for the existing entry
510
511
  begin
@@ -521,9 +522,9 @@ module ActiveLDAP
521
522
  @@logger.debug("finished make_subtypes for #{attr}")
522
523
  # Add subtype to any existing values
523
524
  if @ldap_data.has_key? safe_attr
524
- value.each do |v|
525
+ value.each do |v|
525
526
  @ldap_data[safe_attr].push(v)
526
- end
527
+ end
527
528
  else
528
529
  @ldap_data[safe_attr] = value
529
530
  end
@@ -531,7 +532,7 @@ module ActiveLDAP
531
532
  end
532
533
  @exists = true
533
534
  # Populate schema data
534
- send(:objectClass=, @ldap_data['objectClass'])
535
+ send(:apply_objectclass, @ldap_data['objectClass'])
535
536
 
536
537
  # Populate real data now that we have the schema with aliases
537
538
  @ldap_data.each do |pair|
@@ -542,10 +543,11 @@ module ActiveLDAP
542
543
  @exists = false
543
544
  # Create what should be the authoritative DN
544
545
  @dn = "#{dnattr()}=#{val},#{base()}"
545
- send(:objectClass=, required_classes())
546
+ send(:apply_objectclass, required_classes())
546
547
 
547
548
  # Setup dn attribute (later rdn too!)
548
549
  attr_sym = "#{dnattr()}=".to_sym
550
+ @@logger.debug("new: setting dnattr: #{dnattr()} = #{val}")
549
551
  send(attr_sym, val)
550
552
  end
551
553
  end
@@ -560,79 +562,10 @@ module ActiveLDAP
560
562
  # attributes dynamically without schema awareness
561
563
  def attributes
562
564
  @@logger.debug("stub: attributes called")
565
+ send(:apply_objectclass, @data['objectClass']) if @data['objectClass'] != @last_oc
563
566
  return @attr_methods.keys
564
567
  end
565
568
 
566
- # objectClass=
567
- #
568
- # objectClass= special case for updating appropriately
569
- # This updates the objectClass entry in @data. It also
570
- # updating all required and allowed attributes while
571
- # removing defined attributes that are no longer valid
572
- # given the new objectclasses.
573
- def objectClass=(val)
574
- @@logger.debug("stub: objectClass=(#{val.inspect}) called")
575
- if val.class != Array
576
- raise TypeError, 'objectClass must be an Array'
577
- end
578
-
579
- val.each do |klass|
580
- unless klass.class == String
581
- raise TypeError, "Value in array is not a String. (#{klass.class})"
582
- end
583
- unless Base.schema.names("objectClasses").member? klass
584
- # TODO MAKE OWN EXCEPTION
585
- raise RuntimeError, "objectClass '#{klass}' unknown to LDAP server"
586
- end
587
- end
588
-
589
- # make sure this doesn't drop any of the required objectclasses
590
- required_classes().each do |oc|
591
- unless val.member? oc
592
- raise "'#{oc}' must be a defined objectClass for class '#{self.class}'"
593
- end
594
- end
595
-
596
- # Set the actual objectClass data
597
- define_attribute_methods('objectClass')
598
- @data['objectClass'] = val.uniq
599
-
600
- # Build |data| from schema
601
- # clear attr_method mapping first
602
- @attr_methods = {}
603
- @must = {}
604
- @may = {}
605
- @sup = {}
606
- val.each do |objc|
607
- # Setup dependencies for validation pre-save
608
- @must[objc] = Base.schema.attr('objectClasses', objc, 'MUST')
609
- @may[objc] = Base.schema.attr('objectClasses', objc, 'MAY')
610
- @sup[objc] = Base.schema.attr('objectClasses', objc, 'SUP')
611
- @must[objc] = [] if @must[objc].nil?
612
- @may[objc] = [] if @may[objc].nil?
613
- @sup[objc] = [] if @sup[objc].nil?
614
-
615
- # setup the alias/attr method mapping for @must
616
- @must[objc].each do |attr|
617
- # Update attr_method with appropriate
618
- define_attribute_methods(attr)
619
- end
620
-
621
- # setup the alias/attr method mapping for @may
622
- @may[objc].each do |attr|
623
- define_attribute_methods(attr)
624
- end
625
- end
626
-
627
- # Delete all now invalid attributes given the new objectClasses
628
- @data.keys.each do |key|
629
- # If it's not a proper aliased attribute, drop it
630
- unless @attr_methods.has_key? key
631
- @data.delete(key)
632
- end
633
- end
634
- end
635
-
636
569
  # exists?
637
570
  #
638
571
  # Return whether the entry exists in LDAP or not
@@ -654,15 +587,38 @@ module ActiveLDAP
654
587
  # Basic validation:
655
588
  # - Verify that every 'MUST' specified in the schema has a value defined
656
589
  # - Enforcement of undefined attributes is handled in the objectClass= method
590
+ # Must call enforce_types() first before enforcement can be guaranteed
657
591
  def validate
658
592
  @@logger.debug("stub: validate called")
659
- @must.each do |oc|
660
- oc[1].each do |req_attr|
661
- # TODO: should I ever check if key exists? bug if it doesnt...
593
+ # Clean up attr values, etc
594
+ send(:enforce_types)
595
+
596
+ # Validate objectclass settings
597
+ @data['objectClass'].each do |klass|
598
+ unless klass.class == String
599
+ raise TypeError, "Value in objectClass array is not a String. (#{klass.class}:#{klass.inspect})"
600
+ end
601
+ unless Base.schema.names("objectClasses").member? klass
602
+ raise ObjectClassError, "objectClass '#{klass}' unknown to LDAP server."
603
+ end
604
+ end
605
+
606
+ # make sure this doesn't drop any of the required objectclasses
607
+ required_classes().each do |oc|
608
+ unless @data['objectClass'].member? oc.to_s
609
+ raise ObjectClassError, "'#{oc}' must be a defined objectClass for class '#{self.class}' as set in the ldap_mapping"
610
+ end
611
+ end
612
+
613
+ # Make sure all MUST attributes have a value
614
+ @data['objectClass'].each do |objc|
615
+ must = Base.schema.attr('objectClasses', objc, 'MUST')
616
+ may = Base.schema.attr('objectClasses', objc, 'MAY')
617
+ must.each do |req_attr|
662
618
  deref = @attr_methods[req_attr]
663
619
  if @data[deref] == []
664
620
  raise AttributeEmpty,
665
- "objectClass '#{oc[0]}' requires attribute '#{Base.schema.attribute_aliases(req_attr).join(', ')}'"
621
+ "objectClass '#{objc}' requires attribute '#{Base.schema.attribute_aliases(req_attr).join(', ')}'"
666
622
  end
667
623
  end
668
624
  end
@@ -836,7 +792,7 @@ module ActiveLDAP
836
792
  @@logger.debug("#write: add successful")
837
793
  @exists = true
838
794
  rescue LDAP::ResultError => detail
839
- raise WriteError, "Could not add LDAP entry[#{Base.conn.err2string(Base.conn.err)}]: #{detail}"
795
+ raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}"
840
796
  end
841
797
  end
842
798
  @@logger.debug("#write: resetting @ldap_data to a dup of @data")
@@ -855,15 +811,25 @@ module ActiveLDAP
855
811
  # give tab completion in irb.
856
812
  def method_missing(name, *args)
857
813
  @@logger.debug("stub: called method_missing(#{name.inspect}, #{args.inspect})")
814
+
815
+ # dynamically update the available attributes without requiring an
816
+ # explicit call. The cache 'last_oc' saves a lot of cpu time.
817
+ if @data['objectClass'] != @last_oc
818
+ @@logger.debug("method_missing(#{name.inspect}, #{args.inspect}): updating apply_objectclass(#{@data['objectClass'].inspect})")
819
+ send(:apply_objectclass, @data['objectClass'])
820
+ end
858
821
  key = name.to_s
859
822
  case key
860
823
  when /^(\S+)=$/
861
824
  real_key = $1
825
+ @@logger.debug("method_missing: attr_methods has_key? #{real_key}")
862
826
  if @attr_methods.has_key? real_key
863
827
  raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size != 1
828
+ @@logger.debug("method_missing: calling :attribute_method=(#{real_key}, #{args[0]})")
864
829
  return send(:attribute_method=, real_key, args[0])
865
830
  end
866
831
  else
832
+ @@logger.debug("method_missing: attr_methods has_key? #{key}")
867
833
  if @attr_methods.has_key? key
868
834
  raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size > 1
869
835
  return attribute_method(key, *args)
@@ -872,29 +838,97 @@ module ActiveLDAP
872
838
  raise NoMethodError, "undefined method `#{key}' for #{self}"
873
839
  end
874
840
 
841
+ # Add available attributes to the methods
842
+ alias_method :__methods, :methods
843
+ def methods
844
+ return __methods + attributes()
845
+ end
846
+
875
847
 
876
848
  private
877
849
 
850
+ # enforce_types
851
+ #
852
+ # enforce_types applies your changes without attempting to write to LDAP. This means that
853
+ # if you set userCertificate to somebinary value, it will wrap it up correctly.
854
+ def enforce_types
855
+ @@logger.debug("stub: enforce_types called")
856
+ send(:apply_objectclass, @data['objectClass']) if @data['objectClass'] != @last_oc
857
+ # Enforce attribute value formatting
858
+ @data.keys.each do |key|
859
+ @data[key] = attribute_input_handler(key, @data[key])
860
+ end
861
+ @@logger.debug("stub: enforce_types done")
862
+ return true
863
+ end
864
+
865
+
866
+
867
+ # apply_objectclass
868
+ #
869
+ # objectClass= special case for updating appropriately
870
+ # This updates the objectClass entry in @data. It also
871
+ # updating all required and allowed attributes while
872
+ # removing defined attributes that are no longer valid
873
+ # given the new objectclasses.
874
+ def apply_objectclass(val)
875
+ @@logger.debug("stub: objectClass=(#{val.inspect}) called")
876
+ new_oc = val
877
+ new_oc = [val] if new_oc.class != Array
878
+ return new_oc if @last_oc == new_oc
879
+
880
+ # Store for caching purposes
881
+ @last_oc = new_oc.dup
882
+
883
+ # Set the actual objectClass data
884
+ define_attribute_methods('objectClass')
885
+ @data['objectClass'] = new_oc.uniq
886
+
887
+ # Build |data| from schema
888
+ # clear attr_method mapping first
889
+ @attr_methods = {}
890
+ attributes = []
891
+ new_oc.each do |objc|
892
+ # get all attributes for the class
893
+ attributes += Base.schema.class_attributes(objc.to_s)
894
+ end
895
+ attributes.uniq
896
+ attributes.each do |attr|
897
+ # Update attr_method with appropriate
898
+ define_attribute_methods(attr)
899
+ end
900
+
901
+ # Delete all now innew_ocid attributes given the new objectClasses
902
+ @data.keys.each do |key|
903
+ # If it's not a proper aliased attribute, drop it
904
+ unless @attr_methods.has_key? key
905
+ @data.delete(key)
906
+ end
907
+ end
908
+ end
909
+
910
+
911
+
878
912
  # Enforce typing:
879
913
  # Hashes are for subtypes
880
914
  # Arrays are for multiple entries
881
915
  def attribute_input_handler(attr, value)
882
916
  @@logger.debug("stub: called attribute_input_handler(#{attr.inspect}, #{value.inspect})")
883
917
  if attr.nil?
884
- raise RuntimeError, 'attr argument must not be nil.'
918
+ raise RuntimeError, 'The first argument, attr, must not be nil. Please report this as a bug!'
885
919
  end
886
920
  binary = Base.schema.binary_required? attr
887
921
  single = Base.schema.single_value? attr
888
922
  case value.class.to_s
889
923
  when 'Array'
890
924
  if single and value.size > 1
891
- raise TypeError, "This attribute can only have a single value"
925
+ raise TypeError, "Attribute #{attr} can only have a single value"
892
926
  end
893
927
  value.map! do |entry|
894
- if entry.class != Hash
895
- @@logger.debug("coercing value for #{attr} into a string because nested values exceeds a useful depth: #{entry.inspect} -> #{entry.to_s}")
896
- entry = entry.to_s
897
- end
928
+ if entry.class != Hash
929
+ @@logger.debug("coercing value for #{attr} into a string because nested values exceeds a useful depth: #{entry.inspect} -> #{entry.to_s}")
930
+ entry = entry.to_s
931
+ end
898
932
  entry = attribute_input_handler(attr, entry)[0]
899
933
  end
900
934
  when 'Hash'
@@ -1073,38 +1107,31 @@ module ActiveLDAP
1073
1107
  # attribute_method
1074
1108
  #
1075
1109
  # Return the value of the attribute called by method_missing?
1076
- def attribute_method(method, arrays = false)
1077
- @@logger.debug("stub: called attribute_method(#{method.inspect}, #{arrays.inspect}")
1110
+ def attribute_method(method, not_array = false)
1111
+ @@logger.debug("stub: called attribute_method(#{method.inspect}, #{not_array.inspect}")
1078
1112
  attr = @attr_methods[method]
1079
1113
 
1080
1114
  # Return a copy of the stored data
1081
- return @data[attr].dup if arrays
1082
- return array_of(@data[attr].dup, false)
1115
+ return array_of(@data[attr].dup, false) if not_array
1116
+ return @data[attr]
1083
1117
  end
1084
1118
 
1085
1119
 
1086
1120
  # attribute_method=
1087
1121
  #
1088
1122
  # Set the value of the attribute called by method_missing?
1089
- def attribute_method=(method, arg)
1090
- @@logger.debug("stub: called attribute_method=(#{method.inspect}, #{arg.inspect}")
1091
- # Copy input
1092
- begin
1093
- value = arg.dup
1094
- rescue TypeError
1095
- value = arg.to_s
1096
- end
1097
-
1123
+ def attribute_method=(method, value)
1124
+ @@logger.debug("stub: called attribute_method=(#{method.inspect}, #{value.inspect})")
1098
1125
  # Get the attr and clean up the input
1099
1126
  attr = @attr_methods[method]
1100
- value = attribute_input_handler(attr, value)
1101
-
1127
+ @@logger.debug("attribute_method=(#{method.inspect}, #{value.inspect}): method maps to #{attr}")
1128
+ #value = attribute_input_handler(attr, value)
1102
1129
  # Assign the value
1103
1130
  @data[attr] = value
1104
1131
 
1105
1132
  # Return a copy of what got saved
1106
1133
  @@logger.debug("stub: exitting attribute_method=")
1107
- return @data[attr].dup
1134
+ return @data[attr]
1108
1135
  end
1109
1136
 
1110
1137
 
@@ -1119,8 +1146,10 @@ module ActiveLDAP
1119
1146
  end
1120
1147
  aliases = Base.schema.attribute_aliases(attr)
1121
1148
  aliases.each do |ali|
1149
+ @@logger.debug("associating #{ali} --> #{attr}")
1122
1150
  @attr_methods[ali] = attr
1123
1151
  end
1152
+ @@logger.debug("stub: leaving define_attribute_methods(#{attr.inspect})")
1124
1153
  end
1125
1154
 
1126
1155
  # array_of
@@ -3,8 +3,9 @@ require 'ldap/schema'
3
3
 
4
4
  module LDAP
5
5
  class Schema2 < Schema
6
- @@cache = {}
7
-
6
+ @@attr_cache = {}
7
+ @@class_cache = {}
8
+
8
9
  # attr
9
10
  #
10
11
  # This is just like LDAP::Schema#attr except that it allows
@@ -18,19 +19,19 @@ module LDAP
18
19
  return '' if at.empty?
19
20
 
20
21
  # Check already parsed options first
21
- if @@cache.has_key? sub \
22
- and @@cache[sub].has_key? type \
23
- and @@cache[sub][type].has_key? at
24
- return @@cache[sub][type][at].dup
22
+ if @@attr_cache.has_key? sub \
23
+ and @@attr_cache[sub].has_key? type \
24
+ and @@attr_cache[sub][type].has_key? at
25
+ return @@attr_cache[sub][type][at].dup
25
26
  end
26
27
 
27
28
  # Initialize anything that is required
28
- unless @@cache.has_key? sub
29
- @@cache[sub] = {}
29
+ unless @@attr_cache.has_key? sub
30
+ @@attr_cache[sub] = {}
30
31
  end
31
32
 
32
- unless @@cache[sub].has_key? type
33
- @@cache[sub][type] = {}
33
+ unless @@attr_cache[sub].has_key? type
34
+ @@attr_cache[sub][type] = {}
34
35
  end
35
36
 
36
37
  at = at.upcase
@@ -47,31 +48,31 @@ module LDAP
47
48
  multi = ''
48
49
  case line
49
50
  when /#{at}\s+[\)A-Z]/
50
- @@cache[sub][type][at] = ['TRUE']
51
+ @@attr_cache[sub][type][at] = ['TRUE']
51
52
  return ['TRUE']
52
53
  when /#{at}\s+'(.+?)'/
53
- @@cache[sub][type][at] = [$1]
54
+ @@attr_cache[sub][type][at] = [$1]
54
55
  return [$1]
55
56
  when /#{at}\s+\((.+?)\)/
56
57
  multi = $1
57
58
  when /#{at}\s+\(([\w\d\s\.]+)\)/
58
59
  multi = $1
59
60
  when /#{at}\s+([\w\d\.]+)/
60
- @@cache[sub][type][at] = [$1]
61
+ @@attr_cache[sub][type][at] = [$1]
61
62
  return [$1]
62
63
  end
63
64
  # Split up multiple matches
64
65
  # if oc then it is sep'd by $
65
66
  # if attr then bu spaces
66
67
  if multi.match(/\$/)
67
- @@cache[sub][type][at] = multi.split("$").collect{|attr| attr.strip}
68
- return @@cache[sub][type][at].dup
68
+ @@attr_cache[sub][type][at] = multi.split("$").collect{|attr| attr.strip}
69
+ return @@attr_cache[sub][type][at].dup
69
70
  elsif not multi.empty?
70
- @@cache[sub][type][at] = multi.gsub(/'/, '').split(' ').collect{|attr| attr.strip}
71
- return @@cache[sub][type][at].dup
71
+ @@attr_cache[sub][type][at] = multi.gsub(/'/, '').split(' ').collect{|attr| attr.strip}
72
+ return @@attr_cache[sub][type][at].dup
72
73
  end
73
74
  end
74
- @@cache[sub][type][at] = []
75
+ @@attr_cache[sub][type][at] = []
75
76
  return []
76
77
  end
77
78
 
@@ -140,24 +141,63 @@ module LDAP
140
141
 
141
142
  return false
142
143
  end # binary_required?
144
+
145
+ # class_attributes
146
+ #
147
+ # Returns an Array of all the valid attributes (but not with full aliases)
148
+ # for the given objectClass
149
+ def class_attributes(objc)
150
+ if @@class_cache.has_key? objc
151
+ return @@class_cache[objc]
152
+ end
153
+
154
+ # First get all the current level attributes
155
+ @@class_cache[objc] = attr('objectClasses', objc, 'MUST') + attr('objectClasses', objc, 'MAY')
156
+
157
+ # Now add all attributes from the parent object (SUPerclasses)
158
+ # Hopefully an iterative approach will be pretty speedy
159
+ # 1. build complete list of SUPs
160
+ # 2. Add attributes from each
161
+ sups = attr('objectClasses', objc, 'SUP')
162
+ loop do
163
+ start_size = sups.size
164
+ new_sups = []
165
+ sups.each do |sup|
166
+ new_sups += attr('objectClasses', sup, 'SUP')
167
+ end
168
+ sups += new_sups
169
+ sups = sups.uniq
170
+ break if sups.size == start_size
171
+ end
172
+ sups.each do |sup|
173
+ @@class_cache[objc] += attr('objectClasses', sup, 'MUST') + attr('objectClasses', sup, 'MAY')
174
+ end
175
+
176
+ # Clean out the dupes.
177
+ @@class_cache[objc] = @@class_cache[objc].uniq
178
+
179
+ # Return the cached value
180
+ return @@class_cache[objc].dup
181
+ end
182
+
143
183
  end # Schema2
144
184
 
145
185
  class Conn
146
186
  def schema(base = nil, attrs = nil, sec = 0, usec = 0)
147
187
  attrs ||= [
148
- 'objectClasses',
149
- 'attributeTypes',
150
- 'matchingRules',
151
- 'matchingRuleUse',
152
- 'dITStructureRules',
153
- 'dITContentRules',
154
- 'nameForms',
155
- 'ldapSyntaxes',
188
+ 'objectClasses',
189
+ 'attributeTypes',
190
+ 'matchingRules',
191
+ 'matchingRuleUse',
192
+ 'dITStructureRules',
193
+ 'dITContentRules',
194
+ 'nameForms',
195
+ 'ldapSyntaxes',
156
196
  ]
157
197
  base ||= root_dse(['subschemaSubentry'], sec, usec)[0]['subschemaSubentry'][0]
158
198
  base ||= 'cn=schema'
159
199
  ent = search2(base, LDAP_SCOPE_BASE, '(objectClass=subschema)',
160
- attrs, false, sec, usec)
200
+ attrs, false, sec, usec)
161
201
  return Schema2.new(ent[0])
162
202
  end
163
203
  end
data/lib/activeldap.rb CHANGED
@@ -212,7 +212,8 @@
212
212
  # what classes are required when creating a new object. Of course, you can leave
213
213
  # that field out to default to ['top'] only. Then you can let each application
214
214
  # choose what objectClasses their objects should have by calling the method e.g.
215
- # Group#objectClass=(value).
215
+ # Group#objectClass=(value) or by modifying the value returned by the accessor,
216
+ # e.g. Group#objectClass.
216
217
  #
217
218
  # Note that is can be very important to define the default :classes value. Due to
218
219
  # implementation choices with most LDAP servers, once an object is created, its
@@ -259,28 +260,26 @@
259
260
  # Group objects that a user is in.
260
261
  #
261
262
  # irb> me = User.new('drewry')
262
- # => ...
263
263
  # irb> me.groups
264
- # => ["cdrom", "audio", "develop"]
265
- #
266
- # Methods created with belongs_to also take an optional argument: objects. This
267
- # argument specifies whether it will return the value of the 'dnattr' of the
268
- # objects, or whether it will return Group objects.
269
- #
270
- # irb> me.groups(true)
271
264
  # => [#<Group:0x000001 ...>, #<Group:0x000002 ...>, ...]
272
265
  # irb> me.groups(true).each { |group| p group.cn };nil
273
266
  # "cdrom"
274
267
  # "audio"
275
268
  # "develop"
276
269
  # => nil
277
- #
278
270
  # (Note: nil is just there to make the output cleaner...)
279
271
  #
272
+ # Methods created with belongs_to also take an optional argument: objects. This
273
+ # argument specifies whether it will return the value of the 'dnattr' of the
274
+ # objects, or whether it will return Group objects.
275
+ #
276
+ # irb> me.groups(false)
277
+ # => ["cdrom", "audio", "develop"]
278
+ #
280
279
  # TIP: If you weren't sure what the distinguished name attribute was for Group,
281
280
  # you could also do the following:
282
281
  #
283
- # irb> me.groups(true).each { |group| p group.dnattr };nil
282
+ # irb> me.groups.each { |group| p group.dnattr };nil
284
283
  # "cdrom"
285
284
  # "audio"
286
285
  # "develop"
@@ -328,9 +327,9 @@
328
327
  # irb> develop = Group.new('develop')
329
328
  # => ...
330
329
  # irb> develop.members
331
- # => ["drewry", "builder"]
332
- # irb> develop.members(true)
333
330
  # => [#<User:0x000001 ...>, #<User:...>]
331
+ # irb> develop.members(false)
332
+ # => ["drewry", "builder"]
334
333
  #
335
334
  #
336
335
  # The arguments for has_many follow the exact same idea that belongs_to's
@@ -392,11 +391,13 @@
392
391
  # * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
393
392
  # * :scope defaults to LDAP::LDAP_SCOPE_SUBTREE. Usually you won't need to change it
394
393
  # * :attrs defaults to [] and is the list of attrs you want back. Empty means all of them.
395
- #
394
+ #
396
395
  # ==== #validate
397
396
  #
398
397
  # validate is a method that verifies that all attributes that are required by the
399
- # objects current objectClasses are populated. This is called by #write prior to
398
+ # objects current objectClasses are populated. This also will call the
399
+ # private "#enforce_types" method. This will make sure that all values defined are
400
+ # valid to be written to LDAP. #validate is called by #write prior to
400
401
  # performing any action. Its explicit use in an application is unnecessary, and
401
402
  # it may become a private method in the future.
402
403
  #
@@ -536,6 +537,11 @@
536
537
  # LDAP server could be created. Check you configuration.rb, Base.connect
537
538
  # arguments, and network connectivity! Also check your LDAP server logs to see
538
539
  # if it ever saw the request.
540
+ #
541
+ # ==== ObjectClassError
542
+ #
543
+ # This exception is raised when an object class is used that is not defined
544
+ # in the schema.
539
545
  #
540
546
  # === Others
541
547
  #
@@ -752,26 +758,26 @@
752
758
  # and everything should work well.
753
759
  #
754
760
  #
755
- # ==== Array results for everything
761
+ # ==== Non-array results for single values
756
762
  #
757
- # Even though Ruby/ActiveLDAP tries to be convenient by returning Arrays only
758
- # when an LDAP object attribute has multiple values, sometimes this can be a
759
- # programmatic nightmare. If you would like to force all returned values to
760
- # come in Array form, just query like so:
763
+ # Even though Ruby/ActiveLDAP attempts to maintain programmatic ease by
764
+ # returning Array values only. By specifying 'false' as an argument to
765
+ # any attribute method you will get back a String if it is single value.
766
+ # This is useful when you are just dumping values for human reading.
767
+ # Here's an example:
761
768
  #
762
769
  # irb> user = User.new('drewry')
763
770
  # => ...
764
- # irb> user.cn(true)
765
- # => ["Will Drewry"]
771
+ # irb> user.cn(false)
772
+ # => "Will Drewry"
766
773
  #
767
- # That's it. Now you can ALWAYS get Arrays back without having to write your own
768
- # wrappers.
774
+ # That's it. Now you can make human-readable output faster.
769
775
  #
770
776
  # ==== Dynamic attribute crawling
771
777
  #
772
- # If you use tab completion in irb, you'll notice that you can't tab complete the
773
- # dynamic attributes available for each object. You can still see what's available with the method call
774
- # #attributes.
778
+ # If you use tab completion in irb, you'll notice that you /can/ tab complete the dynamic
779
+ # attribute methods. You can still see which methods are for attributes using
780
+ # Base#attributes:
775
781
  #
776
782
  # irb> d = Group.new('develop')
777
783
  # => ...
@@ -892,7 +898,7 @@ require 'activeldap/configuration'
892
898
  require 'activeldap/schema2'
893
899
 
894
900
  module ActiveLDAP
895
- VERSION = "0.4.4"
901
+ VERSION = "0.5.0"
896
902
  end
897
903
 
898
904
  ActiveLDAP::Base.class_eval do
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
3
3
  specification_version: 1
4
4
  name: ruby-activeldap
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.4
7
- date: 2004-10-10
6
+ version: 0.5.0
7
+ date: 2004-10-21
8
8
  summary: Ruby/ActiveLDAP is a object-oriented API to LDAP
9
9
  require_paths:
10
10
  - lib