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,330 @@
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
+ extdir = basedir + "ext"
9
+
10
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
+ $LOAD_PATH.unshift( extdir ) unless $LOAD_PATH.include?( extdir )
12
+ }
13
+
14
+ begin
15
+ require 'spec'
16
+ require 'spec/lib/constants'
17
+ require 'spec/lib/helpers'
18
+
19
+ require 'treequel'
20
+ require 'treequel/mixins'
21
+ rescue LoadError
22
+ unless Object.const_defined?( :Gem )
23
+ require 'rubygems'
24
+ retry
25
+ end
26
+ raise
27
+ end
28
+
29
+
30
+ include Treequel::TestConstants
31
+ include Treequel::Constants
32
+
33
+ #####################################################################
34
+ ### C O N T E X T S
35
+ #####################################################################
36
+
37
+ describe Treequel, "mixin" do
38
+ include Treequel::SpecHelpers
39
+
40
+ describe Treequel::Loggable, "mixed into a class" do
41
+ before(:each) do
42
+ @logfile = StringIO.new('')
43
+ Treequel.logger = Logger.new( @logfile )
44
+
45
+ @test_class = Class.new do
46
+ include Treequel::Loggable
47
+
48
+ def log_test_message( level, msg )
49
+ self.log.send( level, msg )
50
+ end
51
+
52
+ def logdebug_test_message( msg )
53
+ self.log_debug.debug( msg )
54
+ end
55
+ end
56
+ @obj = @test_class.new
57
+ end
58
+
59
+
60
+ it "is able to output to the log via its #log method" do
61
+ @obj.log_test_message( :debug, "debugging message" )
62
+ @logfile.rewind
63
+ @logfile.read.should =~ /debugging message/
64
+ end
65
+
66
+ it "is able to output to the log via its #log_debug method" do
67
+ @obj.logdebug_test_message( "sexydrownwatch" )
68
+ @logfile.rewind
69
+ @logfile.read.should =~ /sexydrownwatch/
70
+ end
71
+ end
72
+
73
+ #################################################################
74
+ ### E X A M P L E S
75
+ #################################################################
76
+
77
+ describe Treequel::HashUtilities do
78
+ it "includes a function for stringifying Hash keys" do
79
+ testhash = {
80
+ :foo => 1,
81
+ :bar => {
82
+ :klang => 'klong',
83
+ :barang => { :kerklang => 'dumdumdum' },
84
+ }
85
+ }
86
+
87
+ result = Treequel::HashUtilities.stringify_keys( testhash )
88
+
89
+ result.should be_an_instance_of( Hash )
90
+ result.should_not be_equal( testhash )
91
+ result.should == {
92
+ 'foo' => 1,
93
+ 'bar' => {
94
+ 'klang' => 'klong',
95
+ 'barang' => { 'kerklang' => 'dumdumdum' },
96
+ }
97
+ }
98
+ end
99
+
100
+
101
+ it "includes a function for symbolifying Hash keys" do
102
+ testhash = {
103
+ 'foo' => 1,
104
+ 'bar' => {
105
+ 'klang' => 'klong',
106
+ 'barang' => { 'kerklang' => 'dumdumdum' },
107
+ }
108
+ }
109
+
110
+ result = Treequel::HashUtilities.symbolify_keys( testhash )
111
+
112
+ result.should be_an_instance_of( Hash )
113
+ result.should_not be_equal( testhash )
114
+ result.should == {
115
+ :foo => 1,
116
+ :bar => {
117
+ :klang => 'klong',
118
+ :barang => { :kerklang => 'dumdumdum' },
119
+ }
120
+ }
121
+ end
122
+
123
+ it "includes a function that can be used as the key-collision callback for " +
124
+ "Hash#merge that does recursive merging" do
125
+ hash1 = {
126
+ :foo => 1,
127
+ :bar => [:one],
128
+ :baz => {
129
+ :glom => [:chunker]
130
+ }
131
+ }
132
+ hash2 = {
133
+ :klong => 88.8,
134
+ :bar => [:locke],
135
+ :baz => {
136
+ :trim => :liquor,
137
+ :glom => [:plunker]
138
+ }
139
+ }
140
+
141
+ hash1.merge( hash2, &Treequel::HashUtilities.method(:merge_recursively) ).should == {
142
+ :foo => 1,
143
+ :bar => [:one, :locke],
144
+ :baz => {
145
+ :glom => [:chunker, :plunker],
146
+ :trim => :liquor,
147
+ },
148
+ :klong => 88.8,
149
+ }
150
+ end
151
+
152
+ end
153
+
154
+ describe Treequel::ArrayUtilities do
155
+ it "includes a function for stringifying Array elements" do
156
+ testarray = [:a, :b, :c, [:d, :e, [:f, :g]]]
157
+
158
+ result = Treequel::ArrayUtilities.stringify_array( testarray )
159
+
160
+ result.should be_an_instance_of( Array )
161
+ result.should_not be_equal( testarray )
162
+ result.should == ['a', 'b', 'c', ['d', 'e', ['f', 'g']]]
163
+ end
164
+
165
+
166
+ it "includes a function for symbolifying Array elements" do
167
+ testarray = ['a', 'b', 'c', ['d', 'e', ['f', 'g']]]
168
+
169
+ result = Treequel::ArrayUtilities.symbolify_array( testarray )
170
+
171
+ result.should be_an_instance_of( Array )
172
+ result.should_not be_equal( testarray )
173
+ result.should == [:a, :b, :c, [:d, :e, [:f, :g]]]
174
+ end
175
+ end
176
+
177
+
178
+ describe Treequel::AttributeDeclarations do
179
+ before( :all ) do
180
+ setup_logging( :debug )
181
+ end
182
+ after( :all ) do
183
+ reset_logging()
184
+ end
185
+
186
+ describe "predicate attribute declaration" do
187
+ before( :all ) do
188
+ @testclass = Class.new do
189
+ extend Treequel::AttributeDeclarations
190
+
191
+ def initialize( val )
192
+ @testable = val
193
+ end
194
+
195
+ predicate_attr :testable
196
+ end
197
+ end
198
+
199
+ it "creates a plain predicate method" do
200
+ @testclass.new( true ).should be_testable()
201
+ @testclass.new( false ).should_not be_testable()
202
+ @testclass.new( 1 ).should be_testable()
203
+ @testclass.new( :something_else ).should be_testable()
204
+ end
205
+
206
+ it "creates a mutator" do
207
+ obj = @testclass.new( true )
208
+ obj.testable = false
209
+ obj.should_not be_testable()
210
+ obj.testable = true
211
+ obj.should be_testable()
212
+ end
213
+ end
214
+ end
215
+
216
+ describe Treequel::Delegation do
217
+
218
+ before( :all ) do
219
+ setup_logging( :debug )
220
+ end
221
+ after( :all ) do
222
+ reset_logging()
223
+ end
224
+
225
+ describe "method delegation" do
226
+ before( :all ) do
227
+ @testclass = Class.new do
228
+ extend Treequel::Delegation
229
+
230
+ def initialize( obj )
231
+ @obj = obj
232
+ end
233
+
234
+ def_method_delegators :demand_loaded_object, :delegated_method
235
+ def_method_delegators :nonexistant_method, :erroring_delegated_method
236
+
237
+ def demand_loaded_object
238
+ return @obj
239
+ end
240
+ end
241
+ end
242
+
243
+ before( :each ) do
244
+ @subobj = mock( "delegate" )
245
+ @obj = @testclass.new( @subobj )
246
+ end
247
+
248
+
249
+ it "can be used to set up delegation through a method" do
250
+ @subobj.should_receive( :delegated_method )
251
+ @obj.delegated_method
252
+ end
253
+
254
+ it "passes any arguments through to the delegate object's method" do
255
+ @subobj.should_receive( :delegated_method ).with( :arg1, :arg2 )
256
+ @obj.delegated_method( :arg1, :arg2 )
257
+ end
258
+
259
+ it "reports errors from its caller's perspective" do
260
+ pending "doesn't work under 1.9, but may not be necessary" if
261
+ vvec(RUBY_VERSION) > vvec('1.8.7')
262
+
263
+ begin
264
+ @obj.erroring_delegated_method
265
+ rescue NoMethodError => err
266
+ err.message.should =~ /nonexistant_method/
267
+ err.backtrace.first.should =~ /#{__FILE__}/
268
+ rescue ::Exception => err
269
+ fail "Expected a NoMethodError, but got a %p (%s)" % [ err.class, err.message ]
270
+ else
271
+ fail "Expected a NoMethodError, but no exception was raised."
272
+ end
273
+ end
274
+
275
+ end
276
+
277
+ describe "instance variable delegation (ala Forwardable)" do
278
+ before( :all ) do
279
+ @testclass = Class.new do
280
+ extend Treequel::Delegation
281
+
282
+ def initialize( obj )
283
+ @obj = obj
284
+ end
285
+
286
+ def_ivar_delegators :@obj, :delegated_method
287
+ def_ivar_delegators :@glong, :erroring_delegated_method
288
+
289
+ end
290
+ end
291
+
292
+ before( :each ) do
293
+ @subobj = mock( "delegate" )
294
+ @obj = @testclass.new( @subobj )
295
+ end
296
+
297
+
298
+ it "can be used to set up delegation through a method" do
299
+ @subobj.should_receive( :delegated_method )
300
+ @obj.delegated_method
301
+ end
302
+
303
+ it "passes any arguments through to the delegate's method" do
304
+ @subobj.should_receive( :delegated_method ).with( :arg1, :arg2 )
305
+ @obj.delegated_method( :arg1, :arg2 )
306
+ end
307
+
308
+ it "reports errors from its caller's perspective" do
309
+ pending "doesn't work under 1.9, but may not be necessary" if
310
+ vvec(RUBY_VERSION) > vvec('1.8.7')
311
+
312
+ begin
313
+ @obj.erroring_delegated_method
314
+ rescue NoMethodError => err
315
+ err.message.should =~ /`erroring_delegated_method' for nil/
316
+ err.backtrace.first.should =~ /#{__FILE__}/
317
+ rescue ::Exception => err
318
+ fail "Expected a NoMethodError, but got a %p (%s)" % [ err.class, err.message ]
319
+ else
320
+ fail "Expected a NoMethodError, but no exception was raised."
321
+ end
322
+ end
323
+
324
+ end
325
+
326
+ end
327
+
328
+ end
329
+
330
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.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 'yaml'
18
+ require 'ldap'
19
+ require 'ldap/schema'
20
+ require 'treequel/schema/attributetype'
21
+ rescue LoadError
22
+ unless Object.const_defined?( :Gem )
23
+ require 'rubygems'
24
+ retry
25
+ end
26
+ raise
27
+ end
28
+
29
+
30
+ include Treequel::TestConstants
31
+ include Treequel::Constants
32
+
33
+ #####################################################################
34
+ ### C O N T E X T S
35
+ #####################################################################
36
+
37
+ describe Treequel::Schema::AttributeType do
38
+ include Treequel::SpecHelpers
39
+
40
+
41
+ before( :all ) do
42
+ setup_logging( :fatal )
43
+ @datadir = Pathname( __FILE__ ).dirname.parent.parent + 'data'
44
+ end
45
+
46
+ before( :each ) do
47
+ @schema = mock( "treequel schema object" )
48
+ end
49
+
50
+ after( :all ) do
51
+ reset_logging()
52
+ end
53
+
54
+
55
+ describe "parsed from the 'objectClass' attributeType" do
56
+
57
+ OBJECTCLASS_ATTRTYPE = %{( 2.5.4.0 NAME 'objectClass' } +
58
+ %{DESC 'RFC2256: object classes of the entity' } +
59
+ %{EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )}
60
+
61
+ before( :each ) do
62
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, OBJECTCLASS_ATTRTYPE )
63
+ end
64
+
65
+ it "knows what OID corresponds to the type" do
66
+ @attrtype.oid.should == '2.5.4.0'
67
+ end
68
+
69
+ it "knows what its NAME attribute is" do
70
+ @attrtype.name.should == :objectClass
71
+ end
72
+
73
+ it "knows what its DESC attribute is" do
74
+ @attrtype.desc.should == 'RFC2256: object classes of the entity'
75
+ end
76
+
77
+ it "knows it doesn't have a superior type" do
78
+ @attrtype.sup.should be_nil()
79
+ end
80
+
81
+ it "knows what the name of its equality matching rule is" do
82
+ @attrtype.eqmatch_oid.should == :objectIdentifierMatch
83
+ end
84
+
85
+ it "returns a matchingRule object from its schema for its equality matching rule" do
86
+ @schema.should_receive( :matching_rules ).
87
+ and_return({ :objectIdentifierMatch => :a_matching_rule })
88
+ @attrtype.equality_matching_rule.should == :a_matching_rule
89
+ end
90
+
91
+ it "doesn't have an order matchingRule" do
92
+ @attrtype.ordering_matching_rule.should be_nil()
93
+ end
94
+
95
+ it "returns a matchingRule object from its schema for its substring matching rule" do
96
+ @attrtype.substr_matching_rule.should be_nil()
97
+ end
98
+
99
+ it "knows that it is not obsolete" do
100
+ @attrtype.should_not be_obsolete()
101
+ end
102
+
103
+ it "knows what its syntax OID is" do
104
+ @attrtype.syntax_oid.should == '1.3.6.1.4.1.1466.115.121.1.38'
105
+ end
106
+
107
+ it "knows that its syntax length is not set" do
108
+ @attrtype.syntax_len.should be_nil()
109
+ end
110
+
111
+ it "returns an ldapSyntax object from its schema for its syntax" do
112
+ @schema.should_receive( :ldap_syntaxes ).
113
+ and_return({ '1.3.6.1.4.1.1466.115.121.1.38' => :the_syntax })
114
+ @attrtype.syntax.should == :the_syntax
115
+ end
116
+
117
+
118
+ end
119
+
120
+
121
+ describe "parsed from an attributeType that has a SUP attribute" do
122
+ DERIVED_ATTRTYPE = %{( 1.11.2.11.1 SUP aSuperType } +
123
+ %{SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )}
124
+
125
+ before( :each ) do
126
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, DERIVED_ATTRTYPE )
127
+ end
128
+
129
+ it "can fetch its superior type from its schema" do
130
+ @schema.should_receive( :attribute_types ).
131
+ and_return({ :aSuperType => :the_superior_type })
132
+ @attrtype.sup.should == :the_superior_type
133
+ end
134
+
135
+ it "returns a matchingRule object from its supertype's equality matching rule if it " +
136
+ "doesn't have one" do
137
+ supertype = mock( "superior attribute type object" )
138
+ @schema.should_receive( :attribute_types ).twice.
139
+ and_return({ :aSuperType => supertype })
140
+ supertype.should_receive( :equality_matching_rule ).and_return( :a_matching_rule )
141
+
142
+ @attrtype.equality_matching_rule.should == :a_matching_rule
143
+ end
144
+
145
+ it "returns a matchingRule object from its supertype's ordering matching rule if it " +
146
+ "doesn't have one" do
147
+ supertype = mock( "superior attribute type object" )
148
+ @schema.should_receive( :attribute_types ).twice.
149
+ and_return({ :aSuperType => supertype })
150
+ supertype.should_receive( :ordering_matching_rule ).and_return( :a_matching_rule )
151
+
152
+ @attrtype.ordering_matching_rule.should == :a_matching_rule
153
+ end
154
+
155
+ it "returns a matchingRule object from its supertype's substr matching rule if it " +
156
+ "doesn't have one" do
157
+ supertype = mock( "superior attribute type object" )
158
+ @schema.should_receive( :attribute_types ).twice.
159
+ and_return({ :aSuperType => supertype })
160
+ supertype.should_receive( :substr_matching_rule ).and_return( :a_matching_rule )
161
+
162
+ @attrtype.substr_matching_rule.should == :a_matching_rule
163
+ end
164
+
165
+ end
166
+
167
+
168
+ describe "parsed from an attributeType that has a SUP attribute but no SYNTAX" do
169
+ DERIVED_NOSYN_ATTRTYPE = %{( 1.11.2.11.1 SUP aSuperType )}
170
+
171
+ before( :each ) do
172
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, DERIVED_NOSYN_ATTRTYPE )
173
+ end
174
+
175
+ it "fetches its SYNTAX from its supertype" do
176
+ supertype = mock( "supertype object" )
177
+ @schema.should_receive( :attribute_types ).at_least( :once ).
178
+ and_return({ :aSuperType => supertype })
179
+ supertype.should_receive( :syntax ).and_return( :the_syntax )
180
+
181
+ @attrtype.syntax.should == :the_syntax
182
+ end
183
+
184
+ end
185
+
186
+ describe "parsed from an attributeType that has a list as the value of its NAME attribute" do
187
+
188
+ MULTINAME_ATTRIBUTETYPE = %{( 1.1.1.1 NAME ('firstname' 'secondname') )}
189
+
190
+ before( :each ) do
191
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, MULTINAME_ATTRIBUTETYPE )
192
+ end
193
+
194
+ it "knows what both names are" do
195
+ @attrtype.names.should have(2).members
196
+ @attrtype.names.should include( :firstname, :secondname )
197
+ end
198
+
199
+ it "returns the first of its names for the #name method" do
200
+ @attrtype.name.should == :firstname
201
+ end
202
+
203
+ end
204
+
205
+ describe "parsed from an attributeType that has escaped characters in its DESC attribute" do
206
+
207
+ ESCAPED_DESC_ATTRIBUTETYPE = %{( 1.1.1.1 DESC } +
208
+ %{'This spec\\27s example, which includes a \\5c character.' )}
209
+
210
+ before( :each ) do
211
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, ESCAPED_DESC_ATTRIBUTETYPE )
212
+ end
213
+
214
+ it "unscapes the escaped characters" do
215
+ @attrtype.desc.should == %{This spec's example, which includes a \\ character.}
216
+ end
217
+
218
+ end
219
+
220
+ describe "parsed from an attributeType that has the OBSOLETE attribute" do
221
+
222
+ OBSOLETE_ATTRIBUTETYPE = %{( 1.1.1.1 OBSOLETE )}
223
+
224
+ before( :each ) do
225
+ @attrtype = Treequel::Schema::AttributeType.parse( @schema, OBSOLETE_ATTRIBUTETYPE )
226
+ end
227
+
228
+ it "knows that it's obsolete" do
229
+ @attrtype.should be_obsolete()
230
+ end
231
+
232
+ end
233
+
234
+ end
235
+
236
+
237
+ # vim: set nosta noet ts=4 sw=4: