treequel 1.3.0pre384 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,8 +1,53 @@
1
+ 2011-01-12 Michael Granger <ged@FaerieMUD.org>
2
+
3
+ * lib/treequel/model.rb:
4
+ Always initialize the dirty flag for Treequel::Model objects.
5
+ [4a75d892a89d] [github/master, master, tip]
6
+
7
+ * lib/treequel/branch.rb, spec/treequel/branch_spec.rb:
8
+ Fixed a bug in Treequel::Branch that caused non-schema attribute
9
+ fetches through the index operator to pollute the values cache,
10
+ which in turn caused Treequel::Model schema validations to fail.
11
+ [8cee4f66c05d]
12
+
13
+ * manual/src/models.page:
14
+ More work on the manual
15
+ [1652fd0874ab]
16
+
17
+ * lib/treequel/model.rb, spec/treequel/model_spec.rb:
18
+ Made Treequel::Model#validate run the
19
+ before_validation/after_validation hooks
20
+ [55d6b5f34c2b]
21
+
22
+ 2011-01-11 Michael Granger <ged@FaerieMUD.org>
23
+
24
+ * lib/treequel/exceptions.rb, lib/treequel/model.rb,
25
+ manual/src/models.page, spec/treequel/model_spec.rb:
26
+ Worked on model hooks and validations; added specs, raise exceptions
27
+ by default, started adding info to the manual.
28
+ [2454e05f1302]
29
+
30
+ * lib/treequel/model/errors.rb,
31
+ lib/treequel/model/schemavalidations.rb,
32
+ spec/treequel/model/errors_spec.rb,
33
+ spec/treequel/model/schemavalidations_spec.rb:
34
+ Adding structural-objectClass validation
35
+ [1b9bd6587cf0]
36
+
37
+ * Rakefile:
38
+ Add sysexits to the list of dev dependencies
39
+ [54e20f994ef4]
40
+
41
+ * Manifest.txt:
42
+ Rearranged the release manifest a bit to make it easier to check
43
+ against the source in TextMate
44
+ [fd358623589a]
45
+
1
46
  2011-01-10 Michael Granger <ged@FaerieMUD.org>
2
47
 
3
48
  * Rakefile:
4
49
  Cleaning up dependencies, adding a note to the post-install
5
- [633dfb8ea77a] [github/master, master, tip]
50
+ [633dfb8ea77a]
6
51
 
7
52
  * Manifest.txt:
8
53
  Adding the ChangeLog to the release manifest
data/History.md CHANGED
@@ -1,4 +1,4 @@
1
- ## 1.3.0 [2011-01-07] Michael Granger <ged@FaerieMUD.org>
1
+ ## 1.3.0 [2011-01-13] Michael Granger <ged@FaerieMUD.org>
2
2
 
3
3
  Enhancements:
4
4
 
@@ -12,6 +12,17 @@ Enhancements:
12
12
  - Treequel::Model#errors
13
13
  - Treequel::Model#revert
14
14
  - Treequel::Model#modified?
15
+ - Treequel::Model#after_initialize
16
+ - Treequel::Model#before_validation
17
+ - Treequel::Model#after_validation
18
+ - Treequel::Model#before_save
19
+ - Treequel::Model#before_create
20
+ - Treequel::Model#before_update
21
+ - Treequel::Model#after_create
22
+ - Treequel::Model#after_update
23
+ - Treequel::Model#after_save
24
+ - Treequel::Model#before_destroy
25
+ - Treequel::Model#after_destroy
15
26
  New classes:
16
27
  - Treequel::Model::Errors
17
28
  - Treequel::ValidationFailed
@@ -36,6 +47,7 @@ Bugfixes:
36
47
  - Check for explicit nil DN in .new
37
48
  - Check for nil parent_dn in #parent
38
49
  - Use 'top' instead of :top as objectClass default
50
+ - Don't cache attempts to fetch invalid attributes
39
51
 
40
52
 
41
53
  ## 1.2.2 [2010-12-14] Michael Granger <ged@FaerieMUD.org>
data/Rakefile CHANGED
@@ -24,6 +24,7 @@ hoespec = Hoe.spec 'treequel' do
24
24
  'ruby-termios' => '~> 0.9.6',
25
25
  'ruby-terminfo' => '~> 0.1.1',
