rink 1.0.0 → 1.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.
- data/README.rdoc +60 -5
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/rink.rb +2 -0
- data/lib/rink/console.rb +11 -22
- data/lib/rink/input_method/base.rb +4 -0
- data/lib/rink/input_method/file.rb +4 -0
- data/lib/rink/input_method/readline.rb +4 -0
- data/lib/rink/line_processor/pure_ruby.rb +4 -1
- data/lib/rink/namespace.rb +37 -0
- data/rink.gemspec +6 -3
- data/spec/lib/rink/console_spec.rb +7 -5
- data/spec/lib/rink/namespace_spec.rb +34 -0
- data/spec/lib/rink/pure_ruby_line_processor_spec.rb +2 -0
- metadata +7 -4
data/README.rdoc
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
= rink
|
2
2
|
|
3
|
+
gem install rink
|
4
|
+
|
3
5
|
Makes interactive consoles awesome. More specifically, it does this by automating as much as conceivably possible so
|
4
6
|
that you can get right down to what you really care about: your application.
|
5
7
|
|
@@ -23,8 +25,56 @@ application-specific interactive console, extend the Rink::Console class:
|
|
23
25
|
#...
|
24
26
|
MyConsole.new
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
=== The Ruby Console
|
29
|
+
|
30
|
+
With the default set of options, any input that is not processed as a custom command will be executed as Ruby code,
|
31
|
+
much like the Ruby IRB. Guess there's not much more to say about that, so here's an example to drive the point home:
|
32
|
+
|
33
|
+
>> Interactive Console <<
|
34
|
+
|
35
|
+
Rink::Console > class Person
|
36
|
+
Rink::Console > attr_reader :first_name
|
37
|
+
Rink::Console > def initialize
|
38
|
+
Rink::Console > @first_name = "Colin"
|
39
|
+
Rink::Console > end
|
40
|
+
Rink::Console > end
|
41
|
+
=> nil
|
42
|
+
Rink::Console > Person.new.first_name
|
43
|
+
=> "Colin"
|
44
|
+
Rink::Console >
|
45
|
+
|
46
|
+
|
47
|
+
=== The Rink Namespace
|
48
|
+
|
49
|
+
By default, Rink will execute code within the context of its namespace (see the Rink::Console class documentation for
|
50
|
+
details). Basically, you can choose any Ruby object and run the console within the context of that object. For example:
|
51
|
+
|
52
|
+
class Person
|
53
|
+
attr_reader :first_name
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@first_name = "Colin"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class MyConsole < Rink::Console
|
61
|
+
option :namespace => Person.new
|
62
|
+
end
|
63
|
+
|
64
|
+
MyConsole.new
|
65
|
+
|
66
|
+
>> Interactive Console <<
|
67
|
+
|
68
|
+
MyConsole > self
|
69
|
+
=> #<Person:0x10171c3a0 @first_name="Colin">
|
70
|
+
|
71
|
+
MyConsole > first_name
|
72
|
+
=> "Colin"
|
73
|
+
|
74
|
+
|
75
|
+
=== Custom Commands
|
76
|
+
|
77
|
+
Rink isn't limited only to running Ruby code, however. You can also add specific commands to Rink like so:
|
28
78
|
|
29
79
|
class MyConsole < Rink::Console
|
30
80
|
command :help do |args|
|
@@ -71,10 +121,13 @@ In addition to commands, you can also easily add or override default options in
|
|
71
121
|
MyConsole > greet_me
|
72
122
|
Hello there!
|
73
123
|
|
124
|
+
See the class documentation for Rink::Console for much more detailed information, including how to disable Ruby code
|
125
|
+
processing entirely.
|
126
|
+
|
74
127
|
== Running From The Command Line
|
75
128
|
|
76
|
-
You've seen numerous examples of how to start Rink from within Ruby. The same thing works from within IRB or
|
77
|
-
task. Additionally, Rink ships with a script so that you can run it directly from the command line:
|
129
|
+
You've seen numerous examples of how to start Rink from within Ruby. The same thing works from within IRB or inside of
|
130
|
+
a Rake task. Additionally, Rink ships with a script so that you can run it directly from the command line:
|
78
131
|
|
79
132
|
$ rink
|
80
133
|
|
@@ -113,9 +166,11 @@ You can also pass IO objects in directly:
|
|
113
166
|
end
|
114
167
|
end
|
115
168
|
|
169
|
+
== Dealing With Errors
|
170
|
+
|
116
171
|
Normally, if an error occurs, Rink will print a nicely-formatted message to its output stream. This is helpful if you're
|
117
172
|
using the console but not if you're trying to write tests for one. So, you can disable error catching within Rink by
|
118
|
-
passing
|
173
|
+
passing +:rescue_errors => false+ to the initializer:
|
119
174
|
|
120
175
|
MyConsole.new(:input => "raise", :rescue_errors => false)
|
121
176
|
#=> RuntimeError!
|
data/Rakefile
CHANGED
@@ -8,7 +8,7 @@ begin
|
|
8
8
|
gem.summary = %Q{Makes interactive consoles awesome.}
|
9
9
|
gem.description = %Q{Makes interactive consoles awesome.}
|
10
10
|
gem.email = "sinisterchipmunk@gmail.com"
|
11
|
-
gem.homepage = "http://
|
11
|
+
gem.homepage = "http://www.thoughtsincomputation.com"
|
12
12
|
gem.authors = ["Colin MacKenzie IV"]
|
13
13
|
|
14
14
|
gem.add_development_dependency "rspec", ">= 1.3.0"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.1
|
data/lib/rink.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
#require 'sc-ansi'
|
2
2
|
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "core_ext/object"))
|
3
4
|
require File.expand_path(File.join(File.dirname(__FILE__), "rink/delegation"))
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'rink/namespace'))
|
4
6
|
require File.expand_path(File.join(File.dirname(__FILE__), "rink/lexer"))
|
5
7
|
require File.expand_path(File.join(File.dirname(__FILE__), 'rink/io_methods'))
|
6
8
|
require File.expand_path(File.join(File.dirname(__FILE__), "rink/line_processor/base"))
|
data/lib/rink/console.rb
CHANGED
@@ -2,7 +2,7 @@ module Rink
|
|
2
2
|
class Console
|
3
3
|
extend Rink::Delegation
|
4
4
|
attr_reader :line_processor
|
5
|
-
attr_writer :
|
5
|
+
attr_writer :silenced
|
6
6
|
attr_reader :input, :output
|
7
7
|
delegate :silenced?, :print, :write, :puts, :to => :output, :allow_nil => true
|
8
8
|
delegate :banner, :commands, :to => 'self.class'
|
@@ -11,10 +11,15 @@ module Rink
|
|
11
11
|
# call super with :defer => true -- because otherwise Rink will start the console before your init code executes.
|
12
12
|
def initialize(options = {})
|
13
13
|
options = default_options.merge(options)
|
14
|
+
@namespace = Rink::Namespace.new
|
14
15
|
apply_options(options)
|
15
16
|
run(options) unless options[:defer]
|
16
17
|
end
|
17
18
|
|
19
|
+
def namespace=(ns)
|
20
|
+
@namespace.replace(ns)
|
21
|
+
end
|
22
|
+
|
18
23
|
# The Ruby object within whose context the console will be run.
|
19
24
|
# For example:
|
20
25
|
# class CustomNamespace
|
@@ -37,16 +42,7 @@ module Rink
|
|
37
42
|
# Rink::Console.new(:namespace => :self)
|
38
43
|
#
|
39
44
|
def namespace
|
40
|
-
@namespace
|
41
|
-
# We want namespace to be any object, and in order to do that, Rink will call namespace#binding.
|
42
|
-
# But by default, the namespace should be TOPLEVEL_BINDING. If we set @namespace to this,
|
43
|
-
# Rink will call TOPLEVEL_BINDING#binding, which is an error. So instead we'll create a singleton
|
44
|
-
# object and override #binding on that object to return TOPLEVEL_BINDING. Effectively, that
|
45
|
-
# singleton object becomes (more-or-less) a proxy into the toplevel object. (Is there a better way?)
|
46
|
-
klass = Class.new(Object)
|
47
|
-
klass.send(:define_method, :binding) { TOPLEVEL_BINDING }
|
48
|
-
klass.new
|
49
|
-
end
|
45
|
+
@namespace.ns
|
50
46
|
end
|
51
47
|
|
52
48
|
# Runs a series of commands in the context of this Console. Input can be either a string
|
@@ -103,10 +99,7 @@ module Rink
|
|
103
99
|
|
104
100
|
if options[:namespace]
|
105
101
|
ns = options[:namespace] == :self ? self : options[:namespace]
|
106
|
-
|
107
|
-
@namespace = ns
|
108
|
-
@namespace_binding = nil
|
109
|
-
end
|
102
|
+
@namespace.replace(ns)
|
110
103
|
end
|
111
104
|
|
112
105
|
if @input
|
@@ -255,10 +248,10 @@ module Rink
|
|
255
248
|
include Rink::IOMethods
|
256
249
|
|
257
250
|
def evaluate_scanner_statement
|
258
|
-
_caller =
|
251
|
+
_caller = @namespace.evaluate("caller")
|
259
252
|
scanner.each_top_level_statement do |code, line_no|
|
260
253
|
begin
|
261
|
-
return
|
254
|
+
return @namespace.evaluate(code, self.class.name, line_no)
|
262
255
|
rescue
|
263
256
|
# clean out the backtrace so that it starts with the console line instead of program invocation.
|
264
257
|
_caller.reverse.each { |line| $!.backtrace.pop if $!.backtrace.last == line }
|
@@ -267,10 +260,6 @@ module Rink
|
|
267
260
|
end
|
268
261
|
end
|
269
262
|
|
270
|
-
def namespace_binding
|
271
|
-
@namespace_binding ||= namespace.send(:binding)
|
272
|
-
end
|
273
|
-
|
274
263
|
def prepare_scanner_for(code)
|
275
264
|
# the scanner prompt should be empty at first because we've already received the first line. Nothing to prompt for.
|
276
265
|
scanner.set_prompt nil
|
@@ -319,7 +308,7 @@ module Rink
|
|
319
308
|
# Runs the autocomplete method from the line processor, then reformats its result to be an array.
|
320
309
|
def autocomplete(line)
|
321
310
|
return [] unless @line_processor
|
322
|
-
result = @line_processor.autocomplete(line, namespace)
|
311
|
+
result = @line_processor.autocomplete(line, namespace.ns)
|
323
312
|
case result
|
324
313
|
when String
|
325
314
|
[result]
|
@@ -119,7 +119,7 @@ module Rink
|
|
119
119
|
|
120
120
|
def autocomplete(line, namespace)
|
121
121
|
# Borrowed from irb/completion.rb
|
122
|
-
case line
|
122
|
+
result = case line
|
123
123
|
when /^(\/[^\/]*\/)\.([^.]*)$/
|
124
124
|
autocomplete_for_regexp($1, Regexp.quote($2))
|
125
125
|
|
@@ -161,6 +161,9 @@ module Rink
|
|
161
161
|
candidates = eval("methods | private_methods | local_variables | self.class.constants", namespace.send(:binding))
|
162
162
|
(candidates|RESERVED_WORDS).grep(/^#{Regexp.quote(line)}/)
|
163
163
|
end
|
164
|
+
result.kind_of?(Array) ?
|
165
|
+
result.collect { |r| r.kind_of?(String) ? r : r.to_s } :
|
166
|
+
result.kind_of?(String) ? result : result.to_s
|
164
167
|
end
|
165
168
|
|
166
169
|
private
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rink
|
2
|
+
class Namespace
|
3
|
+
def initialize(ns = self.default_ns)
|
4
|
+
replace(ns)
|
5
|
+
end
|
6
|
+
|
7
|
+
def binding
|
8
|
+
@ns_binding
|
9
|
+
end
|
10
|
+
|
11
|
+
def ns
|
12
|
+
@namespace
|
13
|
+
end
|
14
|
+
|
15
|
+
def replace(ns)
|
16
|
+
return ns if ns.eql?(@namespace)
|
17
|
+
@ns_binding = ns.instance_eval { binding }
|
18
|
+
#ns.send(:binding) # this no worky in 1.9
|
19
|
+
@namespace = ns
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate(code, *filename_lineno)
|
23
|
+
eval code, @ns_binding, *filename_lineno
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_ns
|
27
|
+
# We want namespace to be any object, and in order to do that, Rink will call namespace#binding.
|
28
|
+
# But by default, the namespace should be TOPLEVEL_BINDING. If we set @namespace to this,
|
29
|
+
# Rink will call TOPLEVEL_BINDING#binding, which is an error. So instead we'll create a singleton
|
30
|
+
# object and override #binding on that object to return TOPLEVEL_BINDING. Effectively, that
|
31
|
+
# singleton object becomes (more-or-less) a proxy into the toplevel object. (Is there a better way?)
|
32
|
+
klass = Class.new(Object)
|
33
|
+
klass.send(:define_method, :binding) { TOPLEVEL_BINDING }
|
34
|
+
klass.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/rink.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rink}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Colin MacKenzie IV"]
|
12
|
-
s.date = %q{2010-08-
|
12
|
+
s.date = %q{2010-08-07}
|
13
13
|
s.default_executable = %q{rink}
|
14
14
|
s.description = %q{Makes interactive consoles awesome.}
|
15
15
|
s.email = %q{sinisterchipmunk@gmail.com}
|
@@ -38,18 +38,20 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/rink/lexer.rb",
|
39
39
|
"lib/rink/line_processor/base.rb",
|
40
40
|
"lib/rink/line_processor/pure_ruby.rb",
|
41
|
+
"lib/rink/namespace.rb",
|
41
42
|
"lib/rink/output_method/base.rb",
|
42
43
|
"lib/rink/output_method/io.rb",
|
43
44
|
"rink.gemspec",
|
44
45
|
"spec/lib/core_ext/object_spec.rb",
|
45
46
|
"spec/lib/rink/console_spec.rb",
|
46
47
|
"spec/lib/rink/io_methods_spec.rb",
|
48
|
+
"spec/lib/rink/namespace_spec.rb",
|
47
49
|
"spec/lib/rink/pure_ruby_line_processor_spec.rb",
|
48
50
|
"spec/lib/rink_spec.rb",
|
49
51
|
"spec/spec.opts",
|
50
52
|
"spec/spec_helper.rb"
|
51
53
|
]
|
52
|
-
s.homepage = %q{http://
|
54
|
+
s.homepage = %q{http://www.thoughtsincomputation.com}
|
53
55
|
s.rdoc_options = ["--charset=UTF-8"]
|
54
56
|
s.require_paths = ["lib"]
|
55
57
|
s.rubygems_version = %q{1.3.6}
|
@@ -61,6 +63,7 @@ Gem::Specification.new do |s|
|
|
61
63
|
"spec/lib/rink",
|
62
64
|
"spec/lib/rink/console_spec.rb",
|
63
65
|
"spec/lib/rink/io_methods_spec.rb",
|
66
|
+
"spec/lib/rink/namespace_spec.rb",
|
64
67
|
"spec/lib/rink/pure_ruby_line_processor_spec.rb",
|
65
68
|
"spec/lib/rink_spec.rb",
|
66
69
|
"spec/spec.opts",
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rink::Console do
|
4
|
-
class ExampleObject
|
4
|
+
class ::ExampleObject
|
5
5
|
def inspect
|
6
6
|
"#<example>"
|
7
7
|
end
|
@@ -13,7 +13,7 @@ describe Rink::Console do
|
|
13
13
|
else options = {}
|
14
14
|
end
|
15
15
|
input = input.flatten.join("\n")
|
16
|
-
subject.run(input,
|
16
|
+
subject.run(input, {:output => @output, :silent => false, :rescue_errors => false}.merge(options))
|
17
17
|
end
|
18
18
|
|
19
19
|
before(:each) { @input = ""; @output = "" }
|
@@ -93,16 +93,16 @@ describe Rink::Console do
|
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should print return value, inspected" do
|
96
|
-
console("ExampleObject.new")
|
96
|
+
console("ExampleObject.new", :rescue_errors => false)
|
97
97
|
@output.should =~ / => #<example>$/
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should not raise exceptions" do
|
101
|
-
proc { console("test") }.should_not raise_error
|
101
|
+
proc { console("test", :rescue_errors => true) }.should_not raise_error
|
102
102
|
end
|
103
103
|
|
104
104
|
it "should print exceptions" do
|
105
|
-
console("test")
|
105
|
+
console("test", :rescue_errors => true)
|
106
106
|
@output.should =~ /ArgumentError: /
|
107
107
|
end
|
108
108
|
|
@@ -115,6 +115,8 @@ describe Rink::Console do
|
|
115
115
|
it "should allow setting namespace" do
|
116
116
|
obj = ExampleObject.new
|
117
117
|
subject.namespace = obj
|
118
|
+
subject.namespace.should == obj
|
119
|
+
|
118
120
|
console("self").should == obj
|
119
121
|
end
|
120
122
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rink::Namespace do
|
4
|
+
class ExampleObject
|
5
|
+
def initialize
|
6
|
+
@i = 5
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { Rink::Namespace.new }
|
11
|
+
|
12
|
+
it "should use the top ns by default" do
|
13
|
+
subject.binding.should == TOPLEVEL_BINDING
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a different ns" do
|
17
|
+
subject { Rink::Namespace.new(ExampleObject.new) }
|
18
|
+
|
19
|
+
it "should not use the toplevel binding" do
|
20
|
+
subject.binding.should_not == TOPLEVEL_BINDING
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should evaluate code" do
|
24
|
+
subject.evaluate("@i").should == 5
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be replaceable" do
|
29
|
+
subject.evaluate("@i").should_not == 5
|
30
|
+
|
31
|
+
subject.replace(ExampleObject.new)
|
32
|
+
subject.evaluate("@i").should == 5
|
33
|
+
end
|
34
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 1
|
9
|
+
version: 1.0.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Colin MacKenzie IV
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-08-
|
17
|
+
date: 2010-08-07 00:00:00 -04:00
|
18
18
|
default_executable: rink
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -60,18 +60,20 @@ files:
|
|
60
60
|
- lib/rink/lexer.rb
|
61
61
|
- lib/rink/line_processor/base.rb
|
62
62
|
- lib/rink/line_processor/pure_ruby.rb
|
63
|
+
- lib/rink/namespace.rb
|
63
64
|
- lib/rink/output_method/base.rb
|
64
65
|
- lib/rink/output_method/io.rb
|
65
66
|
- rink.gemspec
|
66
67
|
- spec/lib/core_ext/object_spec.rb
|
67
68
|
- spec/lib/rink/console_spec.rb
|
68
69
|
- spec/lib/rink/io_methods_spec.rb
|
70
|
+
- spec/lib/rink/namespace_spec.rb
|
69
71
|
- spec/lib/rink/pure_ruby_line_processor_spec.rb
|
70
72
|
- spec/lib/rink_spec.rb
|
71
73
|
- spec/spec.opts
|
72
74
|
- spec/spec_helper.rb
|
73
75
|
has_rdoc: true
|
74
|
-
homepage: http://
|
76
|
+
homepage: http://www.thoughtsincomputation.com
|
75
77
|
licenses: []
|
76
78
|
|
77
79
|
post_install_message:
|
@@ -104,6 +106,7 @@ test_files:
|
|
104
106
|
- spec/lib/core_ext/object_spec.rb
|
105
107
|
- spec/lib/rink/console_spec.rb
|
106
108
|
- spec/lib/rink/io_methods_spec.rb
|
109
|
+
- spec/lib/rink/namespace_spec.rb
|
107
110
|
- spec/lib/rink/pure_ruby_line_processor_spec.rb
|
108
111
|
- spec/lib/rink_spec.rb
|
109
112
|
- spec/spec.opts
|