treequel-shell 1.10.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.
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'abbrev'
5
+ require 'trollop'
6
+ require 'highline'
7
+ require 'shellwords'
8
+ require 'loggability'
9
+
10
+ # Work around MacOS X's vendored 'sysexits' that does the same thing,
11
+ # but with a different API
12
+ gem 'sysexits'
13
+ require 'sysexits'
14
+
15
+ require 'treequel'
16
+ require 'treequel/mixins'
17
+ require 'treequel/constants'
18
+
19
+
20
+ # A tool for displaying information about a directory's records and schema artifacts.
21
+ class Treequel::What
22
+ extend Sysexits,
23
+ Loggability
24
+ include Sysexits,
25
+ Treequel::ANSIColorUtilities,
26
+ Treequel::Constants::Patterns,
27
+ Treequel::HashUtilities
28
+
29
+ # Loggability API -- log to Treequel's logger
30
+ log_to :treequel
31
+
32
+
33
+ # Highline color scheme
34
+ COLOR_SCHEME = HighLine::ColorScheme.new do |scheme|
35
+ scheme[:header] = [ :bold, :yellow ]
36
+ scheme[:subheader] = [ :bold, :white ]
37
+ scheme[:key] = [ :white ]
38
+ scheme[:value] = [ :bold, :white ]
39
+ scheme[:error] = [ :red ]
40
+ scheme[:warning] = [ :yellow ]
41
+ scheme[:message] = [ :reset ]
42
+ end
43
+
44
+
45
+ ### Run the utility with the given +args+.
46
+ def self::run( args )
47
+ HighLine.color_scheme = COLOR_SCHEME
48
+
49
+ oparser = self.make_option_parser
50
+ opts = Trollop.with_standard_exception_handling( oparser ) do
51
+ oparser.parse( args )
52
+ end
53
+
54
+ pattern = oparser.leftovers.join( ' ' ) if oparser.leftovers
55
+
56
+ self.new( opts ).run( pattern )
57
+ exit :ok
58
+
59
+ rescue => err
60
+ self.log.fatal "Oops: %s: %s" % [ err.class.name, err.message ]
61
+ self.log.debug { ' ' + err.backtrace.join("\n ") }
62
+
63
+ exit :software_error
64
+ end
65
+
66
+
67
+ ### Create and configure a command-line option parser (a Trollop::Parser) for the command.
68
+ def self::make_option_parser
69
+ progname = File.basename( $0 )
70
+ default_directory = Treequel.directory_from_config
71
+ loglevels = Loggability::LOG_LEVELS.keys.join( ', ' )
72
+
73
+ return Trollop::Parser.new do
74
+ banner "Usage: #{progname} [OPTIONS] [PATTERN]"
75
+
76
+ text ''
77
+ text %{Search for an object in an LDAP directory that matches PATTERN and } +
78
+ %{display some information about it.}
79
+ text ''
80
+ text %{The PATTERN can be the DN (or RDN relative to the base) of an entry, } +
81
+ %{a search filter, or the name of an artifact in the directory's schema, } +
82
+ %{such as an objectClass, matching rule, syntax, etc.}
83
+ text ''
84
+ text %{If no PATTERN is specified, general information about the directory is } +
85
+ %{output instead.}
86
+ text ''
87
+
88
+ text 'Connection Options:'
89
+ opt :ldapurl, "Specify the directory to connect to.",
90
+ :default => default_directory.uri.to_s
91
+ text ''
92
+
93
+ text 'Display Options:'
94
+ opt :attrtypes, "Show attribute types for objects that have them."
95
+ opt :objectclasses, "Show objectclasses for objects that have them."
96
+ opt :syntaxes, "Show syntaxes for objects that have them."
97
+ opt :matching_rules, "Show matching rules for objects that have them."
98
+ opt :matching_rule_uses, "Show matching rule uses for objects that have them."
99
+ opt :all, "Show any of the above that are applicable."
100
+ text ''
101
+
102
+ text 'Other Options:'
103
+ opt :debug, "Turn debugging on. Also sets the --loglevel to 'debug'."
104
+ opt :loglevel, "Set the logging level. Must be one of: #{loglevels}",
105
+ :default => Loggability[ Treequel ].level.to_s
106
+ opt :binddn, "The DN of the user to bind as. Defaults to anonymous binding.",
107
+ :type => :string
108
+ end
109
+ end
110
+
111
+
112
+ #################################################################
113
+ ### I N S T A N C E M E T H O D S
114
+ #################################################################
115
+
116
+ ### Create a new instance of the command and set it up with the given
117
+ ### +options+.
118
+ def initialize( options )
119
+ Loggability.format_with( :color ) if $stderr.tty?
120
+
121
+ if options.debug
122
+ $DEBUG = true
123
+ $VERBOSE = true
124
+ Loggability.level = :debug
125
+ elsif options.loglevel
126
+ Loggability.level = options.loglevel
127
+ end
128
+
129
+ @options = options
130
+ if @options.all?
131
+ @options[:attrtypes] =
132
+ @options[:objectclasses] =
133
+ @options[:syntaxes] =
134
+ @options[:matching_rules] =
135
+ @options[:matching_rule_uses] =
136
+ true
137
+ end
138
+
139
+ @directory = Treequel.directory( options.ldapurl )
140
+ @prompt = HighLine.new
141
+
142
+ @prompt.wrap_at = @prompt.output_cols - 10
143
+
144
+ self.log.debug "Created new treewhat command object for %s" % [ @directory ]
145
+ end
146
+
147
+
148
+ ######
149
+ public
150
+ ######
151
+
152
+ # The LDAP directory the command will connect to
153
+ attr_reader :directory
154
+
155
+ # The Trollop options hash the command will read its configuration from
156
+ attr_reader :options
157
+
158
+ # The HighLine object to use for prompting and displaying stuff
159
+ attr_reader :prompt
160
+
161
+
162
+ ### Display an +object+ highlighted as a header.
163
+ def print_header( object )
164
+ self.prompt.say( self.prompt.color(object.to_s, :header) )
165
+ end
166
+
167
+
168
+ ### Run the command with the specified +pattern+.
169
+ def run( pattern=nil )
170
+ self.log.debug "Running with pattern = %p" % [ pattern ]
171
+
172
+ self.bind_to_directory if self.options.binddn
173
+
174
+ case pattern
175
+
176
+ # No argument
177
+ when NilClass, ''
178
+ self.show_directory_overview
179
+
180
+ # DN/RDN or filter if it contains a '='
181
+ when %r/=/
182
+ self.show_entry( pattern )
183
+
184
+ # Otherwise, try to find a schema item that matches
185
+ else
186
+ self.show_schema_artifact( pattern )
187
+ end
188
+
189
+ end
190
+
191
+
192
+ ### Prompt for a password and then bind to the command's directory using the binddn in
193
+ ### the options.
194
+ def bind_to_directory
195
+ binddn = self.options.binddn or
196
+ raise ArgumentError, "no binddn in the options hash?!"
197
+ self.log.debug "Attempting to bind to the directory as %s" % [ binddn ]
198
+
199
+ pass = self.prompt.ask( "password: " ) {|q| q.echo = '*' }
200
+ user = Treequel::Branch.new( self.directory, binddn )
201
+
202
+ self.directory.bind_as( user, pass )
203
+ self.log.debug " bound as %s" % [ user ]
204
+
205
+ return true
206
+ end
207
+
208
+
209
+ ### Show general information about the directory if the user doesn't give a pattern on
210
+ ### the command line.
211
+ def show_directory_overview
212
+ pr = self.prompt
213
+ dir = self.directory
214
+
215
+ self.print_header( dir.uri.to_s )
216
+ pr.say( "\n" )
217
+ pr.say( dir.schema.to_s )
218
+
219
+ self.show_column_list( dir.schema.attribute_types.values, 'Attribute Types' ) if
220
+ self.options.attrtypes
221
+ self.show_column_list( dir.schema.object_classes.values, "Object Classes" ) if
222
+ self.options.objectclasses
223
+ self.show_column_list( dir.schema.ldap_syntaxes.values, "Syntaxes" ) if
224
+ self.options.syntaxes
225
+ self.show_column_list( dir.schema.matching_rules.values, "Matching Rules" ) if
226
+ self.options.matching_rules
227
+ self.show_column_list( dir.schema.matching_rule_uses.values, "Matching Rule Uses" ) if
228
+ self.options.matching_rule_uses
229
+ end
230
+
231
+
232
+ ### Show the items from the given +enum+ under the specified +subheading+ in a columnized list.
233
+ def show_column_list( enum, subheading )
234
+ pr = self.prompt
235
+ items = nil
236
+
237
+ if enum.first.respond_to?( :name )
238
+ items = enum.map( &:name ).map( &:to_s ).uniq
239
+ else
240
+ items = enum.map( &:oid ).uniq
241
+ end
242
+
243
+ pr.say( "\n" )
244
+ pr.say( pr.color(subheading, :subheader) )
245
+ pr.say( pr.list(items.sort_by(&:downcase), :columns_down) )
246
+ end
247
+
248
+
249
+ #
250
+ # 'Show entry' mode
251
+ #
252
+
253
+ ### Fetch an entry from the directory and display it like Treequel's editing mode.
254
+ def show_entry( pattern )
255
+ dir = self.directory
256
+ branch = Treequel::Branch.new( dir, pattern )
257
+
258
+ if !branch.exists?
259
+ branch = Treequel::Branch.new( dir, pattern + ',' + dir.base_dn )
260
+ end
261
+
262
+ if !branch.exists?
263
+ branch = dir.filter( pattern ).first
264
+ end
265
+
266
+ if !branch
267
+ self.prompt.say( self.prompt.color("No match.", :error) )
268
+ end
269
+
270
+ yaml = self.branch_as_yaml( branch )
271
+ self.prompt.say( yaml )
272
+ end
273
+
274
+
275
+ ### Return the specified Treequel::Branch object as YAML. If +include_operational+ is true,
276
+ ### include the entry's operational attributes. If +extra_objectclasses+ contains
277
+ ### one or more objectClass OIDs, include their MUST and MAY attributes when building the
278
+ ### YAML representation of the branch.
279
+ def branch_as_yaml( object, include_operational=false )
280
+ object.include_operational_attrs = include_operational
281
+
282
+ # Make sure the displayed entry has the MUST attributes
283
+ entryhash = stringify_keys( object.must_attributes_hash )
284
+ entryhash.merge!( object.entry || {} )
285
+ entryhash['objectClass'] ||= []
286
+
287
+ entryhash.delete( 'dn' ) # Special attribute, can't be edited
288
+
289
+ yaml = entryhash.to_yaml
290
+ yaml[ 5, 0 ] = self.prompt.color( "# #{object.dn}\n", :header )
291
+
292
+ # Make comments out of MAY attributes that are unset
293
+ mayhash = stringify_keys( object.may_attributes_hash )
294
+ self.log.debug "MAY hash is: %p" % [ mayhash ]
295
+ mayhash.delete_if {|attrname,val| entryhash.key?(attrname) }
296
+ yaml << mayhash.to_yaml[5..-1].gsub( /\n\n/, "\n" ).gsub( /^/, '# ' )
297
+
298
+ return yaml
299
+ end
300
+
301
+
302
+
303
+
304
+ #
305
+ # 'Show schema artifact' mode
306
+ #
307
+
308
+ SCHEMA_ARTIFACT_TYPES = [
309
+ :object_classes,
310
+ :attribute_types,
311
+ :ldap_syntaxes,
312
+ :matching_rules,
313
+ :matching_rule_uses,
314
+ ]
315
+
316
+ ### Find an artifact in the directory's schema that matches +pattern+, and display it
317
+ ### if it exists.
318
+ def show_schema_artifact( pattern )
319
+ pr = self.prompt
320
+ schema = self.directory.schema
321
+ artifacts = SCHEMA_ARTIFACT_TYPES.
322
+ collect {|type| schema.send( type ).values.uniq }.flatten
323
+
324
+ if match = find_exact_matching_artifact( artifacts, pattern )
325
+ self.display_schema_artifact( match )
326
+ elsif match = find_substring_matching_artifact( artifacts, pattern )
327
+ pr.say( "No exact match. Falling back to substring match:" )
328
+ self.display_schema_artifact( match )
329
+ else
330
+ pr.say( pr.color("No match.", :error) )
331
+ end
332
+ end
333
+
334
+
335
+ ### Display a schema artifact in a readable way.
336
+ def display_schema_artifact( artifact )
337
+ self.prompt.say( self.prompt.color(artifact.class.name.sub(/.*::/, ''), :header) + ' ' )
338
+ self.prompt.say( self.prompt.color(artifact.to_s, :subheader) )
339
+
340
+ # Display some other stuff depending on what kind of thing it is
341
+ case artifact
342
+ when Treequel::Schema::AttributeType
343
+ self.display_attrtype_details( artifact )
344
+ end
345
+ end
346
+
347
+
348
+ ### Display additional details for the specified +attrtype+ (a Treequel::Schema::AttributeType).
349
+ def display_attrtype_details( attrtype )
350
+ ocs = self.directory.schema.object_classes.values.find_all do |oc|
351
+ ( oc.must_oids | oc.may_oids ).include?( attrtype.name.to_sym )
352
+ end
353
+
354
+ if ocs.empty?
355
+ self.prompt.say "No objectClasses with the '%s' attribute are in the current schema." %
356
+ [ attrtype.name ]
357
+ else
358
+ ocnames = ocs.uniq.map( &:name ).map( &:to_s ).sort
359
+
360
+ self.prompt.say "objectClasses with the '%s' attribute in the current schema:" %
361
+ [ attrtype.name ]
362
+ self.prompt.say( self.prompt.list(ocnames, :columns_across) )
363
+ end
364
+ end
365
+
366
+
367
+ ### Try to find an artifact in +artifacts+ whose name or oid matches +pattern+ exactly.
368
+ ### Returns the first matching artifact.
369
+ def find_exact_matching_artifact( artifacts, pattern )
370
+ self.log.debug "Trying to find an exact match for %p in %d artifacts." %
371
+ [ pattern, artifacts.length ]
372
+ return artifacts.find do |obj|
373
+ (obj.respond_to?( :names ) && obj.names.map(&:to_s).include?(pattern) ) ||
374
+ (obj.respond_to?( :name ) && obj.name.to_s == pattern ) ||
375
+ (obj.respond_to?( :oid ) && obj.oid == pattern )
376
+ end
377
+ end
378
+
379
+
380
+ ### Try to find an artifact in +artifacts+ whose name or oid contains +pattern+.
381
+ ### Returns the first matching artifact.
382
+ def find_substring_matching_artifact( artifacts, pattern )
383
+ pattern = Regexp.new( Regexp.escape(pattern), Regexp::IGNORECASE )
384
+
385
+ return artifacts.find do |obj|
386
+ (obj.respond_to?( :names ) && obj.names.find {|name| name.to_s =~ pattern} ) ||
387
+ (obj.respond_to?( :name ) && obj.name.to_s =~ pattern ) ||
388
+ (obj.respond_to?( :oid ) && obj.oid =~ pattern )
389
+ end
390
+ end
391
+
392
+
393
+ end # class Treequel::What
394
+
395
+
396
+ Treequel::What.run( ARGV.dup )
397
+
metadata ADDED
@@ -0,0 +1,238 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: treequel-shell
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.10.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Granger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain:
12
+ - !binary |-
13
+ LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMRENDQWhTZ0F3SUJB
14
+ Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREE4TVF3d0NnWURWUVFEREFOblpX
15
+ UXgKRnpBVkJnb0praWFKay9Jc1pBRVpGZ2RmWVdWeWFXVmZNUk13RVFZS0Na
16
+ SW1pWlB5TEdRQkdSWURiM0puTUI0WApEVEV3TURreE5qRTBORGcxTVZvWERU
17
+ RXhNRGt4TmpFME5EZzFNVm93UERFTU1Bb0dBMVVFQXd3RFoyVmtNUmN3CkZR
18
+ WUtDWkltaVpQeUxHUUJHUllIWDJGbGNtbGxYekVUTUJFR0NnbVNKb21UOGl4
19
+ a0FSa1dBMjl5WnpDQ0FTSXcKRFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURD
20
+ Q0FRb0NnZ0VCQUx5Ly9CRnhDMWYvY1BTbnd0SkJXb0ZpRnJpcgpoN1JpY0kr
21
+ am9xL29jVlhRcUk0VERXUHlGLzh0cWt2dCtyRDk5WDlxczJZZVI4Q1UvWWlJ
22
+ cExXclFPWVNUNzBKCnZEbjdVdmhiMm11RlZxcTYrdm9iZVRrSUxCRU82cGlv
23
+ bldERzhqU2JvM3FLbTFSaktKRHdnOXA0d05LaFB1dTgKS0d1ZS9CRmI2N0tm
24
+ bHF5QXBQbVBlYjNWZGQ5Y2xzcHpxZUZxcDdjVUJNRXBGUzZMV3h5NEdrK3F2
25
+ RkZKQkpMQgpCVUhFL0xaVkpNVnpmcEM1VXErUW1ZN0IrRkgvUXFObmRuM3RP
26
+ SGdzUGFkTFROaW11QjFzQ3VMMWE0ejNQZXBkClRlTEJFRm1FYW81RGszSy9R
27
+ OG84dmxiSUIvakJEVFV4NkRqYmd4dzc3OTA5eDZnSTlkb1U0TEQ1WE1jQ0F3
28
+ RUEKQWFNNU1EY3dDUVlEVlIwVEJBSXdBREFMQmdOVkhROEVCQU1DQkxBd0hR
29
+ WURWUjBPQkJZRUZKZW9Ha09yOWw0Qgorc2FNa1cvWlhUNFVlU3ZWTUEwR0NT
30
+ cUdTSWIzRFFFQkJRVUFBNElCQVFCRzJLT2J2WUkyZUh5eUJVSlNKM2pOCnZF
31
+ blUzZDYwem5BWGJyU2QycWIzcjFsWTFFUEREM2JjeTBNZ2dDZkdkZzNYdTU0
32
+ ejIxb3F5SWRrOHVHdFdCUEwKSElhOUVnZkZHU1VFZ3ZjSXZhWXFpTjRqVFV0
33
+ aWRmRUZ3K0x0anM4QVA5Z1dnU0lZUzZHcjM4VjBXR0ZGTnpJSAphT0Qyd211
34
+ OW9vL1JmZlc0aFMvOEd1dmZNemN3N0NRMzU1d0ZSNEtCL255emUrRXNaMVk1
35
+ RGVyQ0FhZ01WdURRClUwQkxtV0RGelBHR1dsUGVRQ3JZSENyK0FjSnorTlJu
36
+ YUhDS0xaZFNLai9SSHVUT3QrZ2JsUmV4OEZBaDhOZUEKY21saFhlNDZwWk5K
37
+ Z1dLYnhaYWg4NWpJang5NWhSOHZPSStOQU01aUg5a09xSzEzRHJ4YWNUS1Bo
38
+ cWo1UGp3RgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
39
+ date: 2012-08-29 00:00:00.000000000 Z
40
+ dependencies:
41
+ - !ruby/object:Gem::Dependency
42
+ name: loggability
43
+ requirement: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '0.5'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: '0.5'
57
+ - !ruby/object:Gem::Dependency
58
+ name: treequel
59
+ requirement: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ~>
63
+ - !ruby/object:Gem::Version
64
+ version: '1.10'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ version: '1.10'
73
+ - !ruby/object:Gem::Dependency
74
+ name: highline
75
+ requirement: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: '1.6'
81
+ type: :runtime
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: '1.6'
89
+ - !ruby/object:Gem::Dependency
90
+ name: trollop
91
+ requirement: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ version: '2.0'
105
+ - !ruby/object:Gem::Dependency
106
+ name: sysexits
107
+ requirement: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ version: '1.0'
113
+ type: :runtime
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ~>
119
+ - !ruby/object:Gem::Version
120
+ version: '1.0'
121
+ - !ruby/object:Gem::Dependency
122
+ name: hoe-mercurial
123
+ requirement: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ~>
127
+ - !ruby/object:Gem::Version
128
+ version: 1.4.0
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ~>
135
+ - !ruby/object:Gem::Version
136
+ version: 1.4.0
137
+ - !ruby/object:Gem::Dependency
138
+ name: hoe-highline
139
+ requirement: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ~>
143
+ - !ruby/object:Gem::Version
144
+ version: 0.1.0
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: 0.1.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: rdoc
155
+ requirement: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ~>
159
+ - !ruby/object:Gem::Version
160
+ version: '3.10'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ~>
167
+ - !ruby/object:Gem::Version
168
+ version: '3.10'
169
+ - !ruby/object:Gem::Dependency
170
+ name: hoe
171
+ requirement: !ruby/object:Gem::Requirement
172
+ none: false
173
+ requirements:
174
+ - - ~>
175
+ - !ruby/object:Gem::Version
176
+ version: '3.0'
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ none: false
181
+ requirements:
182
+ - - ~>
183
+ - !ruby/object:Gem::Version
184
+ version: '3.0'
185
+ description: ! "Treequel-Shell is a collection of LDAP tools based on the Treequel
186
+ LDAP\ntoolkit.\n\nIt includes:\n\ntreequel :: an LDAP shell/editor; treat your LDAP
187
+ directory like a filesystem!\ntreewhat :: an LDAP schema explorer. Dump objectClasses
188
+ and attribute type info\n in several convenient formats."
189
+ email:
190
+ - ged@FaerieMUD.org
191
+ executables:
192
+ - treeirb
193
+ - treequel
194
+ - treewhat
195
+ extensions: []
196
+ extra_rdoc_files:
197
+ - History.rdoc
198
+ - Manifest.txt
199
+ - README.rdoc
200
+ files:
201
+ - ChangeLog
202
+ - History.rdoc
203
+ - Manifest.txt
204
+ - README.rdoc
205
+ - Rakefile
206
+ - bin/treeirb
207
+ - bin/treequel
208
+ - bin/treewhat
209
+ homepage: https://bitbucket.org/ged/Treequel-Shell
210
+ licenses:
211
+ - BSD
212
+ post_install_message:
213
+ rdoc_options:
214
+ - -f
215
+ - fivefish
216
+ - -t
217
+ - Treequel
218
+ require_paths:
219
+ - lib
220
+ required_ruby_version: !ruby/object:Gem::Requirement
221
+ none: false
222
+ requirements:
223
+ - - ! '>='
224
+ - !ruby/object:Gem::Version
225
+ version: 1.8.7
226
+ required_rubygems_version: !ruby/object:Gem::Requirement
227
+ none: false
228
+ requirements:
229
+ - - ! '>='
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ requirements: []
233
+ rubyforge_project: treequel-shell
234
+ rubygems_version: 1.8.24
235
+ signing_key:
236
+ specification_version: 3
237
+ summary: Treequel-Shell is a collection of LDAP tools based on the Treequel LDAP toolkit
238
+ test_files: []