ripl 0.1.2 → 0.2.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.
@@ -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