26
26
  'columnize' => '~> 0.3.1',
27
+ 'sysexits' => '~> 1.0.2',
27
28
  }
28
29
 
29
30
  self.spec_extras[:licenses] = ["BSD"]
@@ -320,7 +320,7 @@ class Treequel::Branch
320
320
  value.freeze if
321
321
  self.class.freeze_converted_values? &&
322
322
  value.respond_to?( :freeze )
323
- @values[ attrsym ] = value
323
+ @values[ attrsym ] = value if value
324
324
  end
325
325
 
326
326
  return @values[ attrsym ]
@@ -40,9 +40,34 @@ module Treequel
40
40
  end
41
41
  end
42
42
 
43
+ ######
44
+ public
45
+ ######
46
+
43
47
  # @return [Treequel::Model::Errors] the validation errors
44
48
  attr_reader :errors
45
- end
49
+
50
+ end # class ValidationFailed
51
+
52
+ ### Exception class raised when a before_* hooks returns a false value when saving
53
+ ### or destroying a Treequel::Model object.
54
+ class BeforeHookFailed < Treequel::ModelError
55
+
56
+ ### Create a new Treequel::BeforeHookFailed exception that indicates that the
57
+ ### specified +hook+ returned a false value.
58
+ def initialize( hook )
59
+ @hook = hook.to_sym
60
+ super "The 'before' hook failed when trying to %s" % [ hook ]
61
+ end
62
+
63
+ ######
64
+ public
65
+ ######
66
+
67
+ # The hook that failed
68
+ attr_reader :hook
69
+
70
+ end # class BeforeHookFailed
46
71
 
47
72
  end # module Treequel
48
73
 
@@ -47,11 +47,11 @@ class Treequel::Model::Errors < ::Hash
47
47
  end
48
48
 
49
49
 
50
- ### Adds an error for the given +attribute+.
51
- ### @param [Symbol, #to_sym] attribute the attribute with an error
50
+ ### Adds an error for the given +subject+.
51
+ ### @param [Symbol, #to_sym] subject the subject of the error
52
52
  ### @param [String] message the description of the error condition
53
- def add( attribute, message )
54
- self[ attribute ] << message
53
+ def add( subject, message )
54
+ self[ subject ] << message
55
55
  end
56
56
 
57
57
 
@@ -12,12 +12,21 @@ module Treequel::Model::SchemaValidations
12
12
  def validate( options={} )
13
13
  return unless options[:with_schema]
14
14
 
15
+ self.validate_structural_objectclass
15
16
  self.validate_must_attributes
16
17
  self.validate_may_attributes
17
18
  self.validate_attribute_syntax
18
19
  end
19
20
 
20
21
 
22
+ ### Ensure that the object has at least one structural objectClass.
23
+ def validate_structural_objectclass
24
+ unless self.object_classes.any? {|oc| oc.structural? }
25
+ self.errors.add( :entry, "must have at least one structural objectClass" )
26
+ end
27
+ end
28
+
29
+
21
30
  ### Validate that all attributes that MUST be included according to the entry's
22
31
  ### objectClasses have at least one value.
23
32
  def validate_must_attributes
@@ -53,6 +53,16 @@ class Treequel::Model < Treequel::Branch
53
53
  :with_schema => true,
54
54
  }
55
55
 
56
+ # Defaults for #save options
57
+ DEFAULT_SAVE_OPTIONS = {
58
+ :raise_on_failure => true,
59
+ }
60
+
61
+ # Defaults for #destroy options
62
+ DEFAULT_DESTROY_OPTIONS = {
63
+ :raise_on_failure => true,
64
+ }
65
+
56
66
 
57
67
  #################################################################
58
68
  ### C L A S S M E T H O D S
@@ -191,6 +201,7 @@ class Treequel::Model < Treequel::Branch
191
201
  def initialize( directory, dn, entry=nil, from_directory=false )
192
202
  if from_directory
193
203
  super( directory, dn, entry )
204
+ @dirty = false
194
205
  else
195
206
  super( directory, dn )
196
207
  @values = entry ? symbolify_keys( entry ) : {}
@@ -218,6 +229,7 @@ class Treequel::Model < Treequel::Branch
218
229
  HOOKS.each do |hook|
219
230
  define_method( hook ) do |*args|
