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.
- 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
|
+
|