Commits

Michael Granger committed 20ea1f1

* Treequel::Branch:
- New methods: #rdn=, #split_dn, #<=>
- Added Comparable interface
- Made the proxy method actually check the schema for valid attribute type OIDs instead of
assuming every message wanted a sub-branch.
* Treequel::Directory:
- Implemented #move
- Fixed non-functional #children.
- Same fix for #method_missing as in Branch.
* Renamed 'shelldapper' to 'treequel' to reflect a planned change in direction (i.e., closer)
to Sequel's 'sequel' shell.

Comments (0)

Files changed (1)

+#!/usr/bin/env ruby
+
+require 'readline'
+require 'logger'
+require 'shellwords'
+require 'abbrev'
+require 'treequel'
+require 'treequel/mixins'
+
+
+class Shell
+	include Treequel::Loggable
+
+	### Create a new shell that will traverse the directory at the specified +uri+.
+	def initialize( uri )
+		Treequel.logger.level = Logger::DEBUG
+
+		@uri        = uri
+		@quit       = false
+		@dir        = Treequel.directory( @uri )
+		@currbranch = @dir
+
+		@commands = self.find_commands
+		@completions = @commands.abbrev
+		@command_table = make_command_table( @commands )
+	end
+
+
+	### The command loop: run the shell until the user wants to quit
+	def run
+		$stderr.puts "Connected to %s" % [ @uri ]
+
+		self.setup_completion
+
+		until @quit
+			input = Readline.readline( @currbranch.dn + '> ', true )
+			self.log.debug "Input is: %p" % [ input ]
+
+			# EOL makes the shell quit
+			if input.nil?
+				@quit = true
+
+			# Parse everything else into command + everything else
+			else
+				command, *args = Shellwords.shellwords( input )
+
+				begin
+					if meth = @command_table[ command ]
+						meth.call( *args )
+					else
+						self.handle_missing_command( command )
+					end
+				rescue => err
+					$stderr.puts "Error: %s" % [ err.message ]
+					err.backtrace.each do |frame|
+						self.log.debug "  " + frame
+					end
+				end
+			end
+		end
+
+		$stderr.puts "done."
+	end
+
+
+	#########
+	protected
+	#########
+
+	### Set up Readline completion
+	def setup_completion
+		Readline.completion_proc = self.method( :completion_callback ).to_proc
+		Readline.completer_word_break_characters = ''
+	end
+
+
+	### Handle completion requests from Readline.
+	def completion_callback( input )
+		if command = @completions[ input ]
+			return []
+		end
+	end
+
+
+	### Quit the shell.
+	def quit_command( *args )
+		$stderr.puts "Okay, exiting."
+		@quit = true
+	end
+
+
+	### Show the completions hash
+	def show_completions_command
+		$stderr.puts "Completions:",
+			@completions.inspect
+	end
+
+
+	### Display LDIF for the specified RDNs.
+	def cat_command( *args )
+		args.each do |rdn|
+			branch = rdn.split( /\s*,\s*/ ).inject( @currbranch ) do |branch, dnpair|
+				attribute, value = dnpair.split( /\s*=\s*/, 2 )
+				branch.send( attribute, value )
+			end
+
+			$stdout.puts( branch.to_ldif )
+		end
+	end
+
+
+	### List the children of the current branch.
+	def ls_command( *args )
+		$stdout.puts *@currbranch.children.collect {|b| b.rdn }.sort
+	end
+
+
+	### Change the current working DN.
+	def cd_command( *args )
+		rdn = args.shift
+		pairs = rdn.split( /\s*,\s*/ )
+		pairs.each do |dnpair|
+			self.log.debug "  cd to %p" % [ dnpair ]
+			attribute, value = dnpair.split( /=/, 2 )
+			self.log.debug "  changing to %s( %p )" % [ attribute, value ]
+			@currbranch = @currbranch.send( attribute, value )
+		end
+	end
+
+
+	### Handle a command from the user that doesn't exist.
+	def handle_missing_command( *args )
+		command = args.shift || '(testing?)'
+		$stderr.puts "Unknown command %p" % [ command ]
+		$stderr.puts "Known commands: ", '  ' + @commands.join(', ')
+	end
+
+
+	### Find methods that implement commands and return them in a sorted Array.
+	def find_commands
+		return self.methods.
+			grep( /^(\w+)_command$/ ).
+			collect {|mname| mname[/^(\w+)_command$/, 1] }.
+			sort
+	end
+
+
+	#######
+	private
+	#######
+
+	### Create a command table that maps command abbreviations to the Method object that
+	### implements it.
+	def make_command_table( commands )
+		table = commands.abbrev
+		table.keys.each do |abbrev|
+			mname = table.delete( abbrev )
+			table[ abbrev ] = self.method( mname + '_command' )
+		end
+
+		return table
+	end
+
+end
+
+
+if __FILE__ == $0
+	ldapuri = URI( ARGV.shift || 'ldap://localhost' )
+	Shell.new( ldapuri ).run
+end
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.