220
231
  self.log.debug "#{hook} default hook called."
232
+ true
221
233
  end
222
234
  end
223
235
 
@@ -321,31 +333,36 @@ class Treequel::Model < Treequel::Branch
321
333
  ### Return +true+ if the model object passes all of its validations.
322
334
  def valid?( opts={} )
323
335
  self.errors.clear
324
-
325
- return false if self.before_validation == false
326
- self.validate
327
- self.after_validation
328
-
336
+ self.validate( opts )
329
337
  return self.errors.empty? ? true : false
330
338
  end
331
339
 
332
340
 
333
- ### Validate the object with the specified +options+.
341
+ ### Validate the object with the specified +options+. Appending validation errors onto
342
+ ### the #errors object.
334
343
  ### @param [Hash] options options for validation.
335
344
  ### @option options [Boolean] :with_schema whether or not to run the schema validations
336
345
  def validate( options={} )
337
346
  options = DEFAULT_VALIDATION_OPTIONS.merge( options )
338
347
 
348
+ self.before_validation or
349
+ raise Treequel::BeforeHookFailed, :validation
339
350
  self.errors.add( :objectClass, 'must have at least one' ) if self.object_classes.empty?
340
351
 
341
352
  super( options )
353
+ self.after_validation
342
354
  end
343
355
 
344
356
 
345
357
  ### Write any pending changes in the model object to the directory.
346
- def save
358
+ ### @param [Hash] opts save options
359
+ ### @option opts [Boolean] :raise_on_failure (true) raise a Treequel::ValidationFailed or
360
+ ### Treequel::BeforeHookFailed if either the validations or before_{save,create}
361
+ def save( opts={} )
362
+ opts = DEFAULT_SAVE_OPTIONS.merge( opts )
363
+
347
364
  self.log.debug "Saving %s..." % [ self.dn ]
348
- raise Treequel::ValidationFailed, self.errors unless self.valid?
365
+ raise Treequel::ValidationFailed, self.errors unless self.valid?( opts )
349
366
  self.log.debug " validation succeeded."
350
367
 
351
368
  unless mods = self.modifications
@@ -354,24 +371,25 @@ class Treequel::Model < Treequel::Branch
354
371
  end
355
372
 
356
373
  self.log.debug " got %d modifications." % [ mods.length ]
357
- self.before_save( mods ) or return nil
374
+ self.before_save( mods ) or
375
+ raise Treequel::BeforeHookFailed, :save
358
376
 
359
377
  if self.exists?
360
- self.log.debug " entry already exists: updating..."
361
- self.before_update( mods ) or return nil
362
- self.modify( mods )
363
- self.after_update( mods )
364
-
378
+ self.update( mods )
365
379
  else
366
- self.log.debug " entry doesn't exist: creating..."
367
- self.before_create( mods ) or return nil
368
380
  self.create( mods )
369
- self.after_create( mods )
370
381
  end
371
382
 
372
383
  self.after_save( mods )
373
384
 
374
385
  return true
386
+ rescue Treequel::BeforeHookFailed => err
387
+ self.log.info( err.message )
388
+ raise if opts[:raise_on_failure]
389
+ rescue Treequel::ValidationFailed => err
390
+ self.log.error( "Save aborted: validation failed." )
391
+ self.log.info( err.errors.full_messages.join(', ') )
392
+ raise if opts[:raise_on_failure]
375
393
  end
376
394
 
377
395
 
@@ -437,6 +455,21 @@ class Treequel::Model < Treequel::Branch
437
455
  end
438
456
 
439
457
 
458
+ ### Like #delete, but runs destroy hooks before and after deleting.
459
+ def destroy( opts={} )
460
+ opts = DEFAULT_DESTROY_OPTIONS.merge( opts )
461
+
462
+ self.before_destroy or raise Treequel::BeforeHookFailed, :destroy
463
+ self.delete
464
+ self.after_destroy
465
+
466
+ return true
467
+ rescue Treequel::BeforeHookFailed => err
468
+ self.log.info( err.message )
469
+ raise if opts[:raise_on_failure]
470
+ end
471
+
472
+
440
473
  ### Override Branch#search to inject the 'objectClass' attribute to the
441
474
  ### selected attribute list if there is one.
442
475
  def search( scope=:subtree, filter='(objectClass=*)', parameters={}, &block )
