treequel 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +130 -1
- data/Rakefile +8 -3
- data/Rakefile.local +2 -0
- data/bin/treeirb +6 -2
- data/bin/treequel +5 -4
- data/lib/treequel/branch.rb +133 -72
- data/lib/treequel/branchcollection.rb +16 -5
- data/lib/treequel/branchset.rb +37 -6
- data/lib/treequel/constants.rb +12 -0
- data/lib/treequel/directory.rb +96 -41
- data/lib/treequel/filter.rb +42 -1
- data/lib/treequel/model/objectclass.rb +111 -0
- data/lib/treequel/model.rb +363 -0
- data/lib/treequel/monkeypatches.rb +65 -0
- data/lib/treequel/schema/attributetype.rb +108 -18
- data/lib/treequel/schema/ldapsyntax.rb +15 -0
- data/lib/treequel/schema/matchingrule.rb +24 -0
- data/lib/treequel/schema/matchingruleuse.rb +24 -0
- data/lib/treequel/schema/objectclass.rb +70 -5
- data/lib/treequel/schema/table.rb +5 -15
- data/lib/treequel/schema.rb +64 -1
- data/lib/treequel.rb +5 -5
- data/rake/documentation.rb +27 -0
- data/rake/hg.rb +14 -2
- data/rake/manual.rb +1 -1
- data/spec/lib/constants.rb +9 -7
- data/spec/lib/control_behavior.rb +1 -0
- data/spec/lib/matchers.rb +1 -0
- data/spec/treequel/branch_spec.rb +229 -20
- data/spec/treequel/branchcollection_spec.rb +73 -39
- data/spec/treequel/branchset_spec.rb +59 -8
- data/spec/treequel/control_spec.rb +1 -0
- data/spec/treequel/controls/contentsync_spec.rb +1 -0
- data/spec/treequel/controls/pagedresults_spec.rb +1 -0
- data/spec/treequel/controls/sortedresults_spec.rb +1 -0
- data/spec/treequel/directory_spec.rb +46 -10
- data/spec/treequel/filter_spec.rb +28 -5
- data/spec/treequel/mixins_spec.rb +7 -14
- data/spec/treequel/model/objectclass_spec.rb +330 -0
- data/spec/treequel/model_spec.rb +433 -0
- data/spec/treequel/monkeypatches_spec.rb +118 -0
- data/spec/treequel/schema/attributetype_spec.rb +116 -0
- data/spec/treequel/schema/ldapsyntax_spec.rb +8 -0
- data/spec/treequel/schema/matchingrule_spec.rb +6 -1
- data/spec/treequel/schema/matchingruleuse_spec.rb +5 -0
- data/spec/treequel/schema/objectclass_spec.rb +41 -3
- data/spec/treequel/schema/table_spec.rb +31 -18
- data/spec/treequel/schema_spec.rb +13 -16
- data/spec/treequel_spec.rb +22 -0
- data.tar.gz.sig +1 -0
- metadata +40 -7
- metadata.gz.sig +0 -0
- data/spec/treequel/utils_spec.rb +0 -49
data/ChangeLog
CHANGED
@@ -1,4 +1,133 @@
|
|
1
|
-
|
1
|
+
304[tip] 92c28b14730a 2010-09-20 13:51 -0700 ged
|
2
|
+
Added tag 1.1.0 for changeset b415e0fce774
|
3
|
+
|
4
|
+
303[1.1.0] b415e0fce774 2010-09-20 13:51 -0700 ged
|
5
|
+
Added signature for changeset 4ba782a3a7e4
|
6
|
+
|
7
|
+
302 4ba782a3a7e4 2010-09-17 09:09 -0700 ged
|
8
|
+
Include mixins for inherited objectClasses, too.
|
9
|
+
|
10
|
+
301 f075a27a35eb 2010-09-17 09:03 -0700 ged
|
11
|
+
Updated build system
|
12
|
+
|
13
|
+
300 c77bac0bf816 2010-09-17 09:02 -0700 ged
|
14
|
+
Manual updates for the Model section (paired with Mahlon)
|
15
|
+
|
16
|
+
299 1b375c5dc123 2010-09-15 16:46 -0700 ged
|
17
|
+
Use the #syntax method instead of the syntax_oid in #inspect output so inherited syntaxes are shown
|
18
|
+
|
19
|
+
298 0786411cd707 2010-09-15 16:43 -0700 ged
|
20
|
+
Use the system LDAP config if no URI is given
|
21
|
+
|
22
|
+
297 d3a6c89d8004 2010-09-15 16:42 -0700 ged
|
23
|
+
Un-spam debugging on entry lookup
|
24
|
+
|
25
|
+
296 2101482df41f 2010-09-15 16:41 -0700 ged
|
26
|
+
Overriding #inspect in Treequel::Model to show the list of extensions applied to the entry
|
27
|
+
|
28
|
+
295 a384412c0e6f 2010-08-25 16:56 -0700 ged
|
29
|
+
Rescue the right error in Treequel::Model#method_missing and add a spec for the negative case.
|
30
|
+
|
31
|
+
294 b220a83d31b5 2010-08-25 16:48 -0700 ged
|
32
|
+
Load the entry from Model's #method_missing to catch calls to methods that are added via
|
33
|
+
|
34
|
+
293 2753f8405caf 2010-08-23 17:12 -0700 ged
|
35
|
+
Fixing object/attribute mapping for attributes whose types that inherit their syntax from their superclass
|
36
|
+
|
37
|
+
292 2e2b035d3721 2010-08-23 15:19 -0700 ged
|
38
|
+
Coverage improvements, manual work
|
39
|
+
|
40
|
+
291 693a163bd068 2010-08-23 08:36 -0700 ged
|
41
|
+
Started catching the manual up to 1.1.0.
|
42
|
+
|
43
|
+
290 df0b0594cb62 2010-08-23 08:36 -0700 ged
|
44
|
+
Made Treequel::Branch#delete with no arguments delete the entry
|
45
|
+
|
46
|
+
289 31fc0ea1ea31 2010-08-19 16:35 -0700 ged
|
47
|
+
Fixes for Ruby 1.9.2.
|
48
|
+
|
49
|
+
288 7c7d3e2034b0 2010-08-19 07:21 -0700 ged
|
50
|
+
Build system update; fixed ruby-termios dependency
|
51
|
+
|
52
|
+
287 f9151315d37f 2010-08-18 17:35 -0700 ged
|
53
|
+
Implement Branchset operators
|
54
|
+
|
55
|
+
286 cd3ef7e3ffb0 2010-08-16 17:20 -0700 ged
|
56
|
+
Reworked operational attributes to use the 'USAGE' attribute at Mahlon's suggestion.
|
57
|
+
|
58
|
+
285 dfb032f5e5c1 2010-08-16 17:19 -0700 ged
|
59
|
+
Fixes for Treequel::Model instantiation and lookup.
|
60
|
+
|
61
|
+
284 844ee21c6916 2010-08-16 17:05 -0700 ged
|
62
|
+
Made Treequel::Schema::Table Enumerable
|
63
|
+
|
64
|
+
283 ff2629196b4e 2010-08-06 16:11 -0700 ged
|
65
|
+
Don't add the objectClass attribute when searching through Treequel::Model if
|
66
|
+
|
67
|
+
282 d4e58760ee34 2010-08-06 15:02 -0700 ged
|
68
|
+
Handle Sequel's Ruby1.9 Symbol-operator workaround in filter attributes
|
69
|
+
|
70
|
+
281 d60c6b83db01 2010-08-06 15:02 -0700 ged
|
71
|
+
Also output empty-string attributes as non-binary
|
72
|
+
|
73
|
+
280 bb182f705fe5 2010-08-05 14:07 -0700 ged
|
74
|
+
Unwrap base64ed LDIF lines before wrapping them to the new line length.
|
75
|
+
|
76
|
+
279 32084630894f 2010-08-05 11:14 -0700 ged
|
77
|
+
Fix LDIF-generation for really reals.
|
78
|
+
|
79
|
+
278 aabbab99b093 2010-08-05 10:40 -0700 ged
|
80
|
+
Optimizations, logging cleanup.
|
81
|
+
|
82
|
+
277 7a4f2e5bef0a 2010-08-02 08:45 -0700 ged
|
83
|
+
Bugfixes.
|
84
|
+
|
85
|
+
276 2f741e5294bf 2010-07-29 14:34 -0700 ged
|
86
|
+
Filter component symmetry, Model refactor
|
87
|
+
|
88
|
+
275 bc0bc3aea136 2010-07-28 09:06 -0700 ged
|
89
|
+
Added support for Sequel-style #or: branchset.filter( :something ).or( :somethingelse ).
|
90
|
+
|
91
|
+
274 30091794b910 2010-07-28 07:14 -0700 ged
|
92
|
+
Added Treequel::Model#respond_to?
|
93
|
+
|
94
|
+
273 e9c908b1f426 2010-07-27 08:05 -0700 ged
|
95
|
+
Bugfix, make object conversion work for setting attributes too.
|
96
|
+
|
97
|
+
272 30ba889041a8 2010-07-26 20:04 -0700 ged
|
98
|
+
Bugfixes, coverage of LDIF-generation.
|
99
|
+
|
100
|
+
271 a6dd7b685a6a 2010-07-16 18:32 -0700 ged
|
101
|
+
Fixing the "cd .." special case
|
102
|
+
|
103
|
+
270 2793fa802dad 2010-07-14 20:16 -0700 ged
|
104
|
+
Untaint objectClasses passed to Treequel::Model::mixins_for_objectclasses.
|
105
|
+
|
106
|
+
269 b76dd9ca1f3f 2010-07-13 19:14 -0700 ged
|
107
|
+
The workaround for two-param syntax mapping Procs didn't work after all. Modifying
|
108
|
+
|
109
|
+
268 70cc87a200ad 2010-07-13 19:08 -0700 ged
|
110
|
+
Added schema-object roundtripping, with a script/specs to test it.
|
111
|
+
|
112
|
+
267 47e865bfef9e 2010-07-13 12:31 -0700 ged
|
113
|
+
Added the ability to set the default results class on a per-directory basis.
|
114
|
+
|
115
|
+
266 c61373e3dc49 2010-07-13 08:03 -0700 ged
|
116
|
+
Fixes for Apache DS and bugfix in Treequel::Model.
|
117
|
+
|
118
|
+
265 601003ff3077 2010-07-10 17:16 -0700 ged
|
119
|
+
Treequel::Model bugfix
|
120
|
+
|
121
|
+
264 a76065cfeba0 2010-07-09 07:19 -0700 ged
|
122
|
+
Adding Treequel::Model, bumping version to 1.1.0.
|
123
|
+
|
124
|
+
263:262,261 6dd6cb37f5ce 2010-07-09 07:16 -0700 ged
|
125
|
+
Merged with 47bb9cd7af3b
|
126
|
+
|
127
|
+
262:258 3f4134d20d9d 2010-07-08 09:40 -0700 ged
|
128
|
+
Delegate the Treequel::Directory#root_dse method through its #conn
|
129
|
+
|
130
|
+
261 0188c2ba5b7e 2010-07-07 23:21 -0700 ged
|
2
131
|
Added tag 1.0.4 for changeset 0ec4ff0ce67f
|
3
132
|
|
4
133
|
260[1.0.4] 0ec4ff0ce67f 2010-07-07 23:21 -0700 ged
|
data/Rakefile
CHANGED
@@ -18,6 +18,7 @@ BEGIN {
|
|
18
18
|
libdir = basedir + "lib"
|
19
19
|
extdir = libdir + Config::CONFIG['sitearch']
|
20
20
|
|
21
|
+
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
21
22
|
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
22
23
|
$LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
|
23
24
|
}
|
@@ -76,7 +77,7 @@ elsif VERSION_FILE.exist?
|
|
76
77
|
PKG_VERSION = VERSION_FILE.read[ /VERSION\s*=\s*['"](\d+\.\d+\.\d+)['"]/, 1 ]
|
77
78
|
end
|
78
79
|
|
79
|
-
PKG_VERSION = '0.0.0' unless defined?( PKG_VERSION )
|
80
|
+
PKG_VERSION = '0.0.0' unless defined?( PKG_VERSION ) && !PKG_VERSION.nil?
|
80
81
|
|
81
82
|
PKG_FILE_NAME = "#{PKG_NAME.downcase}-#{PKG_VERSION}"
|
82
83
|
GEM_FILE_NAME = "#{PKG_FILE_NAME}.gem"
|
@@ -169,7 +170,7 @@ include RakefileHelpers
|
|
169
170
|
|
170
171
|
# Set the build ID if the mercurial executable is available
|
171
172
|
if hg = which( 'hg' )
|
172
|
-
id =
|
173
|
+
id = `#{hg} id -n`.chomp
|
173
174
|
PKG_BUILD = "pre%03d" % [(id.chomp[ /^[[:xdigit:]]+/ ] || '1')]
|
174
175
|
else
|
175
176
|
PKG_BUILD = 'pre000'
|
@@ -226,9 +227,9 @@ DEVELOPMENT_DEPENDENCIES = {
|
|
226
227
|
'text-format' => '>= 1.0.0',
|
227
228
|
'tmail' => '>= 1.2.3.1',
|
228
229
|
'diff-lcs' => '>= 1.1.2',
|
229
|
-
'termios' => '>= 0.9.4',
|
230
230
|
'columnize' => '>= 0.3.1',
|
231
231
|
'diff-lcs' => '>= 1.1.2',
|
232
|
+
'ruby-termios' => '>= 0.9.6',
|
232
233
|
'ruby-terminfo' => '>= 0.1.1',
|
233
234
|
}
|
234
235
|
|
@@ -277,6 +278,10 @@ GEMSPEC = Gem::Specification.new do |gem|
|
|
277
278
|
gem.files = RELEASE_FILES
|
278
279
|
gem.test_files = SPEC_FILES
|
279
280
|
|
281
|
+
# signing key and certificate chain
|
282
|
+
gem.signing_key = '/Volumes/Keys/ged-private_gem_key.pem'
|
283
|
+
gem.cert_chain = [File.expand_path('~/.gem/ged-public_gem_cert.pem')]
|
284
|
+
|
280
285
|
DEPENDENCIES.each do |name, version|
|
281
286
|
version = '>= 0' if version.length.zero?
|
282
287
|
gem.add_runtime_dependency( name, version )
|
data/Rakefile.local
CHANGED
data/bin/treeirb
CHANGED
@@ -6,8 +6,12 @@ require 'irb/cmd/nop'
|
|
6
6
|
require 'treequel'
|
7
7
|
|
8
8
|
|
9
|
-
uri = ARGV.shift
|
10
|
-
$dir = Treequel.directory( uri )
|
9
|
+
if uri = ARGV.shift
|
10
|
+
$dir = Treequel.directory( uri )
|
11
|
+
else
|
12
|
+
$dir = Treequel.directory_from_config
|
13
|
+
end
|
14
|
+
|
11
15
|
$stderr.puts "Directory is in $dir:", ' ' + $dir.inspect
|
12
16
|
|
13
17
|
IRB.start( $0 )
|
data/bin/treequel
CHANGED
@@ -571,7 +571,7 @@ class Treequel::Shell
|
|
571
571
|
# Calcuate column widths
|
572
572
|
oclen = children.map do |subbranch|
|
573
573
|
subbranch.include_operational_attrs = true
|
574
|
-
subbranch[:structuralObjectClass].length
|
574
|
+
subbranch[:structuralObjectClass] ? subbranch[:structuralObjectClass].length : 0
|
575
575
|
end.max
|
576
576
|
|
577
577
|
# Set up sorting by collecting all the requested sort criteria as Proc objects which
|
@@ -610,7 +610,7 @@ class Treequel::Shell
|
|
610
610
|
return
|
611
611
|
end
|
612
612
|
|
613
|
-
return self.parent_command if rdn == '..'
|
613
|
+
return self.parent_command( options ) if rdn == '..'
|
614
614
|
|
615
615
|
raise "invalid RDN %p" % [ rdn ] unless RELATIVE_DISTINGUISHED_NAME.match( rdn )
|
616
616
|
|
@@ -868,9 +868,10 @@ class Treequel::Shell
|
|
868
868
|
metadatalen = oclen + 16 + 6 # oc + timestamp + whitespace
|
869
869
|
maxdesclen = self.columns - metadatalen - rdn.length - 5
|
870
870
|
|
871
|
+
modtime = branch[:modifyTimestamp] || branch[:createTimestamp]
|
871
872
|
return "%#{oclen}s %s %s%s %s" % [
|
872
|
-
branch[:structuralObjectClass],
|
873
|
-
|
873
|
+
branch[:structuralObjectClass] || '',
|
874
|
+
modtime.strftime('%Y-%m-%d %H:%M'),
|
874
875
|
rdn,
|
875
876
|
branch[:hasSubordinates] ? '/' : '',
|
876
877
|
single_line_description( branch, maxdesclen )
|
data/lib/treequel/branch.rb
CHANGED
@@ -69,7 +69,8 @@ class Treequel::Branch
|
|
69
69
|
### @param [String] dn The DN of the entry the Branch is wrapping.
|
70
70
|
### @param [LDAP::Entry, Hash] entry The entry object if it's already been fetched.
|
71
71
|
def initialize( directory, dn, entry=nil )
|
72
|
-
raise ArgumentError, "invalid DN" unless
|
72
|
+
raise ArgumentError, "invalid DN" unless
|
73
|
+
dn.match( Patterns::DISTINGUISHED_NAME ) || dn.empty?
|
73
74
|
raise ArgumentError, "can't cast a %s to an LDAP::Entry" % [entry.class.name] unless
|
74
75
|
entry.nil? || entry.is_a?( Hash )
|
75
76
|
|
@@ -104,7 +105,7 @@ class Treequel::Branch
|
|
104
105
|
|
105
106
|
# Whether or not to include operational attributes when fetching the Branch's entry
|
106
107
|
predicate_attr :include_operational_attrs
|
107
|
-
|
108
|
+
alias_method :include_operational_attributes?, :include_operational_attrs?
|
108
109
|
|
109
110
|
### Change the DN the Branch uses to look up its entry.
|
110
111
|
###
|
@@ -124,6 +125,7 @@ class Treequel::Branch
|
|
124
125
|
self.clear_caches
|
125
126
|
@include_operational_attrs = new_setting ? true : false
|
126
127
|
end
|
128
|
+
alias_method :include_operational_attributes=, :include_operational_attrs=
|
127
129
|
|
128
130
|
|
129
131
|
### Return the attribute/s which make up this Branch's RDN.
|
@@ -181,6 +183,7 @@ class Treequel::Branch
|
|
181
183
|
### @return [String]
|
182
184
|
def parent_dn
|
183
185
|
return nil if self.dn == self.directory.base_dn
|
186
|
+
return '' if self.dn.index( ',' ).nil?
|
184
187
|
return self.split_dn( 2 ).last
|
185
188
|
end
|
186
189
|
|
@@ -260,35 +263,21 @@ class Treequel::Branch
|
|
260
263
|
end
|
261
264
|
|
262
265
|
|
263
|
-
###
|
264
|
-
###
|
265
|
-
###
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
def ldif_for_attr( attribute, values, width )
|
270
|
-
ldif = ''
|
271
|
-
|
272
|
-
Array( values ).each do |val|
|
273
|
-
line = "#{attribute}:"
|
266
|
+
### Return the Branch as a Hash.
|
267
|
+
### @see Treequel::Branch#[]
|
268
|
+
### @return [Hash] the entry as a Hash with converted values
|
269
|
+
def to_hash
|
270
|
+
entry = self.entry || self.valid_attributes_hash
|
271
|
+
self.log.debug " making a Hash from an entry: %p" % [ entry ]
|
274
272
|
|
275
|
-
|
276
|
-
|
273
|
+
return entry.keys.inject({}) do |hash, attribute|
|
274
|
+
if attribute == 'dn'
|
275
|
+
hash[ attribute ] = self.dn
|
277
276
|
else
|
278
|
-
|
277
|
+
hash[ attribute ] = self[ attribute ]
|
279
278
|
end
|
280
|
-
|
281
|
-
# calculate how many times the line needs to be split, then add any
|
282
|
-
# additional splits that need to be added because of the additional
|
283
|
-
# fold characters
|
284
|
-
splits = ( line.length / width )
|
285
|
-
splits += ( splits * LDIF_FOLD_SEPARATOR.length ) / width
|
286
|
-
splits.times {|i| line[ width * (i+1), 0 ] = LDIF_FOLD_SEPARATOR }
|
287
|
-
|
288
|
-
ldif << line << "\n"
|
279
|
+
hash
|
289
280
|
end
|
290
|
-
|
291
|
-
return ldif
|
292
281
|
end
|
293
282
|
|
294
283
|
|
@@ -298,32 +287,9 @@ class Treequel::Branch
|
|
298
287
|
attrsym = attrname.to_sym
|
299
288
|
|
300
289
|
unless @values.key?( attrsym )
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
self.log.debug " value is not cached; checking its attributeType"
|
306
|
-
if attribute = directory.schema.attribute_types[ attrsym ]
|
307
|
-
self.log.debug " attribute exists; checking the entry for a value"
|
308
|
-
|
309
|
-
syntax_oid = attribute.syntax_oid
|
310
|
-
|
311
|
-
if attribute.single?
|
312
|
-
self.log.debug " attributeType is SINGLE; unwrapping the Array"
|
313
|
-
@values[ attrsym ] = directory.convert_syntax_value( syntax_oid, value.first )
|
314
|
-
else
|
315
|
-
self.log.debug " attributeType is not SINGLE; keeping the Array"
|
316
|
-
@values[ attrsym ] = value.collect do |raw|
|
317
|
-
directory.convert_syntax_value( syntax_oid, raw )
|
318
|
-
end
|
319
|
-
@values[ attrsym ].freeze if @values[ attrsym ].is_a?( Array )
|
320
|
-
end
|
321
|
-
|
322
|
-
else
|
323
|
-
self.log.info "no attributeType for %p" % [ attrsym ]
|
324
|
-
@values[ attrsym ] = value
|
325
|
-
@values[ attrsym ].freeze
|
326
|
-
end
|
290
|
+
value = self.get_converted_object( attrsym )
|
291
|
+
value.freeze if value.respond_to?( :freeze )
|
292
|
+
@values[ attrsym ] = value
|
327
293
|
else
|
328
294
|
self.log.debug " value is cached."
|
329
295
|
end
|
@@ -349,6 +315,7 @@ class Treequel::Branch
|
|
349
315
|
### @param [Object] value the attribute value
|
350
316
|
def []=( attrname, value )
|
351
317
|
value = [ value ] unless value.is_a?( Array )
|
318
|
+
value.collect! {|val| self.get_converted_attribute(attrname, val) }
|
352
319
|
self.log.debug "Modifying %s to %p" % [ attrname, value ]
|
353
320
|
self.directory.modify( self, attrname.to_s => value )
|
354
321
|
@values.delete( attrname.to_sym )
|
@@ -384,19 +351,25 @@ class Treequel::Branch
|
|
384
351
|
###
|
385
352
|
### @return [TrueClass] if the delete succeeded
|
386
353
|
def delete( *attributes )
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
354
|
+
if attributes.empty?
|
355
|
+
self.log.info "No attributes specified; deleting entire entry for %s" % [ self.dn ]
|
356
|
+
self.directory.delete( self )
|
357
|
+
else
|
358
|
+
self.log.debug "Deleting attributes: %p" % [ attributes ]
|
359
|
+
mods = attributes.flatten.collect do |attribute|
|
360
|
+
if attribute.is_a?( Hash )
|
361
|
+
attribute.collect do |key,vals|
|
362
|
+
vals = Array( vals ).collect {|val| val.to_s }
|
363
|
+
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, key.to_s, vals )
|
364
|
+
end
|
365
|
+
else
|
366
|
+
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, attribute.to_s, [] )
|
393
367
|
end
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
end
|
368
|
+
end.flatten
|
369
|
+
|
370
|
+
self.directory.modify( self, mods )
|
371
|
+
end
|
398
372
|
|
399
|
-
self.directory.modify( self, mods )
|
400
373
|
self.clear_caches
|
401
374
|
|
402
375
|
return true
|
@@ -478,7 +451,8 @@ class Treequel::Branch
|
|
478
451
|
### @param [String] rdn The RDN of the child to fetch.
|
479
452
|
### @return [Treequel::Branch]
|
480
453
|
def get_child( rdn )
|
481
|
-
|
454
|
+
self.log.debug "Getting child %p from base = %p" % [ rdn, self.dn ]
|
455
|
+
newdn = [ rdn, self.dn ].reject {|part| part.empty? }.join( ',' )
|
482
456
|
return self.class.new( self.directory, newdn )
|
483
457
|
end
|
484
458
|
|
@@ -517,6 +491,21 @@ class Treequel::Branch
|
|
517
491
|
end
|
518
492
|
|
519
493
|
|
494
|
+
### Return the receiver's operational attributes as attributeType schema objects.
|
495
|
+
###
|
496
|
+
### @return [Array<Treequel::Schema::AttributeType>] the operational attributes
|
497
|
+
def operational_attribute_types
|
498
|
+
return self.directory.schema.operational_attribute_types
|
499
|
+
end
|
500
|
+
|
501
|
+
|
502
|
+
### Return OIDs (numeric OIDs as Strings, named OIDs as Symbols) for each of the
|
503
|
+
### receiver's operational attributes.
|
504
|
+
def operational_attribute_oids
|
505
|
+
return self.operational_attribute_types.map( &:oid )
|
506
|
+
end
|
507
|
+
|
508
|
+
|
520
509
|
### Return Treequel::Schema::AttributeType instances for each of the receiver's
|
521
510
|
### objectClass's MUST attributeTypes. If any +additional_object_classes+ are given,
|
522
511
|
### include the MUST attributeTypes for them as well. This can be used to predict what
|
@@ -626,22 +615,29 @@ class Treequel::Branch
|
|
626
615
|
end
|
627
616
|
end
|
628
617
|
|
629
|
-
|
618
|
+
# :FIXME: Does the resulting hash need the additional objectClasses? objectClass is
|
619
|
+
# MUST via 'top', so it should already exist in that hash when merged with
|
620
|
+
# this one...
|
621
|
+
# attrhash[ :objectClass ] |= additional_object_classes
|
622
|
+
|
630
623
|
return attrhash
|
631
624
|
end
|
632
625
|
|
633
626
|
|
634
627
|
### Return Treequel::Schema::AttributeType instances for the set of all of the receiver's
|
635
|
-
### MUST and MAY attributeTypes.
|
628
|
+
### MUST and MAY attributeTypes plus the operational attributes.
|
636
629
|
###
|
637
630
|
### @return [Array<Treequel::Schema::AttributeType>]
|
638
631
|
def valid_attribute_types
|
639
|
-
return self.must_attribute_types |
|
632
|
+
return self.must_attribute_types |
|
633
|
+
self.may_attribute_types |
|
634
|
+
self.operational_attribute_types
|
640
635
|
end
|
641
636
|
|
642
637
|
|
643
638
|
### Return a uniqified Array of OIDs (numeric OIDs as Strings, named OIDs as Symbols) for
|
644
|
-
### the set of all of the receiver's MUST and MAY attributeTypes
|
639
|
+
### the set of all of the receiver's MUST and MAY attributeTypes plus the operational
|
640
|
+
### attributes.
|
645
641
|
###
|
646
642
|
### @return [Array<String, Symbol>]
|
647
643
|
def valid_attribute_oids
|
@@ -649,8 +645,10 @@ class Treequel::Branch
|
|
649
645
|
end
|
650
646
|
|
651
647
|
|
652
|
-
### If the given +attroid+ is
|
648
|
+
### If the attribute associated with the given +attroid+ is in the list of valid
|
649
|
+
### attributeTypes for the receiver given its objectClasses, return the
|
653
650
|
### AttributeType object that corresponds with it. If it isn't valid, return nil.
|
651
|
+
### Includes operational attributes.
|
654
652
|
###
|
655
653
|
### @param [String,Symbol] attroid a numeric OID (as a String) or a named OID (as a Symbol)
|
656
654
|
### @return [Treequel::Schema::AttributeType] the validated attributeType
|
@@ -660,7 +658,7 @@ class Treequel::Branch
|
|
660
658
|
|
661
659
|
|
662
660
|
### Return +true+ if the specified +attrname+ is a valid attributeType given the
|
663
|
-
### receiver's current objectClasses.
|
661
|
+
### receiver's current objectClasses. Does not include operational attributes.
|
664
662
|
###
|
665
663
|
### @param [String, Symbol] the OID (numeric or name) of the attribute in question
|
666
664
|
### @return [Boolean]
|
@@ -736,7 +734,6 @@ class Treequel::Branch
|
|
736
734
|
|
737
735
|
### Fetch the entry from the Branch's directory.
|
738
736
|
def lookup_entry
|
739
|
-
self.log.debug "Looking up entry for %p" % [ self ]
|
740
737
|
if self.include_operational_attrs?
|
741
738
|
self.log.debug " including operational attributes."
|
742
739
|
return self.directory.get_extended_entry( self )
|
@@ -747,6 +744,42 @@ class Treequel::Branch
|
|
747
744
|
end
|
748
745
|
|
749
746
|
|
747
|
+
### Get the value associated with +attrsym+, convert it to a Ruby object if the Branch's
|
748
|
+
### directory has a conversion rule, and return it.
|
749
|
+
def get_converted_object( attrsym )
|
750
|
+
return nil unless self.entry
|
751
|
+
value = self.entry[ attrsym.to_s ] or return nil
|
752
|
+
|
753
|
+
if attribute = self.directory.schema.attribute_types[ attrsym ]
|
754
|
+
if attribute.single?
|
755
|
+
value = self.directory.convert_to_object( attribute.syntax.oid, value.first )
|
756
|
+
else
|
757
|
+
value = value.collect do |raw|
|
758
|
+
self.directory.convert_to_object( attribute.syntax.oid, raw )
|
759
|
+
end
|
760
|
+
end
|
761
|
+
else
|
762
|
+
self.log.info "no attributeType for %p" % [ attrsym ]
|
763
|
+
end
|
764
|
+
|
765
|
+
return value
|
766
|
+
end
|
767
|
+
|
768
|
+
|
769
|
+
### Convert the specified +object+ according to the Branch's directory's conversion rules,
|
770
|
+
### and return it.
|
771
|
+
def get_converted_attribute( attrsym, object )
|
772
|
+
if attribute = self.directory.schema.attribute_types[ attrsym ]
|
773
|
+
self.log.debug "converting %p object to a %p attribute" %
|
774
|
+
[ attrsym, attribute.syntax.oid ]
|
775
|
+
return self.directory.convert_to_attribute( attribute.syntax.oid, object )
|
776
|
+
else
|
777
|
+
self.log.info "no attributeType for %p" % [ attrsym ]
|
778
|
+
return object.to_s
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
|
750
783
|
### Clear any cached values when the structural state of the object changes.
|
751
784
|
### @return [void]
|
752
785
|
def clear_caches
|
@@ -793,6 +826,34 @@ class Treequel::Branch
|
|
793
826
|
end
|
794
827
|
end
|
795
828
|
|
829
|
+
|
830
|
+
### Make LDIF for the given +attribute+ and its +values+, wrapping at the given
|
831
|
+
### +width+.
|
832
|
+
###
|
833
|
+
### @param [String] attribute the attribute
|
834
|
+
### @param [Array<String>] values the values for the given +attribute+
|
835
|
+
### @param [Fixnum] width the maximum width of the lines to return
|
836
|
+
def ldif_for_attr( attribute, value, width )
|
837
|
+
unsplit_line = "#{attribute}:"
|
838
|
+
|
839
|
+
if value.empty? || value =~ /\A#{LDIF_SAFE_STRING}\Z/
|
840
|
+
unsplit_line << ' ' << value.to_s
|
841
|
+
else
|
842
|
+
unsplit_line << ': ' << [ value ].pack( 'm' ).chomp
|
843
|
+
end
|
844
|
+
unsplit_line.gsub!( /\n/, '' )
|
845
|
+
|
846
|
+
ldif = ''
|
847
|
+
ldif << unsplit_line.slice!( 0, width ) << LDIF_FOLD_SEPARATOR until
|
848
|
+
unsplit_line.empty?
|
849
|
+
|
850
|
+
ldif.rstrip!
|
851
|
+
ldif << "\n"
|
852
|
+
|
853
|
+
return ldif
|
854
|
+
end
|
855
|
+
|
856
|
+
|
796
857
|
end # class Treequel::Branch
|
797
858
|
|
798
859
|
|
@@ -178,19 +178,30 @@ class Treequel::BranchCollection
|
|
178
178
|
end
|
179
179
|
|
180
180
|
|
181
|
-
### Return a new Treequel::BranchCollection that includes both the receiver's
|
182
|
-
### those in +other_object+ (
|
181
|
+
### Return either a new Treequel::BranchCollection that includes both the receiver's
|
182
|
+
### Branchsets and those in +other_object+ (if it responds_to #branchsets), or the results
|
183
|
+
### from executing the BranchCollection's search with +other_object+ appended if it doesn't.
|
184
|
+
### @param [Object] other_object
|
183
185
|
def +( other_object )
|
184
186
|
if other_object.respond_to?( :branchsets )
|
185
187
|
return self.class.new( self.branchsets + other_object.branchsets )
|
186
|
-
elsif other_object.respond_to?( :
|
187
|
-
return self.class.new( self.branchsets + [other_object.branchset] )
|
188
|
-
else
|
188
|
+
elsif other_object.respond_to?( :collection )
|
189
189
|
return self.class.new( self.branchsets + [other_object] )
|
190
|
+
else
|
191
|
+
return self.all + Array( other_object )
|
190
192
|
end
|
191
193
|
end
|
192
194
|
|
193
195
|
|
196
|
+
### Return the results from each of the receiver's Branchsets without the +other_object+.
|
197
|
+
### @param [#dn] other_object the object to omit from the results. Must respond_to #dn.
|
198
|
+
### @return [Array<Treequel::Branch>]
|
199
|
+
def -( other_object )
|
200
|
+
other_dn = other_object.dn
|
201
|
+
return self.reject {|branch| branch.dn == other_dn }
|
202
|
+
end
|
203
|
+
|
204
|
+
|
194
205
|
### Return a new Treequel::BranchCollection that contains the union of the branchsets from both
|
195
206
|
### collections.
|
196
207
|
def &( other_collection )
|
data/lib/treequel/branchset.rb
CHANGED
@@ -176,12 +176,26 @@ class Treequel::Branchset
|
|
176
176
|
end
|
177
177
|
|
178
178
|
|
179
|
-
###
|
180
|
-
###
|
181
|
-
### @param [Treequel::Branchset]
|
182
|
-
###
|
183
|
-
|
184
|
-
|
179
|
+
### If given another Branchset or a BranchCollection, return a BranchCollection that includes
|
180
|
+
### them both. If given anything else, execute the search and return
|
181
|
+
### @param [Treequel::Branchset, Treequel::Branch] other the branchset or branch to combine
|
182
|
+
### with
|
183
|
+
### @return [Treequel::BranchCollection, Array<Treequel::Branch>]
|
184
|
+
def +( other )
|
185
|
+
if other.is_a?( Treequel::BranchCollection ) || other.is_a?( Treequel::Branchset )
|
186
|
+
return Treequel::BranchCollection.new( self, other )
|
187
|
+
else
|
188
|
+
return self.all + Array( other )
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
### Return the results of executing the search without the +other_object+.
|
194
|
+
### @param [#dn] other_object the object to omit from the results; must respond_to #dn.
|
195
|
+
### @return [Array<Treequel::Branch>]
|
196
|
+
def -( other_object )
|
197
|
+
other_dn = other_object.dn
|
198
|
+
return self.reject {|branch| branch.dn == other_dn }
|
185
199
|
end
|
186
200
|
|
187
201
|
|
@@ -276,6 +290,23 @@ class Treequel::Branchset
|
|
276
290
|
end
|
277
291
|
|
278
292
|
|
293
|
+
### Add an alternate filter to an existing filter by ORing them.
|
294
|
+
### @param filterspec the filter spec to OR with the existing filter
|
295
|
+
### @raises [Treequel::InvalidOperation] if there is no existing filter
|
296
|
+
### @see Treequel::Filter.new for specifics on what +filterspec+ can be
|
297
|
+
def or( *filterspec )
|
298
|
+
opts = self.options
|
299
|
+
existing_filter = self.filter
|
300
|
+
raise Treequel::ExpressionError, "no existing filter" if
|
301
|
+
existing_filter.promiscuous?
|
302
|
+
|
303
|
+
newfilter = Treequel::Filter.new( *filterspec )
|
304
|
+
|
305
|
+
self.log.debug "cloning %p with alternative filterspec: %p" % [ self, filterspec ]
|
306
|
+
return self.clone( :filter => (self.filter | newfilter) )
|
307
|
+
end
|
308
|
+
|
309
|
+
|
279
310
|
### If called with no argument, returns the current scope of the Branchset. If
|
280
311
|
### called with an argument (which should be one of the keys of
|
281
312
|
### Treequel::Constants::SCOPE), returns a clone of the receiving Branchset
|
data/lib/treequel/constants.rb
CHANGED
@@ -141,6 +141,18 @@ module Treequel::Constants
|
|
141
141
|
FEATURE_NAMES = FEATURE_OIDS.invert.freeze
|
142
142
|
|
143
143
|
|
144
|
+
### The list of RFC4512 operational attributes that "SHOULD" be maintained.
|
145
|
+
### (http://tools.ietf.org/html/rfc4512#section-3.4)
|
146
|
+
MINIMAL_OPERATIONAL_ATTRIBUTES = [
|
147
|
+
:creatorsName,
|
148
|
+
:createTimestamp,
|
149
|
+
:modifiersName,
|
150
|
+
:modifyTimestamp,
|
151
|
+
:structuralObjectClass,
|
152
|
+
:governingStructureRule,
|
153
|
+
]
|
154
|
+
|
155
|
+
|
144
156
|
### OIDs of RFC values
|
145
157
|
module OIDS
|
146
158
|
|