rink 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,139 @@
1
+ module Rink
2
+ # Blatantly ripped from ActiveSupport and pasted here to avoid the extra dependency.
3
+ # Only applied to objects internal to Rink.
4
+ module Delegation
5
+ # Provides a delegate class method to easily expose contained objects' methods
6
+ # as your own. Pass one or more methods (specified as symbols or strings)
7
+ # and the name of the target object as the final <tt>:to</tt> option (also a symbol
8
+ # or string). At least one method and the <tt>:to</tt> option are required.
9
+ #
10
+ # Delegation is particularly useful with Active Record associations:
11
+ #
12
+ # class Greeter < ActiveRecord::Base
13
+ # def hello() "hello" end
14
+ # def goodbye() "goodbye" end
15
+ # end
16
+ #
17
+ # class Foo < ActiveRecord::Base
18
+ # belongs_to :greeter
19
+ # delegate :hello, :to => :greeter
20
+ # end
21
+ #
22
+ # Foo.new.hello # => "hello"
23
+ # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
24
+ #
25
+ # Multiple delegates to the same target are allowed:
26
+ #
27
+ # class Foo < ActiveRecord::Base
28
+ # belongs_to :greeter
29
+ # delegate :hello, :goodbye, :to => :greeter
30
+ # end
31
+ #
32
+ # Foo.new.goodbye # => "goodbye"
33
+ #
34
+ # Methods can be delegated to instance variables, class variables, or constants
35
+ # by providing them as a symbols:
36
+ #
37
+ # class Foo
38
+ # CONSTANT_ARRAY = [0,1,2,3]
39
+ # @@class_array = [4,5,6,7]
40
+ #
41
+ # def initialize
42
+ # @instance_array = [8,9,10,11]
43
+ # end
44
+ # delegate :sum, :to => :CONSTANT_ARRAY
45
+ # delegate :min, :to => :@@class_array
46
+ # delegate :max, :to => :@instance_array
47
+ # end
48
+ #
49
+ # Foo.new.sum # => 6
50
+ # Foo.new.min # => 4
51
+ # Foo.new.max # => 11
52
+ #
53
+ # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
54
+ # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
55
+ # delegated to.
56
+ #
57
+ # Person = Struct.new(:name, :address)
58
+ #
59
+ # class Invoice < Struct.new(:client)
60
+ # delegate :name, :address, :to => :client, :prefix => true
61
+ # end
62
+ #
63
+ # john_doe = Person.new("John Doe", "Vimmersvej 13")
64
+ # invoice = Invoice.new(john_doe)
65
+ # invoice.client_name # => "John Doe"
66
+ # invoice.client_address # => "Vimmersvej 13"
67
+ #
68
+ # It is also possible to supply a custom prefix.
69
+ #
70
+ # class Invoice < Struct.new(:client)
71
+ # delegate :name, :address, :to => :client, :prefix => :customer
72
+ # end
73
+ #
74
+ # invoice = Invoice.new(john_doe)
75
+ # invoice.customer_name # => "John Doe"
76
+ # invoice.customer_address # => "Vimmersvej 13"
77
+ #
78
+ # If the object to which you delegate can be nil, you may want to use the
79
+ # :allow_nil option. In that case, it returns nil instead of raising a
80
+ # NoMethodError exception:
81
+ #
82
+ # class Foo
83
+ # attr_accessor :bar
84
+ # def initialize(bar = nil)
85
+ # @bar = bar
86
+ # end
87
+ # delegate :zoo, :to => :bar
88
+ # end
89
+ #
90
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
91
+ #
92
+ # class Foo
93
+ # attr_accessor :bar
94
+ # def initialize(bar = nil)
95
+ # @bar = bar
96
+ # end
97
+ # delegate :zoo, :to => :bar, :allow_nil => true
98
+ # end
99
+ #
100
+ # Foo.new.zoo # returns nil
101
+ #
102
+ def delegate(*methods)
103
+ options = methods.pop
104
+ unless options.is_a?(Hash) && to = options[:to]
105
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
106
+ end
107
+
108
+ if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
109
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
110
+ end
111
+
112
+ prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
113
+
114
+ file, line = caller.first.split(':', 2)
115
+ line = line.to_i
116
+
117
+ methods.each do |method|
118
+ on_nil =
119
+ if options[:allow_nil]
120
+ 'return'
121
+ else
122
+ %(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
123
+ end
124
+
125
+ module_eval(<<-EOS, file, line)
126
+ def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
127
+ #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
128
+ rescue NoMethodError # rescue NoMethodError
129
+ if #{to}.nil? # if client.nil?
130
+ #{on_nil}
131
+ else # else
132
+ raise # raise
133
+ end # end
134
+ end # end
135
+ EOS
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,41 @@
1
+ module Rink
2
+ module InputMethod
3
+ class Base
4
+ STDIN_FILE_NAME = "(line)"
5
+
6
+ def initialize(prompt = " > ", file = STDIN_FILE_NAME)
7
+ @prompt = prompt
8
+ @output = nil
9
+ @filename = file
10
+ end
11
+
12
+ attr_reader :filename
13
+ attr_accessor :prompt
14
+ attr_accessor :output
15
+
16
+ def input
17
+ raise NotImplementedError, "input"
18
+ end
19
+
20
+ def print(*args)
21
+ @output.print(*args) if @output
22
+ end
23
+
24
+ def puts(*args)
25
+ @output.puts(*args) if @output
26
+ end
27
+
28
+ def write(*args)
29
+ @output.write(*args) if @output
30
+ end
31
+
32
+ def gets
33
+ raise NotImplementedError, "gets"
34
+ end
35
+
36
+ def readable_after_eof?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ module Rink
2
+ module InputMethod
3
+ class File < Rink::InputMethod::Base
4
+ def initialize(file)
5
+ super
6
+ @io = file
7
+ @line_num = 0
8
+ @lines = []
9
+ end
10
+
11
+ def eof?
12
+ @io.eof?
13
+ end
14
+
15
+ def gets
16
+ print @prompt
17
+ line = @lines[@line_num += 1] = @io.gets
18
+ line += "\n" unless !line || line =~ /\n/
19
+ print(line)
20
+ line
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ module Rink
2
+ module InputMethod
3
+ class IO < Rink::InputMethod::Base
4
+ attr_accessor :input
5
+
6
+ def initialize(input = $stdin)
7
+ super()
8
+ @input = input
9
+ @line_num = 0
10
+ @lines = []
11
+ end
12
+
13
+ def gets
14
+ print @prompt
15
+ line = @lines[@line_num += 1] = input.gets
16
+ line += "\n" unless !line || line =~ /\n/
17
+ print line if line
18
+ line
19
+ end
20
+
21
+ def eof?
22
+ input.eof?
23
+ end
24
+
25
+ def readable_after_eof?
26
+ true
27
+ end
28
+
29
+ def [](line_number)
30
+ @lines[line_number]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,49 @@
1
+ begin
2
+ require 'readline'
3
+
4
+ module Rink
5
+ module InputMethod
6
+ class Readline < Rink::InputMethod::Base
7
+ attr_accessor :completion_append_character, :completion_proc, :prompt
8
+
9
+ def initialize(completion_proc = proc { |line| [] })
10
+ super()
11
+
12
+ @completion_append_character = nil
13
+ @completion_proc = completion_proc
14
+ @line_num = 0
15
+ @lines = []
16
+ @eof = false
17
+ end
18
+
19
+ def gets
20
+ # in case they were changed. Do we need this here?
21
+ ::Readline.completion_append_character = completion_append_character
22
+ ::Readline.completion_proc = completion_proc
23
+
24
+ if line = ::Readline.readline(@prompt, false)
25
+ ::Readline::HISTORY.push(line) if !line.empty?
26
+ @lines[@line_num += 1] = line + "\n"
27
+ else
28
+ @eof = true
29
+ line
30
+ end
31
+ end
32
+
33
+ def eof?
34
+ @eof
35
+ end
36
+
37
+ def readable_after_eof?
38
+ true
39
+ end
40
+
41
+ def [](line_num)
42
+ @line[num]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ rescue LoadError
48
+ # Fail silently. Rink will fall back to an IO input type with STDIN.
49
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "input_method/base"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "input_method/readline"))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "input_method/io"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "input_method/file"))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), "output_method/base"))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "output_method/io"))
7
+ require 'stringio'
8
+
9
+ module Rink
10
+ module IOMethods
11
+ def setup_input_method(input)
12
+ @input = case input
13
+ when Rink::InputMethod::Base
14
+ input
15
+ when STDIN
16
+ defined?(Rink::InputMethod::Readline) ? Rink::InputMethod::Readline.new : Rink::InputMethod::IO.new
17
+ when File
18
+ Rink::InputMethod::File.new(input)
19
+ when String
20
+ Rink::InputMethod::IO.new(StringIO.new(input))
21
+ when ::IO, StringIO
22
+ Rink::InputMethod::IO.new(input)
23
+ #when nil
24
+ # nil
25
+ else raise ArgumentError, "Unexpected input type: #{input.class}"
26
+ end
27
+ end
28
+
29
+ def setup_output_method(output)
30
+ @output = case output
31
+ when Rink::OutputMethod::Base
32
+ output
33
+ when STDOUT, STDERR, ::IO, StringIO then
34
+ Rink::OutputMethod::IO.new(output)
35
+ when String
36
+ Rink::OutputMethod::IO.new(StringIO.new(output))
37
+ when nil then
38
+ nil
39
+ else
40
+ raise ArgumentError, "Unexpected ouptut type: #{output.class}"
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/rink/lexer.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'irb/ruby-lex'
2
+
3
+ module Rink
4
+ class Lexer < ::RubyLex
5
+ extend Rink::Delegation
6
+ attr_accessor :output
7
+ delegate :print, :puts, :write, :p, :to => :output
8
+
9
+ def initialize(output = nil)
10
+ @output = output
11
+ super()
12
+ end
13
+
14
+ # RubyLex prompts unconditionally once the prompt is first set (not very reset-friendly), so we need to fix that.
15
+ def set_prompt(p = nil, &block)
16
+ if p.nil? && !block_given?
17
+ @prompt = nil # this will go back to NOT prompting
18
+ else
19
+ super
20
+ end
21
+ end
22
+ # overriding this method because we don't want to prompt
23
+ # def each_top_level_statement
24
+ # initialize_input
25
+ # catch(:TERM_INPUT) do
26
+ # loop do
27
+ # begin
28
+ # @continue = false
29
+ # prompt
30
+ # unless l = lex
31
+ # throw :TERM_INPUT if @line == ''
32
+ # else
33
+ # #p l
34
+ # @line.concat l
35
+ # if @ltype or @continue or @indent > 0
36
+ # next
37
+ # end
38
+ # end
39
+ # if @line != "\n"
40
+ # yield @line, @exp_line_no
41
+ # end
42
+ # break unless l
43
+ # @line = ''
44
+ # @exp_line_no = @line_no
45
+ #
46
+ # @indent = 0
47
+ # @indent_stack = []
48
+ # prompt
49
+ # rescue TerminateLineInput
50
+ # initialize_input
51
+ # prompt
52
+ # get_readed
53
+ # end
54
+ # end
55
+ # end
56
+ # end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Rink
2
+ module LineProcessor
3
+ # The Line Processor takes partial lines and performs operations on them. This is usually triggered by some special
4
+ # character or combination of characters, such as TAB, ARROW UP, ARROW DOWN, and so forth.
5
+ #
6
+ class Base
7
+ attr_reader :source
8
+
9
+ def initialize(source = nil)
10
+ @source = source
11
+ end
12
+
13
+ # Autocomplete is usually triggered by a TAB character and generally involves looking at the beginning of a line
14
+ # and finding the command the user is most likely trying to type. This saves typing for the user and creates a more
15
+ # intuitive interface.
16
+ #
17
+ # This method returns either a single String or an array of Strings.
18
+ def autocomplete(line, namespace)
19
+ raise NotImplementedError, "autocomplete"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,180 @@
1
+ module Rink
2
+ module LineProcessor
3
+ # Performs autocompletion based on method names of objects used. This algorithm is identical to that of IRB.
4
+ class PureRuby < Rink::LineProcessor::Base
5
+ OPERATORS = ["%", "&", "*", "**", "+", "-", "/", "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
6
+ "[]", "[]=", "^"
7
+ ] unless defined?(OPERATORS)
8
+
9
+ RESERVED_WORDS = [ "BEGIN", "END", "alias", "and", "begin", "break", "case", "class", "def", "defined", "do",
10
+ "else", "elsif", "end", "ensure", "false", "for", "if", "in", "module", "next", "nil", "not",
11
+ "or", "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
12
+ "until", "when", "while", "yield"
13
+ ] unless defined?(RESERVED_WORDS)
14
+
15
+ def autocomplete_for_regexp(receiver, message)
16
+ candidates = Regexp.instance_methods(true)
17
+ select_message(receiver, message, candidates)
18
+ end
19
+
20
+ def autocomplete_for_array(receiver, message)
21
+ candidates = Array.instance_methods(true)
22
+ select_message(receiver, message, candidates)
23
+ end
24
+
25
+ def autocomplete_for_proc_or_hash(receiver, message)
26
+ candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
27
+ select_message(receiver, message, candidates)
28
+ end
29
+
30
+ def autocomplete_for_symbol(sym)
31
+ if Symbol.respond_to?(:all_symbols)
32
+ candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
33
+ candidates.grep(/^#{sym}/)
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def autocomplete_for_absolute_constant_or_class_methods(receiver)
40
+ candidates = Object.constants
41
+ candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
42
+ end
43
+
44
+ def autocomplete_for_constant_or_class_methods(receiver, message)
45
+ begin
46
+ candidates = eval("#{receiver}.constants | #{receiver}.methods", bind)
47
+ rescue Exception
48
+ candidates = []
49
+ end
50
+ candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
51
+ end
52
+
53
+ def autocomplete_for_symbol_method(receiver, message)
54
+ candidates = Symbol.instance_methods(true)
55
+ select_message(receiver, message, candidates)
56
+ end
57
+
58
+ def autocomplete_for_numeric(receiver, message)
59
+ begin
60
+ candidates = eval(receiver, bind).methods
61
+ rescue Exception
62
+ candidates = []
63
+ end
64
+ select_message(receiver, message, candidates)
65
+ end
66
+
67
+ def autocomplete_for_hex_numeric(receiver, message)
68
+ begin
69
+ candidates = eval(receiver, bind).methods
70
+ rescue Exception
71
+ candidates = []
72
+ end
73
+ select_message(receiver, message, candidates)
74
+ end
75
+
76
+ def autocomplete_for_global_variable(obj)
77
+ global_variables.grep(Regexp.new(obj))
78
+ end
79
+
80
+ def autocomplete_for_variable(receiver, message)
81
+ gv = eval("global_variables", bind)
82
+ lv = eval("local_variables", bind)
83
+ cv = eval("self.class.constants", bind)
84
+
85
+ if (gv | lv | cv).include?(receiver)
86
+ # foo.func and foo is local var.
87
+ candidates = eval("#{receiver}.methods", bind)
88
+ elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
89
+ # Foo::Bar.func
90
+ begin
91
+ candidates = eval("#{receiver}.methods", bind)
92
+ rescue Exception
93
+ candidates = []
94
+ end
95
+ else
96
+ # func1.func2
97
+ candidates = []
98
+ ObjectSpace.each_object(Module){|m|
99
+ begin
100
+ name = m.name
101
+ rescue Exception
102
+ name = ""
103
+ end
104
+ next if name != "IRB::Context" and
105
+ /^(IRB|SLex|RubyLex|RubyToken)/ =~ name
106
+ candidates.concat m.instance_methods(false)
107
+ }
108
+ candidates.sort!
109
+ candidates.uniq!
110
+ end
111
+ select_message(receiver, message, candidates)
112
+ end
113
+
114
+ def autocomplete_for_string(message)
115
+ receiver = ""
116
+ candidates = String.instance_methods(true)
117
+ select_message(receiver, message, candidates)
118
+ end
119
+
120
+ def autocomplete(line, namespace)
121
+ # Borrowed from irb/completion.rb
122
+ case line
123
+ when /^(\/[^\/]*\/)\.([^.]*)$/
124
+ autocomplete_for_regexp($1, Regexp.quote($2))
125
+
126
+ when /^([^\]]*\])\.([^.]*)$/
127
+ autocomplete_for_array($1, Regexp.quote($2))
128
+
129
+ when /^([^\}]*\})\.([^.]*)$/
130
+ autocomplete_for_proc_or_hash($1, Regexp.quote($2))
131
+
132
+ when /^(:[^:.]*)$/
133
+ autocomplete_for_symbol($1)
134
+
135
+ when /^::([A-Z][^:\.\(]*)$/
136
+ autocomplete_for_absolute_constant_or_class_methods($1)
137
+
138
+ when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
139
+ autocomplete_for_constant_or_class_methods($1, Regexp.quote($4))
140
+
141
+ when /^(:[^:.]+)\.([^.]*)$/
142
+ autocomplete_for_symbol_method($1, Regexp.quote($2))
143
+
144
+ when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
145
+ autocomplete_for_numeric($1, Regexp.quote($5))
146
+
147
+ when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/
148
+ autocomplete_for_hex_numeric($1, Regexp.quote($2))
149
+
150
+ when /^(\$[^.]*)$/
151
+ autocomplete_for_global_variable(Regexp.quote($1))
152
+
153
+ # when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
154
+ when /^((\.?[^.]+)+)\.([^.]*)$/
155
+ autocomplete_for_variable($1, Regexp.quote($3))
156
+
157
+ when /^\.([^.]*)$/
158
+ # unknown(maybe String)
159
+ autocomplete_for_string(Regexp.quote($1))
160
+ else
161
+ candidates = eval("methods | private_methods | local_variables | self.class.constants", namespace.send(:binding))
162
+ (candidates|RESERVED_WORDS).grep(/^#{Regexp.quote(line)}/)
163
+ end
164
+ end
165
+
166
+ private
167
+ def select_message(receiver, message, candidates)
168
+ candidates.grep(/^#{message}/).collect do |e|
169
+ case e
170
+ when /^[a-zA-Z_]/
171
+ receiver + "." + e
172
+ when /^[0-9]/
173
+ when *OPERATORS
174
+ #receiver + " " + e
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,31 @@
1
+ module Rink
2
+ module OutputMethod
3
+ class Base
4
+ attr_writer :silenced
5
+
6
+ def output
7
+ raise NotImplementedError, "output"
8
+ end
9
+
10
+ def initialize(silenced = false)
11
+ @silenced = silenced
12
+ end
13
+
14
+ def write(*args)
15
+ print(*args)
16
+ end
17
+
18
+ def puts(*args)
19
+ print args.join("\n"), "\n"
20
+ end
21
+
22
+ def print(*args)
23
+ raise NotImplementedError, "print" unless silenced?
24
+ end
25
+
26
+ def silenced?
27
+ @silenced
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ module Rink
2
+ module OutputMethod
3
+ class IO < Rink::OutputMethod::Base
4
+ attr_accessor :io
5
+
6
+ def initialize(io)
7
+ super()
8
+ @io = io
9
+ end
10
+
11
+ def output
12
+ @io
13
+ end
14
+
15
+ def print(*args)
16
+ return if silenced?
17
+ args = args.flatten.join
18
+ @io.print(*args)
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/rink.rb ADDED
@@ -0,0 +1,12 @@
1
+ #require 'sc-ansi'
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "rink/delegation"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "rink/lexer"))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rink/io_methods'))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "rink/line_processor/base"))
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "rink/line_processor/pure_ruby"))
8
+ require File.expand_path(File.join(File.dirname(__FILE__), "rink/console"))
9
+
10
+ module Rink
11
+
12
+ end