ripl 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ == 0.2.0
2
+ * Full support for plugins
3
+ * Add history support for all shells
4
+ * Add ~/.riplrc
5
+ * Add usage and more commandline options
6
+
1
7
  == 0.1.2
2
8
  * Add support for commands
3
9
  * Add -I, -r and -v options
@@ -10,24 +10,27 @@ Install the gem with:
10
10
 
11
11
  == Features
12
12
 
13
- * Same as irb
13
+ * Similar to irb
14
14
  * Reads ~/.irbrc on startup
15
15
  * Appends to ~/.irb_history on exit
16
16
  * Autocompletion (from bond)
17
17
  * _ for last result
18
18
  * Type 'exit' or Ctrl-D to exit
19
- * Commandline options: -r, -I, -v
19
+ * 6 common commandline options: -f, -r, -I, -d, -h, -v
20
+ * IRB.conf -> Ripl.config
20
21
  * Enhancements over irb
22
+ * ~200 lines vs irb's 5000+ lines
23
+ * Easily extendable with plugins
24
+ * Customizable completion and completion of method arguments (from bond)
25
+ * Easy to create custom shells for gems and apps i.e. Ripl.start
26
+ * Easy to create and invoke ripl subcommands
21
27
  * ~/.irbrc errors caught
22
- * Provides hooks for plugins
23
- * Method argument and customizable autocompletion (from bond)
24
- * Less than 150 loc vs irb's 5000+ loc
25
- * Simple configuration i.e. one hash
26
28
  * Different from irb
27
29
  * No multi-line evaluation
28
30
  * No irb subsessions or workspaces
29
- * No IRB.conf features i.e. dynamic prompts and auto indent
30
- * Most commandline options not supported
31
+ * No IRB.conf features i.e. preconfigured prompts and auto indent
32
+
33
+ Note: Irb features not in ripl can be implemented as plugins.
31
34
 
32
35
  == Philosophy
33
36
 
