treequel 1.0.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.
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,487 @@
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
+ begin
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
+
17
+ require 'treequel/directory'
18
+ require 'treequel/branch'
19
+ rescue LoadError
20
+ unless Object.const_defined?( :Gem )
21
+ require 'rubygems'
22
+ retry
23
+ end
24
+ raise
25
+ end
26
+
27
+
28
+ include Treequel::TestConstants
29
+ include Treequel::Constants
30
+
31
+ #####################################################################
32
+ ### C O N T E X T S
33
+ #####################################################################
34
+
35
+ describe Treequel::Directory do
36
+ include Treequel::SpecHelpers
37
+
38
+ before( :all ) do
39
+ setup_logging( :fatal )
40
+ end
41
+
42
+ after( :all ) do
43
+ reset_logging()
44
+ end
45
+
46
+
47
+ before( :each ) do
48
+ @options = {
49
+ :host => TEST_HOST,
50
+ :port => TEST_PORT,
51
+ :base_dn => TEST_BASE_DN,
52
+ :connect_type => :plain,
53
+ }
54
+ @conn = mock( "LDAP connection", :set_option => true, :bound? => false )
55
+ LDAP::SSLConn.stub!( :new ).and_return( @conn )
56
+ @conn.stub!( :root_dse ).and_return( nil )
57
+ end
58
+
59
+
60
+ it "is created with reasonable default options if none are specified" do
61
+ dir = Treequel::Directory.new
62
+
63
+ dir.host.should == 'localhost'
64
+ dir.port.should == 389
65
+ dir.connect_type.should == :tls
66
+ dir.base_dn.should == ''
67
+ end
68
+
69
+ it "is created with the specified options if options are specified" do
70
+ dir = Treequel::Directory.new( @options )
71
+
72
+ dir.host.should == TEST_HOST
73
+ dir.port.should == TEST_PORT
74
+ dir.connect_type.should == @options[:connect_type]
75
+ dir.base_dn.should == TEST_BASE_DN
76
+ end
77
+
78
+ it "binds immediately if user/pass is included in the ldap URI" do
79
+ conn = mock( "LDAP connection", :set_option => true )
80
+
81
+ LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
82
+ and_return( conn )
83
+ conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
84
+
85
+ dir = Treequel::Directory.new( @options.merge( :bind_dn => TEST_BIND_DN, :pass => TEST_BIND_PASS ))
86
+ dir.instance_variable_get( :@bound_as ).should == TEST_BIND_DN
87
+ end
88
+
89
+ it "uses the first namingContext from the Root DSE if no base is specified" do
90
+ conn = mock( "LDAP connection", :set_option => true )
91
+ LDAP::Conn.stub!( :new ).and_return( conn )
92
+ conn.should_receive( :root_dse ).and_return( TEST_DSE )
93
+
94
+ @dir = Treequel::Directory.new( @options.merge(:base_dn => nil) )
95
+ @dir.base_dn.should == TEST_BASE_DN
96
+ end
97
+
98
+
99
+ describe "instances without existing connections" do
100
+
101
+ before( :each ) do
102
+ @dir = Treequel::Directory.new( @options )
103
+ @conn = mock( "ldap connection", :set_option => true )
104
+ end
105
+
106
+
107
+ it "stringifies as a description which includes the host, port, connection type and base" do
108
+ @dir.to_s.should =~ /#{Regexp.quote(TEST_HOST)}/
109
+ @dir.to_s.should =~ /#{TEST_PORT}/
110
+ @dir.to_s.should =~ /\b#{@dir.connect_type}\b/
111
+ @dir.to_s.should =~ /#{TEST_BASE_DN}/i
112
+ end
113
+
114
+ it "connects on demand to the configured directory server" do
115
+ LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
116
+ and_return( @conn )
117
+ @dir.conn.should == @conn
118
+ end
119
+
120
+ it "connects with TLS on demand to the configured directory server if configured to do so" do
121
+ @dir.connect_type = :tls
122
+ LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT, true ).
123
+ and_return( @conn )
124
+ @dir.conn.should == @conn
125
+ end
126
+
127
+ it "connects over SSL on demand to the configured directory server if configured to do so" do
128
+ @dir.connect_type = :ssl
129
+ LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
130
+ and_return( @conn )
131
+ @dir.conn.should == @conn
132
+ end
133
+ end
134
+
135
+ describe "instances with a connection" do
136
+
137
+ before( :each ) do
138
+ @conn = mock( "ldap connection", :bound? => false )
139
+
140
+ @dir = Treequel::Directory.new( @options )
141
+ @dir.instance_variable_set( :@conn, @conn )
142
+
143
+ @schema = mock( "Directory schema" )
144
+ @conn.stub!( :schema ).and_return( :the_schema )
145
+ Treequel::Schema.stub!( :new ).with( :the_schema ).and_return( @schema )
146
+ @schema.stub!( :attribute_types ).and_return({ :cn => :a_value, :ou => :a_value })
147
+ end
148
+
149
+ it "can bind with the given user DN and password" do
150
+ @conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
151
+ @dir.bind( TEST_BIND_DN, TEST_BIND_PASS )
152
+ end
153
+
154
+ it "can bind with the DN of the given Branch (or a quack-alike) and password" do
155
+ branch = stub( "branch", :dn => TEST_BIND_DN )
156
+ @conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
157
+ @dir.bind( branch, TEST_BIND_PASS )
158
+ end
159
+
160
+ it "can temporarily bind as another user for the duration of a block" do
161
+ dupconn = mock( "duplicate connection" )
162
+ @conn.should_receive( :dup ).and_return( dupconn )
163
+ dupconn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
164
+ @conn.should_not_receive( :bind )
165
+
166
+ @dir.bound_as( TEST_BIND_DN, TEST_BIND_PASS ) do
167
+ @dir.conn.should == dupconn
168
+ end
169
+
170
+ @dir.conn.should == @conn
171
+ end
172
+
173
+ it "knows if its underlying connection is already bound" do
174
+ @conn.should_receive( :bound? ).and_return( false, true )
175
+ @dir.should_not be_bound()
176
+ @dir.should be_bound()
177
+ end
178
+
179
+
180
+ it "can be unbound, which replaces the bound connection with a duplicate that is unbound" do
181
+ dupconn = mock( "duplicate connection" )
182
+ @conn.should_receive( :bound? ).and_return( true )
183
+ @conn.should_receive( :dup ).and_return( dupconn )
184
+ @conn.should_receive( :unbind )
185
+
186
+ @dir.unbind
187
+
188
+ @dir.conn.should == dupconn
189
+ end
190
+
191
+
192
+ it "doesn't do anything if told to unbind but the current connection is not bound" do
193
+ @conn.should_receive( :bound? ).and_return( false )
194
+ @conn.should_not_receive( :dup )
195
+ @conn.should_not_receive( :unbind )
196
+
197
+ @dir.unbind
198
+
199
+ @dir.conn.should == @conn
200
+ end
201
+
202
+ it "can look up a Branch's corresponding LDAP::Entry hash" do
203
+ branch = mock( "branch" )
204
+
205
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
206
+
207
+ @conn.should_receive( :search_ext2 ).
208
+ with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)' ).
209
+ and_return([ :the_entry ])
210
+
211
+ @dir.get_entry( branch ).should == :the_entry
212
+ end
213
+
214
+ it "can look up a Branch's corresponding LDAP::Entry hash with operational attributes included" do
215
+ branch = mock( "branch" )
216
+
217
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
218
+
219
+ @conn.should_receive( :search_ext2 ).
220
+ with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)', ['*', '+'] ).
221
+ and_return([ :the_extended_entry ])
222
+
223
+ @dir.get_extended_entry( branch ).should == :the_extended_entry
224
+ end
225
+
226
+ it "can search for entries and return them as Sequel::Branch objects" do
227
+ base = TEST_PEOPLE_DN
228
+ filter = '(|(uid=jonlong)(uid=margento))'
229
+ branch = mock( "branch" )
230
+
231
+ found_branch1 = stub( "entry1 branch" )
232
+ found_branch2 = stub( "entry2 branch" )
233
+
234
+ # Do the search
235
+ entries = [
236
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
237
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
238
+ ]
239
+ @conn.should_receive( :search_ext2 ).
240
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
241
+ and_return( entries )
242
+
243
+ # Turn found entries into Branch objects
244
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
245
+ and_return( found_branch1 )
246
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
247
+ and_return( found_branch2 )
248
+
249
+ @dir.search( base, :base, filter ).should == [ found_branch1, found_branch2 ]
250
+ end
251
+
252
+
253
+ it "can search for entries and yield them as Sequel::Branch objects" do
254
+ base = TEST_PEOPLE_DN
255
+ filter = '(|(uid=jonlong)(uid=margento))'
256
+ branch = mock( "branch", :dn => "thedn" )
257
+
258
+ found_branch1 = stub( "entry1 branch" )
259
+ found_branch2 = stub( "entry2 branch" )
260
+
261
+ # Do the search
262
+ entries = [
263
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
264
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
265
+ ]
266
+ @conn.should_receive( :search_ext2 ).
267
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
268
+ and_return( entries )
269
+
270
+ # Turn found entries into Branch objects
271
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
272
+ and_return( found_branch1 )
273
+ Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
274
+ and_return( found_branch2 )
275
+
276
+ results = []
277
+ @dir.search( base, :base, filter ) do |branch|
278
+ results << branch
279
+ end
280
+
281
+ results.should == [ found_branch1, found_branch2 ]
282
+ end
283
+
284
+
285
+ it "catches plain RuntimeErrors raised by #search2 and re-casts them as " +
286
+ "more-interesting errors" do
287
+ @conn.should_receive( :search_ext2 ).
288
+ and_raise( RuntimeError.new('no result returned by search') )
289
+ @conn.should_receive( :err ).and_return( -1 )
290
+
291
+ expect {
292
+ @dir.search( TEST_BASE_DN, :base, '(objectClass=*)' )
293
+ }.to raise_error( LDAP::ResultError, /can't contact/i )
294
+ end
295
+
296
+ describe "and a custom search results class" do
297
+
298
+ before( :each ) do
299
+ @customclass = Class.new {
300
+ def self::new_from_entry( entry, directory )
301
+ new( entry, directory, 'a_dn' )
302
+ end
303
+ def initialize( entry, directory, dn )
304
+ @entry = entry
305
+ @directory = directory
306
+ @dn = dn
307
+ end
308
+ attr_reader :entry, :directory, :dn
309
+ }
310
+
311
+ end
312
+
313
+ it "can search for entries and return them as instances of a custom class" do
314
+ filter = '(|(uid=jonlong)(uid=margento))'
315
+ base = mock( "branch" )
316
+
317
+ found_branch1 = stub( "entry1 branch" )
318
+ found_branch2 = stub( "entry2 branch" )
319
+
320
+ # Do the search
321
+ entries = [
322
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
323
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
324
+ ]
325
+ @conn.should_receive( :search_ext2 ).
326
+ with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
327
+ false, nil, nil, 0, 0, 0, '', nil ).
328
+ and_return( entries )
329
+
330
+ rval = @dir.search( base, :base, filter, :results_class => @customclass )
331
+
332
+ rval[0].should be_an_instance_of( @customclass )
333
+ rval[0].entry.should == entries[0]
334
+ rval[0].directory.should == @dir
335
+ rval[1].should be_an_instance_of( @customclass )
336
+ rval[1].entry.should == entries[1]
337
+ rval[1].directory.should == @dir
338
+ end
339
+
340
+
341
+ it "returns instances of the base argument if it responds to new_from_entry and no " +
342
+ "custom class is specified" do
343
+
344
+ base = @customclass.new( nil, nil, TEST_PEOPLE_DN )
345
+ filter = '(|(uid=jonlong)(uid=margento))'
346
+ branch = mock( "branch" )
347
+
348
+ found_branch1 = stub( "entry1 branch" )
349
+ found_branch2 = stub( "entry2 branch" )
350
+
351
+ # Do the search
352
+ entries = [
353
+ { 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
354
+ { 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
355
+ ]
356
+ @conn.should_receive( :search_ext2 ).
357
+ with( TEST_PEOPLE_DN, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
358
+ false, nil, nil, 0, 0, 0, '', nil ).
359
+ and_return( entries )
360
+
361
+ rval = @dir.search( base, :base, filter )
362
+
363
+ rval[0].should be_an_instance_of( @customclass )
364
+ rval[0].entry.should == entries[0]
365
+ rval[0].directory.should == @dir
366
+ rval[1].should be_an_instance_of( @customclass )
367
+ rval[1].entry.should == entries[1]
368
+ rval[1].directory.should == @dir
369
+ end
370
+
371
+ end
372
+
373
+ it "can turn a DN string into an RDN string from its base" do
374
+ @dir.rdn_to( TEST_PERSON_DN ).should == TEST_PERSON_DN.sub( /,#{TEST_BASE_DN}$/, '' )
375
+ end
376
+
377
+ it "can fetch the server's schema" do
378
+ @conn.should_receive( :schema ).and_return( :the_schema )
379
+ Treequel::Schema.should_receive( :new ).with( :the_schema ).
380
+ and_return( :the_parsed_schema )
381
+ @dir.schema.should == :the_parsed_schema
382
+ end
383
+
384
+ it "creates branches for messages that match valid attributeType OIDs" do
385
+ @schema.should_receive( :attribute_types ).
386
+ and_return({ :cn => :a_value, :ou => :a_value })
387
+
388
+ @dir.stub!( :bound? ).and_return( false )
389
+ rval = @dir.ou( :people )
390
+ rval.dn.downcase.should == TEST_PEOPLE_DN.downcase
391
+ end
392
+
393
+ it "doesn't create branches for messages that don't match valid attributeType OIDs" do
394
+ @schema.should_receive( :attribute_types ).
395
+ and_return({ :cn => :a_value, :ou => :a_value })
396
+
397
+ expect { @dir.void('sbc') }.to raise_error( NoMethodError )
398
+ end
399
+
400
+ it "can modify the record corresponding to a Branch in the directory" do
401
+ branch = mock( "branch" )
402
+ branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
403
+
404
+ @conn.should_receive( :modify ).with( :the_branches_dn, 'cn' => ['nomblywob'] )
405
+
406
+ @dir.modify( branch, 'cn' => ['nomblywob'] )
407
+ end
408
+
409
+ it "can delete the record corresponding to a Branch from the directory" do
410
+ branch = mock( "branch" )
411
+ branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
412
+
413
+ @conn.should_receive( :delete ).once.with( :the_branches_dn )
414
+
415
+ @dir.delete( branch )
416
+ end
417
+
418
+ it "can create an entry for a Branch" do
419
+ newattrs = {
420
+ :cn => 'Chilly T',
421
+ :desc => 'Audi like Jetta',
422
+ :objectClass => :room,
423
+ }
424
+ rdn_attrs = {
425
+ TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE]
426
+ }
427
+ addattrs = {
428
+ 'cn' => ['Chilly T'],
429
+ 'desc' => ['Audi like Jetta'],
430
+ 'objectClass' => ['room'],
431
+ TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
432
+ }
433
+
434
+ branch = mock( "new person branch" )
435
+ branch.should_receive( :dn ).and_return( TEST_PERSON_DN )
436
+ branch.should_receive( :rdn_attributes ).at_least( :once ).and_return( rdn_attrs )
437
+
438
+ room_objectclass = stub( 'room objectClass', :structural? => true )
439
+ @schema.should_receive( :object_classes ).at_least( :once ).and_return({
440
+ :room => room_objectclass,
441
+ })
442
+
443
+ @conn.should_receive( :add ).with( TEST_PERSON_DN, addattrs )
444
+
445
+ @dir.create( branch, newattrs )
446
+ end
447
+
448
+
449
+ it "can move a record to a new dn within the same branch" do
450
+ @dir.stub!( :bound? ).and_return( false )
451
+ branch = mock( "sibling branch obj" )
452
+ branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
453
+ branch.should_receive( :split_dn ).at_least( :once ).
454
+ and_return([ TEST_PERSON_RDN, TEST_PEOPLE_DN ])
455
+
456
+ @conn.should_receive( :modrdn ).with( TEST_PERSON_DN, TEST_PERSON2_RDN, true )
457
+ branch.should_receive( :dn= ).with( TEST_PERSON2_DN )
458
+
459
+ @dir.move( branch, TEST_PERSON2_DN )
460
+ end
461
+
462
+
463
+ ### Datatype conversion
464
+
465
+ it "allows a mapping to be overridden by a block for a valid syntax OID" do
466
+ @dir.add_syntax_mapping( OIDS::BIT_STRING_SYNTAX ) do |unconverted_value|
467
+ unconverted_value.to_sym
468
+ end
469
+ @dir.convert_syntax_value( OIDS::BIT_STRING_SYNTAX, 'a_value' ).should == :a_value
470
+ end
471
+
472
+ it "allows a mapping to be overridden by a Hash for a valid syntax OID" do
473
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
474
+ @dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == true
475
+ end
476
+
477
+ it "allows a mapping to be cleared by adding a nil mapping" do
478
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
479
+ @dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX )
480
+ @dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == 'true'
481
+ end
482
+
483
+ end
484
+ end
485
+
486
+
487
+ # vim: set nosta noet ts=4 sw=4: