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,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: