rish 0.1

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,79 @@
1
+ #!/usr/bin/env/ruby
2
+ # -*- ruby -*-
3
+
4
+ # Copyright (c) 2010 by Mikio L. Braun
5
+ # rish is distributed under a BSD-style license. See COPYING
6
+
7
+ require 'rish/shell'
8
+ require 'rish/autoreload'
9
+
10
+ def banner
11
+ puts <<EOS
12
+
13
+ rish - Ruby Interactive Shell
14
+
15
+ an autoreloading interactive shell with
16
+ interactive help, shell-outs and profiling.
17
+
18
+ written by Mikio L. Braun
19
+
20
+ Type 'exit' or hit Ctrl-D on an empty line to exit.
21
+ Type '@h' to get help on built-in commands
22
+
23
+ EOS
24
+ end
25
+
26
+ def usage
27
+ puts <<EOS
28
+ Usage: rish [options] files_to_require.rb ...
29
+
30
+ Options:
31
+ -a dir : load all *.rb files in dir/ on startup
32
+ -w dir : watch all *.rb files in dir/ for changes
33
+ -I dir : add dir/ to load-path
34
+ --irb : use IRB as shell (default)
35
+ --rish : user rish's own shell
36
+ -h, --help : show help (this)
37
+ EOS
38
+ end
39
+
40
+ #
41
+ # Main
42
+ #
43
+
44
+ banner
45
+
46
+ type = :irb
47
+
48
+ until ARGV.empty?
49
+ cmd = ARGV.shift
50
+ case cmd
51
+ when '-I'
52
+ dir = ARGV.shift
53
+ $: << dir
54
+ when '-a'
55
+ dir = ARGV.shift
56
+ puts "Loading all ruby files in #{dir}/"
57
+ Dir.glob("#{dir}/**/*.rb").each {|fn| require fn}
58
+ when '-w'
59
+ dir = ARGV.shift
60
+ Rish::Autoreload.watch_directory dir
61
+ when '-h', '--help'
62
+ usage
63
+ exit
64
+ when '-e'
65
+ cmd = ARGV.shift
66
+ puts eval(cmd)
67
+ exit
68
+ when '--irb'
69
+ type = :irb
70
+ when '--rish'
71
+ type = :rish
72
+ else
73
+ require cmd
74
+ end
75
+ end
76
+
77
+ Rish::Autoreload.check_directories
78
+
79
+ shell = Rish.shell(type)
@@ -0,0 +1,123 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ require 'pp'
5
+ require 'set'
6
+
7
+ module Rish
8
+ # This module tracks loaded files and their timestamps and allows to reload
9
+ # files which have changed automatically by calling reload.
10
+ #
11
+ # There is nothing magically happening here. Basically, you can
12
+ # reload a file by removing it from $" (the list of all loaded
13
+ # files) and require'ing it again.
14
+ module Autoreload
15
+
16
+ # stores the normalized filenames and their File.mtime timestamps
17
+ @timestamps = Hash.new
18
+ @notfound = Set.new
19
+ @verbose = false
20
+ @watched_dirs = []
21
+
22
+ # Set verbosity flag. Setting this to true will report each file
23
+ # that has been reloaded.
24
+ def self.verbose=(flag)
25
+ @verbose = flag
26
+ end
27
+
28
+ # Find the full path to a file.
29
+ def self.locate(file)
30
+ return nil if @notfound.include? file
31
+ $:.each do |dir|
32
+ fullpath = File.join(dir, file)
33
+ if File.exists? fullpath
34
+ return fullpath
35
+ elsif File.exists?(fullpath + '.rb')
36
+ return fullpath + '.rb'
37
+ elsif File.exists?(fullpath + '.so')
38
+ return fullpath + '.so'
39
+ end
40
+ end
41
+ # puts "[JML::AutoReload] File #{file} not found!"
42
+ @notfound.add file
43
+ return nil
44
+ end
45
+
46
+ # Store the time stamp of a file.
47
+ def self.timestamp(file)
48
+ path = locate(file)
49
+ if path
50
+ file = normalize(path, file)
51
+ @timestamps[file] = File.mtime(path)
52
+ end
53
+ end
54
+
55
+ # Put the extension on a filename.
56
+ def self.normalize(path, file)
57
+ if File.extname(file) == ""
58
+ return file + File.extname(path)
59
+ else
60
+ return file
61
+ end
62
+ end
63
+
64
+ # Show all stored files and their timestamp.
65
+ def self.dump
66
+ pp @timestamps
67
+ end
68
+
69
+ # Reload a file. With force=true, file is reloaded in
70
+ # any case.
71
+ def self.reload(file, force=false)
72
+ path = locate(file)
73
+ file = normalize(path, file)
74
+
75
+ if force or (path and File.mtime(path) > @timestamps[file])
76
+ puts "[JML::AutoReload] reloading #{file}" if @verbose
77
+
78
+ # delete file from list of loaded modules, and reload
79
+ $".delete file
80
+ require file
81
+ return true
82
+ else
83
+ return false
84
+ end
85
+ end
86
+
87
+ # Reload all files which were required.
88
+ def self.reload_all(force=false)
89
+ @timestamps.each_key do |file|
90
+ self.reload(file, force)
91
+ end
92
+ check_directories
93
+ end
94
+
95
+ # Add directories to be watched.
96
+ def self.watch_directory(dir)
97
+ @watched_dirs << dir
98
+ end
99
+
100
+ # Reload any new files in the watched directories.
101
+ def self.check_directories
102
+ @watched_dirs.each do |dir|
103
+ Dir.glob("#{dir}/**/*.rb").each do |fn|
104
+ if @timestamps.include? fn
105
+ reload(fn)
106
+ else
107
+ require(fn)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ # Overwrite 'require' to register the time stamps instead.
116
+ module Kernel # :nodoc:
117
+ alias old_require require
118
+
119
+ def require(file)
120
+ Rish::Autoreload.timestamp(file)
121
+ old_require(file)
122
+ end
123
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ module Rish
5
+ # Collects commands which are available trough extensions.
6
+ module Commands
7
+ module_function
8
+
9
+ # Show help for a command, or just the builtin commands.
10
+ def help(word=nil)
11
+ if word
12
+ puts %x{qri #{word}}
13
+ else
14
+ puts <<EOS
15
+ Rish builtin-commands
16
+
17
+ @p <expr> profile expression (requires --debug on JRuby)
18
+ ?<expr> show help for command expr (doesn't work so well on IRB :( )
19
+ @h show this help
20
+ !<cmd> Run cmd in a shell
21
+ !<cmd> & Run cmd in background
22
+ !&<cmd> Same as above (version for IRB which doesn't like an "&"
23
+ as last element on line)
24
+ EOS
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ require 'profiler'
5
+ require 'rish/autoreload'
6
+ require 'rish/commands'
7
+
8
+ module Rish
9
+ # The main Hook class.
10
+ #
11
+ # This adds the main functionality realized through before
12
+ # and after hooks.
13
+ #
14
+ # If you want to add further capabilities, here is the place to do
15
+ # so.
16
+ class Hook
17
+ @profile = false
18
+
19
+ # Called with each input line.
20
+ #
21
+ # If a special command is recognized, the input line is changed
22
+ # accordingly. For example, "!ls" becomes "%x{ls}"
23
+ def before(line)
24
+ Autoreload::reload_all
25
+ if line[0] == ?!
26
+ if line[1] == ?&
27
+ line = line[2..-1] + "&"
28
+ end
29
+ if line[-1] == ?&
30
+ "Thread.new { %x{#{line[1...-1]}} };"
31
+ else
32
+ "%x{#{line[1..-1]}}"
33
+ end
34
+ elsif line[0] == ?? and line.size > 2
35
+ "Rish::Commands.help \"#{line[1..-1]}\""
36
+ elsif line[0] == ?@
37
+ if line[1] == ?p
38
+ Profiler__::start_profile
39
+ @profile = true
40
+ line = line[2..-1]
41
+ elsif line[1] == ?h
42
+ 'Rish::Commands.help'
43
+ else
44
+ line
45
+ end
46
+ else
47
+ line
48
+ end
49
+ end
50
+
51
+ # Called on the result of an evaluation.
52
+ #
53
+ # Used here mainly to clean up after the profiler.
54
+ def after(result)
55
+ if @profile
56
+ Profiler__::stop_profile
57
+ Profiler__::print_profile($stdout)
58
+ @profile = false
59
+ end
60
+ result
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,188 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ require 'readline'
5
+ require 'pp'
6
+
7
+ module Rish
8
+ # Rish's own interactive mode.
9
+ #
10
+ # Similar to IRB, but much smaller. The advantage it has is that it
11
+ # doesn't look for line continuations and works better with rish's
12
+ # built-in commands (well, is this a feature or a bug?)
13
+ #
14
+ # Also, if the line ends in ";", the result is not plotted, just as in
15
+ # matlab.
16
+ class Interactive
17
+ # Start an interactive shell.
18
+ def run
19
+ read_history
20
+
21
+ #binding = Workspace.new.binding
22
+ binding = Object::TOPLEVEL_BINDING
23
+
24
+ Readline.completion_proc = proc do |s|
25
+ if /([a-zA-Z_]+)(::|\.)([^.]*)/ =~ s
26
+ prefix = $1
27
+ sep = $2
28
+ s = $3
29
+
30
+ candidates = get_candidates(binding, prefix + sep)
31
+ else
32
+ prefix = ''
33
+ sep = ''
34
+
35
+ candidates = get_candidates(binding)
36
+ end
37
+
38
+ candidates = candidates.select {|m| starts_with(m, s)}
39
+ candidates.map! {|c| prefix + sep + c}
40
+ end
41
+
42
+ thread = Thread.current
43
+
44
+ state = :input
45
+
46
+ trap('SIGINT') do
47
+ case state
48
+ when :input
49
+ puts "Exiting..."
50
+ thread.raise Interrupt
51
+ exit
52
+ when :calc
53
+ thread.raise Interrupt
54
+ end
55
+ end
56
+
57
+ lineno = 0
58
+ s = 'okay'
59
+ while s
60
+ lineno += 1
61
+ state = :input
62
+ s = Readline.readline("interactive:#{lineno}> ", true)
63
+ #print "interactive:#{lineno}> "
64
+ #s = gets.chomp
65
+ break if (s.nil? or s == 'exit' or s == 'quit')
66
+ begin
67
+ s = @hook.before(s) if @hook
68
+ state = :calc
69
+ result = eval(s, binding, 'interactive', lineno)
70
+ state = :input
71
+ result = @hook.after(result) if @hook
72
+ if s[-1] != ?;
73
+ print_result(result)
74
+ $ans = result
75
+ end
76
+ rescue SyntaxError => e
77
+ puts "SyntaxError: #{e}"
78
+ rescue StandardError, ScriptError => e
79
+ puts "#{e.class}: #{e.to_s[0..1000]}"
80
+ print_backtrace e.backtrace
81
+ rescue SignalException => e
82
+ puts "Caught #{e.class}"
83
+ print_backtrace e.backtrace
84
+ end
85
+ end
86
+
87
+ write_history
88
+ exit
89
+ end
90
+
91
+ # Add a hook which is called before and after each line.
92
+ #
93
+ # The hook object as to have a +before+ and +after+ method.
94
+ # The +before+ method is called with the input line
95
+ # and has to return it or a changed version. Likewise,
96
+ # the +after+ method gets the result of the evaluation and
97
+ # has to return the value or a changed version.
98
+ def hook=(h)
99
+ @hook = h
100
+ end
101
+
102
+ #######
103
+ private
104
+ #######
105
+
106
+ def history_file
107
+ File.join(ENV['HOME'], '.rish_history')
108
+ end
109
+
110
+ def print_result(result)
111
+ puts result.inspect unless result.nil?
112
+ end
113
+
114
+ def read_history
115
+ begin
116
+ open(history_file, 'r').each do |l|
117
+ Readline::HISTORY << l.chomp
118
+ end
119
+ rescue Errno::ENOENT
120
+ # do nothing
121
+ end
122
+ end
123
+
124
+ def write_history
125
+ open(history_file, 'w') do |f|
126
+ Readline::HISTORY.each {|l| f.puts l }
127
+ end
128
+ end
129
+
130
+ # For cleanin up JRuby's stack traces.
131
+ BACKTRACE_HIDE = [ 'sun/', 'java/', 'org/jruby', 'interactive', ':', __FILE__ ]
132
+
133
+ def print_backtrace(bt)
134
+ bt.each do |t|
135
+ unless BACKTRACE_HIDE.inject(false) do |flag,h|
136
+ flag ||= starts_with(t, h)
137
+ end
138
+ puts " #{t}"
139
+ end
140
+ end
141
+ end
142
+
143
+ def starts_with(long, short)
144
+ long[0...short.length] == short
145
+ end
146
+
147
+ def get_candidates(binding, where='')
148
+ candidates = []
149
+ %w(methods constants local_variables).each do |m|
150
+ begin
151
+ candidates += eval(where + m, binding)
152
+ rescue
153
+ end
154
+ end
155
+
156
+ if where == ''
157
+ eval('self.class.ancestors', binding).each do |an|
158
+ %w(methods constants).each do |m|
159
+ begin
160
+ candidates += an.send(m)
161
+ rescue
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ return candidates.sort.uniq
168
+ end
169
+ end
170
+ end
171
+
172
+ if __FILE__ == $0
173
+ include M
174
+
175
+ puts "Interactive with a before and after hook"
176
+ class MyInteractive < Interactive # :nodoc:
177
+ def process_line(line)
178
+ puts "This line is great: #{line}"
179
+ line
180
+ end
181
+
182
+ def process_result(result)
183
+ print " => "
184
+ result
185
+ end
186
+ end
187
+ MyInteractive.new.run
188
+ end
@@ -0,0 +1,104 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ require 'irb'
5
+
6
+ module IRB
7
+ # Start a rish-shell.
8
+ #
9
+ # This code is basically the same as IRB.start with the
10
+ # irb object replaced by our own version.
11
+ def IRB.rish_start(ap_path=nil, hook=nil)
12
+ $0 = File::basename(ap_path, ".rb") if ap_path
13
+
14
+ IRB.setup(ap_path)
15
+
16
+ if @CONF[:SCRIPT]
17
+ irb = Rish::Irb::RishIrb.new(nil, @CONF[:SCRIPT])
18
+ else
19
+ irb = Rish::Irb::RishIrb.new
20
+ end
21
+
22
+ irb.hook = hook
23
+
24
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
25
+ @CONF[:MAIN_CONTEXT] = irb.context
26
+
27
+ trap("SIGINT") do
28
+ irb.signal_handle
29
+ end
30
+
31
+ catch(:IRB_EXIT) do
32
+ irb.eval_input
33
+ end
34
+ # print "\n"
35
+ end
36
+ end
37
+
38
+ module Rish
39
+ # Rish's irb extensions.
40
+ #
41
+ # This module defines two classes derived from IRB::Irb and
42
+ # IRB::Context which basically add after and before hooks
43
+ # to the evaluation to implement automatic reloading and
44
+ # profiling.
45
+ module Irb
46
+ # Our version of IRB::Irb which uses a RishContext.
47
+ class RishIrb < IRB::Irb
48
+ # Create new object.
49
+ #
50
+ # Overwrites @context to RishContext.
51
+ def initialize(workspace = nil, input_method = nil, output_method = nil)
52
+ super(workspace, input_method, output_method)
53
+ @context = RishContext.new(self, workspace, input_method, output_method)
54
+ end
55
+
56
+ # Add a hook in the context object.
57
+ def hook=(h)
58
+ @context.hook = h
59
+ end
60
+ end
61
+
62
+ # Our version of IRB::Context which adds before and after hooks
63
+ # to evaluate.
64
+ class RishContext < IRB::Context
65
+ # Create new context object.
66
+
67
+ def initialize(irb, workspace = nil, input_method = nil, output_method = nil)
68
+ super(irb, workspace, input_method, output_method)
69
+ end
70
+
71
+ # Add a hook.
72
+ def hook=(h)
73
+ @hook = h
74
+ end
75
+
76
+ # Overwritten version of evaluate which calls the +before+ and
77
+ # +after+ methods of the hook, if present. +before+ gets the
78
+ # input line and may return a changed version, +after+ gets the
79
+ # result and may return a changed version.
80
+ def evaluate(line, line_no)
81
+ line = @hook.before(line) if @hook
82
+ val = super(line, line_no)
83
+ val = @hook.after(val) if @hook
84
+ val
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ if __FILE__ == $0
91
+ class RishHook # :nodoc:
92
+ def before(line)
93
+ puts "Before"
94
+ line
95
+ end
96
+
97
+ def after(val)
98
+ puts "After"
99
+ val
100
+ end
101
+ end
102
+
103
+ IRB.rish_start(nil, RishHook.new)
104
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2010 by Mikio L. Braun
2
+ # rish is distributed under a BSD-style license. See COPYING
3
+
4
+ require 'profiler'
5
+
6
+ require 'rish/hook'
7
+ require 'rish/irb'
8
+ require 'rish/interactive'
9
+
10
+ # The Rish main module.
11
+ #
12
+ # Call Rish.shell to start a shell.
13
+ module Rish
14
+ module_function
15
+
16
+ # Start a new Rish shell. Supported types are :irb and :rish.
17
+ def shell(type=:irb)
18
+ hook = Hook.new
19
+ case type
20
+ when :irb
21
+ IRB.rish_start(nil, hook)
22
+ when :rish
23
+ i = Interactive.new
24
+ i.hook = hook
25
+ i.run
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rish
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ version: "0.1"
9
+ platform: ruby
10
+ authors:
11
+ - Mikio L. Braun
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-04-14 00:00:00 +02:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: fastri
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 0
28
+ version: "0"
29
+ type: :runtime
30
+ version_requirements: *id001
31
+ description: |
32
+ An interactive Ruby shell with auto-reloading, shell-outs, profiling,
33
+ and integrated help.
34
+
35
+ email: mikiobraun@gmail.com
36
+ executables:
37
+ - rish
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - bin/rish
44
+ - lib/rish/commands.rb
45
+ - lib/rish/autoreload.rb
46
+ - lib/rish/irb.rb
47
+ - lib/rish/hook.rb
48
+ - lib/rish/interactive.rb
49
+ - lib/rish/shell.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/mikiobraun/rish
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Ruby interactive shell
80
+ test_files: []
81
+