treequel 1.0.1 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/ChangeLog +176 -14
  2. data/LICENSE +1 -1
  3. data/Rakefile +61 -45
  4. data/Rakefile.local +20 -0
  5. data/bin/treequel +502 -269
  6. data/examples/ldap-rack-auth.rb +2 -0
  7. data/lib/treequel.rb +221 -18
  8. data/lib/treequel/branch.rb +410 -201
  9. data/lib/treequel/branchcollection.rb +25 -13
  10. data/lib/treequel/branchset.rb +42 -40
  11. data/lib/treequel/constants.rb +233 -3
  12. data/lib/treequel/control.rb +95 -0
  13. data/lib/treequel/controls/contentsync.rb +138 -0
  14. data/lib/treequel/controls/pagedresults.rb +162 -0
  15. data/lib/treequel/controls/sortedresults.rb +216 -0
  16. data/lib/treequel/directory.rb +212 -65
  17. data/lib/treequel/exceptions.rb +11 -12
  18. data/lib/treequel/filter.rb +1 -12
  19. data/lib/treequel/mixins.rb +83 -47
  20. data/lib/treequel/monkeypatches.rb +29 -0
  21. data/lib/treequel/schema.rb +23 -19
  22. data/lib/treequel/schema/attributetype.rb +33 -3
  23. data/lib/treequel/schema/ldapsyntax.rb +0 -11
  24. data/lib/treequel/schema/matchingrule.rb +0 -11
  25. data/lib/treequel/schema/matchingruleuse.rb +0 -11
  26. data/lib/treequel/schema/objectclass.rb +36 -10
  27. data/lib/treequel/schema/table.rb +159 -0
  28. data/lib/treequel/sequel_integration.rb +7 -7
  29. data/lib/treequel/utils.rb +4 -66
  30. data/rake/documentation.rb +89 -0
  31. data/rake/helpers.rb +375 -307
  32. data/rake/hg.rb +16 -2
  33. data/rake/manual.rb +11 -6
  34. data/rake/packaging.rb +20 -35
  35. data/rake/publishing.rb +22 -62
  36. data/spec/lib/constants.rb +20 -0
  37. data/spec/lib/control_behavior.rb +44 -0
  38. data/spec/lib/matchers.rb +51 -0
  39. data/spec/treequel/branch_spec.rb +88 -29
  40. data/spec/treequel/branchcollection_spec.rb +24 -1
  41. data/spec/treequel/branchset_spec.rb +123 -51
  42. data/spec/treequel/control_spec.rb +48 -0
  43. data/spec/treequel/controls/contentsync_spec.rb +38 -0
  44. data/spec/treequel/controls/pagedresults_spec.rb +138 -0
  45. data/spec/treequel/controls/sortedresults_spec.rb +171 -0
  46. data/spec/treequel/directory_spec.rb +186 -16
  47. data/spec/treequel/mixins_spec.rb +42 -3
  48. data/spec/treequel/schema/attributetype_spec.rb +22 -20
  49. data/spec/treequel/schema/objectclass_spec.rb +67 -46
  50. data/spec/treequel/schema/table_spec.rb +134 -0
  51. data/spec/treequel_spec.rb +277 -15
  52. metadata +89 -108
  53. data/bin/treequel.orig +0 -963
  54. data/examples/ldap-monitor.rb +0 -143
  55. data/examples/ldap-monitor/public/css/master.css +0 -328
  56. data/examples/ldap-monitor/public/images/card_small.png +0 -0
  57. data/examples/ldap-monitor/public/images/chain_small.png +0 -0
  58. data/examples/ldap-monitor/public/images/globe_small.png +0 -0
  59. data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
  60. data/examples/ldap-monitor/public/images/plug.png +0 -0
  61. data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
  62. data/examples/ldap-monitor/public/images/tick.png +0 -0
  63. data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
  64. data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
  65. data/examples/ldap-monitor/views/backends.erb +0 -41
  66. data/examples/ldap-monitor/views/connections.erb +0 -74
  67. data/examples/ldap-monitor/views/databases.erb +0 -39
  68. data/examples/ldap-monitor/views/dump_subsystem.erb +0 -14
  69. data/examples/ldap-monitor/views/index.erb +0 -14
  70. data/examples/ldap-monitor/views/layout.erb +0 -35
  71. data/examples/ldap-monitor/views/listeners.erb +0 -30
  72. data/rake/rdoc.rb +0 -30
  73. data/rake/win32.rb +0 -190
