dietrb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/README ADDED
@@ -0,0 +1 @@
1
+ IRB on a diet, for MacRuby / Ruby 1.9
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ task :default => :spec
2
+
3
+ desc "Run the specs"
4
+ task :spec do
5
+ sh "macbacon #{FileList['spec/**/*_spec.rb'].join(' ')}"
6
+ end
7
+
8
+ desc "Run specs with Kicker"
9
+ task :kick do
10
+ sh "kicker -e 'rake spec' lib spec"
11
+ end
12
+
13
+ begin
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gemspec|
16
+ gemspec.name = "dietrb"
17
+ gemspec.summary = gemspec.description = "IRB on a diet, for MacRuby / Ruby 1.9"
18
+ gemspec.email = "eloy.de.enige@gmail.com"
19
+ gemspec.homepage = "http://github.com/alloy/dietrb"
20
+ gemspec.authors = ["Eloy Duran"]
21
+
22
+ gemspec.required_ruby_version = ::Gem::Requirement.new("~> 1.9")
23
+ end
24
+ rescue LoadError
25
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/dietrb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'irb'
4
+
5
+ IRB::Context.new(self).run
data/dietrb.gemspec ADDED
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{dietrb}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Eloy Duran"]
12
+ s.date = %q{2010-02-07}
13
+ s.default_executable = %q{dietrb}
14
+ s.description = %q{IRB on a diet, for MacRuby / Ruby 1.9}
15
+ s.email = %q{eloy.de.enige@gmail.com}
16
+ s.executables = ["dietrb"]
17
+ s.extra_rdoc_files = [
18
+ "README"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "README",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "bin/dietrb",
26
+ "dietrb.gemspec",
27
+ "lib/irb.rb",
28
+ "lib/irb/context.rb",
29
+ "lib/irb/source.rb",
30
+ "spec/context_spec.rb",
31
+ "spec/source_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/alloy/dietrb}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.required_ruby_version = Gem::Requirement.new("~> 1.9")
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{IRB on a diet, for MacRuby / Ruby 1.9}
40
+ s.test_files = [
41
+ "spec/context_spec.rb",
42
+ "spec/source_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ else
52
+ end
53
+ else
54
+ end
55
+ end
56
+
@@ -0,0 +1,78 @@
1
+ require 'readline'
2
+
3
+ module IRB
4
+ class Context
5
+ attr_reader :object, :binding, :line, :source
6
+
7
+ def initialize(object)
8
+ @object = object
9
+ @binding = object.instance_eval { binding }
10
+ @line = 1
11
+ clear_buffer
12
+ end
13
+
14
+ def evaluate(source)
15
+ result = eval("_ = (#{source})", @binding)
16
+ puts format_result(result)
17
+ result
18
+ rescue Exception => e
19
+ puts format_exception(e)
20
+ end
21
+
22
+ def readline
23
+ Readline.readline(prompt, true)
24
+ end
25
+
26
+ def run
27
+ while line = readline
28
+ continue = process_line(line)
29
+ break unless continue
30
+ end
31
+ end
32
+
33
+ # Returns whether or not the user wants to continue the current runloop.
34
+ # This can only be done at a code block indentation level of 0.
35
+ #
36
+ # For instance, this will continue:
37
+ #
38
+ # process_line("def foo") # => true
39
+ # process_line("quit") # => true
40
+ # process_line("end") # => true
41
+ #
42
+ # But at code block indentation level 0, `quit' means exit the runloop:
43
+ #
44
+ # process_line("quit") # => false
45
+ def process_line(line)
46
+ @source << line
47
+ return false if @source.to_s == "quit"
48
+
49
+ if @source.valid?
50
+ evaluate(@source)
51
+ clear_buffer
52
+ end
53
+ @line += 1
54
+
55
+ true
56
+ end
57
+
58
+ PROMPT = "irb(%s):%03d:%d> "
59
+
60
+ def prompt
61
+ PROMPT % [@object.inspect, @line, @source.level]
62
+ end
63
+
64
+ def format_result(result)
65
+ "=> #{result.inspect}"
66
+ end
67
+
68
+ def format_exception(e)
69
+ "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
70
+ end
71
+
72
+ private
73
+
74
+ def clear_buffer
75
+ @source = Source.new
76
+ end
77
+ end
78
+ end
data/lib/irb/source.rb ADDED
@@ -0,0 +1,103 @@
1
+ require 'ripper'
2
+
3
+ module IRB
4
+ class Source
5
+ attr_reader :buffer
6
+
7
+ def initialize(buffer = [])
8
+ @buffer = buffer
9
+ end
10
+
11
+ # Adds a source line to the buffer and flushes the cached reflection.
12
+ def <<(source)
13
+ @reflection = nil
14
+ @buffer << source.chomp
15
+ end
16
+
17
+ # Returns the accumulated source as a string, joined by newlines.
18
+ def source
19
+ @buffer.join("\n")
20
+ end
21
+
22
+ alias_method :to_s, :source
23
+
24
+ # Reflects on the accumulated source and returns the current code block
25
+ # indentation level.
26
+ def level
27
+ reflect.level
28
+ end
29
+
30
+ # Reflects on the accumulated source to see if it's a valid code block.
31
+ def valid?
32
+ reflect.valid?
33
+ end
34
+
35
+ # Returns a Reflector for the accumulated source and caches it.
36
+ def reflect
37
+ @reflection ||= Reflector.new(source)
38
+ end
39
+
40
+ class Reflector < Ripper::SexpBuilder
41
+ def initialize(source)
42
+ super
43
+ @level = 0
44
+ @valid = !parse.nil?
45
+ end
46
+
47
+ # Returns the code block indentation level.
48
+ #
49
+ # Reflector.new("").level # => 0
50
+ # Reflector.new("class Foo").level # => 1
51
+ # Reflector.new("class Foo; def foo").level # => 2
52
+ # Reflector.new("class Foo; def foo; end").level # => 1
53
+ # Reflector.new("class Foo; def foo; end; end").level # => 0
54
+ attr_reader :level
55
+
56
+ # Returns whether or not the source is a valid code block, but does not
57
+ # take syntax errors into account.
58
+ #
59
+ # For example, this is not a valid full code block:
60
+ #
61
+ # def foo; p :ok
62
+ #
63
+ # This however is:
64
+ #
65
+ # def foo; p :ok; end
66
+ def valid?
67
+ @valid
68
+ end
69
+
70
+ INCREASE_LEVEL_KEYWORDS = %w{ class module def begin if unless case while for do }
71
+
72
+ def on_kw(token) #:nodoc:
73
+ case token
74
+ when *INCREASE_LEVEL_KEYWORDS
75
+ @level += 1
76
+ when "end"
77
+ @level -= 1
78
+ end
79
+ super
80
+ end
81
+
82
+ def on_lbracket(token) #:nodoc:
83
+ @level += 1
84
+ super
85
+ end
86
+
87
+ def on_rbracket(token) #:nodoc:
88
+ @level -= 1
89
+ super
90
+ end
91
+
92
+ def on_lbrace(token) #:nodoc:
93
+ @level += 1
94
+ super
95
+ end
96
+
97
+ def on_rbrace(token) #:nodoc:
98
+ @level -= 1
99
+ super
100
+ end
101
+ end
102
+ end
103
+ end
data/lib/irb.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'irb/context'
2
+ require 'irb/source'
@@ -0,0 +1,162 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'tempfile'
3
+
4
+ main = self
5
+
6
+ describe "IRB::Context" do
7
+ before do
8
+ @context = IRB::Context.new(main)
9
+ end
10
+
11
+ it "initializes with an object and stores a copy of its binding" do
12
+ @context.object.should == main
13
+ eval("self", @context.binding).should == main
14
+ eval("x = :ok", @context.binding)
15
+ eval("y = x", @context.binding)
16
+ eval("y", @context.binding).should == :ok
17
+ end
18
+
19
+ it "initializes with an 'empty' state" do
20
+ @context.line.should == 1
21
+ @context.source.should.be.instance_of IRB::Source
22
+ @context.source.to_s.should == ""
23
+ end
24
+
25
+ it "does not use the same binding copy of the top level object" do
26
+ lambda { eval("x", @context.binding) }.should.raise NameError
27
+ end
28
+
29
+ it "returns a prompt string, displaying line number and code indentation level" do
30
+ @context.prompt.should == "irb(main):001:0> "
31
+ @context.instance_variable_set(:@line, 23)
32
+ @context.prompt.should == "irb(main):023:0> "
33
+ @context.source << "def foo"
34
+ @context.prompt.should == "irb(main):023:1> "
35
+ end
36
+
37
+ it "describes the context's object in the prompt" do
38
+ @context.prompt.should == "irb(main):001:0> "
39
+ o = Object.new
40
+ IRB::Context.new(o).prompt.should == "irb(#{o.inspect}):001:0> "
41
+ end
42
+
43
+ it "returns a formatted exception message" do
44
+ begin; DoesNotExist; rescue NameError => e; exception = e; end
45
+ @context.format_exception(exception).should ==
46
+ "NameError: uninitialized constant Bacon::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}"
47
+ end
48
+ end
49
+
50
+ describe "IRB::Context, when evaluating source" do
51
+ before do
52
+ @context = IRB::Context.new(main)
53
+ def @context.puts(string); @printed = string; end
54
+ end
55
+
56
+ it "evaluates code with the object's binding" do
57
+ @context.evaluate("self").should == main
58
+ end
59
+
60
+ it "prints the result" do
61
+ @context.evaluate("Hash[:foo, :foo]")
62
+ printed = @context.instance_variable_get(:@printed)
63
+ printed.should == "=> {:foo=>:foo}"
64
+ end
65
+
66
+ it "assigns the result to the local variable `_'" do
67
+ result = @context.evaluate("Object.new")
68
+ @context.evaluate("_").should == result
69
+ @context.evaluate("_").should == result
70
+ end
71
+
72
+ it "coerces the given source to a string first" do
73
+ o = Object.new
74
+ def o.to_s; "self"; end
75
+ @context.evaluate(o).should == main
76
+ end
77
+
78
+ it "rescues any type of exception" do
79
+ lambda {
80
+ @context.evaluate("DoesNotExist")
81
+ @context.evaluate("raise Exception")
82
+ }.should.not.raise
83
+ end
84
+
85
+ it "prints the exception that occurs" do
86
+ @context.evaluate("DoesNotExist")
87
+ printed = @context.instance_variable_get(:@printed)
88
+ printed.should.match /^NameError: uninitialized constant DoesNotExist/
89
+ end
90
+ end
91
+
92
+ class << Readline
93
+ attr_reader :received
94
+
95
+ def stub_input(*input)
96
+ @input = input
97
+ end
98
+
99
+ def readline(prompt, history)
100
+ @received = [prompt, history]
101
+ @input.shift
102
+ end
103
+ end
104
+
105
+ describe "IRB::Context, when receiving input" do
106
+ before do
107
+ @context = IRB::Context.new(main)
108
+ end
109
+
110
+ it "prints the prompt, reads a line, saves it to the history and returns it" do
111
+ Readline.stub_input("def foo")
112
+ @context.readline.should == "def foo"
113
+ Readline.received.should == ["irb(main):001:0> ", true]
114
+ end
115
+
116
+ it "processes the output" do
117
+ Readline.stub_input("def foo")
118
+ def @context.process_line(line); @received = line; false; end
119
+ @context.run
120
+ @context.instance_variable_get(:@received).should == "def foo"
121
+ end
122
+
123
+ it "adds the received code to the source buffer" do
124
+ @context.process_line("def foo")
125
+ @context.process_line("p :ok")
126
+ @context.source.to_s.should == "def foo\np :ok"
127
+ end
128
+
129
+ it "increases the current line number" do
130
+ @context.line.should == 1
131
+ @context.process_line("def foo")
132
+ @context.line.should == 2
133
+ @context.process_line("p :ok")
134
+ @context.line.should == 3
135
+ end
136
+
137
+ it "evaluates the buffered source once it's a valid code block" do
138
+ def @context.evaluate(source); @evaled = source; end
139
+
140
+ @context.process_line("def foo")
141
+ @context.process_line(":ok")
142
+ @context.process_line("end; p foo")
143
+
144
+ source = @context.instance_variable_get(:@evaled)
145
+ source.to_s.should == "def foo\n:ok\nend; p foo"
146
+ end
147
+
148
+ it "returns whether or not the runloop should continue, but only if the level is 0" do
149
+ @context.process_line("def foo").should == true
150
+ @context.process_line("quit").should == true
151
+ @context.process_line("end").should == true
152
+
153
+ @context.process_line("quit").should == false
154
+ end
155
+
156
+ it "exits the runloop if the user wishes so" do
157
+ Readline.stub_input("quit", "def foo")
158
+ def @context.process_line(line); @received = line; super; end
159
+ @context.run
160
+ @context.instance_variable_get(:@received).should.not == "def foo"
161
+ end
162
+ end
@@ -0,0 +1,137 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe "IRB::Source" do
4
+ before do
5
+ @source = IRB::Source.new
6
+ end
7
+
8
+ it "initializes with an empty buffer" do
9
+ @source.buffer.should == []
10
+ end
11
+
12
+ it "appends source to the buffer, removing trailing newlines" do
13
+ @source << "foo\n"
14
+ @source << "bar\r\n"
15
+ @source.buffer.should == %w{ foo bar }
16
+ end
17
+
18
+ it "returns the full buffered source, joined by newlines" do
19
+ @source.source.should == ""
20
+ @source << "foo\n"
21
+ @source.source.should == "foo"
22
+ @source << "bar\r\n"
23
+ @source.source.should == "foo\nbar"
24
+ end
25
+
26
+ it "aliases #to_s to #source" do
27
+ @source << "foo"
28
+ @source << "bar"
29
+ @source.to_s.should == "foo\nbar"
30
+ end
31
+
32
+ it "returns that the accumulated source is a valid code block" do
33
+ [
34
+ ["def foo", "p :ok", "end"],
35
+ ["class A; def", "foo(x); p x", "end; end"]
36
+ ].each do |buffer|
37
+ IRB::Source.new(buffer).should.be.valid
38
+ end
39
+ end
40
+
41
+ it "returns that the accumulated source is not a valid code block" do
42
+ [
43
+ ["def foo", "p :ok"],
44
+ ["class A; def", "foo(x); p x", "end"]
45
+ ].each do |buffer|
46
+ IRB::Source.new(buffer).should.not.be.valid
47
+ end
48
+ end
49
+
50
+ it "returns the current code block indentation level" do
51
+ @source.level.should == 0
52
+ @source << "class A"
53
+ @source.level.should == 1
54
+ @source << " def foo"
55
+ @source.level.should == 2
56
+ @source << " p :ok"
57
+ @source.level.should == 2
58
+ @source << " end"
59
+ @source.level.should == 1
60
+ @source << " class B"
61
+ @source.level.should == 2
62
+ @source << " def bar"
63
+ @source.level.should == 3
64
+ @source << " p :ok; end"
65
+ @source.level.should == 2
66
+ @source << " end; end"
67
+ @source.level.should == 0
68
+ end
69
+
70
+ it "caches the reflection when possible" do
71
+ @source << "def foo"
72
+ reflection = @source.reflect
73
+ @source.level
74
+ @source.valid?
75
+ @source.reflect.should == reflection
76
+
77
+ @source << "end"
78
+ @source.level
79
+ new_reflection = @source.reflect
80
+ new_reflection.should.not == reflection
81
+ @source.valid?
82
+ @source.reflect.should == new_reflection
83
+ end
84
+ end
85
+
86
+ describe "IRB::Source::Reflector" do
87
+ def reflect(source)
88
+ IRB::Source::Reflector.new(source)
89
+ end
90
+
91
+ it "returns whether or not the source is a valid code block" do
92
+ reflect("def foo").should.not.be.valid
93
+ reflect("def foo; p :ok").should.not.be.valid
94
+ reflect("def foo; p :ok; end").should.be.valid
95
+ end
96
+
97
+ it "returns the code block indentation level" do
98
+ reflect("").level.should == 0
99
+ reflect("class A").level.should == 1
100
+ reflect("class A; def foo").level.should == 2
101
+ reflect("class A; def foo; p :ok").level.should == 2
102
+ reflect("class A; def foo; p :ok; end").level.should == 1
103
+ reflect("class A; class B").level.should == 2
104
+ reflect("class A; class B; def bar").level.should == 3
105
+ reflect("class A; class B; def bar; p :ok; end").level.should == 2
106
+ reflect("class A; class B; def bar; p :ok; end; end; end").level.should == 0
107
+ end
108
+
109
+ it "correctly increases and decreases the code block indentation level for keywords" do
110
+ [
111
+ "class A",
112
+ "module A",
113
+ "def foo",
114
+ "begin",
115
+ "if x == :ok",
116
+ "unless x == :ko",
117
+ "case x",
118
+ "while x",
119
+ "for x in xs",
120
+ "x.each do"
121
+ ].each do |open|
122
+ reflect(open).level.should == 1
123
+ reflect("#{open}\nend").level.should == 0
124
+ end
125
+ end
126
+
127
+ it "correctly increases and decreases the code block indentation level for literals" do
128
+ [
129
+ ["lambda { |x|", "}"],
130
+ ["{", "}"],
131
+ ["[", "]"]
132
+ ].each do |open, close|
133
+ reflect(open).level.should == 1
134
+ reflect("#{open}\n#{close}").level.should == 0
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+
4
+ ROOT = File.expand_path('../../', __FILE__)
5
+ $:.unshift File.join(ROOT, 'lib')
6
+
7
+ require 'irb'
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dietrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eloy Duran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-07 00:00:00 +01:00
13
+ default_executable: dietrb
14
+ dependencies: []
15
+
16
+ description: IRB on a diet, for MacRuby / Ruby 1.9
17
+ email: eloy.de.enige@gmail.com
18
+ executables:
19
+ - dietrb
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - .gitignore
26
+ - README
27
+ - Rakefile
28
+ - VERSION
29
+ - bin/dietrb
30
+ - dietrb.gemspec
31
+ - lib/irb.rb
32
+ - lib/irb/context.rb
33
+ - lib/irb/source.rb
34
+ - spec/context_spec.rb
35
+ - spec/source_spec.rb
36
+ - spec/spec_helper.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/alloy/dietrb
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ~>
49
+ - !ruby/object:Gem::Version
50
+ version: "1.9"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: IRB on a diet, for MacRuby / Ruby 1.9
65
+ test_files:
66
+ - spec/context_spec.rb
67
+ - spec/source_spec.rb
68
+ - spec/spec_helper.rb