Skip to content

Instantly share code, notes, and snippets.

@jorrizza
Created August 1, 2011 13:42
Show Gist options
  • Save jorrizza/1118135 to your computer and use it in GitHub Desktop.
Save jorrizza/1118135 to your computer and use it in GitHub Desktop.
ActiveRecord Shell
ActiveRecord SHell (or ARSH for short).
Here's the code that actually works. I have been working on a
half-baked pager for the shell, but the code's so ugly I'm not willing
to share it. At least this allows someone to continue the "project".
The original idea was this:
- ls should list available ActiveRecord classes and modules containing
ActiveRecord classes.
- cd should enter an ActiveRecord class or module. If it's a module, ls
will display the classes and/or modules in that. If it's a class, ls
will display the available objects as numbered files (with a pager).
- cat should display the object's content in YAML format.
- ed should start a person's $EDITOR and let him edit that object's YAML.
- find should search in objects. Like find -field string ClassName.
- Support class paths by adding forward slashes, like ClassName/4315
- Fully support tab completion where possible.
#!/usr/bin/env ruby
# The magic
require File.join(File.dirname(__FILE__), '..', 'lib', 'arsh')
# Rails boot
begin
puts "Loading Rails..."
require File.join('.', 'config', 'application')
require File.join('.', 'config', 'boot')
Rails.application.require_environment!
rescue
$stderr.puts "Please run #{$0} in your Rails root."
exit 1
end
ARSH::Shell.new self.class
require 'readline'
# ARSH - the ActiveRecord SHell.
module ARSH
# The commands used in ARSH.
class Commands
# Standard output of the command.
attr_reader :out
# Parses the command in line and starts the right function.
def initialize(line, shell)
@params = line.split.reverse
command = @params.pop
@params.reverse!
@shell = shell
@out = ""
# Rails has some awesome deprecation warnings we don't like.
# So temporarily send those to /dev/null.
#old_stderr = $stderr
#$stderr = File.open('/dev/null', 'a+')
case command
when 'exit'
@shell.bye
when 'pwd'
@out = @shell.pwd.to_s
when 'ls'
ls
when 'cd'
cd
else
@out = "No such command: #{command}"
end
#$stderr = old_stderr
end
# Directory listing.
def ls
ar_classes = @shell.pwd.constants.map do |c|
klass = @shell.pwd.const_get c
if klass.is_a?(Class) && klass.ancestors.include?(ActiveRecord::Base)
klass.to_s
end
end.compact
if @shell.pwd == Object && @params.first
if ar_classes.include? @params.first
klass = @shell.pwd.const_get @params.first.to_sym
ls_files klass
else
@out = "No such directory: #{@params.first}"
end
elsif @shell.pwd == Object
@out = ar_classes.join ' '
else
ls_files @shell.pwd
end
end
# Lists files in a directory.
def ls_files(klass)
files = klass.limit(50).map do |obj|
klass.name.downcase + '_' + obj.id.to_s
end
@out = ""
if files.length == 50
@out = files.join(' ') + "\nThere's a lot of files to list. Maybe you'd like to use find instead\?"
else
@out = files.join ' '
end
end
# Change directory.
def cd
if !@params.first || @params.first == '..'
@shell.pwd = Object
else
klass = @shell.pwd.const_get @params.first.to_sym
if !klass || !klass.ancestors.include?(ActiveRecord::Base)
@out = "No such directory: #{@params.first}"
else
@shell.pwd = klass
end
end
end
end
# Shell wrapper for ARSH.
class Shell
PROMPT = "#{self.name} %s$ "
# The parent directory, e.g. the parent class.
attr_accessor :pwd
# Start the shell with 'pwd' as starter class.
def initialize(pwd)
@pwd = pwd
# Make sure our shell stays clean after an exit.
@stty_save = `stty -g`.chomp
trap('INT') { bye }
runner
end
# Quit.
def bye
puts "Bye!"
system('stty', @stty_save)
exit
end
# The shell itself.
def runner
loop do
prompt = PROMPT % @pwd.name
line = Readline.readline(prompt, true)
bye unless line
out = Commands.new(line, self).out
puts out unless out.empty?
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment