davidrichards-sirb 0.6.14
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 +227 -0
- data/VERSION.yml +4 -0
- data/bin/sirb +33 -0
- data/lib/overrides/array.rb +6 -0
- data/lib/overrides/file.rb +17 -0
- data/lib/overrides/module.rb +70 -0
- data/lib/overrides/symbol.rb +39 -0
- data/lib/sirb/enumerable_statistics.rb +350 -0
- data/lib/sirb/functional.rb +114 -0
- data/lib/sirb/general_statistics.rb +72 -0
- data/lib/sirb/inter_enumerable_statistics.rb +139 -0
- data/lib/sirb/lib_loader.rb +45 -0
- data/lib/sirb/runner.rb +274 -0
- data/lib/sirb/sproc/proc.rb +5 -0
- data/lib/sirb/sproc/proc_source.rb +130 -0
- data/lib/sirb/sproc/sproc.rb +79 -0
- data/lib/sirb/sproc/usage_notes.txt +25 -0
- data/lib/sirb/sproc.rb +29 -0
- data/lib/sirb/thread_support.rb +20 -0
- data/lib/sirb/unbound_method.rb +5 -0
- data/lib/sirb.rb +52 -0
- data/lib/stored_procedures.rb +10 -0
- data/spec/lib/overrides/array_spec.rb +7 -0
- data/spec/lib/overrides/file_spec.rb +13 -0
- data/spec/lib/overrides/module_spec.rb +86 -0
- data/spec/lib/overrides/symbol_spec.rb +39 -0
- data/spec/lib/sirb/enumerable_statistics_spec.rb +85 -0
- data/spec/lib/sirb/functional_spec.rb +75 -0
- data/spec/lib/sirb/general_statistics_spec.rb +40 -0
- data/spec/lib/sirb/inter_enumerable_statistics_spec.rb +55 -0
- data/spec/lib/sirb/lib_loader_spec.rb +39 -0
- data/spec/lib/sirb/runner_spec.rb +9 -0
- data/spec/lib/sirb/sproc/proc_spec.rb +9 -0
- data/spec/lib/sirb/sproc/sproc_spec.rb +25 -0
- data/spec/lib/sirb/unbound_method_spec.rb +12 -0
- data/spec/lib/sirb_spec.rb +9 -0
- data/spec/spec_helper.rb +15 -0
- metadata +97 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
class LibLoader
|
2
|
+
class << self
|
3
|
+
def libs
|
4
|
+
@libs ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add_lib(name, &block)
|
8
|
+
name = name.to_s
|
9
|
+
block ||= lambda{require name}
|
10
|
+
self.libs[name] = self.safe_require(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def all_libs
|
14
|
+
self.libs.keys.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def libs_loaded
|
18
|
+
self.libs.map {|k, v| k if v }.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
def failed_libs
|
22
|
+
self.libs.map {|k, v| k unless v }.compact
|
23
|
+
end
|
24
|
+
|
25
|
+
def safe_require(&block)
|
26
|
+
begin
|
27
|
+
block.call
|
28
|
+
# If the lib is already loaded, it may return false, but it's available.
|
29
|
+
# If there's a problem with this, you should require the lib in a block
|
30
|
+
# and raise an exception if it fails.
|
31
|
+
true
|
32
|
+
# If the laod returns false, then
|
33
|
+
rescue Exception => e # Very important
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
return nil if self.failed_libs.empty?
|
40
|
+
"Libs loaded:\n\t" + (self.libs_loaded.empty? ? "None" : self.libs_loaded.join(", ")) +
|
41
|
+
"\nLibs NOT loaded:\n\t" + (self.failed_libs.empty? ? "None" : self.failed_libs.join(", "))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/sirb/runner.rb
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
# So, this is interesting. It's a class to store operations from blocks,
|
2
|
+
# and run them.
|
3
|
+
|
4
|
+
# Persistent Store from Standard Library
|
5
|
+
require 'pstore'
|
6
|
+
|
7
|
+
module Sirb #:nodoc:
|
8
|
+
|
9
|
+
class Runner
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Storing everything in local .pstore for now. Uses the home directory,when possible.
|
13
|
+
def pstore
|
14
|
+
File.mkdir_p(dirname)
|
15
|
+
@@pstore ||= PStore.new(filename)
|
16
|
+
end
|
17
|
+
protected :pstore
|
18
|
+
|
19
|
+
def dirname
|
20
|
+
@@dirname ||= File.expand_path(File.join(ENV['HOME'], '.sirb'))
|
21
|
+
end
|
22
|
+
protected :dirname
|
23
|
+
|
24
|
+
def filename
|
25
|
+
@@filename ||= File.join(dirname, "proc.pstore")
|
26
|
+
end
|
27
|
+
protected :filename
|
28
|
+
|
29
|
+
# Needs a lambda, instead of a block, because we're serializing this and
|
30
|
+
# the code I use to serialize this needs to be able to find the string
|
31
|
+
# that created this code.
|
32
|
+
def add_command(name, l, description=nil)
|
33
|
+
coerce name
|
34
|
+
pstore.transaction { pstore[@name] = new(name.to_s, l, description) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_command(name)
|
38
|
+
coerce name
|
39
|
+
pstore.transaction { pstore.delete(@name) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Very harsh, but necessary if you have an unknown issue with a stored block.
|
43
|
+
def reset_commands
|
44
|
+
puts "Are you sure you want to remove all commands?(yN)\nThis will delete the contents of #{filename}"
|
45
|
+
result = gets.strip
|
46
|
+
return false unless result == 'y'
|
47
|
+
`rm -rf #{filename}`
|
48
|
+
puts "Successfully reset all commands."
|
49
|
+
end
|
50
|
+
|
51
|
+
def commands
|
52
|
+
pstore.transaction { pstore.roots }
|
53
|
+
end
|
54
|
+
|
55
|
+
def commands_as_commands
|
56
|
+
commands.map {|cmd| find(cmd)}
|
57
|
+
end
|
58
|
+
protected :commands_as_commands
|
59
|
+
|
60
|
+
def find(name)
|
61
|
+
return nil unless name
|
62
|
+
coerce name
|
63
|
+
begin
|
64
|
+
pstore.transaction { pstore[@name] }
|
65
|
+
rescue Exception => e
|
66
|
+
# Don't know a good way to look in the pstore for something that doesn't
|
67
|
+
# exist
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def methodize(name)
|
73
|
+
eval stringify_proc(name)
|
74
|
+
end
|
75
|
+
|
76
|
+
def stringify_proc(name)
|
77
|
+
cmd = find(name)
|
78
|
+
cmd_name = cmd.name
|
79
|
+
matched = cmd.block_source.match(/^(\|.+\|)(.+)$/)
|
80
|
+
cmd_params = matched ? matched[1] : ''
|
81
|
+
cmd_body = matched ? matched[2] : nil
|
82
|
+
cmd_body ||= cmd.block_source
|
83
|
+
cmd_body.lstrip!
|
84
|
+
"def #{cmd_name}(#{cmd_params})\n#{cmd_body}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def run(name, *data)
|
88
|
+
coerce name
|
89
|
+
raise ArgumentError, "Unknown command: #{name}" unless self.find(@name)
|
90
|
+
self.find(@name).run(*data)
|
91
|
+
end
|
92
|
+
|
93
|
+
def coerce(name)
|
94
|
+
@name = name.to_sym
|
95
|
+
end
|
96
|
+
protected :coerce
|
97
|
+
|
98
|
+
def sirb_help(command=nil)
|
99
|
+
return puts(command_help(command)) if command
|
100
|
+
puts(sirb_description, all_commands_description, '')
|
101
|
+
end
|
102
|
+
|
103
|
+
def sirb_description
|
104
|
+
|
105
|
+
result = left_justify %{
|
106
|
+
This is Irb, with some extra libraries and commands loaded.
|
107
|
+
You have loaded the following libraries:
|
108
|
+
}
|
109
|
+
result += tab_indent( LibLoader.libs_loaded.join("\n") )
|
110
|
+
result += "\n\n"
|
111
|
+
|
112
|
+
if not LibLoader.failed_libs.empty?
|
113
|
+
result += "The following libraries are not available on your system at this time:\n"
|
114
|
+
result += tab_indent( LibLoader.failed_libs.join("\n") )
|
115
|
+
result += "\n\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
result
|
119
|
+
end
|
120
|
+
|
121
|
+
def all_commands_description
|
122
|
+
result = ''
|
123
|
+
|
124
|
+
if commands.empty?
|
125
|
+
result += "You have not stored any custom commands at this time.\n\n"
|
126
|
+
else
|
127
|
+
result += "You have setup the following commands:\n\n"
|
128
|
+
result += commands_as_commands.map{ |cmd| cmd.command_help }.join("\n")
|
129
|
+
result += "\n\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
result += "You can store a command like this:\n"
|
133
|
+
result += tab_indent(%{
|
134
|
+
set :command_name, lambda{|list params| command }, "Optional description"
|
135
|
+
})
|
136
|
+
|
137
|
+
result
|
138
|
+
|
139
|
+
end
|
140
|
+
protected :all_commands_description
|
141
|
+
|
142
|
+
def command_help(command)
|
143
|
+
cmd = find(command)
|
144
|
+
cmd ? cmd.command_help : "Could not find the command: #{command}"
|
145
|
+
end
|
146
|
+
protected :command_help
|
147
|
+
|
148
|
+
# Probably need to setup some decorators here instead...
|
149
|
+
def tab_indent(str)
|
150
|
+
return "" unless str
|
151
|
+
str.strip.split("\n").map {|line| "\t#{line.strip}" }.join("\n")
|
152
|
+
end
|
153
|
+
|
154
|
+
def left_justify(str)
|
155
|
+
return "" unless str
|
156
|
+
str.split("\n").map {|line| line.lstrip }.join("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
attr_reader :name, :description
|
162
|
+
# Needs a lambda, instead of a block, because we're serializing this and
|
163
|
+
# the code I use to serialize this needs to be able to find the string
|
164
|
+
# that created this code.
|
165
|
+
def initialize(name, l, description=nil)
|
166
|
+
@name, @block, @description = name, l, description
|
167
|
+
end
|
168
|
+
|
169
|
+
# Kind of loose: allows me to use reduce, map, or just call a stored
|
170
|
+
# command. This may just seem to work, but we'll see.
|
171
|
+
def run(*data)
|
172
|
+
case @block.arity
|
173
|
+
when 2
|
174
|
+
# Same as reduce
|
175
|
+
data.inject &@block
|
176
|
+
when 1
|
177
|
+
# Same as map
|
178
|
+
data.map &@block
|
179
|
+
when -1
|
180
|
+
# Same as call
|
181
|
+
@block.call
|
182
|
+
else
|
183
|
+
# Probably not going to work.
|
184
|
+
data.inject &@block
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def command_help
|
189
|
+
result = (self.description and not self.description.empty?) ? formatted_description : empty_description
|
190
|
+
result + source_description
|
191
|
+
end
|
192
|
+
|
193
|
+
def arity_desc
|
194
|
+
case @block.arity
|
195
|
+
when -1
|
196
|
+
"takes 0 arguments"
|
197
|
+
when 1
|
198
|
+
"takes 1 argument, returns an array"
|
199
|
+
when 2
|
200
|
+
"takes 2 arguments, returns a single value--reduce function"
|
201
|
+
else
|
202
|
+
"takes #{@block.arity} arguments"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
protected :arity_desc
|
206
|
+
|
207
|
+
def block_source
|
208
|
+
@block.source
|
209
|
+
end
|
210
|
+
|
211
|
+
def name_line
|
212
|
+
"* #{self.name} (#{arity_desc})\n"
|
213
|
+
end
|
214
|
+
protected :name_line
|
215
|
+
|
216
|
+
def source_description
|
217
|
+
"\n\tSource: #{block_source}"
|
218
|
+
end
|
219
|
+
|
220
|
+
def empty_description
|
221
|
+
"#{name_line}\tThere is no other information available for the #{self.name.to_s} command."
|
222
|
+
end
|
223
|
+
protected :empty_description
|
224
|
+
|
225
|
+
def formatted_description
|
226
|
+
name_line +
|
227
|
+
Runner.tab_indent(self.description)
|
228
|
+
end
|
229
|
+
protected :formatted_description
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
# These should be available in the console generally
|
234
|
+
module CommandLineMethods
|
235
|
+
|
236
|
+
def store_command(name, l, description=nil)
|
237
|
+
cmd = Runner.add_command(name, l, description)
|
238
|
+
cmd.name.to_s + " added"
|
239
|
+
end
|
240
|
+
alias set store_command
|
241
|
+
|
242
|
+
|
243
|
+
# May want to move this to delegate (standard library)
|
244
|
+
# http://www.ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html
|
245
|
+
def method_missing(sym, *args, &block)
|
246
|
+
# base, extension = breakdown_method(sym)
|
247
|
+
# if Runner.find(base) and extension == 'methodize'
|
248
|
+
# Runner.methodize(base)
|
249
|
+
# elsif Runner.find(base) and extension == 'to_s'
|
250
|
+
# require 'rubygems'
|
251
|
+
# require 'ruby-debug'
|
252
|
+
# debugger
|
253
|
+
# Runner.stringify_proc(base)
|
254
|
+
if Runner.find(sym)
|
255
|
+
Runner.run(sym, *args)
|
256
|
+
# elsif Runner.respond_to?(sym)
|
257
|
+
elsif Runner.respond_to?(sym)
|
258
|
+
Runner.send(sym, *args, &block)
|
259
|
+
else
|
260
|
+
super
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Doesn't work.
|
265
|
+
def breakdown_method(sym)
|
266
|
+
s = sym.to_s
|
267
|
+
r = /^(.+)\.(.+)$/
|
268
|
+
md = s.match r
|
269
|
+
md ? [md[1].to_sym, md[2]] : [nil,nil]
|
270
|
+
end
|
271
|
+
protected :breakdown_method
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'irb/ruby-lex'
|
3
|
+
|
4
|
+
# Tell the ruby interpreter to load code lines of required files
|
5
|
+
# into this filename -> lines Hash. This behaviour seems to be
|
6
|
+
# very undocumented and therefore shouldn't really be relied on.
|
7
|
+
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
8
|
+
|
9
|
+
module ProcSource
|
10
|
+
def get_lines(filename, start_line = 0)
|
11
|
+
case filename
|
12
|
+
# special "(irb)" descriptor?
|
13
|
+
when "(irb)"
|
14
|
+
IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -1)
|
15
|
+
# special "(eval...)" descriptor?
|
16
|
+
when /^\(eval.+\)$/
|
17
|
+
EVAL_LINES__[filename][start_line .. -1]
|
18
|
+
# regular file
|
19
|
+
else
|
20
|
+
# Ruby already parsed this file? (see disclaimer above)
|
21
|
+
if lines = SCRIPT_LINES__[filename]
|
22
|
+
lines[(start_line - 1) .. -1]
|
23
|
+
# If the file exists we're going to try reading it in
|
24
|
+
elsif File.exist?(filename)
|
25
|
+
begin
|
26
|
+
File.readlines(filename)[(start_line - 1) .. -1]
|
27
|
+
rescue
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def handle(proc)
|
35
|
+
filename, line = proc.source_descriptor
|
36
|
+
lines = get_lines(filename, line) || []
|
37
|
+
|
38
|
+
lexer = RubyLex.new
|
39
|
+
lexer.set_input(StringIO.new(lines.join))
|
40
|
+
|
41
|
+
state = :before_constructor
|
42
|
+
nesting_level = 1
|
43
|
+
start_token, end_token = nil, nil
|
44
|
+
found = false
|
45
|
+
while token = lexer.token
|
46
|
+
# we've not yet found any proc-constructor -- we'll try to find one.
|
47
|
+
if [:before_constructor, :check_more].include?(state)
|
48
|
+
# checking more and newline? -> done
|
49
|
+
if token.is_a?(RubyToken::TkNL) and state == :check_more
|
50
|
+
state = :done
|
51
|
+
break
|
52
|
+
end
|
53
|
+
# token is Proc?
|
54
|
+
if token.is_a?(RubyToken::TkCONSTANT) and
|
55
|
+
token.instance_variable_get(:@name) == "Proc"
|
56
|
+
# method call?
|
57
|
+
if lexer.token.is_a?(RubyToken::TkDOT)
|
58
|
+
method = lexer.token
|
59
|
+
# constructor?
|
60
|
+
if method.is_a?(RubyToken::TkIDENTIFIER) and
|
61
|
+
method.instance_variable_get(:@name) == "new"
|
62
|
+
unless state == :check_more
|
63
|
+
# okay, code will follow soon.
|
64
|
+
state = :before_code
|
65
|
+
else
|
66
|
+
# multiple procs on one line
|
67
|
+
return
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# token is lambda or proc call?
|
72
|
+
elsif token.is_a?(RubyToken::TkIDENTIFIER) and
|
73
|
+
%w{proc lambda}.include?(token.instance_variable_get(:@name))
|
74
|
+
unless state == :check_more
|
75
|
+
# okay, code will follow soon.
|
76
|
+
state = :before_code
|
77
|
+
else
|
78
|
+
# multiple procs on one line
|
79
|
+
return
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# we're waiting for the code start to appear.
|
84
|
+
elsif state == :before_code
|
85
|
+
if token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO)
|
86
|
+
# found the code start, update state and remember current token
|
87
|
+
state = :in_code
|
88
|
+
start_token = token
|
89
|
+
end
|
90
|
+
|
91
|
+
# okay, we're inside code
|
92
|
+
elsif state == :in_code
|
93
|
+
if token.is_a?(RubyToken::TkRBRACE) or token.is_a?(RubyToken::TkEND)
|
94
|
+
nesting_level -= 1
|
95
|
+
if nesting_level == 0
|
96
|
+
# we're done!
|
97
|
+
end_token = token
|
98
|
+
# parse another time to check if there are multiple procs on one line
|
99
|
+
# we can't handle that case correctly so we return no source code at all
|
100
|
+
state = :check_more
|
101
|
+
end
|
102
|
+
elsif token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) or
|
103
|
+
token.is_a?(RubyToken::TkBEGIN) or token.is_a?(RubyToken::TkCASE) or
|
104
|
+
token.is_a?(RubyToken::TkCLASS) or token.is_a?(RubyToken::TkDEF) or
|
105
|
+
token.is_a?(RubyToken::TkFOR) or token.is_a?(RubyToken::TkIF) or
|
106
|
+
token.is_a?(RubyToken::TkMODULE) or token.is_a?(RubyToken::TkUNLESS) or
|
107
|
+
token.is_a?(RubyToken::TkUNTIL) or token.is_a?(RubyToken::TkWHILE) or
|
108
|
+
token.is_a?(RubyToken::TklBEGIN)
|
109
|
+
nesting_level += 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if start_token and end_token
|
115
|
+
start_line, end_line = start_token.line_no - 1, end_token.line_no - 1
|
116
|
+
source = lines[start_line .. end_line]
|
117
|
+
start_offset = start_token.char_no
|
118
|
+
start_offset += 1 if start_token.is_a?(RubyToken::TkDO)
|
119
|
+
end_offset = -(source.last.length - end_token.char_no)
|
120
|
+
source.first.slice!(0 .. start_offset)
|
121
|
+
source.last.slice!(end_offset .. -1)
|
122
|
+
|
123
|
+
# Can't use .strip because newline at end of code might be important
|
124
|
+
# (Stuff would break when somebody does proc { ... #foo\n})
|
125
|
+
proc.source = source.join.gsub(/^ | $/, "")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module_function :handle, :get_lines
|
130
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class Sproc < Proc
|
2
|
+
|
3
|
+
class << self
|
4
|
+
# An alternate constructor for the class, to allow string input.
|
5
|
+
def build(arg=nil, &block)
|
6
|
+
block_given? ? infer_sproc(block) : infer_sproc(arg)
|
7
|
+
end
|
8
|
+
|
9
|
+
def infer_sproc(arg)
|
10
|
+
case arg
|
11
|
+
when Proc
|
12
|
+
Proc.new(&arg)
|
13
|
+
when String
|
14
|
+
Sproc.from_string(arg)
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Don't know how to create a sproc from #{arg.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
protected :infer_sproc
|
20
|
+
end
|
21
|
+
|
22
|
+
def source_descriptor
|
23
|
+
if md = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)>$/.match(old_inspect)
|
24
|
+
filename, line = md.captures
|
25
|
+
return filename, line.to_i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :source
|
30
|
+
def source
|
31
|
+
ProcSource.handle(self) unless @source
|
32
|
+
@source
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :old_inspect :inspect
|
36
|
+
def inspect
|
37
|
+
if source
|
38
|
+
"sproc {#{source}}"
|
39
|
+
else
|
40
|
+
old_inspect
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ==(other)
|
45
|
+
if self.source and other.source
|
46
|
+
self.source == other.source
|
47
|
+
else
|
48
|
+
self.object_id == other.object_id
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def _dump(depth = 0)
|
53
|
+
if source
|
54
|
+
source
|
55
|
+
else
|
56
|
+
raise(TypeError, "Can't serialize Proc with unknown source code.")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_yaml(*args)
|
61
|
+
self.source # force @source to be set
|
62
|
+
super.sub("object:Proc", "sproc")
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.allocate; from_string ""; end
|
66
|
+
|
67
|
+
def self.from_string(string)
|
68
|
+
result = eval("Sproc.new {#{string}}")
|
69
|
+
result.source = string
|
70
|
+
return result
|
71
|
+
end
|
72
|
+
|
73
|
+
def self._load(code)
|
74
|
+
self.from_string(code)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.marshal_load; end
|
78
|
+
def marshal_load; end
|
79
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
if __FILE__ == $0 then
|
2
|
+
require "pstore"
|
3
|
+
|
4
|
+
code = lambda { puts "Hello World!" }
|
5
|
+
|
6
|
+
File.open("proc.marshalled", "w") { |file| Marshal.dump(code, file) }
|
7
|
+
code = File.open("proc.marshalled") { |file| Marshal.load(file) }
|
8
|
+
|
9
|
+
code.call
|
10
|
+
|
11
|
+
store = PStore.new("proc.pstore")
|
12
|
+
store.transaction do
|
13
|
+
store["proc"] = code
|
14
|
+
end
|
15
|
+
store.transaction do
|
16
|
+
code = store["proc"]
|
17
|
+
end
|
18
|
+
|
19
|
+
code.call
|
20
|
+
|
21
|
+
File.open("proc.yaml", "w") { |file| YAML.dump(code, file) }
|
22
|
+
code = File.open("proc.yaml") { |file| YAML.load(file) }
|
23
|
+
|
24
|
+
code.call
|
25
|
+
end
|
data/lib/sirb/sproc.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# This is an adaptation from Florian Groß's submission to Ruby Quiz #38.
|
2
|
+
# http://rubyquiz.com/quiz38.html
|
3
|
+
#
|
4
|
+
# I needed a serializable Proc, I don't care about the closure of
|
5
|
+
# the proc. I just need to be able to store procs between Sirb sessions.
|
6
|
+
# I'm using these procs with explicitly-passed context anyway, so they
|
7
|
+
# are meant to be portable functions.
|
8
|
+
|
9
|
+
# Loads everything in Sproc, want to break this up, make it more useful
|
10
|
+
# and understandable.
|
11
|
+
Dir.glob("#{File.dirname(__FILE__)}/sproc/*.rb").each { |file| require file }
|
12
|
+
|
13
|
+
require 'yaml'
|
14
|
+
YAML.add_ruby_type(/^proc/) do |type, val|
|
15
|
+
Proc.from_string(val["source"])
|
16
|
+
end
|
17
|
+
|
18
|
+
EVAL_LINES__ = Hash.new
|
19
|
+
|
20
|
+
alias :old_eval :eval
|
21
|
+
def eval(code, *args)
|
22
|
+
context, descriptor, start_line, *more = *args
|
23
|
+
descriptor ||= "(eval#{code.hash})"
|
24
|
+
start_line ||= 0
|
25
|
+
lines ||= code.grep(/.*/)
|
26
|
+
EVAL_LINES__[descriptor] ||= Array.new
|
27
|
+
EVAL_LINES__[descriptor][start_line, lines.length] = lines
|
28
|
+
old_eval(code, context, descriptor, start_line, *more)
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sirb #:nodoc:
|
2
|
+
|
3
|
+
# This adds some concurrency and distributed processing to your programs.
|
4
|
+
# It follows loosely the interface offered by Erlang though the implementation
|
5
|
+
# is different:
|
6
|
+
# * spawn
|
7
|
+
# * send
|
8
|
+
# * receive
|
9
|
+
# * link
|
10
|
+
# * system_process
|
11
|
+
# * spawn_link
|
12
|
+
# * disconnect_node
|
13
|
+
# * monitor_node
|
14
|
+
# * node
|
15
|
+
# * nodes
|
16
|
+
# * is_alive
|
17
|
+
module ThreadSupport
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/sirb.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
module Sirb
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'sirb/lib_loader'
|
7
|
+
|
8
|
+
Dir.glob("#{File.dirname(__FILE__)}/overrides/*.rb").each { |file| require file }
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
LibLoader.add_lib('rubygems')
|
12
|
+
LibLoader.add_lib('mathn')
|
13
|
+
LibLoader.add_lib('bigdecimal') {
|
14
|
+
require 'bigdecimal'
|
15
|
+
require 'bigdecimal/math'
|
16
|
+
}
|
17
|
+
LibLoader.add_lib('set')
|
18
|
+
LibLoader.add_lib('matrix')
|
19
|
+
LibLoader.add_lib('narray')
|
20
|
+
LibLoader.add_lib('rnum')
|
21
|
+
LibLoader.add_lib('rgl') {
|
22
|
+
require 'rgl/dot'
|
23
|
+
require 'rgl/adjacency'
|
24
|
+
require 'rgl/traversal'
|
25
|
+
require 'rgl/topsort'
|
26
|
+
}
|
27
|
+
LibLoader.add_lib('tenacious_g')
|
28
|
+
LibLoader.add_lib('rbtree')
|
29
|
+
LibLoader.add_lib('statisticus')
|
30
|
+
|
31
|
+
|
32
|
+
LibLoader.add_lib('general statistics') {
|
33
|
+
require 'sirb/general_statistics'
|
34
|
+
include Sirb::GeneralStatistics
|
35
|
+
}
|
36
|
+
|
37
|
+
LibLoader.add_lib('enumerable statistics') {
|
38
|
+
require 'sirb/enumerable_statistics'
|
39
|
+
class Array
|
40
|
+
include Sirb::EnumerableStatistics
|
41
|
+
end
|
42
|
+
# TODO: Add other enumerables, adapt for Matrices, Vectors, graphs, the whole thing.
|
43
|
+
|
44
|
+
require 'sirb/inter_enumerable_statistics'
|
45
|
+
include Sirb::InterEnumerableStatistics
|
46
|
+
|
47
|
+
require 'sirb/functional'
|
48
|
+
require 'sirb/unbound_method'
|
49
|
+
|
50
|
+
}
|
51
|
+
|
52
|
+
puts LibLoader.to_s if LibLoader.to_s
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Called after sirb.rb, when we want to have stored procedures. Sproc/
|
2
|
+
# Proc is pretty draconian, so I want to pull this out for now until
|
3
|
+
# more tests have been done. Probably we'll never want the command runner
|
4
|
+
# if Sirb is being used as a library, instead of as a real Irb instance.
|
5
|
+
|
6
|
+
LibLoader.add_lib('command runner') {
|
7
|
+
require 'sirb/sproc'
|
8
|
+
require 'sirb/runner'
|
9
|
+
include Sirb::CommandLineMethods
|
10
|
+
}
|