@@ -40,14 +43,37 @@ easily customized for gems, applications and is even usable on the web (examples
40
43
  $ ripl
41
44
  >> ...
42
45
 
43
- == Create Custom Shells
46
+ == Plugins
47
+
48
+ A ripl plugin is a module that is included into Ripl::Shell or Ripl::Runner. Being simply modules,
49
+ they can be packaged as gems and reused across shells as needed. ripl highly encourages plugins by
50
+ loading them as early as possible and allowing them to extend most of ripl's functionality.
51
+
52
+ As an example plugin, let's color error messages red:
53
+
54
+ # Place in ~/.riplrc
55
+ module Ripl
56
+ module RedError
57
+ def format_error(error)
58
+ "\e[31m#{super}\e[m"
59
+ end
60
+ end
61
+ end
62
+ Ripl::Shell.send :include, Ripl::RedError
44
63
 
45
- Creating a custom shell is as simple as:
64
+ Note this plugin extends format_error() by invoking the original format_error() with super. This is
65
+ possible for any method that is available for extension by plugins. To see methods are available for
66
+ extension, see Ripl::Shell::API and Ripl::Runner::API.
46
67
 
47
- #!/usr/bin/env ruby
68
+ If we want to add a config for this plugin, we can simply add a key to Ripl.config that matches the
69
+ underscored version of the plugin name i.e. Ripl.config[:red_error].
70
+
71
+ == Create Custom Shells
72
+
73
+ Creating and starting a custom shell is as simple as:
48
74
 
49
75
  require 'ripl'
50
- # Do whatever setup you want ...
76
+ # Define plugins, load files, etc...
51
77
  Ripl.start
52
78
 
53
79
  Ripl.start takes options to customize your shell. For example if you wanted to
@@ -63,11 +89,17 @@ $PATH. For example, the file 'ripl-my_gem' would be invoked with `ripl my_gem`.
63
89
  command you can take arguments and parse your options as you please. For an example command,
64
90
  see {ripl-rails}[http://github.com/cldwalker/ripl-rails].
65
91
 
92
+ == Customize ripl
93
+
94
+ Since ripl is highly customizable, it loads ~/.riplrc before it does anything. This ruby file should
95
+ require and/or define plugins. Any ripl configurations via Ripl.config should also be done here.
96
+ For an example riplrc, see {mine}[http://github.com/cldwalker/dotfiles/tree/master/.riplrc].
97
+
66
98
  == Credits
67
99
  * janlelis for bug fix and tweaks
68
100
 
69
101
  == irb alternatives
70
- Some other irb alternatives which I encourage you to check out:
102
+ Some other irb alternatives to check out:
71
103
 
72
104
  * {ir}[http://github.com/raggi/ir]: nice and light
73
105
  * {irb2}[http://github.com/wycats/irb2]: yehuda katz's partial attempt at rewriting irb
data/bin/ripl CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'ripl'
4
- Ripl.run
4
+ Ripl::Runner.run
@@ -1,44 +1,16 @@
1
- require 'ripl/shell'
2
- require 'ripl/version'
3
-
4
1
  module Ripl
5
- extend self
6
-
7
- def run
8
- parse_options(ARGV)
9
- ARGV[0] ? run_command(ARGV) : start
10
- end
11
-
12
- def parse_options(argv)
13
- while argv[0] =~ /^-/
14
- case argv.shift
15
- when /-I=?(.*)/
16
- $LOAD_PATH.unshift(*$1.split(":"))
17
- when /-r=?(.*)/
18
- require $1
19
- when '-v', '--version'
20
- puts Ripl::VERSION; exit
21
- end
22
- end
2
+ def self.config
3
+ @config ||= {:readline=>true, :riplrc=>'~/.riplrc'}
23
4
  end
24
5
 
25
- def run_command(argv)
26
- exec "ripl-#{argv.shift}", *argv
27
- rescue Errno::ENOENT
28
- raise unless $!.message =~ /No such file or directory.*ripl-(\w+)/
29
- abort "`#{$1}' is not a ripl command."
30
- end
31
-
32
- def start(options={})
33
- shell(options).loop
34
- end
6
+ def self.start(*args); Runner.start(*args); end
35
7
 
36
- def shell(options={})
37
- @shell ||= begin
38
- require 'ripl/readline_shell'
39
- ReadlineShell.new(options)
40
- rescue LoadError
41
- Shell.new(options)
42
- end
8
+ def self.shell(options={})
9
+ @shell ||= Shell.create(config.merge(options))
43
10
  end
44
11
  end
12
+
13
+ require 'ripl/shell'
14
+ require 'ripl/runner'
15
+ require 'ripl/history'
16
+ require 'ripl/version'
@@ -1,11 +1,9 @@
1
- module Ripl
2
- module Completion
3
- def start_completion
4
- require 'bond'
5
- Bond.start
6
- true
7
- rescue LoadError
8
- false
9
- end
1
+ require 'bond'
2
+
3
+ module Ripl::Completion
4
+ def before_loop
5
+ super
6
+ Bond.start(config[:completion] || {}) unless Bond.started?
10
7
  end
11
8
  end
9
+ Ripl::Shell.send :include, Ripl::Completion
@@ -0,0 +1,24 @@
1
+ module Ripl::History
2
+ def history_file
3
+ @history_file ||= File.expand_path(config[:history])
4
+ end
5
+
6
+ def history; @history; end
7
+
8
+ def get_input
9
+ (@history << super)[-1]
10
+ end
11
+
12
+ def before_loop
13
+ config[:history], @history = '~/.irb_history', []
14
+ super
15
+ at_exit { write_history }
16
+ File.exists?(history_file) &&
17
+ IO.readlines(history_file).each {|e| history << e.chomp }
18
+ end
19
+
20
+ def write_history
21
+ File.open(history_file, 'w') {|f| f.write Array(history).join("\n") }
22
+ end
23
+ end
24
+ Ripl::Shell.send :include, Ripl::History
@@ -0,0 +1,9 @@
1
+ require 'readline'
2
+ module Ripl::Readline
3
+ def get_input
4
+ Readline.readline prompt, true
5
+ end
6
+
7
+ def history; Readline::HISTORY; end
8
+ end
9
+ Ripl::Shell.send :include, Ripl::Readline
@@ -0,0 +1,67 @@
1
+ module Ripl::Runner
2
+ def self.included(mod)
3
+ mod.extend(self)
4
+ end
5
+
6
+ module API
7
+ def run
8
+ load_rc(Ripl.config[:riplrc])
9
+ @riplrc = true
10
+ parse_options(ARGV)
11
+ ARGV[0] ? run_command(ARGV) : start
12
+ end
13
+
14
+ def parse_options(argv)
15
+ while argv[0] =~ /^-/
16
+ case argv.shift
17
+ when /-I=?(.*)/
18
+ $LOAD_PATH.unshift(*$1.split(":"))
19
+ when /-r=?(.*)/
20
+ require $1
21
+ when '-d'
22
+ $DEBUG = true
23
+ when '-v', '--version'
24
+ puts Ripl::VERSION; exit
25
+ when '-f'
26
+ ENV['RIPL_IRBRC'] = 'false'
27
+ when '-h', '--help'
28
+ puts IO.readlines(__FILE__).grep(/^#/).map {|e| e.sub(/^#\s/,'') }; exit
29
+ end
30
+ end
31
+ end
32
+
33
+ def run_command(argv)
34
+ exec "ripl-#{argv.shift}", *argv
35
+ rescue Errno::ENOENT
36
+ raise unless $!.message =~ /No such file or directory.*ripl-(\w+)/
37
+ abort "`#{$1}' is not a ripl command."
38
+ end
39
+
40
+ def start(options={})
41
+ load_rc(Ripl.config[:riplrc]) unless @riplrc
42
+ Ripl.config[:irbrc] = ENV['RIPL_IRBRC'] != 'false' if ENV['RIPL_IRBRC']
43
+ Ripl.shell(options).loop
44
+ end
45
+
46
+ def load_rc(file)
47
+ load file if File.exists?(File.expand_path(file))
48
+ rescue StandardError, SyntaxError
49
+ warn "Error while loading #{file}:\n"+ format_error($!)
50
+ end
51
+
52
+ def format_error(err)
53
+ "#{err.class}: #{err.message}\n #{err.backtrace.join("\n ")}"
54
+ end
55
+ end
56
+ extend API
57
+ end
58
+ __END__
59
+ # Usage: ripl [OPTIONS] [COMMAND] [ARGS]
60
+ #
61
+ # Options:
62
+ # -f Supress loading ~/.irbrc
63
+ # -d, --debug Set $DEBUG to true (same as `ruby -d')
64
+ # -I=PATH Add to front of $LOAD_PATH. Delimit multiple paths with ':'
65
+ # -r, --require=FILE Require file (same as `ruby -r')
66
+ # -v, --version Print ripl version
67
+ # -h, --help Print help
@@ -1,66 +1,79 @@
1
- module Ripl
2
- class Shell
3
- OPTIONS = {:name=>'ripl', :line=>1, :result_prompt=>'=> ', :prompt=>'>> ',
4
- :binding=>TOPLEVEL_BINDING, :irbrc=>'~/.irbrc', :history=>'~/.irb_history'}
1
+ class Ripl::Shell
2
+ OPTIONS = {:name=>'ripl', :line=>1, :result_prompt=>'=> ', :prompt=>'>> ',
3
+ :binding=>TOPLEVEL_BINDING, :irbrc=>'~/.irbrc'}
5
4
 
6
- attr_accessor :line, :binding, :result_prompt
7
- def initialize(options={})
8
- @options = OPTIONS.merge options
9
- @name, @binding, @line = @options.values_at(:name, :binding, :line)
10
- @irbrc = @options[:irbrc]
5
+ def self.create(options={})
6
+ require 'ripl/readline' if options[:readline]
7
+ require 'ripl/completion'
8
+ new(options)
9
+ rescue LoadError
10
+ new(options)
11
+ end
12
+
13
+ attr_accessor :line, :binding, :result_prompt, :last_result
14
+ def initialize(options={})
15
+ @options = OPTIONS.merge options
16
+ @name, @binding, @line = @options.values_at(:name, :binding, :line)
17
+ @irbrc = @options[:irbrc]
18
+ end
19
+
20
+ def loop
21
+ before_loop
22
+ during_loop
23
+ after_loop
24
+ end
25
+
26
+ def config; Ripl.config; end
27
+
28
+ module API
29
+ def before_loop
30
+ Ripl::Runner.load_rc(@irbrc) if @irbrc
11
31
  end
12
32
 
13
- def loop
14
- before_loop
33
+ def during_loop
15
34
  while true do
35
+ @error_raised = nil
16
36
  input = get_input
17
37
  break if !input || input == 'exit'
18
- puts loop_once(input)
38
+ loop_once(input)
39
+ puts(format_result(@last_result)) unless @error_raised
19
40
  end
20
- after_loop
21
41
  end
22
42
 
23
43
  def get_input
24
- print @options[:prompt]
44
+ print prompt
25
45
  $stdin.gets.chomp
26
46
  end
27
47
 
28
- def loop_once(input)
29
- begin
30
- result = loop_eval(input)
31
- rescue Exception => e
32
- print_eval_error(e)
33
- end
48
+ def prompt
49
+ @options[:prompt].respond_to?(:call) ? @options[:prompt].call : @options[:prompt]
50
+ end
34
51
 
52
+ def loop_once(input)
53
+ @last_result = loop_eval(input)
54
+ eval("_ = Ripl.shell.last_result", @binding)
55
+ rescue Exception => e
56
+ @error_raised = true
57
+ print_eval_error(e)
58
+ ensure
35
59
  @line += 1
36
- format_result result
37
60
  end
38
61
 
39
- def print_eval_error(e)
40
- warn format_error(e)
62
+ def loop_eval(str)
63
+ eval(str, @binding, "(#{@name})", @line)
41
64
  end
42
65
 
43
- def format_error(e)
44
- "#{e.class}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
66
+ def print_eval_error(err)
67
+ warn format_error(err)
45
68
  end
46
69
 
70
+ def format_error(err); Ripl::Runner.format_error(err); end
71
+
47
72
  def format_result(result)
48
73
  @options[:result_prompt] + result.inspect
49
74
  end
50
- end
51
-
52
- module Hooks
53
- def before_loop
54
- load @irbrc if @irbrc && File.exists?(File.expand_path(@irbrc))
55
- rescue StandardError, SyntaxError
56
- warn "Error while loading #{@irbrc}:\n"+ format_error($!)
57
- end
58
-
59
- def loop_eval(str)
60
- eval('_ = '+str, @binding, "(#{@name})", @line)
61
- end
62
75
 
63
76
  def after_loop; end
64
77
  end
65
- Shell.send :include, Hooks
78
+ include API
66
79
  end
@@ -1,3 +1,3 @@
1
1
  module Ripl
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ripl
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
8
  - 2
10
- version: 0.1.2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Gabriel Horner
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-01 00:00:00 -04:00
18
+ date: 2010-11-08 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -45,7 +45,9 @@ extra_rdoc_files:
45
45
  - LICENSE.txt
46
46
  files:
47
47
  - lib/ripl/completion.rb
48
- - lib/ripl/readline_shell.rb
48
+ - lib/ripl/history.rb
49
+ - lib/ripl/readline.rb
50
+ - lib/ripl/runner.rb
49
51
  - lib/ripl/shell.rb
50
52
  - lib/ripl/version.rb
51
53
  - lib/ripl.rb
@@ -1,25 +0,0 @@
1
- require 'readline'
2
- require 'ripl/completion'
3
-
4
- class Ripl::ReadlineShell < Ripl::Shell
5
- include Ripl::Completion
6
-
7
- def get_input
8
- Readline.readline @options[:prompt], true
9
- end
10
-
11
- def history_file
12
- @history_file ||= File.expand_path(@options[:history])
13
- end
14
-
15
- def before_loop
16
- start_completion
17
- super
18
- File.exists?(history_file) &&
19
- IO.readlines(history_file).each {|e| Readline::HISTORY << e.chomp }
20
- end
21
-
22
- def after_loop
23
- File.open(history_file, 'w') {|f| f.write Readline::HISTORY.to_a.join("\n") }
24
- end
25
- end