data/rake/hg.rb CHANGED
@@ -22,6 +22,8 @@ unless defined?( HG_DOTDIR )
22
22
  ###
23
23
 
24
24
  module MercurialHelpers
25
+ require './helpers.rb' unless defined?( RakefileHelpers )
26
+ include RakefileHelpers
25
27
 
26
28
  ###############
27
29
  module_function
@@ -83,7 +85,7 @@ unless defined?( HG_DOTDIR )
83
85
 
84
86
  ### Return the list of files which are of status 'unknown'
85
87
  def get_unknown_files
86
- list = read_command_output( 'hg', 'status', '-un', '--no-color' )
88
+ list = read_command_output( 'hg', 'status', '-un', '--color', 'never' )
87
89
  list = list.split( /\n/ )
88
90
 
89
91
  trace "New files: %p" % [ list ]
@@ -208,8 +210,20 @@ unless defined?( HG_DOTDIR )
208
210
  task :add => :newfiles
209
211
 
210
212
 
213
+ desc "Pull and update from the default repo"
214
+ task :pull do
215
+ paths = get_repo_paths()
216
+ if origin_url = paths['default']
217
+ ask_for_confirmation( "Pull and update from '#{origin_url}'?", false ) do
218
+ run 'hg', 'pull', '-u'
219
+ end
220
+ else
221
+ trace "Skipping pull: No 'default' path."
222
+ end
223
+ end
224
+
211
225
  desc "Check the current code in if tests pass"
212
- task :checkin => ['hg:newfiles', 'test', COMMIT_MSG_FILE] do
226
+ task :checkin => ['hg:pull', 'hg:newfiles', 'test', COMMIT_MSG_FILE] do
213
227
  targets = get_target_args()
214
228
  $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
215
229
  ask_for_confirmation( "Continue with checkin?" ) do
data/rake/manual.rb CHANGED
@@ -147,9 +147,14 @@ module Manual
147
147
 
148
148
  layout = self.config['layout'].sub( /\.page$/, '' )
149
149
  templatepath = @layouts_dir + "#{layout}.page"
150
- template = ERB.new( templatepath.read )
151
- page = self
150
+ template = nil
151
+ if Object.const_defined?( :Encoding )
152
+ template = ERB.new( templatepath.read(:encoding => 'UTF-8') )
153
+ else
154
+ template = ERB.new( templatepath.read )
155
+ end
152
156
 
157
+ page = self
153
158
  html = template.result( binding() )
154
159
 
155
160
  # Use Tidy to clean up the html if 'cleanup' is turned on, but remove the Tidy
@@ -565,7 +570,7 @@ module Manual
565
570
  manual_pages = setup_page_conversion_tasks( sourcedir, outputdir, catalog )
566
571
 
567
572
  desc "Build the manual"
568
- task :build => [ :rdoc, :copy_resources, :copy_apidocs, :generate_pages ]
573
+ task :build => [ :apidocs, :copy_resources, :copy_apidocs, :generate_pages ]
569
574
 
570
575
  task :clobber do
571
576
  RakeFileUtils.verbose( $verbose ) do
@@ -686,8 +691,8 @@ module Manual
686
691
  end
687
692
 
688
693
  desc "Copy API documentation to the manual output directory"
689
- task :copy_apidocs => :rdoc do
690
- cp_r( RDOCDIR, outputdir )
694
+ task :copy_apidocs => :apidocs do
695
+ cp_r( API_DOCSDIR, outputdir )
691
696
  end