@@ -493,6 +526,28 @@ class Treequel::Model < Treequel::Branch
493
526
  end
494
527
 
495
528
 
529
+ ### Update the object's entry with the specified +mods+.
530
+ ### @param [Array<LDAP::Mod>] mods the modifications to make
531
+ def update( mods )
532
+ self.log.debug " entry already exists: updating..."
533
+ self.before_update( mods ) or
534
+ raise Treequel::BeforeHookFailed, :update
535
+ self.modify( mods )
536
+ self.after_update( mods )
537
+ end
538
+
539
+
540
+ ### Create the entry for the object, using the specified +mods+ to set the attributes.
541
+ ### @param [Array<LDAP::Mod>] mods the modifications to set attributes
542
+ def create( mods )
543
+ self.log.debug " entry doesn't exist: creating..."
544
+ self.before_create( mods ) or
545
+ raise Treequel::BeforeHookFailed, :create
546
+ super( mods )
547
+ self.after_create( mods )
548
+ end
549
+
550
+
496
551
  ### Delete specific key/value +pairs+ from the entry.
497
552
  ### @param [Hash] pairs key/value pairs to delete from the entry.
498
553
  def delete_specific_values( pairs )
@@ -801,6 +801,15 @@ describe Treequel::Branch do
801
801
  2.times { @branch[ :description ] }
802
802
  end
803
803
 
