rmtools 1.2.0 → 1.2.2b

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/Manifest.txt CHANGED
@@ -17,8 +17,8 @@ lib/rmtools/dev/binding.rb
17
17
  lib/rmtools/dev/logging.rb
18
18
  lib/rmtools/dev/trace_format.rb
19
19
  lib/rmtools/dev/highlight.rb
20
+ lib/rmtools/dev/code_reading.rb
20
21
  lib/rmtools/dev/timer.rb
21
- lib/rmtools/dev/code_reader.rb
22
22
  lib/rmtools/xml/xpath.rb
23
23
  lib/rmtools/xml/string.rb
24
24
  lib/rmtools/xml/libxml.rb
@@ -33,6 +33,7 @@ lib/rmtools/core/string_compliance.rb
33
33
  lib/rmtools/core/kernel.rb
34
34
  lib/rmtools/core/boolean.rb
35
35
  lib/rmtools/core/aliases.rb
36
+ lib/rmtools/core/threadify.rb
36
37
  lib/rmtools/core/module.rb
37
38
  lib/rmtools/core/object.rb
38
39
  lib/rmtools/core/deprecation.rb
@@ -66,7 +67,6 @@ lib/rmtools/fs.rb
66
67
  lib/rmtools/ip.rb
67
68
  lib/rmtools/lang.rb
68
69
  lib/rmtools/functional.rb
69
- lib/rmtools/load.rb
70
70
  lib/rmtools/install.rb
71
71
  lib/rmtools/functional/decorate.rb
72
72
  lib/rmtools/functional/fold.rb
@@ -88,6 +88,7 @@ lib/rmtools/conversions/string.rb
88
88
  lib/rmtools/conversions/enum.rb
89
89
  lib/rmtools/conversions/int.rb
90
90
  lib/rmtools/enumerable.rb
91
+ lib/rmtools/init.rb
91
92
  lib/rmtools/dev_min.rb
92
93
  lib/rmtools_dev.rb
93
94
  ./Rakefile
data/README.txt CHANGED
@@ -14,7 +14,7 @@ Methods for basic classes addon collection.
14
14
  * Slightly extended StringScanner
15
15
  * Proof of concept: Regexp reverse (wonder if someone did it earlier in Ruby)
16
16
  * Kernel#whose? to find classes and/or modules knowing some method
