treequel 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +354 -0
- data/LICENSE +27 -0
- data/README +66 -0
- data/Rakefile +345 -0
- data/Rakefile.local +43 -0
- data/bin/treeirb +14 -0
- data/bin/treequel +229 -0
- data/examples/company-directory.rb +112 -0
- data/examples/ldap-monitor.rb +143 -0
- data/examples/ldap-monitor/public/css/master.css +328 -0
- data/examples/ldap-monitor/public/images/card_small.png +0 -0
- data/examples/ldap-monitor/public/images/chain_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
- data/examples/ldap-monitor/public/images/plug.png +0 -0
- data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
- data/examples/ldap-monitor/public/images/tick.png +0 -0
- data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
- data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
- data/examples/ldap-monitor/views/backends.erb +41 -0
- data/examples/ldap-monitor/views/connections.erb +74 -0
- data/examples/ldap-monitor/views/databases.erb +39 -0
- data/examples/ldap-monitor/views/dump_subsystem.erb +14 -0
- data/examples/ldap-monitor/views/index.erb +14 -0
- data/examples/ldap-monitor/views/layout.erb +35 -0
- data/examples/ldap-monitor/views/listeners.erb +30 -0
- data/examples/ldap_state.rb +62 -0
- data/lib/treequel.rb +145 -0
- data/lib/treequel/branch.rb +589 -0
- data/lib/treequel/branchcollection.rb +204 -0
- data/lib/treequel/branchset.rb +360 -0
- data/lib/treequel/constants.rb +604 -0
- data/lib/treequel/directory.rb +541 -0
- data/lib/treequel/exceptions.rb +32 -0
- data/lib/treequel/filter.rb +704 -0
- data/lib/treequel/mixins.rb +325 -0
- data/lib/treequel/schema.rb +245 -0
- data/lib/treequel/schema/attributetype.rb +252 -0
- data/lib/treequel/schema/ldapsyntax.rb +96 -0
- data/lib/treequel/schema/matchingrule.rb +124 -0
- data/lib/treequel/schema/matchingruleuse.rb +124 -0
- data/lib/treequel/schema/objectclass.rb +289 -0
- data/lib/treequel/sequel_integration.rb +26 -0
- data/lib/treequel/utils.rb +169 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/helpers.rb +434 -0
- data/rake/hg.rb +261 -0
- data/rake/manual.rb +782 -0
- data/rake/packaging.rb +135 -0
- data/rake/publishing.rb +318 -0
- data/rake/rdoc.rb +30 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/rake/win32.rb +190 -0
- data/spec/lib/constants.rb +93 -0
- data/spec/lib/helpers.rb +100 -0
- data/spec/treequel/branch_spec.rb +569 -0
- data/spec/treequel/branchcollection_spec.rb +213 -0
- data/spec/treequel/branchset_spec.rb +376 -0
- data/spec/treequel/directory_spec.rb +487 -0
- data/spec/treequel/filter_spec.rb +482 -0
- data/spec/treequel/mixins_spec.rb +330 -0
- data/spec/treequel/schema/attributetype_spec.rb +237 -0
- data/spec/treequel/schema/ldapsyntax_spec.rb +83 -0
- data/spec/treequel/schema/matchingrule_spec.rb +158 -0
- data/spec/treequel/schema/matchingruleuse_spec.rb +137 -0
- data/spec/treequel/schema/objectclass_spec.rb +262 -0
- data/spec/treequel/schema_spec.rb +118 -0
- data/spec/treequel/utils_spec.rb +49 -0
- data/spec/treequel_spec.rb +179 -0
- metadata +169 -0
@@ -0,0 +1,204 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require 'ldap'
|
5
|
+
|
6
|
+
require 'treequel'
|
7
|
+
require 'treequel/mixins'
|
8
|
+
require 'treequel/constants'
|
9
|
+
require 'treequel/branch'
|
10
|
+
|
11
|
+
|
12
|
+
# A Treequel::BranchCollection is a union of Treequel::Branchset
|
13
|
+
# objects, suitable for performing operations on multiple branches
|
14
|
+
# of the directory at once.
|
15
|
+
#
|
16
|
+
# For example, if you have hosts under ou=Hosts in two different
|
17
|
+
# subdomains (e.g., acme.com, seattle.acme.com, and newyork.acme.com),
|
18
|
+
# and you want to search for a host by its CN, you could do so like
|
19
|
+
# this:
|
20
|
+
#
|
21
|
+
# # Top-level hosts, and those in the 'seattle' subdomain, but not
|
22
|
+
# # those in the 'newyork' subdomain:
|
23
|
+
# west_coast_hosts = dir.ou( :hosts ) + dir.dc( :seattle ).ou( :hosts )
|
24
|
+
# west_coast_www_hosts = west_coast_hosts.filter( :cn => 'www' )
|
25
|
+
#
|
26
|
+
# # And one that includes hosts in all three DCs:
|
27
|
+
# all_hosts = west_coast_hosts + dir.dc( :newyork ).ou( :hosts )
|
28
|
+
# all_ns_hosts = all_hosts.filter( :cn => 'ns*' )
|
29
|
+
#
|
30
|
+
# Note that you could accomplish most of what BranchCollection does
|
31
|
+
# using filters, but some people might find this a bit more readable.
|
32
|
+
#
|
33
|
+
# == Authors
|
34
|
+
#
|
35
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
36
|
+
#
|
37
|
+
# :include: LICENSE
|
38
|
+
#
|
39
|
+
#--
|
40
|
+
#
|
41
|
+
# Please see the file LICENSE in the base directory for licensing details.
|
42
|
+
#
|
43
|
+
class Treequel::BranchCollection
|
44
|
+
include Enumerable,
|
45
|
+
Treequel::Loggable,
|
46
|
+
Treequel::Constants
|
47
|
+
|
48
|
+
extend Treequel::Delegation
|
49
|
+
|
50
|
+
|
51
|
+
#################################################################
|
52
|
+
### C L A S S M E T H O D S
|
53
|
+
#################################################################
|
54
|
+
|
55
|
+
### Create a delegator that will return an instance of the receiver created with the results of
|
56
|
+
### iterating over the branchsets and calling the delegated method.
|
57
|
+
def self::def_cloning_delegators( *symbols )
|
58
|
+
symbols.each do |methname|
|
59
|
+
# Create the method body
|
60
|
+
methodbody = Proc.new {|*args|
|
61
|
+
mutated_branchsets = self.branchsets.
|
62
|
+
collect {|bs| bs.send(methname, *args) }.flatten
|
63
|
+
self.class.new( *mutated_branchsets )
|
64
|
+
}
|
65
|
+
|
66
|
+
# ...and install it
|
67
|
+
self.send( :define_method, methname, &methodbody )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
private_class_method :def_cloning_delegators
|
71
|
+
|
72
|
+
|
73
|
+
#################################################################
|
74
|
+
### I N S T A N C E M E T H O D S
|
75
|
+
#################################################################
|
76
|
+
|
77
|
+
### Create a new Treequel::BranchCollection that will operate on the given +branchsets+.
|
78
|
+
def initialize( *branchsets )
|
79
|
+
@branchsets = branchsets.flatten.collect do |obj|
|
80
|
+
if obj.respond_to?( :each )
|
81
|
+
obj
|
82
|
+
else
|
83
|
+
Treequel::Branchset.new( obj )
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
### Declare some delegator methods that clone the receiver with the results of mapping
|
90
|
+
### the branchsets with the delegated method.
|
91
|
+
def_cloning_delegators :filter, :scope, :select, :select_all, :select_more, :timeout,
|
92
|
+
:without_timeout
|
93
|
+
|
94
|
+
### Delegate some methods through the collection directly
|
95
|
+
def_method_delegators :branchsets, :include?
|
96
|
+
|
97
|
+
|
98
|
+
######
|
99
|
+
public
|
100
|
+
######
|
101
|
+
|
102
|
+
alias_method :all, :entries
|
103
|
+
|
104
|
+
|
105
|
+
# The collection's branchsets
|
106
|
+
attr_reader :branchsets
|
107
|
+
|
108
|
+
|
109
|
+
### Return a human-readable string representation of the object suitable for debugging.
|
110
|
+
def inspect
|
111
|
+
"#<%s:0x%0x %d branchsets: %p>" % [
|
112
|
+
self.class.name,
|
113
|
+
self.object_id * 2,
|
114
|
+
self.branchsets.length,
|
115
|
+
self.branchsets.collect {|bs| bs.to_s },
|
116
|
+
]
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
### Iterate over the Treequel::Branches found by each member branchset, yielding each
|
121
|
+
### one in turn.
|
122
|
+
def each( &block )
|
123
|
+
raise LocalJumpError, "no block given" unless block
|
124
|
+
self.branchsets.each do |bs|
|
125
|
+
bs.each( &block )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
### Return the first Treequel::Branch that is returned from the collection's branchsets.
|
131
|
+
def first
|
132
|
+
branch = nil
|
133
|
+
|
134
|
+
self.branchsets.each do |bs|
|
135
|
+
break if branch = bs.first
|
136
|
+
end
|
137
|
+
|
138
|
+
return branch
|
139
|
+
end
|
140
|
+
|
141
|
+
### Overridden to support Branchset#map
|
142
|
+
def map( attribute=nil, &block )
|
143
|
+
if attribute
|
144
|
+
if block
|
145
|
+
super() {|branch| block.call(branch[attribute]) }
|
146
|
+
else
|
147
|
+
super() {|branch| branch[attribute] }
|
148
|
+
end
|
149
|
+
else
|
150
|
+
super( &block )
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
### Append operator: add the specified +object+ (either a Treequel::Branchset or an object
|
156
|
+
### that responds to #branchset and returns a Treequel::Branchset) to the collection
|
157
|
+
### and return the receiver.
|
158
|
+
def <<( object )
|
159
|
+
if object.respond_to?( :branchset )
|
160
|
+
self.branchsets << object.branchset
|
161
|
+
else
|
162
|
+
self.branchsets << object
|
163
|
+
end
|
164
|
+
|
165
|
+
return self
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
### Return a new Treequel::BranchCollection that includes both the receiver's Branchsets and
|
170
|
+
### those in +other_object+ (or +other_object+ itself if it's a Branchset).
|
171
|
+
def +( other_object )
|
172
|
+
if other_object.respond_to?( :branchsets )
|
173
|
+
return self.class.new( self.branchsets + other_object.branchsets )
|
174
|
+
elsif other_object.respond_to?( :branchset )
|
175
|
+
return self.class.new( self.branchsets + [other_object.branchset] )
|
176
|
+
else
|
177
|
+
return self.class.new( self.branchsets + [other_object] )
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
### Return a new Treequel::BranchCollection that contains the union of the branchsets from both
|
183
|
+
### collections.
|
184
|
+
def &( other_collection )
|
185
|
+
return self.class.new( self.branchsets & other_collection.branchsets )
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
### Return a new Treequel::BranchCollection that contains the intersection of the branchsets
|
190
|
+
### from both collections.
|
191
|
+
def |( other_collection )
|
192
|
+
return self.class.new( self.branchsets | other_collection.branchsets )
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
### Return the base DN of all of the collection's Branchsets.
|
197
|
+
def base_dns
|
198
|
+
return self.branchsets.collect {|bs| bs.base_dn }
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
end # class Treequel::BranchCollection
|
203
|
+
|
204
|
+
|
@@ -0,0 +1,360 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'ldap'
|
6
|
+
|
7
|
+
require 'treequel'
|
8
|
+
require 'treequel/mixins'
|
9
|
+
require 'treequel/constants'
|
10
|
+
require 'treequel/branch'
|
11
|
+
require 'treequel/filter'
|
12
|
+
require 'treequel/sequel_integration'
|
13
|
+
|
14
|
+
|
15
|
+
# A branchset represents an abstract set of LDAP records returned by
|
16
|
+
# a search in a directory. It can be used to create, retrieve, update,
|
17
|
+
# and delete records.
|
18
|
+
#
|
19
|
+
# Search results are fetched on demand, so a branchset can be kept
|
20
|
+
# around and reused indefinitely (branchsets never cache results):
|
21
|
+
#
|
22
|
+
# people = directory.ou( :people )
|
23
|
+
# davids = people.filter(:firstName => 'david') # no records are retrieved
|
24
|
+
# davids.all # records are retrieved
|
25
|
+
# davids.all # records are retrieved again
|
26
|
+
#
|
27
|
+
# Most branchset methods return modified copies of the branchset
|
28
|
+
# (functional style), so you can reuse different branchsets to access
|
29
|
+
# data:
|
30
|
+
#
|
31
|
+
# # (employeeId < 2000)
|
32
|
+
# veteran_davids = davids.filter( :employeeId < 2000 )
|
33
|
+
#
|
34
|
+
# # (&(employeeId < 2000)(|(deactivated >= '2008-12-22')(!(deactivated=*))))
|
35
|
+
# active_veteran_davids =
|
36
|
+
# veteran_davids.filter([:or, ['deactivated >= ?', Date.today], [:not, [:deactivated]] ])
|
37
|
+
#
|
38
|
+
# # (&(employeeId < 2000)(|(deactivated >= '2008-12-22')(!(deactivated=*)))(mobileNumber=*))
|
39
|
+
# active_veteran_davids_with_cellphones =
|
40
|
+
# active_veteran_davids.filter( [:mobileNumber] )
|
41
|
+
#
|
42
|
+
# Branchsets are Enumerable objects, so they can be manipulated using any of the
|
43
|
+
# Enumerable methods, such as map, inject, etc.
|
44
|
+
#
|
45
|
+
# == Authors
|
46
|
+
#
|
47
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
48
|
+
#
|
49
|
+
# :include: LICENSE
|
50
|
+
#
|
51
|
+
#--
|
52
|
+
#
|
53
|
+
# Please see the file LICENSE in the base directory for licensing details.
|
54
|
+
#
|
55
|
+
class Treequel::Branchset
|
56
|
+
include Enumerable,
|
57
|
+
Treequel::Loggable,
|
58
|
+
Treequel::Constants
|
59
|
+
|
60
|
+
# The default scope to use when searching if none is specified
|
61
|
+
DEFAULT_SCOPE = :subtree
|
62
|
+
DEFAULT_SCOPE.freeze
|
63
|
+
|
64
|
+
# The default filter to use when searching if non is specified
|
65
|
+
DEFAULT_FILTER = :objectClass
|
66
|
+
DEFAULT_FILTER.freeze
|
67
|
+
|
68
|
+
|
69
|
+
# The default options hash for new Branchsets
|
70
|
+
DEFAULT_OPTIONS = {
|
71
|
+
:filter => DEFAULT_FILTER,
|
72
|
+
:scope => DEFAULT_SCOPE,
|
73
|
+
:timeout => 0, # Floating-point timeout -> sec, usec
|
74
|
+
:select => [], # Attributes to return -> attrs
|
75
|
+
:order => '', # Sorting criteria -> s_attr/s_proc
|
76
|
+
:limit => 0, # Limit -> number of results
|
77
|
+
}.freeze
|
78
|
+
|
79
|
+
|
80
|
+
#################################################################
|
81
|
+
### I N S T A N C E M E T H O D S
|
82
|
+
#################################################################
|
83
|
+
|
84
|
+
### Create a new Branchset for a search from the DN of the specified +branch+ (a
|
85
|
+
### Treequel::Branch), with the given +options+.
|
86
|
+
def initialize( branch, options={} )
|
87
|
+
super()
|
88
|
+
@branch = branch
|
89
|
+
@options = DEFAULT_OPTIONS.merge( options )
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
######
|
94
|
+
public
|
95
|
+
######
|
96
|
+
|
97
|
+
alias_method :all, :entries
|
98
|
+
|
99
|
+
# The branchset's search options hash
|
100
|
+
attr_accessor :options
|
101
|
+
|
102
|
+
# The branchset's base branch that will be used when searching as the basedn
|
103
|
+
attr_accessor :branch
|
104
|
+
|
105
|
+
|
106
|
+
### Returns the DN of the Branchset's branch.
|
107
|
+
def base_dn
|
108
|
+
return self.branch.dn
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
### Override the default clone method to support cloning with different options.
|
113
|
+
def clone( options={} )
|
114
|
+
self.log.debug "cloning %p with options = %p" % [ self, options ]
|
115
|
+
newset = super()
|
116
|
+
newset.options = @options.merge( options )
|
117
|
+
return newset
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
### Return a string representation of the Branchset's filter
|
122
|
+
def uri
|
123
|
+
# :scheme,
|
124
|
+
# :host, :port,
|
125
|
+
# :dn,
|
126
|
+
# :attributes,
|
127
|
+
# :scope,
|
128
|
+
# :filter,
|
129
|
+
# :extensions,
|
130
|
+
uri = self.branch.uri
|
131
|
+
uri.attributes = self.select.join(',')
|
132
|
+
uri.scope = SCOPE_NAME[ self.scope ]
|
133
|
+
uri.filter = self.filter_string
|
134
|
+
# :TODO: Add extensions? Support extensions in Branchset?
|
135
|
+
|
136
|
+
return uri
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
### Return the Branchset as a stringified URI.
|
141
|
+
def to_s
|
142
|
+
return "%s/%s" % [ self.branch.dn, self.filter_string ]
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
### Return a human-readable string representation of the object suitable for debugging.
|
147
|
+
def inspect
|
148
|
+
"#<%s:0x%0x base_dn='%s', filter=%s, scope=%s, select=%s, limit=%d, timeout=%0.3f>" % [
|
149
|
+
self.class.name,
|
150
|
+
self.object_id * 2,
|
151
|
+
self.base_dn,
|
152
|
+
self.filter_string,
|
153
|
+
self.scope,
|
154
|
+
self.select.empty? ? '*' : self.select.join(','),
|
155
|
+
self.limit,
|
156
|
+
self.timeout,
|
157
|
+
]
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
### Return an LDAP filter string made up of the current filter components.
|
162
|
+
def filter_string
|
163
|
+
return self.filter.to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
### Create a BranchCollection from the results of the Branchset and return it.
|
168
|
+
def collection
|
169
|
+
Treequel::BranchCollection.new( self.all )
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
### Iterate over the entries which match the current criteria and yield each of them
|
174
|
+
### as Treequel::Branch objects to the supplied block.
|
175
|
+
def each( &block )
|
176
|
+
raise LocalJumpError, "no block given" unless block
|
177
|
+
directory = self.branch.directory
|
178
|
+
|
179
|
+
directory.search( self.branch, self.scope, self.filter,
|
180
|
+
:selectattrs => self.select,
|
181
|
+
:timeout => self.timeout,
|
182
|
+
# :sortby => self.order,
|
183
|
+
:limit => self.limit,
|
184
|
+
&block
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
### Fetch the first entry which matches the current criteria and return it as an instance of
|
189
|
+
### the object that is set as the +branch+ (e.g., Treequel::Branch).
|
190
|
+
def first
|
191
|
+
self.branch.directory.search( self.branch, self.scope, self.filter,
|
192
|
+
:selectattrs => self.select,
|
193
|
+
:timeout => self.timeout,
|
194
|
+
# :sortby => self.order,
|
195
|
+
:limit => 1
|
196
|
+
).first
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
### Either maps entries which match the current criteria into an Array of the given
|
201
|
+
### +attribute+, or falls back to the block form if no +attribute+ is specified. If both an
|
202
|
+
### +attribute+ and a +block+ are given, the +block+ is called once for each +attribute+ value
|
203
|
+
### instead of with each Branch.
|
204
|
+
def map( attribute=nil, &block ) # :yields: branch or attribute
|
205
|
+
if attribute
|
206
|
+
if block
|
207
|
+
super() {|branch| block.call(branch[attribute]) }
|
208
|
+
else
|
209
|
+
super() {|branch| branch[attribute] }
|
210
|
+
end
|
211
|
+
else
|
212
|
+
super( &block )
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
### Map the results returned by the search into a hash keyed by the first value of +keyattr+
|
218
|
+
### in the entry. If the optional +valueattr+ argument is given, the values will be the
|
219
|
+
### first corresponding attribute, else the value will be the whole entry.
|
220
|
+
def to_hash( keyattr, valueattr=nil )
|
221
|
+
return self.inject({}) do |hash, branch|
|
222
|
+
key = branch[ keyattr ]
|
223
|
+
key = key.first if key.respond_to?( :first )
|
224
|
+
|
225
|
+
value = valueattr ? branch[ valueattr ] : branch.entry
|
226
|
+
value = value.first if value.respond_to?( :first )
|
227
|
+
|
228
|
+
hash[ key ] = value
|
229
|
+
hash
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
###
|
235
|
+
### Mutators
|
236
|
+
###
|
237
|
+
|
238
|
+
### Returns a clone of the receiving Branchset with the given +filterspec+ added
|
239
|
+
### to it.
|
240
|
+
def filter( *filterspec )
|
241
|
+
if filterspec.empty?
|
242
|
+
opts = self.options
|
243
|
+
opts[:filter] = Treequel::Filter.new(opts[:filter]) unless
|
244
|
+
opts[:filter].is_a?( Treequel::Filter )
|
245
|
+
return opts[:filter]
|
246
|
+
else
|
247
|
+
self.log.debug "cloning %p with filterspec: %p" % [ self, filterspec ]
|
248
|
+
newfilter = Treequel::Filter.new( *filterspec )
|
249
|
+
return self.clone( :filter => self.filter + newfilter )
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
### If called with no argument, returns the current scope of the Branchset. If
|
255
|
+
### called with an argument (which should be one of the keys of
|
256
|
+
### Treequel::Constants::SCOPE), returns a clone of the receiving Branchset
|
257
|
+
### with the +new_scope+.
|
258
|
+
def scope( new_scope=nil )
|
259
|
+
if new_scope
|
260
|
+
self.log.debug "cloning %p with new scope: %p" % [ self, new_scope ]
|
261
|
+
return self.clone( :scope => new_scope.to_sym )
|
262
|
+
else
|
263
|
+
return @options[:scope]
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
### If called with one or more +attributes+, returns a clone of the receiving
|
269
|
+
### Branchset that will only fetch the +attributes+ specified. If no +attributes+
|
270
|
+
### are specified, return the list of attributes that will be fetched by the
|
271
|
+
### receiving Branchset. An empty Array means that it should fetch all
|
272
|
+
### attributes, which is the default.
|
273
|
+
def select( *attributes )
|
274
|
+
if attributes.empty?
|
275
|
+
return self.options[:select].collect {|attribute| attribute.to_s }
|
276
|
+
else
|
277
|
+
self.log.debug "cloning %p with new selection: %p" % [ self, attributes ]
|
278
|
+
return self.clone( :select => attributes )
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
### Returns a clone of the receiving Branchset that will fetch all attributes.
|
284
|
+
def select_all
|
285
|
+
return self.clone( :select => [] )
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
### Return a clone of the receiving Branchset that will fetch the specified
|
290
|
+
### +attributes+ in addition to its own.
|
291
|
+
def select_more( *attributes )
|
292
|
+
return self.select( *(Array(@options[:select]) | attributes) )
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
### If called with a +new_limit+, returns a clone of the receiving Branchset that will
|
297
|
+
### fetch (at most) +new_limit+ Branches. If no +new_limit+ argument is specified,
|
298
|
+
### returns the Branchset's current limit. A limit of '0' means that all Branches
|
299
|
+
### will be fetched.
|
300
|
+
def limit( new_limit=nil )
|
301
|
+
if new_limit.nil?
|
302
|
+
return self.options[:limit]
|
303
|
+
else
|
304
|
+
self.log.debug "cloning %p with new limit: %p" % [ self, new_limit ]
|
305
|
+
return self.clone( :limit => Integer(new_limit) )
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
### Return a clone of the receiving Branchset that has no restriction on the number
|
311
|
+
### of Branches that will be fetched.
|
312
|
+
def without_limit
|
313
|
+
return self.clone( :limit => 0 )
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
### Return a clone of the receiving Branchset that will search with its timeout
|
318
|
+
### set to +seconds+, which is in floating-point seconds.
|
319
|
+
def timeout( seconds=nil )
|
320
|
+
if seconds
|
321
|
+
return self.clone( :timeout => seconds )
|
322
|
+
else
|
323
|
+
return @options[:timeout]
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
### Return a clone of the receiving Branchset that will not use a timeout when
|
329
|
+
### searching.
|
330
|
+
def without_timeout
|
331
|
+
return self.clone( :timeout => 0 )
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
# Hiding this until we figure out how to do server-side ordering (i.e.,
|
336
|
+
# http://tools.ietf.org/html/rfc2891)
|
337
|
+
|
338
|
+
### Return a clone of the receiving Branchsest that will order its results by the
|
339
|
+
### +attributes+ specified.
|
340
|
+
def __order( attribute=:__default__ ) # :nodoc:
|
341
|
+
if attribute == :__default__
|
342
|
+
if block_given?
|
343
|
+
sort_func = Proc.new
|
344
|
+
return self.clone( :order => sort_func )
|
345
|
+
else
|
346
|
+
return self.options[:order]
|
347
|
+
end
|
348
|
+
elsif attribute.nil?
|
349
|
+
self.log.debug "cloning %p with no order" % [ self ]
|
350
|
+
return self.clone( :order => nil )
|
351
|
+
else
|
352
|
+
self.log.debug "cloning %p with new order: %p" % [ self, attribute ]
|
353
|
+
return self.clone( :order => attribute.to_sym )
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
end # class Treequel::Branchset
|
359
|
+
|
360
|
+
|