804
+ it "doesn't cache nil values that don't correspond to an attribute type in the schema" do
805
+ @conn.should_receive( :search_ext2 ).
806
+ with( TEST_HOSTS_DN, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
807
+ and_return([ @entry ])
808
+
809
+ @branch[ :string_beans ]
810
+ @branch.instance_variable_get( :@values ).should_not have_key( :string_beans )
811
+ end
812
+
804
813
  it "freezes the values fetched from its entry by default to prevent accidental " +
805
814
  "in-place modification" do
806
815
  @conn.should_receive( :search_ext2 ).
@@ -47,8 +47,8 @@ describe Treequel::Model::Errors do
47
47
 
48
48
  it "knows how many errors there are" do
49
49
  @errors.add( :l, "is not valid" )
50
- @errors.add( :description, "must " )
51
- @errors.add( :description, "must have at least one description" )
50
+ @errors.add( :description, "must be this tall to ride" )
51
+ @errors.add( :description, "must have at least one value" )
52
52
 
53
53
  @errors.count.should == 3
54
54
  end
@@ -45,10 +45,23 @@ describe Treequel::Model::SchemaValidations do
45
45
  # MUST ( sn $ cn )
46
46
  # MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
47
47
 
48
+ it "adds an error if the object doesn't have at least one structural objectClass" do
49
+ @conn.stub( :search_ext2 ).and_return( [] )
50
+ @modelobj.object_class = ['ipHost', 'ieee802device']
51
+ @modelobj.cn = 'testhost'
52
+ @modelobj.ip_host_number = '127.0.0.1'
53
+
54
+ @modelobj.should_not be_valid()
55
+ @modelobj.errors.should have( 1 ).member
56
+ @modelobj.errors.full_messages.
57
+ should include( 'entry must have at least one structural objectClass' )
58
+ end
59
+
48
60
  it "adds an error if the object doesn't have at least one value for all of its MUST attributes" do
49
61
  @conn.stub( :search_ext2 ).and_return( [] )
50
62
  @modelobj.object_class = 'person'
51
- @modelobj.validate
63
+
64
+ @modelobj.should_not be_valid()
52
65
  @modelobj.errors.should have( 2 ).members
53
66
  @modelobj.errors.full_messages.should include( 'cn MUST have at least one value' )
54
67
  @modelobj.errors.full_messages.should include( 'sn MUST have at least one value' )
@@ -66,7 +79,7 @@ describe Treequel::Model::SchemaValidations do
66
79
 
67
80
  # ..then remove the objectclass that grants it and validate
68
81
  @modelobj.object_class -= ['inetOrgPerson']
69
- @modelobj.validate
82
+ @modelobj.should_not be_valid()
70
83
 
71
84
  @modelobj.errors.full_messages.
72
85
  should == [%{displayName is not allowed by entry's objectClasses}]
@@ -92,7 +105,7 @@ describe Treequel::Model::SchemaValidations do
92
105
  @modelobj.uid_number = "something that's not a number"
93
106
  @modelobj.gid_number = "also not a number"
94
107
 
95
- @modelobj.validate
108
+ @modelobj.should_not be_valid()
96
109
 
97
110
  @modelobj.errors.should have( 2 ).members
98
111
  @modelobj.errors.full_messages.should include( "uidNumber isn't a valid Integer value" )
@@ -446,7 +446,53 @@ describe Treequel::Model do
446
446
  end
447
447
 
448
448
  it "knows if any validation errors have been encountered" do
449
- @obj.errors.should be_a( Hash )
449
+ @obj.errors.should be_a( Treequel::Model::Errors )
450
+ end
451
+
452
+ it "can delete its entry with callbacks" do
453
+ class << @obj; attr_reader :callbacks; end
454
+ @obj.instance_variable_set( :@callbacks, [] )
455
+ def @obj.before_destroy
456
+ self.callbacks << :before_destroy
457
+ end
458
+ def @obj.after_destroy
459
+ self.callbacks << :after_destroy
460
+ end
461
+
462
+ @conn.should_receive( :delete ).with( @obj.dn )
463
+
464
+ @obj.destroy
465
+ @obj.callbacks.should == [ :before_destroy, :after_destroy ]
466
+ end
467
+
468
+ it "doesn't delete its entry if the destroy callback returns something falseish" do
469
+ def @obj.before_destroy
470
+ false
471
+ end
472
+ def @obj.after_destroy
473
+ fail "shouldn't call the after_destroy hook, either"
474
+ end
475
+
476
+ @conn.should_not_receive( :delete )
477
+
478
+ expect {
479
+ @obj.destroy
480
+ }.to raise_error( Treequel::BeforeHookFailed, /destroy/i )
481
+ end
482
+
483
+ it "doesn't raise a BeforeHookFailed if destroyed without :raise_on_failure" do
484
+ def @obj.before_destroy
485
+ false
486
+ end
487
+
488
+ @conn.should_not_receive( :delete )
489
+
490
+ result = nil
491
+ expect {
492
+ result = @obj.destroy( :raise_on_failure => false )
493
+ }.to_not raise_error()
494
+
495
+ result.should be_false()
450
496
  end
451
497
 
452
498
 
@@ -501,6 +547,54 @@ describe Treequel::Model do
501
547
  @obj.save
502
548
  end
503
549
 
550
+
551
+ it "calls update hooks when saved" do
552
+ class << @obj; attr_reader :callbacks; end
553
+ @obj.instance_variable_set( :@callbacks, [] )
554
+ def @obj.before_update( mods )
555
+ self.callbacks << :before_update
556
+ end
557
+ def @obj.after_update( mods )
558
+ self.callbacks << :after_update
559
+ end
560
+
561
+ @conn.should_receive( :modify )
562
+
563
+ @obj.save
564
+ @obj.callbacks.should == [ :before_update, :after_update ]
565
+ end
566
+
567
+ it "doesn't modify its entry if the before_update callback returns something falseish" do
568
+ def @obj.before_update( mods )
569
+ false
570
+ end
571
+ def @obj.after_update( mods )
572
+ fail "shouldn't call the after_update hook, either"
573
+ end
574
+
575
+ @conn.should_not_receive( :modify )
576
+
577
+ expect {
578
+ @obj.save
579
+ }.to raise_error( Treequel::BeforeHookFailed, /update/i )
580
+ end
581
+
582
+ it "doesn't raise a BeforeHookFailed if saved without :raise_on_failure" do
583
+ def @obj.before_update( mods )
584
+ false
585
+ end
586
+
587
+ @conn.stub( :search_ext2 ).with( @obj.dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
588
+ and_return( [] )
589
+ @conn.should_not_receive( :modify )
590
+
591
+ result = nil
592
+ expect {
593
+ result = @obj.save( :raise_on_failure => false )
594
+ }.to_not raise_error()
595
+
596
+ result.should be_false()
597
+ end
504
598
  end
505
599
 
506
600
 
@@ -593,11 +687,11 @@ describe Treequel::Model do
593
687
 
594
688
  it "creates the entry in the directory when saved" do
595
689
  @conn.stub( :search_ext2 ).
596
- with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)').
690
+ with( @obj.dn, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)').
597
691
  and_return( [] )
598
692
 
599
693
  @conn.should_receive( :add ).
600
- with( TEST_PERSON_DN, [
694
+ with( @obj.dn, [
601
695
  ldap_mod_add("cn", "James"),
602
696
  ldap_mod_add("displayName", "J. Random Hacker"),
603
697
  ldap_mod_add("gidNumber", "200"),
@@ -612,6 +706,58 @@ describe Treequel::Model do
612
706
  @obj.save
613
707
  end
614
708
 
709
+ it "calls creation hooks when saved" do
710
+ class << @obj; attr_reader :callbacks; end
711
+ @obj.instance_variable_set( :@callbacks, [] )
712
+ def @obj.before_create( mods )
713
+ self.callbacks << :before_create
714
+ end
715
+ def @obj.after_create( mods )
716
+ self.callbacks << :after_create
717
+ end
718
+
719
+ @conn.stub( :search_ext2 ).with( @obj.dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
720
+ and_return( [] )
721
+ @conn.should_receive( :add )
722
+
723
+ @obj.save
724
+ @obj.callbacks.should == [ :before_create, :after_create ]
725
+ end
726
+
727
+ it "doesn't add its entry if the before_create callback returns something falseish" do
728
+ def @obj.before_create( mods )
729
+ false
730
+ end
731
+ def @obj.after_create( mods )
732
+ fail "shouldn't call the after_create hook, either"
733
+ end
734
+
735
+ @conn.stub( :search_ext2 ).with( @obj.dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
736
+ and_return( [] )
737
+ @conn.should_not_receive( :add )
738
+
739
+ expect {
740
+ @obj.save
741
+ }.to raise_error( Treequel::BeforeHookFailed, /create/i )
742
+ end
743
+
744
+ it "doesn't raise a BeforeHookFailed if saved without :raise_on_failure" do
745
+ def @obj.before_create( mods )
746
+ false
747
+ end
748
+
749
+ @conn.stub( :search_ext2 ).with( @obj.dn, LDAP::LDAP_SCOPE_BASE, "(objectClass=*)" ).
750
+ and_return( [] )
751
+ @conn.should_not_receive( :add )
752
+
753
+ result = nil
754
+ expect {
755
+ result = @obj.save( :raise_on_failure => false )
756
+ }.to_not raise_error()
757
+
758
+ result.should be_false()
759
+ end
760
+
615
761
  end
616
762
 
617
763
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -7,7 +7,7 @@ version: !ruby/object:Gem::Version
7
7
  - 1
8
8
  - 3
9
9
  - 0
10
- version: 1.3.0pre384
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Granger
@@ -36,7 +36,7 @@ cert_chain:
36
36
  cmlhXe46pZNJgWKbxZah85jIjx95hR8vOI+NAM5iH9kOqK13DrxacTKPhqj5PjwF
37
37
  -----END CERTIFICATE-----
38
38
 
39
- date: 2011-01-10 00:00:00 -08:00
39
+ date: 2011-01-13 00:00:00 -08:00
40
40
  default_executable:
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
@@ -136,9 +136,25 @@ dependencies:
136
136
  type: :development
137
137
  version_requirements: *id006
138
138
  - !ruby/object:Gem::Dependency
139
- name: hoe
139
+ name: sysexits
140
140
  prerelease: false
141
141
  requirement: &id007 !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ~>
145
+ - !ruby/object:Gem::Version
146
+ hash: 19
147
+ segments:
148
+ - 1
149
+ - 0
150
+ - 2
151
+ version: 1.0.2
152
+ type: :development
153
+ version_requirements: *id007
154
+ - !ruby/object:Gem::Dependency
155
+ name: hoe
156
+ prerelease: false
157
+ requirement: &id008 !ruby/object:Gem::Requirement
142
158
  none: false
143
159
  requirements:
144
160
  - - ">="
@@ -150,7 +166,7 @@ dependencies:
150
166
  - 0
151
167
  version: 2.8.0
152
168
  type: :development
153
- version_requirements: *id007
169
+ version_requirements: *id008
154
170
  description: |-
155
171
  Treequel is an LDAP toolkit for Ruby. It is intended to allow quick, easy
156
172
  access to LDAP directories in a manner consistent with LDAP's hierarchical,
metadata.gz.sig CHANGED
Binary file