treequel 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/ChangeLog +354 -0
  2. data/LICENSE +27 -0
  3. data/README +66 -0
  4. data/Rakefile +345 -0
  5. data/Rakefile.local +43 -0
  6. data/bin/treeirb +14 -0
  7. data/bin/treequel +229 -0
  8. data/examples/company-directory.rb +112 -0
  9. data/examples/ldap-monitor.rb +143 -0
  10. data/examples/ldap-monitor/public/css/master.css +328 -0
  11. data/examples/ldap-monitor/public/images/card_small.png +0 -0
  12. data/examples/ldap-monitor/public/images/chain_small.png +0 -0
  13. data/examples/ldap-monitor/public/images/globe_small.png +0 -0
  14. data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
  15. data/examples/ldap-monitor/public/images/plug.png +0 -0
  16. data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
  17. data/examples/ldap-monitor/public/images/tick.png +0 -0
  18. data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
  19. data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
  20. data/examples/ldap-monitor/views/backends.erb +41 -0
  21. data/examples/ldap-monitor/views/connections.erb +74 -0
  22. data/examples/ldap-monitor/views/databases.erb +39 -0
  23. data/examples/ldap-monitor/views/dump_subsystem.erb +14 -0
  24. data/examples/ldap-monitor/views/index.erb +14 -0
  25. data/examples/ldap-monitor/views/layout.erb +35 -0
  26. data/examples/ldap-monitor/views/listeners.erb +30 -0
  27. data/examples/ldap_state.rb +62 -0
  28. data/lib/treequel.rb +145 -0
  29. data/lib/treequel/branch.rb +589 -0
  30. data/lib/treequel/branchcollection.rb +204 -0
  31. data/lib/treequel/branchset.rb +360 -0
  32. data/lib/treequel/constants.rb +604 -0
  33. data/lib/treequel/directory.rb +541 -0
  34. data/lib/treequel/exceptions.rb +32 -0
  35. data/lib/treequel/filter.rb +704 -0
  36. data/lib/treequel/mixins.rb +325 -0
  37. data/lib/treequel/schema.rb +245 -0
  38. data/lib/treequel/schema/attributetype.rb +252 -0
  39. data/lib/treequel/schema/ldapsyntax.rb +96 -0
  40. data/lib/treequel/schema/matchingrule.rb +124 -0
  41. data/lib/treequel/schema/matchingruleuse.rb +124 -0
  42. data/lib/treequel/schema/objectclass.rb +289 -0
  43. data/lib/treequel/sequel_integration.rb +26 -0
  44. data/lib/treequel/utils.rb +169 -0
  45. data/rake/191_compat.rb +26 -0
  46. data/rake/dependencies.rb +76 -0
  47. data/rake/helpers.rb +434 -0
  48. data/rake/hg.rb +261 -0
  49. data/rake/manual.rb +782 -0
  50. data/rake/packaging.rb +135 -0
  51. data/rake/publishing.rb +318 -0
  52. data/rake/rdoc.rb +30 -0
  53. data/rake/style.rb +62 -0
  54. data/rake/svn.rb +668 -0
  55. data/rake/testing.rb +187 -0
  56. data/rake/verifytask.rb +64 -0
  57. data/rake/win32.rb +190 -0
  58. data/spec/lib/constants.rb +93 -0
  59. data/spec/lib/helpers.rb +100 -0
  60. data/spec/treequel/branch_spec.rb +569 -0
  61. data/spec/treequel/branchcollection_spec.rb +213 -0
  62. data/spec/treequel/branchset_spec.rb +376 -0
  63. data/spec/treequel/directory_spec.rb +487 -0
  64. data/spec/treequel/filter_spec.rb +482 -0
  65. data/spec/treequel/mixins_spec.rb +330 -0
  66. data/spec/treequel/schema/attributetype_spec.rb +237 -0
  67. data/spec/treequel/schema/ldapsyntax_spec.rb +83 -0
  68. data/spec/treequel/schema/matchingrule_spec.rb +158 -0
  69. data/spec/treequel/schema/matchingruleuse_spec.rb +137 -0
  70. data/spec/treequel/schema/objectclass_spec.rb +262 -0
  71. data/spec/treequel/schema_spec.rb +118 -0
  72. data/spec/treequel/utils_spec.rb +49 -0
  73. data/spec/treequel_spec.rb +179 -0
  74. metadata +169 -0