692
697
 
693
698
  # Now group all the resource file tasks into a containing task
@@ -713,7 +718,7 @@ if MANUALDIR.exist?
713
718
 
714
719
  Manual::GenTask.new do |manual|
715
720
  manual.metadata.version = PKG_VERSION
716
- manual.metadata.api_dir = RDOCDIR
721
+ manual.metadata.api_dir = API_DOCSDIR
717
722
  manual.output_dir = MANUALOUTPUTDIR
718
723
  manual.base_dir = MANUALDIR
719
724
  manual.source_dir = 'src'
data/rake/packaging.rb CHANGED
@@ -1,17 +1,27 @@
1
- #
2
- # Packaging Rake Tasks
3
-
4
- #
1
+ #####################################################################
2
+ ### P A C K A G I N G T A S K S
3
+ #####################################################################
5
4
 
6
5
  require 'rbconfig'
7
6
  require 'pathname'
8
- require 'rake/packagetask'
9
- require 'rake/gempackagetask'
7
+ require 'rubygems/package_task'
10
8
 
11
- require Pathname( __FILE__ ).dirname.expand_path + 'hg.rb'
12
9
 
13
10
  include Config
14
11
 
12
+ ### Task: prerelease
13
+ desc "Append the package build number to package versions"
14
+ task :prerelease do
15
+ GEMSPEC.version.version << ".#{PKG_BUILD}"
16
+ Rake::Task[:gem].clear
17
+
18
+ Gem::PackageTask.new( GEMSPEC ) do |pkg|
19
+ pkg.need_zip = true
20
+ pkg.need_tar = true
21
+ end
22
+ end
23
+
24
+
15
25
  ### Task: gem
16
26
  ### Task: package
17
27
  Rake::PackageTask.new( PKG_NAME, PKG_VERSION ) do |task|
@@ -24,34 +34,9 @@ end
24
34
  task :package => [:gem]
25
35
 
26
36
 
27
- ### Task: gem
28
- gempath = PKGDIR + GEM_FILE_NAME
29
-
30
- desc "Build a RubyGem package (#{GEM_FILE_NAME})"
31
- task :gem => gempath.to_s
32
- file gempath.to_s => [PKGDIR.to_s] + GEMSPEC.files do
33
- when_writing( "Creating GEM" ) do
34
- Gem::Builder.new( GEMSPEC ).build
35
- verbose( true ) do
36
- mv GEM_FILE_NAME, gempath
37
- end
38
- end
39
- end
40
-
41
-
42
- prerelease_gempath = PKGDIR + SNAPSHOT_GEM_NAME
43
-
44
- desc "Build a pre-release RubyGem package"
45
- task :prerelease_gem => prerelease_gempath.to_s
46
- file prerelease_gempath.to_s => [PKGDIR.to_s] + GEMSPEC.files do
47
- when_writing( "Creating prerelease GEM" ) do
48
- gemspec = GEMSPEC.clone
49
- gemspec.version = Gem::Version.create( "%s.%s" % [GEMSPEC.version, PKG_BUILD] )
50
- Gem::Builder.new( gemspec ).build
51
- verbose( true ) do
52
- mv SNAPSHOT_GEM_NAME, prerelease_gempath
53
- end
54
- end
37
+ Gem::PackageTask.new( GEMSPEC ) do |pkg|
38
+ pkg.need_zip = true
39
+ pkg.need_tar = true
55
40
  end
56
41
 
57
42
 
data/rake/publishing.rb CHANGED
@@ -34,7 +34,13 @@ class Net::SMTP
34
34
 
35
35
  def do_ssl_start( helodomain, user, secret, authtype )
36
36
  raise IOError, 'SMTP session already started' if @started
37
- check_auth_args user, secret if user or secret
37
+ if user or secret
38
+ if self.method( :check_auth_args ).arity == 3
39
+ check_auth_args( user, secret, authtype )
40
+ else
41
+ check_auth_args( user, secret )
42
+ end
43
+ end
38
44
 
39
45
  # Open the connection
40
46
  @debug_output << "opening connection to #{@address}...\n" if @debug_output
@@ -85,7 +91,6 @@ begin
85
91
  require 'tmail'
86
92
  require 'net/smtp'
87
93
  require 'etc'
88
- require 'rubyforge'
89
94
  require 'socket'
90
95
  require 'text/format'
91
96
 
@@ -134,11 +139,11 @@ begin
134
139
  task :project => :upload # the old name
135
140
 
136
141
  desc "Publish the project docs to #{PROJECT_HOST}"
137
- task :upload_docs => [ :rdoc ] do
142
+ task :upload_docs => [ :apidocs ] do
138
143
  when_writing( "Publishing docs to #{PROJECT_SCPDOCURL}" ) do
139
144
  log "Uploading API documentation to %s:%s" % [ PROJECT_HOST, PROJECT_DOCDIR ]
140
145
  run 'ssh', PROJECT_HOST, "rm -rf #{PROJECT_DOCDIR}"
141
- run 'scp', '-qCr', RDOCDIR, PROJECT_SCPDOCURL
146
+ run 'scp', '-qCr', API_DOCSDIR, PROJECT_SCPDOCURL
142
147
  end
143
148
  end
144
149
 
@@ -195,15 +200,20 @@ begin
195
200
  desc 'Send out a release announcement'
196
201
  task :announce => [RELEASE_ANNOUNCE_FILE] do
197
202
  email = TMail::Mail.new
198
- if $publish_privately
203
+
204
+ if $publish_privately || RELEASE_ANNOUNCE_ADDRESSES.empty?
199
205
  trace "Sending private announce mail"
200
206
  email.to = 'rubymage@gmail.com'
201
207
  else
202
208
  trace "Sending public announce mail"
203
- email.to = 'Ruby-Talk List <ruby-talk@ruby-lang.org>'
209
+ email.to = RELEASE_ANNOUNCE_ADDRESSES
204
210
  email.bcc = 'rubymage@gmail.com'
205
211
  end
206
- email.from = GEMSPEC.email
212
+
213
+ from = prompt_with_default( "Send announcement as:",
214
+ 'Michael Granger <ged@FaerieMUD.org>' ) or fail
215
+
216
+ email.from = from
207
217
  email.subject = "[ANN] #{PKG_NAME} #{PKG_VERSION}"
208
218
  email.body = File.read( RELEASE_ANNOUNCE_FILE )
209
219
  email.date = Time.new
@@ -236,61 +246,11 @@ begin
236
246
  end
237
247
 
238
248
 
239
- desc 'Publish the new release to RubyForge'
240
- task :publish => [:clean, :package, :notes] do |task|
241
- project = RUBYFORGE_PROJECT
242
-
243
- if $publish_privately
244
- log "Skipping push of release files to RubyForge"
245
- else
246
- rf = RubyForge.new
247
- log "Loading RubyForge config"
248
- rf.configure
249
-
250
- group_id = rf.autoconfig['group_ids'][RUBYFORGE_GROUP] or
251
- fail "Your configuration doesn't have a group id for '#{RUBYFORGE_GROUP}'"
252
-
253
- # If this project doesn't yet exist, create it
254
- unless rf.autoconfig['package_ids'].key?( project )
255
- ask_for_confirmation( "Package '#{project}' doesn't exist on RubyForge. Create it?" ) do
256
- log "Creating new package '#{project}'"
257
- rf.create_package( group_id, project )
258
- end
259
- end
260
-
261
- package_id = rf.autoconfig['package_ids'][ project ]
262
-
263
- # Make sure this release doesn't already exist
264
- releases = rf.autoconfig['release_ids']
265
- if releases.key?( GEMSPEC.name ) && releases[ GEMSPEC.name ].key?( PKG_VERSION )
266
- log "Rubyforge seems to already have #{ PKG_FILE_NAME }"
267
- else
268
- config = rf.userconfig or
269
- fail "You apparently haven't set up your RubyForge credentials on this machine."
270
- config['release_notes'] = GEMSPEC.description
271
- config['release_changes'] = File.read( RELEASE_NOTES_FILE )
272
-
273
- files = FileList[ PKGDIR + GEM_FILE_NAME ]
274
- files.include PKGDIR + "#{PKG_FILE_NAME}.tar.gz"
275
- files.include PKGDIR + "#{PKG_FILE_NAME}.tar.bz2"
276
- files.include PKGDIR + "#{PKG_FILE_NAME}.zip"
277
-
278
- log "Releasing #{PKG_FILE_NAME}"
279
- when_writing do
280
- log "Publishing to RubyForge: \n",
281
- "\tproject: #{RUBYFORGE_GROUP}\n",
282
- "\tpackage: #{PKG_NAME.downcase}\n",
283
- "\tpackage version: #{PKG_VERSION}\n",
284
- "\tfiles: " + files.collect {|f| f.to_s }.join(', ') + "\n"
285
-
286
- ask_for_confirmation( "Publish to RubyForge?" ) do
287
- log 'Logging in...'
288
- rf.login
289
- log "Adding the new release to the '#{project}' project"
290
- rf.add_release( group_id, package_id, PKG_VERSION, *files )
291
- end
292
- end
293
- end
249
+ desc 'Publish the new release to Gemcutter'
250
+ task :publish => [:clean, :gem, :notes] do |task|
251
+ ask_for_confirmation( "Publish #{GEM_FILE_NAME} to Gemcutter?", false ) do
252
+ gempath = PKGDIR + GEM_FILE_NAME
253
+ sh 'gem', 'push', gempath
294
254
  end
295
255
  end
296
256
  end
@@ -68,6 +68,11 @@ module Treequel::TestConstants # :nodoc:all
68
68
  TEST_SUBHOSTS_RDN = "#{TEST_HOSTS_DN_ATTR}=#{TEST_HOSTS_DN_VALUE}"
69
69
  TEST_SUBHOSTS_DN = "#{TEST_HOSTS_RDN},#{TEST_SUBDOMAIN_DN}"
70
70
 
71
+ TEST_SUBHOST_DN_ATTR = 'cn'
72
+ TEST_SUBHOST_DN_VALUE = 'ronky'
73
+ TEST_SUBHOST_RDN = "#{TEST_SUBHOST_DN_ATTR}=#{TEST_SUBHOST_DN_VALUE}"
74
+ TEST_SUBHOST_DN = "#{TEST_SUBHOST_RDN},#{TEST_SUBHOSTS_DN}"
75
+
71
76
  TEST_PEOPLE_DN_ATTR = 'ou'
72
77
  TEST_PEOPLE_DN_VALUE = 'People'
73
78
  TEST_PEOPLE_RDN = "#{TEST_PEOPLE_DN_ATTR}=#{TEST_PEOPLE_DN_VALUE}"
@@ -83,6 +88,21 @@ module Treequel::TestConstants # :nodoc:all
83
88
  TEST_PERSON2_RDN = "#{TEST_PERSON2_DN_ATTR}=#{TEST_PERSON2_DN_VALUE}"
84
89
  TEST_PERSON2_DN = "#{TEST_PERSON2_RDN},#{TEST_PEOPLE_DN}"
85
90
 
91
+ TEST_PHONES_DN_ATTR = 'ou'
92
+ TEST_PHONES_DN_VALUE = 'Phones'
93
+ TEST_PHONES_RDN = "#{TEST_PHONES_DN_ATTR}=#{TEST_PHONES_DN_VALUE}"
94
+ TEST_PHONES_DN = "#{TEST_PHONES_RDN},#{TEST_BASE_DN}"
95
+
96
+ TEST_ROOMS_DN_ATTR = 'ou'
97
+ TEST_ROOMS_DN_VALUE = 'Rooms'
98
+ TEST_ROOMS_RDN = "#{TEST_ROOMS_DN_ATTR}=#{TEST_ROOMS_DN_VALUE}"
99
+ TEST_ROOMS_DN = "#{TEST_ROOMS_RDN},#{TEST_BASE_DN}"
100
+
101
+ TEST_ROOM_DN_ATTR = 'cn'
102
+ TEST_ROOM_DN_VALUE = 'broomcloset'
103
+ TEST_ROOM_RDN = "#{TEST_ROOM_DN_ATTR}=#{TEST_ROOM_DN_VALUE}"
104
+ TEST_ROOM_DN = "#{TEST_ROOM_RDN},#{TEST_ROOMS_DN}"
105
+
86
106
  constants.each do |cname|
87
107
  const_get(cname).freeze
88
108
  end
@@ -0,0 +1,44 @@
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 'spec'
13
+ require 'spec/lib/constants'
14
+ require 'spec/lib/helpers'
15
+
16
+ require 'treequel'
17
+ require 'treequel/control'
18
+
19
+ include Treequel::TestConstants
20
+ include Treequel::Constants
21
+
22
+ #####################################################################
23
+ ### C O N T E X T S
24
+ #####################################################################
25
+ describe "A Treequel::Control", :shared => true do
26
+ include Treequel::SpecHelpers
27
+
28
+ before( :each ) do
29
+ raise "Spec doesn't set @control before the Control shared behavior" unless @control
30
+ end
31
+
32
+ it "implements one of either #get_client_controls or #get_server_controls" do
33
+ methods = [
34
+ 'get_client_controls', # 1.8.x
35
+ 'get_server_controls',
36
+ :get_client_controls, # 1.9.x
37
+ :get_server_controls
38
+ ]
39
+ (@control.instance_methods( false ) | methods).should_not be_empty()
40
+ end
41
+
42
+ end
43
+
44
+
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname.new( __FILE__ ).dirname.parent
7
+
8
+ libdir = basedir + "lib"
9
+
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'yaml'
14
+ require 'treequel'
15
+
16
+ require 'spec/lib/constants'
17
+
18
+ ### RSpec matchers
19
+ module Treequel::Matchers
20
+
21
+ class ArrayIncludingMatcher
22
+ def initialize( expected )
23
+ @expected = expected
24
+ end
25
+
26
+ def ==( actual )
27
+ @expected.each do |value|
28
+ return false unless actual.include?( value )
29
+ end
30
+ true
31
+ rescue NoMethodError => ex
32
+ return false
33
+ end
34
+
35
+ def description
36
+ "array_including(#{ @expected.inspect.sub(/^\[|\]$/,"") })"
37
+ end
38
+ end
39
+
40
+
41
+ ###############
42
+ module_function
43
+ ###############
44
+
45
+ ### Return true if the actual value includes the specified +objects+.
46
+ def array_including( *objects )
47
+ ArrayIncludingMatcher.new( objects )
48
+ end
49
+
50
+ end # module Treequel::Matchers
51
+
@@ -9,22 +9,14 @@ BEGIN {
9
9
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
10
  }
11
11
 
12
- begin
13
- require 'spec'
14
- require 'spec/lib/constants'
15
- require 'spec/lib/helpers'
16
-
17
- require 'treequel/branch'
18
- require 'treequel/branchset'
19
- require 'treequel/branchcollection'
20
- rescue LoadError
21
- unless Object.const_defined?( :Gem )
22
- require 'rubygems'
23
- retry
24
- end
25
- raise
26
- end
12
+ require 'spec'
13
+ require 'spec/lib/constants'
14
+ require 'spec/lib/helpers'
15
+ require 'spec/lib/matchers'
27
16
 
17
+ require 'treequel/branch'
18
+ require 'treequel/branchset'
19
+ require 'treequel/branchcollection'
28
20
 
29
21
  include Treequel::TestConstants
30
22
  include Treequel::Constants
@@ -34,7 +26,8 @@ include Treequel::Constants
34
26
  #####################################################################
35
27
 
36
28
  describe Treequel::Branch do
37
- include Treequel::SpecHelpers
29
+ include Treequel::SpecHelpers,
30
+ Treequel::Matchers
38
31
 
39
32
  before( :all ) do
40
33
  setup_logging( :fatal )
@@ -150,6 +143,22 @@ describe Treequel::Branch do
150
143
  @branch.entry.should == :the_extended_entry
151
144
  end
152
145
 
146
+ it "can search its directory for values using itself as a base" do
147
+ @directory.should_receive( :search ).with( @branch, :one, '(objectClass=*)', {} ).
148
+ and_return( :entries )
149
+ @branch.search( :one, '(objectClass=*)' ).should == :entries
150
+ end
151
+
152
+ it "can search its directory for values with a block" do
153
+ @directory.should_receive( :search ).with( @branch, :one, '(objectClass=*)', {} ).
154
+ and_yield( :an_entry )
155
+ yielded_val = nil
156
+ @branch.search( :one, '(objectClass=*)' ) do |val|
157
+ yielded_val = val
158
+ end
159
+ yielded_val.should == :an_entry
160
+ end
161
+
153
162
  it "clears any cached values if its include_operational_attrs attribute is changed" do
154
163
  @directory.should_receive( :get_entry ).with( @branch ).exactly( :once ).
155
164
  and_return( :the_entry )
@@ -201,13 +210,20 @@ describe Treequel::Branch do
201
210
 
202
211
  lambda {
203
212
  @branch.facelart( 'sbc' )
204
- }.should raise_error( NoMethodError )
213
+ }.should raise_exception( NoMethodError, /undefined method.*facelart/i )
205
214
  end
206
215
 
216
+ it "don't create sub-branches for multi-value RDNs with an invalid attribute" do
217
+ @schema.should_receive( :attribute_types ).
218
+ and_return({ :cn => :a_value, :ou => :a_value })
219
+
220
+ lambda {
221
+ @branch.cn( 'benchlicker', :facelart => 'sbc' )
222
+ }.should raise_exception( NoMethodError, /invalid secondary attribute.*facelart/i )
223
+ end
207
224
 
208
225
  it "can return all of its immediate children as Branches" do
209
- @directory.should_receive( :search ).
210
- with( @branch, :one, '(objectClass=*)' ).
226
+ @directory.should_receive( :search ).with( @branch, :one, '(objectClass=*)', {} ).
211
227
  and_return([ :the_children ])
212
228
  @branch.children.should == [ :the_children ]
213
229
  end
@@ -350,13 +366,14 @@ describe Treequel::Branch do
350
366
  end
351
367
 
352
368
  it "knows if a attribute is valid given its objectClasses" do
353
- attrs = mock( "Attribute list", :null_object => true )
369
+ attrtype = mock( "attribute type object" )
354
370
 
355
371
  @branch.should_receive( :valid_attribute_types ).
356
372
  twice.
357
- and_return([ attrs ])
373
+ and_return([ attrtype ])
358
374
 
359
- attrs.should_receive( :names ).twice.and_return([ :cn, :l, :uid ])
375
+ attrtype.should_receive( :valid_name? ).with( :uid ).and_return( true )
376
+ attrtype.should_receive( :valid_name? ).with( :rubberChicken ).and_return( false )
360
377
 
361
378
  @branch.valid_attribute?( :uid ).should be_true()
362
379
  @branch.valid_attribute?( :rubberChicken ).should be_false()
@@ -364,7 +381,7 @@ describe Treequel::Branch do
364
381
 
365
382
  it "can be moved to a new location within the directory" do
366
383
  newdn = "ou=hosts,dc=admin,#{TEST_BASE_DN}"
367
- @directory.should_receive( :move ).with( @branch, newdn, {} )
384
+ @directory.should_receive( :move ).with( @branch, newdn )
368
385
  @branch.move( newdn )
369
386
  end
370
387
 
@@ -379,12 +396,6 @@ describe Treequel::Branch do
379
396
  end
380
397
 
381
398
 
382
- it "can be deleted from the directory" do
383
- @directory.should_receive( :delete ).with( @branch )
384
- @branch.delete
385
- end
386
-
387
-
388
399
  it "can create children under itself" do
389
400
  newattrs = {
390
401
  :ipHostNumber => '127.0.0.1',
@@ -436,6 +447,46 @@ describe Treequel::Branch do
436
447
  end
437
448
 
438
449
 
450
+ it "can delete all values of one of its entry's individual attributes" do
451
+ LDAP::Mod.should_receive( :new ).with( LDAP::LDAP_MOD_DELETE, 'displayName', [] ).
452
+ and_return( :mod_delete )
453
+ @directory.should_receive( :modify ).with( @branch, [:mod_delete] )
454
+
455
+ @branch.delete( :displayName )
456
+ end
457
+
458
+ it "can delete all values of more than one of its entry's individual attributes" do
459
+ LDAP::Mod.should_receive( :new ).with( LDAP::LDAP_MOD_DELETE, 'displayName', [] ).
460
+ and_return( :first_mod_delete )
461
+ LDAP::Mod.should_receive( :new ).with( LDAP::LDAP_MOD_DELETE, 'gecos', [] ).
462
+ and_return( :second_mod_delete )
463
+ @directory.should_receive( :modify ).
464
+ with( @branch, [:first_mod_delete, :second_mod_delete] )
465
+
466
+ @branch.delete( :displayName, :gecos )
467
+ end
468
+
469
+ it "can delete one particular value of its entry's individual attributes" do
470
+ LDAP::Mod.should_receive( :new ).
471
+ with( LDAP::LDAP_MOD_DELETE, 'objectClass', ['apple-user'] ).
472
+ and_return( :mod_delete )
473
+ @directory.should_receive( :modify ).with( @branch, [:mod_delete] )
474
+
475
+ @branch.delete( :objectClass => 'apple-user' )
476
+ end
477
+
478
+ it "can delete particular values of more than one of its entry's individual attributes" do
479
+ LDAP::Mod.should_receive( :new ).
480
+ with( LDAP::LDAP_MOD_DELETE, 'objectClass', ['apple-user', 'inetOrgPerson'] ).
481
+ and_return( :first_mod_delete )
482
+ LDAP::Mod.should_receive( :new ).
483
+ with( LDAP::LDAP_MOD_DELETE, 'cn', [] ).and_return( :second_mod_delete )
484
+ @directory.should_receive( :modify ).
485
+ with( @branch, array_including(:first_mod_delete, :second_mod_delete) )
486
+
487
+ @branch.delete( :objectClass => ['apple-user',:inetOrgPerson], :cn => [] )
488
+ end
489
+
439
490
  it "knows how to represent its DN as an RFC1781-style UFN" do
440
491
  @branch.to_ufn.should =~ /Hosts, acme\.com/i
441
492
  end
@@ -561,6 +612,14 @@ describe Treequel::Branch do
561
612
  end
562
613
  end
563
614
 
615
+ it "can fetch multiple values via #values_at" do
616
+ @branch.should_receive( :[] ).with( :cn ).and_return( :cn_value )
617
+ @branch.should_receive( :[] ).with( :desc ).and_return( :desc_value )
618
+ @branch.should_receive( :[] ).with( :l ).and_return( :l_value )
619
+
620
+ @branch.values_at( :cn, :desc, :l ).should == [ :cn_value, :desc_value, :l_value ]
621
+ end
622
+
564
623
  end
565
624
 
566
625
  end