treequel 1.4.4 → 1.5.0pre445
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +117 -83
- data/Rakefile +9 -8
- data/lib/treequel.rb +3 -3
- data/lib/treequel/branch.rb +2 -1
- data/lib/treequel/branchset.rb +13 -4
- data/lib/treequel/mixins.rb +2 -2
- data/lib/treequel/model.rb +20 -0
- data/lib/treequel/model/objectclass.rb +43 -9
- data/spec/lib/constants.rb +62 -1
- data/spec/lib/helpers.rb +3 -1
- data/spec/treequel/branch_spec.rb +4 -7
- data/spec/treequel/branchset_spec.rb +47 -78
- data/spec/treequel/mixins_spec.rb +16 -0
- data/spec/treequel/model/objectclass_spec.rb +137 -23
- data/spec/treequel/model_spec.rb +20 -34
- metadata +45 -35
- metadata.gz.sig +3 -3
data/lib/treequel/model.rb
CHANGED
@@ -70,6 +70,7 @@ class Treequel::Model < Treequel::Branch
|
|
70
70
|
|
71
71
|
@objectclass_registry = SET_HASH.dup
|
72
72
|
@base_registry = SET_HASH.dup
|
73
|
+
@directory = nil
|
73
74
|
|
74
75
|
class << self
|
75
76
|
attr_reader :objectclass_registry
|
@@ -77,6 +78,25 @@ class Treequel::Model < Treequel::Branch
|
|
77
78
|
end
|
78
79
|
|
79
80
|
|
81
|
+
### Return the Treequel::Directory the Model will use for searches, creating it if it
|
82
|
+
### hasn't been created already. The default Directory will be created by calling
|
83
|
+
### Treequel.directory_from_config.
|
84
|
+
### @return [Treequel::Directory] the default directory
|
85
|
+
def self::directory
|
86
|
+
self.directory = Treequel.directory_from_config unless @directory
|
87
|
+
return @directory
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
### Set the Treequel::Directory that should be used for searches. The receiving class will also
|
92
|
+
### be set as the #results_class of the +newdirectory+.
|
93
|
+
### @param [Treequel::Directory] newdirectory
|
94
|
+
def self::directory=( newdirectory )
|
95
|
+
@directory = newdirectory
|
96
|
+
@directory.results_class = self if @directory
|
97
|
+
end
|
98
|
+
|
99
|
+
|
80
100
|
### Inheritance callback -- add a class-specific objectclass registry to inheriting classes.
|
81
101
|
### @param [Class] subclass the inheriting class
|
82
102
|
def self::inherited( subclass )
|
@@ -10,6 +10,7 @@ require 'treequel/constants'
|
|
10
10
|
# Mixin that provides Treequel::Model characteristics to a mixin module.
|
11
11
|
module Treequel::Model::ObjectClass
|
12
12
|
include Treequel::HashUtilities
|
13
|
+
extend Treequel::Delegation
|
13
14
|
|
14
15
|
|
15
16
|
### Extension callback -- add data structures to the extending +mod+.
|
@@ -30,6 +31,17 @@ module Treequel::Model::ObjectClass
|
|
30
31
|
end
|
31
32
|
|
32
33
|
|
34
|
+
#################################################################
|
35
|
+
### I N S T A N C E M E T H O D S
|
36
|
+
#################################################################
|
37
|
+
|
38
|
+
# Delegate Branchset methods through #search to allow ObjectClass.filter as a shortcut for
|
39
|
+
# ObjectClass.search.filter
|
40
|
+
def_method_delegators :search,
|
41
|
+
:collection, :map, :to_hash, :each, :first,
|
42
|
+
:filter, :scope, :select, :limit, :timeout, :as, :from
|
43
|
+
|
44
|
+
|
33
45
|
### Declare which Treequel::Model subclasses the mixin will register itself with. If this is
|
34
46
|
### used, it should be declared *before* declaring the mixin's bases and/or objectClasses.
|
35
47
|
def model_class( mclass=nil )
|
@@ -75,11 +87,30 @@ module Treequel::Model::ObjectClass
|
|
75
87
|
end
|
76
88
|
|
77
89
|
|
78
|
-
###
|
79
|
-
###
|
80
|
-
###
|
81
|
-
###
|
82
|
-
|
90
|
+
### @overload create( dn, entryhash={} )
|
91
|
+
### Create a new instance of the mixin's model_class in the model_class's default
|
92
|
+
### directory with the given +dn+ and the objectclasses specified by the mixin. If the
|
93
|
+
### optional +entryhash+ is given, it will be used as the initial attributes of the
|
94
|
+
### new entry.
|
95
|
+
### @param [#to_s] dn the DN of the new model object
|
96
|
+
### @param [Hash] entryhash attributes to set on the new entry
|
97
|
+
### @overload create( directory, dn, entryhash={} )
|
98
|
+
### Create a new instance of the mixin's model_class in the specified +directory+
|
99
|
+
### with the given +dn+ and the objectclasses specified by the mixin. If the
|
100
|
+
### optional +entryhash+ is given, it will be used as the initial attributes of the
|
101
|
+
### new entry.
|
102
|
+
### @param [Treequel::Directory] directory the directory to create the entry in (optional)
|
103
|
+
### @param [#to_s] dn the DN of the new model object
|
104
|
+
### @param [Hash] entryhash attributes to set on the new entry
|
105
|
+
def create( directory, dn=nil, entryhash={} )
|
106
|
+
|
107
|
+
# Shift the arguments if the first one isn't a directory
|
108
|
+
unless directory.is_a?( Treequel::Directory )
|
109
|
+
entryhash = dn || {}
|
110
|
+
dn = directory
|
111
|
+
directory = self.model_class.directory
|
112
|
+
end
|
113
|
+
|
83
114
|
entryhash = stringify_keys( entryhash )
|
84
115
|
|
85
116
|
# Add the objectclasses from the mixin
|
@@ -103,17 +134,20 @@ module Treequel::Model::ObjectClass
|
|
103
134
|
### base) that can be used to search the given +directory+ for entries to which
|
104
135
|
### the receiver applies.
|
105
136
|
###
|
106
|
-
### @param [Treequel::Directory] directory the directory to search
|
137
|
+
### @param [Treequel::Directory] directory the directory to search; if not given, this defaults
|
138
|
+
### to the directory associated with the module's
|
139
|
+
### model_class.
|
107
140
|
### @return [Treequel::Branchset, Treequel::BranchCollection] the encapsulated search
|
108
|
-
def search( directory )
|
141
|
+
def search( directory=nil )
|
142
|
+
directory ||= self.model_class.directory
|
109
143
|
bases = self.model_bases
|
110
144
|
objectclasses = self.model_objectclasses
|
111
145
|
|
112
146
|
raise Treequel::ModelError, "%p has no search criteria defined" % [ self ] if
|
113
147
|
bases.empty? && objectclasses.empty?
|
114
148
|
|
115
|
-
Treequel.log.debug "Creating search for %
|
116
|
-
[ self
|
149
|
+
Treequel.log.debug "Creating search for %p using model class %p" %
|
150
|
+
[ self, self.model_class ]
|
117
151
|
|
118
152
|
# Start by making a Branchset or BranchCollection for the mixin's bases. If
|
119
153
|
# the mixin doesn't have any bases, just use the base DN of the directory
|
data/spec/lib/constants.rb
CHANGED
@@ -87,7 +87,7 @@ module Treequel::TestConstants # :nodoc:all
|
|
87
87
|
TEST_PEOPLE_DN = "#{TEST_PEOPLE_RDN},#{TEST_BASE_DN}"
|
88
88
|
|
89
89
|
TEST_PERSON_DN_ATTR = 'uid'
|
90
|
-
TEST_PERSON_DN_VALUE = '
|
90
|
+
TEST_PERSON_DN_VALUE = 'slappy'
|
91
91
|
TEST_PERSON_RDN = "#{TEST_PERSON_DN_ATTR}=#{TEST_PERSON_DN_VALUE}"
|
92
92
|
TEST_PERSON_DN = "#{TEST_PERSON_RDN},#{TEST_PEOPLE_DN}"
|
93
93
|
|
@@ -124,6 +124,67 @@ module Treequel::TestConstants # :nodoc:all
|
|
124
124
|
]
|
125
125
|
TEST_HOST_MULTIVALUE_DN = "#{TEST_HOST_MULTIVALUE_RDN},#{TEST_HOSTS_DN}"
|
126
126
|
|
127
|
+
# Test entry hashes
|
128
|
+
TEST_HOSTS_ENTRY = {
|
129
|
+
'dn' => [TEST_HOSTS_DN],
|
130
|
+
TEST_HOSTS_DN_ATTR => [TEST_HOSTS_DN_VALUE],
|
131
|
+
'objectClass' => ['top', 'organizationalUnit'],
|
132
|
+
'description' => ['Hosts under acme.com'],
|
133
|
+
}
|
134
|
+
|
135
|
+
TEST_PEOPLE_ENTRY = {
|
136
|
+
'dn' => [TEST_PEOPLE_DN],
|
137
|
+
TEST_PEOPLE_DN_ATTR => [TEST_PEOPLE_DN_VALUE],
|
138
|
+
'objectClass' => ['top', 'organizationalUnit'],
|
139
|
+
'description' => ['Acme.com employees'],
|
140
|
+
}
|
141
|
+
|
142
|
+
TEST_PERSON_ENTRY = {
|
143
|
+
'dn' => [TEST_PERSON_DN],
|
144
|
+
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
|
145
|
+
'cn' => ['Slappy the Frog'],
|
146
|
+
'givenName' => ['Slappy'],
|
147
|
+
'sn' => ['Frog'],
|
148
|
+
'l' => ['a forest in England'],
|
149
|
+
'title' => ['Forest Fire Prevention Advocate'],
|
150
|
+
'displayName' => ['Slappy the Frog'],
|
151
|
+
'logonTime' => ['1293167318'],
|
152
|
+
'uidNumber' => ['1121'],
|
153
|
+
'gidNumber' => ['200'],
|
154
|
+
'homeDirectory' => ['/u/j/jrandom'],
|
155
|
+
'description' => [
|
156
|
+
'Smokey the Bear is much more intense in person.',
|
157
|
+
'Alright.'
|
158
|
+
],
|
159
|
+
'objectClass' => %w[
|
160
|
+
top
|
161
|
+
person
|
162
|
+
organizationalPerson
|
163
|
+
inetOrgPerson
|
164
|
+
posixAccount
|
165
|
+
shadowAccount
|
166
|
+
apple-user
|
167
|
+
],
|
168
|
+
}
|
169
|
+
|
170
|
+
TEST_OPERATIONAL_PEOPLE_ENTRY = {
|
171
|
+
TEST_PEOPLE_DN_ATTR => [TEST_PEOPLE_DN_VALUE],
|
172
|
+
'structuralObjectClass' => ['organizationalUnit'],
|
173
|
+
'entryUUID' => ['5035e674-bae3-102b-992e-e9e937d524d6'],
|
174
|
+
'creatorsName' => ['cn=admin,dc=laika,dc=com'],
|
175
|
+
'createTimestamp' => ['20070629232213Z'],
|
176
|
+
'entryCSN' => ['20070629232213.000000Z#000000#000#000000'],
|
177
|
+
'modifiersName' => ['cn=admin,dc=laika,dc=com'],
|
178
|
+
'modifyTimestamp' => ['20070629232213Z'],
|
179
|
+
'entryDN' => [TEST_PEOPLE_DN],
|
180
|
+
'subschemaSubentry' => ['cn=Subschema'],
|
181
|
+
'hasSubordinates' => ['TRUE'],
|
182
|
+
'dn' => [TEST_PEOPLE_DN],
|
183
|
+
'objectClass' => ['top', 'organizationalUnit'],
|
184
|
+
'description' => ['Acme.com employees'],
|
185
|
+
}
|
186
|
+
|
187
|
+
|
127
188
|
constants.each do |cname|
|
128
189
|
const_get(cname).freeze
|
129
190
|
end
|
data/spec/lib/helpers.rb
CHANGED
@@ -127,7 +127,7 @@ module Treequel::SpecHelpers
|
|
127
127
|
### LDAP connection. Also pre-loads the schema object and fixtures some other
|
128
128
|
### external data.
|
129
129
|
def get_fixtured_directory( conn )
|
130
|
-
LDAP::SSLConn.stub( :new ).and_return(
|
130
|
+
LDAP::SSLConn.stub( :new ).and_return( conn )
|
131
131
|
conn.stub( :search_ext2 ).
|
132
132
|
with( "", 0, "(objectClass=*)", ["+"], false, nil, nil, 0, 0, 0, "", nil ).
|
133
133
|
and_return( TEST_DSE )
|
@@ -163,6 +163,8 @@ end
|
|
163
163
|
|
164
164
|
### Mock with Rspec
|
165
165
|
Rspec.configure do |c|
|
166
|
+
include Treequel::TestConstants
|
167
|
+
|
166
168
|
c.mock_with :rspec
|
167
169
|
|
168
170
|
c.extend( Treequel::TestConstants )
|
@@ -28,9 +28,6 @@ include Treequel::Constants
|
|
28
28
|
#####################################################################
|
29
29
|
|
30
30
|
describe Treequel::Branch do
|
31
|
-
include Treequel::SpecHelpers,
|
32
|
-
Treequel::Matchers
|
33
|
-
|
34
31
|
|
35
32
|
before( :all ) do
|
36
33
|
setup_logging( :fatal )
|
@@ -241,13 +238,13 @@ describe Treequel::Branch do
|
|
241
238
|
|
242
239
|
it "clears any cached values if its include_operational_attrs attribute is changed" do
|
243
240
|
@directory.should_receive( :get_entry ).with( @branch ).exactly( :once ).
|
244
|
-
and_return(
|
241
|
+
and_return( TEST_PEOPLE_ENTRY.dup )
|
245
242
|
@directory.should_receive( :get_extended_entry ).with( @branch ).exactly( :once ).
|
246
|
-
and_return(
|
243
|
+
and_return( TEST_OPERATIONAL_PEOPLE_ENTRY.dup )
|
247
244
|
|
248
|
-
@branch.entry.should ==
|
245
|
+
@branch.entry.should == TEST_PEOPLE_ENTRY.dup.tap {|entry| entry.delete('dn') }
|
249
246
|
@branch.include_operational_attrs = true
|
250
|
-
@branch.entry.should ==
|
247
|
+
@branch.entry.should == TEST_OPERATIONAL_PEOPLE_ENTRY.dup.tap {|entry| entry.delete('dn') }
|
251
248
|
end
|
252
249
|
|
253
250
|
it "returns a human-readable representation of itself for #inspect" do
|
@@ -34,6 +34,7 @@ describe Treequel::Branchset do
|
|
34
34
|
:server_controls => [],
|
35
35
|
}
|
36
36
|
|
37
|
+
|
37
38
|
before( :all ) do
|
38
39
|
setup_logging( :fatal )
|
39
40
|
end
|
@@ -56,12 +57,10 @@ describe Treequel::Branchset do
|
|
56
57
|
end
|
57
58
|
|
58
59
|
it "is Enumerable" do
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
and_yield( resultbranch )
|
64
|
-
resultbranch.should_receive( :dn ).and_return( :its_dn )
|
60
|
+
@conn.should_receive( :search_ext2 ).
|
61
|
+
with( TEST_BASE_DN, LDAP::LDAP_SCOPE_SUBTREE, "(objectClass=*)",
|
62
|
+
[], false, [], [], 0, 0, 0, "", nil ).
|
63
|
+
and_return([ TEST_HOSTS_ENTRY.dup ])
|
65
64
|
|
66
65
|
@branchset.all? {|b| b.dn }
|
67
66
|
end
|
@@ -70,20 +69,18 @@ describe Treequel::Branchset do
|
|
70
69
|
# #empty?
|
71
70
|
#
|
72
71
|
it "is empty if it doesn't match at least one entry" do
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
and_return(
|
77
|
-
|
72
|
+
@conn.should_receive( :search_ext2 ).
|
73
|
+
with( TEST_BASE_DN, LDAP::LDAP_SCOPE_SUBTREE, "(objectClass=*)",
|
74
|
+
[], false, [], [], 0, 0, 1, "", nil ).
|
75
|
+
and_return([ ])
|
78
76
|
@branchset.should be_empty()
|
79
77
|
end
|
80
78
|
|
81
79
|
it "isn't empty if it matches at least one entry" do
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
and_return(
|
86
|
-
|
80
|
+
@conn.should_receive( :search_ext2 ).
|
81
|
+
with( TEST_BASE_DN, LDAP::LDAP_SCOPE_SUBTREE, "(objectClass=*)",
|
82
|
+
[], false, [], [], 0, 0, 1, "", nil ).
|
83
|
+
and_return([ TEST_HOSTS_ENTRY.dup ])
|
87
84
|
@branchset.should_not be_empty()
|
88
85
|
end
|
89
86
|
|
@@ -91,16 +88,12 @@ describe Treequel::Branchset do
|
|
91
88
|
# #map
|
92
89
|
#
|
93
90
|
it "can be mapped into an Array of attribute values" do
|
94
|
-
|
95
|
-
|
91
|
+
@conn.should_receive( :search_ext2 ).
|
92
|
+
with( TEST_BASE_DN, LDAP::LDAP_SCOPE_SUBTREE, "(objectClass=*)",
|
93
|
+
[], false, [], [], 0, 0, 0, "", nil ).
|
94
|
+
and_return([ TEST_HOSTS_ENTRY.dup, TEST_PEOPLE_ENTRY.dup ])
|
96
95
|
|
97
|
-
@
|
98
|
-
with( Treequel::Branchset::DEFAULT_SCOPE, @branchset.filter, @params ).
|
99
|
-
and_yield( resultbranch ).and_yield( resultbranch2 )
|
100
|
-
resultbranch.should_receive( :[] ).with( :cn ).and_return([ :first_cn ])
|
101
|
-
resultbranch2.should_receive( :[] ).with( :cn ).and_return([ :second_cn ])
|
102
|
-
|
103
|
-
@branchset.map( :cn ).should == [[:first_cn], [:second_cn]]
|
96
|
+
@branchset.map( :ou ).should == [ ['Hosts'], ['People'] ]
|
104
97
|
end
|
105
98
|
|
106
99
|
|
@@ -108,47 +101,30 @@ describe Treequel::Branchset do
|
|
108
101
|
# #to_hash
|
109
102
|
#
|
110
103
|
it "can be mapped into a Hash of entries keyed by one of its attributes" do
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
resultbranch2.should_receive( :entry ).and_return( :entry2 )
|
124
|
-
|
125
|
-
@branchset.to_hash( :email ).should == {
|
126
|
-
:first_email => :entry1,
|
127
|
-
:second_email => :entry2,
|
104
|
+
@conn.should_receive( :search_ext2 ).
|
105
|
+
with( "dc=acme,dc=com", 2, "(objectClass=*)", [], false, [], [], 0, 0, 0, "", nil ).
|
106
|
+
and_return([ TEST_HOSTS_ENTRY.dup, TEST_PEOPLE_ENTRY.dup ])
|
107
|
+
|
108
|
+
hosthash = TEST_HOSTS_ENTRY.dup
|
109
|
+
hosthash.delete( 'dn' )
|
110
|
+
peoplehash = TEST_PEOPLE_ENTRY.dup
|
111
|
+
peoplehash.delete( 'dn' )
|
112
|
+
|
113
|
+
@branchset.to_hash( :ou ).should == {
|
114
|
+
'Hosts' => hosthash,
|
115
|
+
'People' => peoplehash,
|
128
116
|
}
|
129
117
|
end
|
130
118
|
|
131
119
|
|
132
120
|
it "can be mapped into a Hash of tuples using two attributes" do
|
133
|
-
|
134
|
-
|
121
|
+
@conn.should_receive( :search_ext2 ).
|
122
|
+
with( "dc=acme,dc=com", 2, "(objectClass=*)", [], false, [], [], 0, 0, 0, "", nil ).
|
123
|
+
and_return([ TEST_HOSTS_ENTRY.dup, TEST_PEOPLE_ENTRY.dup ])
|
135
124
|
|
136
|
-
@
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
resultbranch.should_receive( :[] ).with( :email ).
|
141
|
-
and_return([ :first_email ])
|
142
|
-
resultbranch.should_receive( :[] ).with( :cn ).
|
143
|
-
and_return([ :first_cn ])
|
144
|
-
resultbranch2.should_receive( :[] ).with( :email ).
|
145
|
-
and_return([ :second_email, :second_second_email ])
|
146
|
-
resultbranch2.should_receive( :[] ).with( :cn ).
|
147
|
-
and_return([ :second_cn, :second_second_cn ])
|
148
|
-
|
149
|
-
@branchset.to_hash( :email, :cn ).should == {
|
150
|
-
:first_email => :first_cn,
|
151
|
-
:second_email => :second_cn,
|
125
|
+
@branchset.to_hash( :ou, :description ).should == {
|
126
|
+
'Hosts' => TEST_HOSTS_ENTRY['description'].first,
|
127
|
+
'People' => TEST_PEOPLE_ENTRY['description'].first,
|
152
128
|
}
|
153
129
|
end
|
154
130
|
|
@@ -156,8 +132,7 @@ describe Treequel::Branchset do
|
|
156
132
|
# #+
|
157
133
|
#
|
158
134
|
it "can be combined with another instance into a BranchCollection by adding them together" do
|
159
|
-
other_branch =
|
160
|
-
other_branch.stub( :directory ).and_return( @directory )
|
135
|
+
other_branch = @directory.ou( :people )
|
161
136
|
other_branchset = Treequel::Branchset.new( other_branch )
|
162
137
|
|
163
138
|
result = @branchset + other_branchset
|
@@ -167,18 +142,15 @@ describe Treequel::Branchset do
|
|
167
142
|
end
|
168
143
|
|
169
144
|
it "returns the results of the search with the additional Branch if one is added to it" do
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
resultbranch2 = mock( "Result Branch 2" )
|
145
|
+
@conn.should_receive( :search_ext2 ).
|
146
|
+
with( "dc=acme,dc=com", 2, "(objectClass=*)", [], false, [], [], 0, 0, 0, "", nil ).
|
147
|
+
and_return([ TEST_HOSTS_ENTRY.dup, TEST_PEOPLE_ENTRY.dup ])
|
174
148
|
|
175
|
-
@
|
176
|
-
with( Treequel::Branchset::DEFAULT_SCOPE, @branchset.filter, @params ).
|
177
|
-
and_yield( resultbranch ).and_yield( resultbranch2 )
|
149
|
+
other_branch = @directory.ou( :netgroups )
|
178
150
|
|
179
151
|
result = @branchset + other_branch
|
180
152
|
result.should have( 3 ).members
|
181
|
-
result.should include( other_branch
|
153
|
+
result.should include( other_branch )
|
182
154
|
end
|
183
155
|
|
184
156
|
#
|
@@ -186,18 +158,15 @@ describe Treequel::Branchset do
|
|
186
158
|
#
|
187
159
|
it "returns the results of the search without the specified object if an object is " +
|
188
160
|
"subtracted from it" do
|
189
|
-
|
190
|
-
resultbranch2 = stub( "Result Branch 2", :dn => TEST_PERSON2_DN )
|
191
|
-
|
192
|
-
otherbranch = stub( "Subtracted Branch", :dn => TEST_PERSON2_DN )
|
161
|
+
otherbranch = @directory.ou( :people )
|
193
162
|
|
194
|
-
@
|
195
|
-
with(
|
196
|
-
|
163
|
+
@conn.should_receive( :search_ext2 ).
|
164
|
+
with( "dc=acme,dc=com", 2, "(objectClass=*)", [], false, [], [], 0, 0, 0, "", nil ).
|
165
|
+
and_return([ TEST_HOSTS_ENTRY.dup, TEST_PEOPLE_ENTRY.dup ])
|
197
166
|
|
198
167
|
result = @branchset - otherbranch
|
199
168
|
result.should have( 1 ).members
|
200
|
-
result.should_not include(
|
169
|
+
result.should_not include( otherbranch )
|
201
170
|
end
|
202
171
|
|
203
172
|
end
|
@@ -245,6 +245,14 @@ describe Treequel, "mixin" do
|
|
245
245
|
@obj.delegated_method( :arg1, :arg2 )
|
246
246
|
end
|
247
247
|
|
248
|
+
it "allows delegation to the delegate object's method with a block" do
|
249
|
+
@subobj.should_receive( :delegated_method ).with( :arg1 ).
|
250
|
+
and_yield( :the_block_argument )
|
251
|
+
blockarg = nil
|
252
|
+
@obj.delegated_method( :arg1 ) {|arg| blockarg = arg }
|
253
|
+
blockarg.should == :the_block_argument
|
254
|
+
end
|
255
|
+
|
248
256
|
it "reports errors from its caller's perspective", :ruby_1_8_only => true do
|
249
257
|
begin
|
250
258
|
@obj.erroring_delegated_method
|
@@ -291,6 +299,14 @@ describe Treequel, "mixin" do
|
|
291
299
|
@obj.delegated_method( :arg1, :arg2 )
|
292
300
|
end
|
293
301
|
|
302
|
+
it "allows delegation to the delegate's method with a block" do
|
303
|
+
@subobj.should_receive( :delegated_method ).with( :arg1 ).
|
304
|
+
and_yield( :the_block_argument )
|
305
|
+
blockarg = nil
|
306
|
+
@obj.delegated_method( :arg1 ) {|arg| blockarg = arg }
|
307
|
+
blockarg.should == :the_block_argument
|
308
|
+
end
|
309
|
+
|
294
310
|
it "reports errors from its caller's perspective", :ruby_1_8_only => true do
|
295
311
|
begin
|
296
312
|
@obj.erroring_delegated_method
|