dietrb 0.1.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.
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