17
- * Method code lookup over all loaded libs (it can't handle evals yet), see dev/code_lookup.rb
17
+ * Method code lookup over all loaded libs (it can't handle evals yet), see dev/code_reading.rb
18
18
  * Coloring is now made by singleton `Painter' and have option for transparent coloring
19
19
 
20
20
  === Version 1.1.14
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
  require 'lib/rmtools/install'
3
3
  compile_manifest
4
4
 
5
- RMTOOLS_VERSION = '1.2.0'
5
+ RMTOOLS_VERSION = '1.2.2b'
6
6
  begin
7
7
  require 'hoe'
8
8
  config = Hoe.spec 'rmtools' do
data/ext/rmtools.cpp CHANGED
@@ -20,15 +20,17 @@ using namespace std;
20
20
  static VALUE object_define_new_value(VALUE self, VALUE new_obj)
21
21
  {
22
22
  if (FIXNUM_P(self) || self == Qnil || self == Qfalse || self == Qtrue || self == Qundef) {
23
- const char* msg = STR2CSTR(rb_mod_name(rb_obj_class(self)));
23
+ VALUE tmp = rb_mod_name(rb_obj_class(self));
24
+ const char* msg = StringValuePtr(tmp);
24
25
  rb_raise(rb_eTypeError, "can't redefine %s", msg);
25
26
  }
26
27
  if (FIXNUM_P(new_obj) || new_obj == Qnil || new_obj == Qfalse || new_obj == Qtrue || new_obj == Qundef) {
27
- const char* msg = STR2CSTR(rb_mod_name(rb_obj_class(new_obj)));
28
+ VALUE tmp = rb_mod_name(rb_obj_class(self));
29
+ const char* msg = StringValuePtr(tmp);
28
30
  rb_raise(rb_eTypeError, "can't define object as %s", msg);
29
31
  }
30
32
  // Place the definition of the new object in the slot of self
31
- memcpy(reinterpret_cast<void*>(self), reinterpret_cast<void*>(new_obj), SLOT_SIZE);
33
+ memcpy(reinterpret_cast<void*>(self), reinterpret_cast<void*>(rb_funcall(new_obj, rb_intern("clone"), 0)), SLOT_SIZE);
32
34
  return self;
33
35
  }
34
36
 
data/lib/rmtools.rb CHANGED
@@ -1,2 +1,2 @@
1
- require 'rmtools/load'
1
+ require 'rmtools/init'
2
2
  RMTools::require 'dev_min'
@@ -12,7 +12,7 @@ class Array
12
12
  if Hash === defaults
13
13
  opts, defaults = defaults, []
14
14
  return_hash = true
15
- $log.warn "fetch_opts(<hash>) now changed, if you want to jsut fetch hash options, use `opts = <hash>.merge(opts||{})' construction"
15
+ $log.warn "fetch_opts(<hash>) now changed, if you want to jsut fetch hash options, use `opts = <hash>.merge(opts||{})' construction", :caller => 2
16
16
  else
17
17
  return_hash = false
18
18
  end
@@ -46,4 +46,4 @@ class Class
46
46
 
47
47
  end
48
48
 
49
- [Hash, Regexp, File, Dir].each {|klass| klass.__init__}
49
+ [Hash, Regexp, File, Dir, Range, Class, Module].each {|klass| klass.__init__}
@@ -3,6 +3,8 @@ module Kernel
3
3
 
4
4
  # re-require
5
5
  def require!(file)
6
+ ['.rb', '.so', '.dll', ''].each {|ext| $".delete "#{file}#{ext}"}
7
+ file = File.expand_path file
6
8
  ['.rb', '.so', '.dll', ''].each {|ext| $".delete "#{file}#{ext}"}
7
9
  require file
8
10
  end
@@ -23,12 +25,13 @@ module Kernel
23
25
  end
24
26
 
25
27
  def executing? file=$0
26
- caller(0)[0] =~ /^#{file}:/
28
+ caller[0] =~ /^#{file}:/
27
29
  end
28
30
 
29
31
  def whose?(method, *opts)
30
- opts = opts.get_opts [:flags], :ns => :public
31
- checker = :"#{ns}_method_defined?"
32
+ opts = *opts.get_opts([:flags], :ns => :public)
33
+ opts[:modules] ||= opts[:mod]
34
+ checker = :"#{opts[:ns]}_method_defined?"
32
35
  if Array === method
33
36
  methods = method.to_syms
34
37
  else
@@ -46,6 +46,27 @@ class Object
46
46
 
47
47
  def in(*container)
48
48
  container.size == 1 ? container[0].include?(self) : container.include?(self)
49
+ end
50
+
51
+ def deep_clone
52
+ _deep_clone({})
53
+ end
54
+
55
+ protected
56
+ def _deep_clone(cloning_map)
57
+ return cloning_map[self] if cloning_map.key? self
58
+ cloning_obj = clone
59
+ cloning_map[self] = cloning_obj
60
+ cloning_obj.instance_variables.each do |var|
61
+ val = cloning_obj.instance_variable_get(var)
62
+ begin
63
+ val = val._deep_clone(cloning_map)
64
+ rescue TypeError
65
+ next
66
+ end
67
+ cloning_obj.instance_variable_set(var, val)
68
+ end
69
+ cloning_obj
49
70
  end
50
71
 
51
72
  end
@@ -0,0 +1,17 @@
1
+ module RMTools
2
+
3
+ def threadify(ary, max_threads=4)
4
+ forks = []
5
+ ary.each do |e|
6
+ if max_threads > forks.size
7
+ forks << fork { yield e }
8
+ end
9
+ if max_threads == forks.size
10
+ forks.delete Process.wait
11
+ end
12
+ end
13
+ Process.waitall
14
+ end
15
+
16
+ module_function:threadify
17
+ end
@@ -76,6 +76,33 @@ module ActiveRecord
76
76
  find_by_sql(["SELECT * FROM #{quoted_table_name} WHERE #{primary_key} = ?", id])[0]
77
77
  end
78
78
 
79
+ # values must be 2-tuple array [[column1, value1], [column2, value2], ...] and columns must be in order they've been created
80
+ def self.insert_unless_exist(table, values)
81
+ table = connection.quote_table_name table
82
+ if execute_sanitized(["SELECT COUNT(*) FROM #{table} WHERE #{vaues.firsts.map {|v| "#{connection.quote_column_name v}=?"}*' AND '} LIMIT 1", *values.lasts]).to_a.flatten > 0
83
+ false
84
+ else
85
+ execute_sanitized ["INSERT INTO #{table} VALUES (#{['?']*values.size*','})", *values.lasts]
86
+ true
87
+ end
88
+ end
89
+
90
+ def resource_path
91
+ "#{self.class.name.tableize}/#{id}"
92
+ end
93
+
94
+ end
95
+
96
+ class Relation
97
+
98
+ def any?
99
+ limit(1).count != 0
100
+ end
101
+
102
+ def empty?
103
+ limit(1).count == 0
104
+ end
105
+
79
106
  end
80
107
 
81
108
  end
@@ -0,0 +1,556 @@
1
+ # encoding: utf-8
2
+ RMTools::require 'text/string_scanner'
3
+
4
+ module RMTools
5
+ class CodeReader
6
+ attr_reader :MethodCache, :stack
7
+
8
+ module Defaults
9
+ Leftop = leftop = '\[{(<=>~+\-*,;:^&\|'
10
+ rightop = '\])}?!'
11
+ sname = '\w<=>~+\-*/\]\[\^%!?@&\|'
12
+ compname = "([\\.:#{sname}]+)"
13
+ name = "([#{sname}]+)"
14
+ mname = '([:\w]+)'
15
+ call_ = '(\{s*\||([\w][?!]?|\)|\.((\[\]|[<=>])?=[=~]?|[<>*\-+/%^&\|]+))[ \t]*\{)'
16
+ space = '[ \t]+'
17
+ space_ = '[ \t]*'
18
+ kw = 'if|elsif|else|unless|while|until|case|begin|rescue|when|then|or|and|not'
19
+ heredoc_handle = %q{<<(-)?(\w+|`[^`]+`|'[^']+'|"[^"]+")}
20
+ heredoc = %{([\\s#{leftop}?]|[#{leftop}\\w!?][ \t])\\s*#{heredoc_handle}}
21
+ re_sugar = %{(^|[#{leftop}\\n?;]|\W!|\\b(#{kw})[ \t]|[\\w#{rightop}]/)\\s*/}
22
+ percent = '%([xwqrQW])?([\\/<({\[!\|])'
23
+ simple = [re_sugar, percent, '[#\'"`]']*'|'
24
+ mod_def = "module +#{mname}"
25
+ class_def = "class(?: *(<<) *| +)([$@:\\w]+)(?: *< *#{mname})?"
26
+ method_def = "def +#{compname}"
27
+ alias_def = "alias +:?#{name} +:?#{name}"
28
+ Ender = '\s*\)? *(?:end\b|[\n;})\]])'
29
+
30
+ StringParseRE = /#{heredoc}|#{simple}|[{}]|[^\w#{rightop}'"`\/\\]\?\\?\S/m
31
+ HeredocParseRE = /\n|#{heredoc}|#{simple}/m
32
+ ArgumentsParseRE = /#{simple}|[{}()\[\]\n;,\|]| end\b|[:@$.\w!?]+(\b|[( ] *)| +[:?]\s+| *=>\s*/m
33
+
34
+ StringRE = /(^['"`]$)|^#{percent}$/
35
+ RERE = %r{(?:^|[#{leftop}\w!\s/])\s*(/)}
36
+ HeredocRE = heredoc_handle.to_re
37
+ Symbol = /^:#{name}$/
38
+ Attrs = /\s(c)?(?:attr_(reader|writer|accessor))[( ] *((?::\w+ *,\s*)*:\w+)#{Ender}/
39
+ Include = /\s(include|extend) +#{mname}/
40
+ AliasMethod = /\salias_method :#{name} *,\s*:#{name}/
41
+ Beginners = /(([#{leftop}\n]?\s*)(if|unless|while|until))#{
42
+ }|(.)?(?:(do|for)|begin|case)/
43
+ EOF = /($0\s*==\s*__FILE__\s*|__FILE__\s*==\s*\$0\s*)?\n/
44
+ BlockOpen = /(?:^\{\s*\||.\{)$/
45
+ Ord = /^\W\?\\?\S$/
46
+
47
+ MainParseRE = /#{simple
48
+ }|#{call_}|[{}]#{
49
+ }|(^|\n)=begin\b#{
50
+ }|^\s*[;\(]? *(#{mod_def}|#{method_def})#{
51
+ }|:#{name}#{
52
+ }|[^\w#{rightop}'"`\/\\]\?\\?\S#{
53
+ }|#{heredoc
54
+ }|(^|[#{leftop}\n])\s*((if|unless)\b|#{
55
+ }[;\(]? *#{class_def})#{
56
+ }|(^|[\n;])\s*(while|until)\b#{
57
+ }|(^|[#{leftop}\s?])(do|case|begin|for)\b#{
58
+ }\s(c)?(?:attr_(reader|writer|accessor))[( ] *((?::\w+ *,\s*)*:\w+)#{Ender
59
+ }|\salias_method +:#{name} *,\s*:#{name
60
+ }|\s(include|extend)[( ] *#{mname
61
+ }|(^|[;\s])(#{alias_def}|end|__END__)\b/m
62
+
63
+ ModDef = mod_def.to_re
64
+ ClassDef = class_def.to_re
65
+ MethodDef = method_def.to_re
66
+ AliasDef = alias_def.to_re
67
+
68
+ def self::Class(s, m)
69
+ debug
70
+ _stack = clean_stack
71
+ if _stack[-1] == [:block]
72
+ stack << [:beginner]
73
+ elsif m[1]
74
+ if m[2] =~ /^[@$]/
75
+ stack << [:beginner]
76
+ elsif _stack.any? and _stack[-1][0] == :def
77
+ stack << [:beginner]
78
+ else
79
+ slf = _stack.lasts*'::'
80
+ name = m[2].sub 'self.', ''
81
+ name.sub! 'self', slf
82
+ name = fix_module_name slf, name
83
+ stack << [:singleton, name]
84
+ end
85
+ else
86
+ new = clean_stack.lasts*'::'
87
+ stack << [:class, m[2]]
88
+ name = fix_module_name new, m[3] if m[3]
89
+ new << '::' if new.b
90
+ new << m[2]
91
+ MethodCache[new] ||= {}
92
+ inherit! new, name if m[3]
93
+ end
94
+ end
95
+
96
+ def self::Module(s, m)
97
+ debug
98
+ stack << [:mod, m[1]]
99
+ MethodCache[clean_stack.lasts*'::'] = {}
100
+ end
101
+
102
+ def self::Method(s, m)
103
+ debug
104
+ _stack = clean_stack(true)
105
+ if _stack[-1] == [:block]
106
+ stack << [:beginner]
107
+ end
108
+ start = s.pos - s.matched[/[^\n]+$/].size
109
+ name = m[1].sub(/::([^:.]+)$/, '.\1')
110
+ name.sub!(/#{_stack.last[1]}\./, 'self.') if _stack.any?
111
+ if name[/^self\.(.+)/]
112
+ stack << [:def, "#{_stack.lasts*'::'}.#$1", start]
113
+ elsif name['.'] and name =~ /^[A-Z]/
114
+ mod, name = name/'.'
115
+ fix_module_name(_stack.lasts*'::', mod) >> '.' >> name
116
+ stack << [:def, name, start]
117
+ else
118
+ prefix = (_stack.any? && _stack[-1][0] == :singleton) ? _stack[-1][1]+'.' : _stack.lasts*'::'+'#'
119
+ stack << [:def, prefix+name, start]
120
+ end
121
+ end
122
+
123
+ def self::Alias(s, m)
124
+ debug
125
+ _stack = clean_stack
126
+ case _stack.any? && _stack[-1][0]
127
+ when false, :def, :block
128
+ return
129
+ when :singleton
130
+ prefix = _stack[-1][1]
131
+ new, old = '.'+m[1], '.'+m[2]
132
+ else
133
+ prefix = _stack.lasts*'::'
134
+ new, old = '#'+m[1], '#'+m[2]
135
+ end
136
+ MethodCache[prefix][new] = MethodCache[prefix][old] || "def #{new}(*args)\n #{old}(*args)\nend"
137
+ end
138
+
139
+ end if !defined? Defaults
140
+
141
+ Closers = {'<' => '>', '{' => '}', '[' => ']', '(' => ')'}
142
+
143
+ def init_instructions
144
+ [
145
+ [/^\#/, lambda {|s, m| s.scan_until(/\n/)}],
146
+ [@Processor::StringRE, method(:string)],
147
+
148
+ [/^\{$/, lambda {|s, m|
149
+ debug
150
+ $log.debug @curls_count
151
+ @curls_count += 1
152
+ }],
153
+
154
+ [/^\}$/, method(:curl_close)],
155
+ [@Processor::Ord],
156
+
157
+ [@Processor::BlockOpen, lambda {|s, m|
158
+ debug
159
+ $log.debug @curls_count
160
+ @stack << [:block]
161
+ }],
162
+
163
+ [@Processor::ModDef, @Processor.method(:Module)],
164
+ [@Processor::ClassDef, @Processor.method(:Class)],
165
+ [@Processor::MethodDef, @Processor.method(:Method)],
166
+ [@Processor::AliasDef, @Processor.method(:Alias)],
167
+ [@Processor::Symbol],
168
+ [@Processor::RERE, method(:string)],
169
+ [@Processor::HeredocRE, method(:heredoc)],
170
+
171
+ [/(^|\n)=begin/, lambda {|s, m| s.scan_until(/\n=end\s*\n/m)}],
172
+
173
+ [@Processor::Attrs, lambda {|s, m|
174
+ attr_accessors s, m
175
+ if s.matched =~ / end$/
176
+ end!(s)
177
+ elsif s.matched =~ /[^\?]}$/
178
+ curl_close
179
+ end
180
+ }],
181
+
182
+ [@Processor::Include, lambda {|s, m|
183
+ _stack = clean_stack
184
+ if _stack.empty?
185
+ if m[1] == 'include'
186
+ inherit! 'Object', m[2]
187
+ else
188
+ inherit_singletons! 'Object', m[2]
189
+ end
190
+ elsif !_stack[-1][0].in([:def, :block]) and m[2] =~ /^[A-Z]/
191
+ if m[1] == 'include'
192
+ inherit! _stack.lasts*'::', m[2]
193
+ else
194
+ inherit_singletons! _stack.lasts*'::', m[2]
195
+ end
196
+ end
197
+ }],
198
+
199
+ [@Processor::AliasMethod, lambda {|s, m|
200
+ _stack = clean_stack
201
+ if _stack[-1][0] == :class
202
+ new, old = m[1..2]
203
+ prefix = _stack.lasts*'::'
204
+ @MethodCache[prefix][new] = @MethodCache[prefix][old] || "def #{new}(*args)\n #{old}(*args)\nend"
205
+ end
206
+ }],
207
+
208
+ [@Processor::Beginners, lambda {|s, m|
209
+ debug
210
+ $log.debug [m, s.last, s.string[s.last-1,1].to_s]
211
+ if (m[2] and s.last != 0 and m[2].tr(' \t', '').empty? and !(s.string[s.last-1,1].to_s)[/[\n;({\[]/])
212
+ else
213
+ if m[3] == 'if' and @stack.empty? and s.check_until(@Processor::EOF) and s.matched != "\n"
214
+ throw :EOF
215
+ end
216
+ @stack << [m[5] ? :block : :beginner]
217
+ end
218
+ }],
219
+
220
+ [/(^|[\s;])end.?/, method(:end!)],
221
+ [/(^|[\s;])__END__/, lambda {|s, m| throw :EOF}]
222
+ ].dup
223
+ end
224
+
225
+ def debug(s,m)
226
+ $log.debug(:caller=>2) {"#{s.string[0, s.pos].count("\n")+1}:#{s.head.size + s.matched_size - ((s.head+s.matched).reverse.index("\n") || 0)}"}
227
+ $log.debug(@stack, :caller=>2)
228
+ $log.debug(:caller=>2) {Painter.g(s.head+s.matched)}
229
+ end
230
+
231
+ def initialize(instructions_module=Defaults)
232
+ @MethodCache = {'Object' => {}}
233
+ @ReadPaths = {}
234
+ @Processor = instructions_module
235
+ @Instructions = init_instructions
236
+ @MainParseRE = @Processor::MainParseRE
237
+ add_method_seeker('get_opts') {|s, args| $log << args}
238
+ end
239
+
240
+ def add_instruction(re, &callback)
241
+ @MainParseRE |= re
242
+ @Instructions << [re, callback]
243
+ end
244
+
245
+ def add_method_seeker(name, *args, &callback)
246
+ pattern = /(?:^|[\s#{@Processor::Leftop}])(#{name})[( ] */
247
+ add_instruction(pattern) {|s, m|
248
+ yield s, arguments(s, m)
249
+ }
250
+ end
251
+
252
+ # Parser methods
253
+
254
+ def arguments(s, m)
255
+ arg_list = [m[1]]
256
+ counts = {'}' => 0, ']' => 0, ')' => 0}
257
+ catch(:EOL) {s.each(@Processor::ArgumentsParseRE, [
258
+ [/^[{\[(]$/, lambda {|s, m| counts[Closers[m[0]]] += 1}],
259
+ [/^[}\])]$/, lambda {|s, m|
260
+ if counts[m[0]] > 0
261
+ counts[m[0]] -= 1
262
+ else
263
+ curl_close
264
+ throw :EOL
265
+ end
266
+ }],
267
+ [/^[,\|]$/, lambda {|s, m| s.scan_until(/\s*/m)}],
268
+ [/[#\n;]| end\b/, lambda {|s, m|
269
+ s.scan_until(/\n/) if m[0] == '#'
270
+ heredoc_list.each {|opener| string(s, [nil]*4+opener)}
271
+ throw :EOL
272
+ }],
273
+ [@Processor::StringRE, lambda {|s, m|
274
+ str = [s.pos-1, string(s, m)]
275
+ str[1] ? arg_list << s.string[str[0]...str[1]] : arg_list << s.string[str[0]-1..str[0]]
276
+ }],
277
+ [@Processor::RERE, lambda {|s, m|
278
+ str = [s.pos-1, string(s, m)]
279
+ arg_list << s.string[str[0]...str[1]]
280
+ }],
281
+ [/^ +[:?]\s+$/],
282
+ [/^ *=>\s*$/, lambda {|s, m| arg_list << '=>'}],
283
+ [/^[:@$.\w!?]+([( ] *)?$/, lambda {|s, m|
284
+ str = [s.pos-s.matched_size, arguments(s, m) && s.pos]
285
+ arg_list << s.string[str[0]...str[1]] if str[1]
286
+ }]
287
+ ])}
288
+ arg_list
289
+ end
290
+
291
+ def string(s, m)
292
+ debug
293
+ return if m[1] and s.- == '$'
294
+ opener = m[1] || m[3] || m[5]
295
+ $log.log {"entering #{opener}-quotes, matched as '#{Painter.g s.matched}' at #{s.string[0..s.pos].count("\n")+1}"}
296
+ if opener == m[5]
297
+ closer = opener = m[5].tr('`\'"', '')
298
+ quote_re = /\\|\n#{'\s*' if m[4]}#{closer}/
299
+ else
300
+ closer = Closers[opener] || opener
301
+ quote_re = /\\|#{Regexp.escape closer}/
302
+ end
303
+ openers_cnt = 1
304
+ inner_curls_count = 0
305
+ backslash = false
306
+ quote_re |= /#\{/ if (m[5] and m[5].ord != ?') or closer =~ /[\/"`]/ or (m[2] =~ /[xrQW]/ or m[3])
307
+ instructions = [
308
+ [@Processor::Ord],
309
+ [/\s*#{Regexp.escape closer}$/, lambda {|s, m|
310
+ if backslash
311
+ backslash = false
312
+ break if s.- == '\\' and m[0] == closer
313
+ end
314
+ if (openers_cnt -= 1) == 0
315
+ $log.log {"exiting through #{closer}-quotes at #{s.string[0...s.pos].count("\n")+1}"}
316
+ throw :EOS
317
+ else
318
+ $log.log 'decreasing openers count'
319
+ end
320
+ }],
321
+ [/\\/, lambda {|s, m|
322
+ prev = s.-
323
+ backslash = true
324
+ if prev == '\\'
325
+ i = 2
326
+ while prev == '\\'
327
+ prev = s.prev_in i
328
+ i += 1
329
+ backslash = !backslash
330
+ end
331
+ end
332
+ $log.log {"#{!backslash ? 'closed' : 'found'} \\ in #{opener}-quotes at #{s.string[0, s.pos].count("\n")+1}"}
333
+ }],
334
+ [/\#\{/, lambda {|s, m|
335
+ if backslash
336
+ backslash = false
337
+ if s.- == '\\'
338
+ openers_cnt += 1 if closer == '}'
339
+ break
340
+ end
341
+ end
342
+ $log.log "entering curls"
343
+ inner_curls_count += 1
344
+ catch(:inner_out) {s.each(@Processor::StringParseRE, [
345
+ [/^\#$/, lambda {|s, m|
346
+ $log.log 'reading comment'
347
+ s.scan_until(/\n/)}],
348
+ [/^\{$/, lambda {|s, m|
349
+ $log.log "increasing curls count"
350
+ inner_curls_count += 1}],
351
+ [/^\}$/, lambda {|s, m|
352
+ if (inner_curls_count -= 1) == 0
353
+ $log.log "exiting curls"
354
+ throw :inner_out
355
+ else
356
+ $log.log "decreasing curls count"
357
+ end}],
358
+ [@Processor::HeredocRE, method(:heredoc)],
359
+ [@Processor::StringRE, method(:string)],
360
+ [@Processor::RERE, method(:string)]
361
+ ])}
362
+ }]
363
+ ]
364
+ if closer != opener
365
+ quote_re |= /#{Regexp.escape opener}/
366
+ instructions << [/#{Regexp.escape opener}$/, lambda {|s, m|
367
+ if backslash
368
+ backslash = false
369
+ break if s.- == '\\'
370
+ end
371
+ $log.log 'increasing openers count'
372
+ openers_cnt += 1
373
+ }]
374
+ $log.debug [quote_re,instructions]
375
+ end
376
+
377
+ catch(:EOS) {s.each(quote_re, instructions)}
378
+ s.pos
379
+ end
380
+
381
+ def heredoc(s=nil, m)
382
+ heredoc_list = [m[1..2]]
383
+ catch(:EOL) {s.each(@Processor::HeredocParseRE, [
384
+ [/[#\n]/, lambda {|s, m|
385
+ s.scan_until(/\n/) if m[0] == '#'
386
+ heredoc_list.each {|opener| string(s, [nil]*4+opener)}
387
+ throw :EOL
388
+ }],
389
+ [@Processor::HeredocRE, lambda {|s, m| heredoc_list << m[1..2]}],
390
+ [@Processor::StringRE, method(:string)],
391
+ [@Processor::RERE, method(:string)]
392
+ ])}
393
+ end
394
+
395
+ def curl_close(*)
396
+ if @curls_count == 0
397
+ @stack.pop
398
+ else
399
+ @curls_count -= 1
400
+ end
401
+ end
402
+
403
+ def end!(s, *)
404
+ debug
405
+ if s.+ !~ /[?!(]/
406
+ exit = @stack.pop
407
+ case exit[0]
408
+ when :def
409
+ prefix, name = exit[1].sharp_split(/[.@#]/, 2)
410
+ if !name
411
+ prefix, name = 'Object', prefix
412
+ end
413
+ if @MethodCache[prefix]
414
+ (@MethodCache[prefix][name] ||= []) << (path.inline ? [path, exit[2]...s.pos] : s.string[exit[2]...s.pos])
415
+ end
416
+ end
417
+ end
418
+ end
419
+
420
+ def attr_accessors(s, m)
421
+ _stack = clean_stack
422
+ if _stack[-1][0] == :class
423
+ prefix = _stack.lasts*'::'
424
+ attrs = (m[3]/',').map {|attr| (m[1] ? '.' : '#')+attr.strip[1..-1]}
425
+ if m[2].in %w(reader accessor)
426
+ attrs.each {|attr| (@MethodCache[prefix][attr] ||= []) << "def #{'self.' if m[1]}#{attr}\n #{'@' if m[1]}@#{attr}\nend"}
427
+ end
428
+ if m[2].in %w(writer accessor)
429
+ attrs.each {|attr| (@MethodCache[prefix][attr] ||= []) << "def #{'self.' if m[1]}#{attr}=value\n #{'@' if m[1]}@#{attr} = value\nend"}
430
+ end
431
+ end
432
+ end
433
+
434
+ def parse_file(path)
435
+ @stack = []
436
+
437
+ if path.inline
438
+ return if @ReadPaths[path]
439
+ lines = get_lines(path)[0]
440
+ @ReadPaths[path] = true
441
+ else
442
+ lines = path.sharp_split(/\n/)
443
+ end
444
+ if RUBY_VERSION > '1.9'
445
+ ss = StringScanner lines.join.force_encoding('UTF-8')
446
+ else
447
+ ss = StringScanner lines.join
448
+ end
449
+
450
+ @curls_count = 0
451
+ catch(:EOF) { ss.each @MainParseRE, @Instructions }
452
+ raise "Can't parse: #{@stack.inspect}, #{ss.string[ss.last..ss.pos].inspect}" if @stack.any?
453
+ ss
454
+ end
455
+
456
+ def clean_stack(no_def=false)
457
+ @stack.select {|e| e[0] != :beginner and !no_def || e[0] != :def}
458
+ end
459
+
460
+ def inherit!(descendant, ancestor)
461
+ @MethodCache[descendant].reverse_merge((
462
+ @MethodCache[fix_module_name(descendant, ancestor)] ||= {}
463
+ ).map_values {|defs| defs.dup})
464
+ end
465
+
466
+ def inherit_singletons!(descendant, ancestor)
467
+ (@MethodCache[fix_module_name(descendant, ancestor)] ||= {}).each {|name, defs|
468
+ @MethodCache[descendant][name.sub('#', '.')] = defs.dup if name.ord == ?#
469
+ }
470
+ end
471
+
472
+ def fix_module_name(current, name)
473
+ if name =~ /^::/ or current == ''
474
+ current+name
475
+ elsif name == current or name == 'self'
476
+ current
477
+ elsif name !~ /^[A-Z]/
478
+ current+'#'+name
479
+ else
480
+ path = current+'::'+name
481
+ if @MethodCache[path]
482
+ path
483
+ else
484
+ @MethodCache[name] ||= {}
485
+ name
486
+ end
487
+ end
488
+ end
489
+
490
+ def get_lines(path)
491
+ SCRIPT_LINES__.to_a.select {|d, f| d[path]}.to_a.lasts
492
+ end
493
+
494
+ def print_lines(prefix, name, all)
495
+ map = lambda {|lines|
496
+ if lines.is Array
497
+ lines = SCRIPT_LINES__[lines[0]].join[lines[1]]
498
+ end
499
+ lines }
500
+ methods = all ? @MethodCache[prefix][name].map(&map) : map.call(@MethodCache[prefix][name].last)
501
+ puts methods
502
+ end
503
+
504
+ def code_of(path, name=nil, all=false)
505
+ if name.in [true, :all]
506
+ all = true
507
+ end
508
+ if path.is String
509
+ prefix, name = path.sharp_split(/[.#]/, 2)
510
+ elsif Class === path
511
+ prefix = path.name
512
+ name = ".#{name}"
513
+ else
514
+ prefix = path.class.name
515
+ name = "##{name}"
516
+ end
517
+ if !(@MethodCache[prefix]||{})[name]
518
+ puts "looking up script lines, please wait..."
519
+ SCRIPT_LINES__.each_key {|k| parse_file k
520
+ break if (@MethodCache[prefix]||{})[name]
521
+ }
522
+ end
523
+ if !(@MethodCache[prefix]||{})[name]
524
+ print "nothing was found for #{prefix}#{name}"
525
+ name = name.tr('#.', '.#')
526
+ if (@MethodCache[prefix]||{})[name]
527
+ puts ", but found for #{name}:"
528
+ print_lines prefix, name, all
529
+ else puts ''
530
+ end
531
+ else
532
+ puts "code for #{prefix}#{name}:"
533
+ print_lines prefix, name, all
534
+ end
535
+ end
536
+
537
+ end
538
+
539
+ Reader = CodeReader.new
540
+ end
541
+
542
+ module Kernel
543
+
544
+ def code_of(*args)
545
+ RMTools::Reader.code_of(*args)
546
+ end
547
+
548
+ end
549
+
550
+ class Method
551
+
552
+ def code(all=false)
553
+ RMTools::Reader.code_of(receiver, name, all)
554
+ end
555
+
556
+ end