@@ -0,0 +1,482 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ require 'rubygems'
13
+ gem 'rspec', '>= 1.2.6'
14
+
15
+ require 'spec'
16
+ require 'spec/lib/constants'
17
+ require 'spec/lib/helpers'
18
+
19
+ require 'treequel/filter'
20
+
21
+
22
+ include Treequel::TestConstants
23
+ include Treequel::Constants
24
+
25
+ #####################################################################
26
+ ### C O N T E X T S
27
+ #####################################################################
28
+
29
+ describe Treequel::Filter do
30
+ include Treequel::SpecHelpers
31
+
32
+ before( :all ) do
33
+ setup_logging( :fatal )
34
+ end
35
+
36
+ after( :all ) do
37
+ reset_logging()
38
+ end
39
+
40
+
41
+ it "knows that it is promiscuous (will match any entry) if its component is promiscuous" do
42
+ Treequel::Filter.new.should be_promiscuous()
43
+ end
44
+
45
+ it "knows that it isn't promiscuous if its component isn't promiscuous" do
46
+ Treequel::Filter.new( :uid, 'batgirl' ).should_not be_promiscuous()
47
+ end
48
+
49
+
50
+ it "defaults to selecting everything" do
51
+ Treequel::Filter.new.to_s.should == '(objectClass=*)'
52
+ end
53
+
54
+ it "can be created from a string literal" do
55
+ Treequel::Filter.new( '(uid=bargrab)' ).to_s.should == '(uid=bargrab)'
56
+ end
57
+
58
+ it "wraps string literal instances in parens if it requires them" do
59
+ Treequel::Filter.new( 'uid=bargrab' ).to_s.should == '(uid=bargrab)'
60
+ end
61
+
62
+ it "parses a single Symbol argument as a presence filter" do
63
+ Treequel::Filter.new( :uid ).to_s.should == '(uid=*)'
64
+ end
65
+
66
+ it "parses a single-element Array with a Symbol as a presence filter" do
67
+ Treequel::Filter.new( [:uid] ).to_s.should == '(uid=*)'
68
+ end
69
+
70
+ it "parses a Symbol+value pair as a simple item equal filter" do
71
+ Treequel::Filter.new( :uid, 'bigthung' ).to_s.should == '(uid=bigthung)'
72
+ end
73
+
74
+ it "parses a single-item Symbol+value hash as a simple item equal filter" do
75
+ Treequel::Filter.new({ :uidNumber => 3036 }).to_s.should == '(uidNumber=3036)'
76
+ end
77
+
78
+ it "parses a Symbol+value pair in an Array as a simple item equal filter" do
79
+ Treequel::Filter.new( [:uid, 'bigthung'] ).to_s.should == '(uid=bigthung)'
80
+ end
81
+
82
+ it "parses a multi-value Hash as an ANDed collection of simple item equals filters" do
83
+ expr = Treequel::Filter.new( :givenName => 'Michael', :sn => 'Granger' )
84
+ gnpat = Regexp.quote( '(givenName=Michael)' )
85
+ snpat = Regexp.quote( '(sn=Granger)' )
86
+
87
+ expr.to_s.should =~ /\(&(#{gnpat}#{snpat}|#{snpat}#{gnpat})\)/i
88
+ end
89
+
90
+ it "parses an AND expression with only a single clause" do
91
+ Treequel::Filter.new( [:&, [:uid, 'kunglung']] ).to_s.should == '(&(uid=kunglung))'
92
+ end
93
+
94
+ it "parses an AND expression with multiple clauses" do
95
+ Treequel::Filter.new( [:and, [:uid, 'kunglung'], [:name, 'chunger']] ).to_s.
96
+ should == '(&(uid=kunglung)(name=chunger))'
97
+ end
98
+
99
+ it "parses an OR expression with only a single clause" do
100
+ Treequel::Filter.new( [:|, [:uid, 'kunglung']] ).to_s.should == '(|(uid=kunglung))'
101
+ end
102
+
103
+ it "parses an OR expression with multiple clauses" do
104
+ Treequel::Filter.new( [:or, [:uid, 'kunglung'], [:name, 'chunger']] ).to_s.
105
+ should == '(|(uid=kunglung)(name=chunger))'
106
+ end
107
+
108
+ it "parses an OR expression with String literal clauses" do
109
+ Treequel::Filter.new( :or, ['cn~=facet', 'cn=structure', 'cn=envision'] ).to_s.
110
+ should == '(|(cn~=facet)(cn=structure)(cn=envision))'
111
+ end
112
+
113
+ it "infers the OR-hash form if the expression is Symbol => Array" do
114
+ Treequel::Filter.new( :uid => %w[lar bin fon guh] ).to_s.
115
+ should == '(|(uid=lar)(uid=bin)(uid=fon)(uid=guh))'
116
+ end
117
+
118
+ it "correctly includes OR subfilters in a Hash if the value is an Array" do
119
+ fstr = Treequel::Filter.new( :objectClass => 'inetOrgPerson', :uid => %w[lar bin fon guh] ).to_s
120
+
121
+ fstr.should include('(|(uid=lar)(uid=bin)(uid=fon)(uid=guh))')
122
+ fstr.should include('(objectClass=inetOrgPerson)')
123
+ fstr.should =~ /^\(&/
124
+ end
125
+
126
+ it "parses a NOT expression with only a single clause" do
127
+ Treequel::Filter.new( [:'!', [:uid, 'kunglung']] ).to_s.should == '(!(uid=kunglung))'
128
+ end
129
+
130
+ it "parses a Range item as a boolean ANDed expression" do
131
+ filter = Treequel::Filter.new( :uid, 200..1000 ).to_s.should == '(&(uid>=200)(uid<=1000))'
132
+ end
133
+
134
+ it "parses a exclusive Range correctly" do
135
+ filter = Treequel::Filter.new( :uid, 200...1000 ).to_s.should == '(&(uid>=200)(uid<=999))'
136
+ end
137
+
138
+ it "parses a Range item with non-numeric components" do
139
+ filter = Treequel::Filter.new( :lastName => 'Dale'..'Darby' ).to_s.
140
+ should == '(&(lastName>=Dale)(lastName<=Darby))'
141
+ end
142
+
143
+ it "raises an exception with a NOT expression that contains more than one clause" do
144
+ expect {
145
+ Treequel::Filter.new( :not, [:uid, 'kunglung'], [:name, 'chunger'] )
146
+ }.to raise_error( ArgumentError )
147
+ end
148
+
149
+
150
+ it "parses a Substring item from a filter that includes an asterisk" do
151
+ filter = Treequel::Filter.new( :portrait, "\\ff\\d8\\ff\\e0*" )
152
+ filter.component.class.should == Treequel::Filter::SubstringItemComponent
153
+ end
154
+
155
+ it "parses a Present item from a filter that is only an asterisk" do
156
+ filter = Treequel::Filter.new( :disabled, "*" )
157
+ filter.component.class.should == Treequel::Filter::PresentItemComponent
158
+ end
159
+
160
+ it "raises an error when an extensible item filter is given" do
161
+ expect {
162
+ Treequel::Filter.new( :'cn:1.2.3.4.5:', 'Fred Flintstone' )
163
+ }.to raise_error( NotImplementedError, /extensible.*supported/i )
164
+ end
165
+
166
+
167
+ it "parses a complex nested expression" do
168
+ Treequel::Filter.new(
169
+ [:and,
170
+ [:or,
171
+ [:and, [:chungability,'fantagulous'], [:l, 'the moon']],
172
+ [:chungability, '*grunt*'],
173
+ [:hunker]],
174
+ [:not, [:description, 'mediocre']] ]
175
+ ).to_s.should == '(&(|(&(chungability=fantagulous)(l=the moon))' +
176
+ '(chungability=*grunt*)(hunker=*))(!(description=mediocre)))'
177
+ end
178
+
179
+
180
+ ### Operators
181
+ describe "operator methods" do
182
+
183
+ before( :each ) do
184
+ @filter1 = Treequel::Filter.new( :uid, :buckrogers )
185
+ @filter2 = Treequel::Filter.new( :l, :mars )
186
+ end
187
+
188
+ it "compares as equal with another filter if their components are equal" do
189
+ otherfilter = mock( "other filter" )
190
+ otherfilter.should_receive( :component ).and_return( :componentobj )
191
+ @filter1.component = :componentobj
192
+
193
+ @filter1.should == otherfilter
194
+ end
195
+
196
+ it "creates a new AND filter out of two filters that are added together" do
197
+ result = @filter1 + @filter2
198
+ result.should be_a( Treequel::Filter )
199
+ end
200
+
201
+ it "creates a new AND filter out of two filters that are bitwise-ANDed together" do
202
+ result = @filter1 & @filter2
203
+ result.should be_a( Treequel::Filter )
204
+ end
205
+
206
+ it "doesn't include the left operand in an AND filter if it is promiscuous" do
207
+ pfilter = Treequel::Filter.new
208
+ result = pfilter & @filter2
209
+
210
+ result.should == @filter2
211
+ end
212
+
213
+ it "doesn't include the right operand in an AND filter if it is promiscuous" do
214
+ pfilter = Treequel::Filter.new
215
+ result = @filter1 & pfilter
216
+
217
+ result.should == @filter1
218
+ end
219
+
220
+ end
221
+
222
+ describe "components:" do
223
+
224
+ before( :each ) do
225
+ @filter1 = stub( "filter1", :to_s => '(filter1)' )
226
+ @filter2 = stub( "filter2", :to_s => '(filter2)' )
227
+ end
228
+
229
+
230
+ describe Treequel::Filter::FilterList do
231
+ it "stringifies by joining its stringified members" do
232
+ Treequel::Filter::FilterList.new( @filter1, @filter2 ).to_s.
233
+ should == '(filter1)(filter2)'
234
+ end
235
+ end
236
+
237
+ describe Treequel::Filter::Component do
238
+ it "is an abstract class" do
239
+ expect {
240
+ Treequel::Filter::Component.new
241
+ }.to raise_error( NoMethodError )
242
+ end
243
+
244
+ it "is non-promiscuous by default" do
245
+ Class.new( Treequel::Filter::Component ).new.should_not be_promiscuous()
246
+ end
247
+
248
+ end
249
+
250
+
251
+ describe Treequel::Filter::SimpleItemComponent do
252
+ before( :each ) do
253
+ @component = Treequel::Filter::SimpleItemComponent.new( :uid, 'schlange' )
254
+ end
255
+
256
+ it "can parse a component object from a string literal" do
257
+ comp = Treequel::Filter::SimpleItemComponent.parse_from_string( 'description=screamer' )
258
+ comp.filtertype.should == :equal
259
+ comp.filtertype_op.should == '='
260
+ comp.attribute.should == 'description'
261
+ comp.value.should == 'screamer'
262
+ end
263
+
264
+ it "raises an ExpressionError if it can't parse a string literal" do
265
+ expect { Treequel::Filter::SimpleItemComponent.parse_from_string( 'whatev!' ) }.
266
+ to raise_error( Treequel::ExpressionError, /unable to parse/i )
267
+ end
268
+
269
+ it "uses the 'equal' operator if none is specified" do
270
+ @component.filtertype.should == :equal
271
+ end
272
+
273
+ it "knows what the appropriate operator is for its filtertype" do
274
+ @component.filtertype_op.should == '='
275
+ end
276
+
277
+ it "knows what the appropriate operator is for its filtertype even if it's set to a string" do
278
+ @component.filtertype = 'greater'
279
+ @component.filtertype_op.should == '>='
280
+ end
281
+
282
+ it "stringifies as <attribute><operator><value>" do
283
+ @component.to_s.should == 'uid=schlange'
284
+ end
285
+
286
+ it "uses the '~=' operator if its filtertype is 'approx'" do
287
+ @component.filtertype = :approx
288
+ @component.filtertype_op.should == '~='
289
+ end
290
+
291
+ it "uses the '>=' operator if its filtertype is 'greater'" do
292
+ @component.filtertype = :greater
293
+ @component.filtertype_op.should == '>='
294
+ end
295
+
296
+ it "uses the '<=' operator if its filtertype is 'less'" do
297
+ @component.filtertype = :less
298
+ @component.filtertype_op.should == '<='
299
+ end
300
+
301
+ it "raises an error if it's created with an unknown filtertype" do
302
+ expect {
303
+ Treequel::Filter::SimpleItemComponent.new( :uid, 'schlange', :fork )
304
+ }.to raise_error( Treequel::ExpressionError, /invalid/i )
305
+
306
+ end
307
+
308
+ end
309
+
310
+
311
+ describe Treequel::Filter::SubstringItemComponent do
312
+
313
+ before( :each ) do
314
+ @component = Treequel::Filter::SubstringItemComponent.new( :description, '*basecamp*' )
315
+ end
316
+
317
+
318
+ it "can parse a component object from a string literal" do
319
+ comp = Treequel::Filter::SubstringItemComponent.parse_from_string( 'description=*basecamp*' )
320
+ comp.attribute.should == 'description'
321
+ comp.options.should == ''
322
+ comp.pattern.should == '*basecamp*'
323
+ end
324
+
325
+ it "can parse a component object from a string literal with attribute options" do
326
+ jpeg_portraits = Treequel::Filter::SubstringItemComponent.
327
+ parse_from_string( "portrait;binary=\\xff\\xd8\\xff\\xe0*" )
328
+ jpeg_portraits.attribute.should == 'portrait'
329
+ jpeg_portraits.options.should == ';binary'
330
+ jpeg_portraits.pattern.should == "\\xff\\xd8\\xff\\xe0*"
331
+ end
332
+
333
+ it "raises an ExpressionError if it can't parse a string literal" do
334
+ expect { Treequel::Filter::SubstringItemComponent.parse_from_string( 'whatev>=1' ) }.
335
+ to raise_error( Treequel::ExpressionError, /unable to parse/i )
336
+ end
337
+
338
+ end
339
+
340
+
341
+ describe Treequel::Filter::AndComponent do
342
+ it "stringifies as its filters ANDed together" do
343
+ Treequel::Filter::AndComponent.new( @filter1, @filter2 ).to_s.
344
+ should == '&(filter1)(filter2)'
345
+ end
346
+
347
+ it "allows a single filter" do
348
+ Treequel::Filter::AndComponent.new( @filter1 ).to_s.
349
+ should == '&(filter1)'
350
+ end
351
+ end
352
+
353
+ describe Treequel::Filter::OrComponent do
354
+ it "stringifies as its filters ORed together" do
355
+ Treequel::Filter::OrComponent.new( @filter1, @filter2 ).to_s.
356
+ should == '|(filter1)(filter2)'
357
+ end
358
+
359
+ it "allows a single filter" do
360
+ Treequel::Filter::OrComponent.new( @filter1 ).to_s.
361
+ should == '|(filter1)'
362
+ end
363
+
364
+ end
365
+
366
+ describe Treequel::Filter::NotComponent do
367
+ it "stringifies as the negation of its filter" do
368
+ Treequel::Filter::NotComponent.new( @filter1 ).to_s.
369
+ should == '!(filter1)'
370
+ end
371
+
372
+ it "can't be created with multiple filters" do
373
+ expect {
374
+ Treequel::Filter::NotComponent.new( @filter1, @filter2 )
375
+ }.to raise_error( ArgumentError, /2 for 1/i )
376
+ end
377
+ end
378
+ end
379
+
380
+ describe "support for Sequel expressions" do
381
+
382
+ before( :each ) do
383
+ pending "requires the 'sequel' library" unless Sequel.const_defined?( :Model )
384
+ end
385
+
386
+ it "supports the boolean expression syntax" do
387
+ pending( "inequality operators don't work under 1.9.1" ) if
388
+ vvec( RUBY_VERSION ) > vvec( '1.8.7' )
389
+ filter = Treequel::Filter.new( :uid >= 2000 )
390
+ filter.should be_a( Treequel::Filter )
391
+ filter.to_s.should == '(uid>=2000)'
392
+ end
393
+
394
+ it "supports Sequel expressions in ANDed subexpressions" do
395
+ pending( "inequality operators don't work under 1.9.1" ) if
396
+ vvec( RUBY_VERSION ) > vvec( '1.8.7' )
397
+ filter = Treequel::Filter.new( :and, [:uid >= 1024], [:uid <= 65535] )
398
+ filter.should be_a( Treequel::Filter )
399
+ filter.to_s.should == '(&(uid>=1024)(uid<=65535))'
400
+ end
401
+
402
+ it "advises user to use '>=' instead of '>' in expressions" do
403
+ pending( "inequality operators don't work under 1.9.1" ) if
404
+ vvec( RUBY_VERSION ) > vvec( '1.8.7' )
405
+ expect {
406
+ Treequel::Filter.new( :uid > 1024 )
407
+ }.to raise_error( Treequel::ExpressionError, /greater-than-or-equal/i )
408
+ end
409
+
410
+ it "advises user to use '<=' instead of '<' in expressions" do
411
+ pending( "inequality operators don't work under 1.9.1" ) if
412
+ vvec( RUBY_VERSION ) > vvec( '1.8.7' )
413
+ expect {
414
+ Treequel::Filter.new( :activated < Time.now )
415
+ }.to raise_error( Treequel::ExpressionError, /less-than-or-equal/i )
416
+ end
417
+
418
+ it "supports the 'LIKE' expression syntax with a single string argument" do
419
+ filter = Treequel::Filter.new( :cn.like('mar*n') )
420
+ filter.should be_a( Treequel::Filter )
421
+ filter.to_s.should == '(cn=mar*n)'
422
+ end
423
+
424
+ it "treats a LIKE expression with no asterisks as an 'approx' filter" do
425
+ filter = Treequel::Filter.new( :cn.like('maylin') )
426
+ filter.should be_a( Treequel::Filter )
427
+ filter.to_s.should == '(cn~=maylin)'
428
+ end
429
+
430
+ it "supports the 'LIKE' expression syntax with multiple string arguments" do
431
+ filter = Treequel::Filter.new( :cn.like('may*', 'mah*') )
432
+ filter.should be_a( Treequel::Filter )
433
+ filter.to_s.should == '(|(cn=may*)(cn=mah*))'
434
+ end
435
+
436
+ it "raises an exception when given a 'LIKE' expression with a regex argument" do
437
+ expect {
438
+ Treequel::Filter.new( :cn.like(/^ma.*/) )
439
+ }.to raise_error( Treequel::ExpressionError, /regex/i )
440
+ end
441
+
442
+ it "raises an exception when given a 'LIKE' expression with a regex argument with flags" do
443
+ expect {
444
+ Treequel::Filter.new( :cn.like(/^ma.*/i) )
445
+ }.to raise_error( Treequel::ExpressionError, /regex/i )
446
+ end
447
+
448
+ it "raises an exception when given a 'LIKE' expression with a mix of regex and string " +
449
+ "arguments" do
450
+ expect {
451
+ Treequel::Filter.new( :cn.like('maylin', /^mi.*/i) )
452
+ }.to raise_error( Treequel::ExpressionError, /regex/i )
453
+ end
454
+
455
+ it "supports negation of a 'exists' expression via the Sequel ~ syntax" do
456
+ filter = Treequel::Filter.new( ~:cn )
457
+ filter.should be_a( Treequel::Filter )
458
+ filter.to_s.should == '(!(cn=*))'
459
+ end
460
+
461
+ it "supports negation of a simple equality expression via the Sequel ~ syntax" do
462
+ filter = Treequel::Filter.new( ~{ :l => 'anoos' } )
463
+ filter.should be_a( Treequel::Filter )
464
+ filter.to_s.should == '(!(l=anoos))'
465
+ end
466
+
467
+ it "supports negation of an approximate-match expression via the Sequel ~ syntax" do
468
+ filter = Treequel::Filter.new( ~:cn.like('maylin') )
469
+ filter.should be_a( Treequel::Filter )
470
+ filter.to_s.should == '(!(cn~=maylin))'
471
+ end
472
+
473
+ it "supports negation of a matching expression via the Sequel ~ syntax" do
474
+ filter = Treequel::Filter.new( ~:cn.like('may*i*') )
475
+ filter.should be_a( Treequel::Filter )
476
+ filter.to_s.should == '(!(cn=may*i*))'
477
+ end
478
+ end
479
+ end
480
+
481
+
482
+ # vim: set nosta noet ts=4 sw=4: