assert2 0.3.9 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,9 +49,7 @@ module Test; module Unit; module Assertions
49
49
  # a string of disassembled Ruby
50
50
  #
51
51
  def reflect_source(&block)
52
- rf = RubyReflector.new(nil, false)
53
- rf.block = block
54
- return rf.result
52
+ RubyReflector.new(nil, block, false).result
55
53
  end
56
54
 
57
55
  # This compiles a string and +reflect+s its source...
@@ -0,0 +1,859 @@
1
+ require 'pp'
2
+
3
+
4
+ module Test; module Unit; module Assertions
5
+
6
+ # ERGO
7
+ # :bmethod => [:cval],
8
+ # :cfunc => [:argc, :cfnc],
9
+ # :cref => [:next, :clss],
10
+ # :defs => [:mid, :defn, :recv],
11
+ # :dmethod => [:cval],
12
+ # :dot2 => [:beg, :end],
13
+ # :dot3 => [:beg, :end],
14
+ # :dregx_once => [:next, :lit, :cflag],
15
+ # :fbody => [:orig, :mid, :head],
16
+ # :flip2 => [:cnt, :beg, :end],
17
+ # :flip3 => [:cnt, :beg, :end],
18
+ # :gasgn => [:vid, :value], # entry not supported
19
+ # :ifunc => [:tval, :state, :cfnc],
20
+ # :lasgn => [:vid, :cnt, :value],
21
+ # :last => [],
22
+ # :match => [:lit],
23
+ # :memo => {:u1_value=>:u1_value}, # different uses in enum.c, variabe.c and eval.c ...
24
+ # :method => [:body, :noex, :cnt], # cnt seems to be always 0 in 1.8.4
25
+ # :module => [:cpath, :body],
26
+ # :next => [:stts],
27
+ # :opt_n => [:body],
28
+ # :to_ary => [:head],
29
+
30
+ # This +reflect+s a block of code, by evaluating it, reflecting its
31
+ # source, and reflecting all its intermediate values
32
+ #
33
+ def reflect(&block)
34
+ result = block.call
35
+ rf = RubyReflector.new
36
+ rf.block = block
37
+
38
+ begin
39
+ waz = rf.colorize?
40
+ rf.colorize(false)
41
+ return rf.result + rf.arrow_result(result) + "\n" + rf.format_evaluations
42
+ ensure
43
+ rf.colorize(waz)
44
+ end
45
+ end
46
+
47
+ # This +reflect+s a block of code, /without/ evaluating it.
48
+ # The function only compiles the source and reflects it as
49
+ # a string of disassembled Ruby
50
+ #
51
+ def reflect_source(&block)
52
+ RubyReflector.new(nil, block, false).result
53
+ end
54
+
55
+ # This compiles a string and +reflect+s its source...
56
+ # as another string.
57
+ #
58
+ def reflect_string(string)
59
+ rf = RubyReflector.new # def initialize
60
+ rf.block = proc{}
61
+ rf.reflect_values = false
62
+ # pp string.parse_to_nodes.transform
63
+ got = rf.reflect_nodes(string.parse_to_nodes)
64
+ return got
65
+ end
66
+
67
+ class RubyReflector # this class turns hamburger back into live cattle
68
+ HAS_RIPPER = false
69
+
70
+ begin
71
+ require 'rubygems'
72
+ require 'rubynode'
73
+ HAS_RUBYNODE = true
74
+ rescue LoadError
75
+ HAS_RUBYNODE = false
76
+ end
77
+
78
+ attr_reader :evaluations,
79
+ :result,
80
+ :transformation
81
+ attr_writer :block,
82
+ :reflect_values
83
+
84
+ def initialize(called = nil, yo_block = nil, reflect_values = true) # note that a block, from your context, is not optional
85
+ # FIXME these args are bogus use or lose
86
+ @reflect_values = reflect_values
87
+ @evaluations = []
88
+ @result = ''
89
+ @line = 0
90
+ self.block = yo_block
91
+ end
92
+
93
+ def block=(yo_block)
94
+ @block = yo_block and @block.respond_to?(:body_node) and
95
+ reflect_nodes(@block.body_node)
96
+ end
97
+
98
+ def reflect_nodes(body_node)
99
+ if body_node
100
+ @transformation = body_node.transform(:include_node => true)
101
+ return @result = _send(@transformation)
102
+ end
103
+ rescue
104
+ puts "\nOffending line: #{ @line }"
105
+ raise
106
+ end
107
+
108
+ def absorb_block_args(code_fragments) # CONSIDER a suckier way of detecting
109
+ @captured_block_vars = nil # the block args is indeed remotely possible...
110
+ if code_fragments.first =~ /\|(.*)\|/ or code_fragments[1].to_s =~ /\|(.*)\|/
111
+ @captured_block_vars = $1
112
+ end
113
+ end
114
+
115
+ def detect(expression)
116
+ raise 'FIXME'
117
+ expr = expression
118
+ $__args = nil
119
+ if @args and @captured_block_vars
120
+ expr = "#{@captured_block_vars} = $__args.kind_of?(Array) && $__args.length == 1 ? $__args.first : $__args\n" +
121
+ expr
122
+ $__args = @args
123
+ end
124
+
125
+ begin
126
+ intermediate = eval(expr, @block.binding)
127
+ @evaluations << [expression, intermediate, nil]
128
+ rescue SyntaxError => e
129
+ if e.message.index('syntax error, unexpected \',\'') and expression !~ /\[ /
130
+ return detect('[ ' + expression + ' ]')
131
+ end # faint prayer to infinite recursion diety here! (-;
132
+
133
+ @evaluations << [expression, nil, e.message]
134
+ rescue => e
135
+ @evaluations << [expression, nil, e.message]
136
+ end
137
+ end
138
+
139
+ def eval_intermediate(expression)
140
+ detect(expression) if @reflect_values
141
+ return expression
142
+ end
143
+
144
+ def short_inspect(intermediate)
145
+ pretty = intermediate.inspect
146
+ # ERGO Proc is prob'ly rare here!
147
+ pretty = { '#<Proc' => '<Proc>' }.fetch(pretty.split(':').first, pretty)
148
+ prettier = pretty[0..90]
149
+ prettier << '*** ' unless prettier == pretty
150
+ return prettier
151
+ end
152
+ private :short_inspect
153
+
154
+ # ERGO spew the backrefs (?) any regular expression matchers may emit!
155
+ # ERGO don't eval the caller of a block without its block!
156
+
157
+ def format_evaluations
158
+ max_line = @evaluations.map{|exp, val, prob| exp.length}.sort.last
159
+ already = {}
160
+ lines = []
161
+
162
+ @evaluations.each do |exp, val, prob|
163
+ line = " #{ exp.center(max_line) } "
164
+
165
+ line << if prob then
166
+ orange('--? ' + prob)
167
+ else
168
+ green('--> ') + bold(short_inspect(val))
169
+ end
170
+
171
+ lines << line unless already[line] == true
172
+ already[line] = true
173
+ end
174
+
175
+ return lines.compact.join("\n")
176
+ end
177
+
178
+ def _send(node, thence = '')
179
+ return '' unless node
180
+ return node.to_s + thence if node.class == Symbol
181
+ target = :"_#{ node.first }"
182
+ last = node.last
183
+ (@line = last[:node].line) rescue nil
184
+ exp = send(target, last)
185
+ exp << thence if exp.length > 0
186
+ return exp
187
+ end
188
+
189
+ %w( args beg body cond cpath defn else end ensr
190
+ first head iter ivar lit mid next second
191
+ stts recv resq rest value var vid ).each do |sender|
192
+ define_method sender + '_' do |node, *args|
193
+ return _send(node[sender.to_sym], *args)
194
+ end
195
+ end
196
+
197
+ ########################################################
198
+ #### structures
199
+
200
+ def _block(node)
201
+ return node.map{|n| _send(n, "\n") }.join
202
+ end
203
+
204
+ def _module(node, what = 'module')
205
+ return what + ' ' + cpath_(node) + "\n" +
206
+ body_(node) +
207
+ "\nend\n"
208
+ end
209
+
210
+ def _method(node)
211
+ p node
212
+ return ''
213
+ end
214
+
215
+ def _class(node); _module(node, 'class'); end
216
+ def _self(node); 'self'; end
217
+ def _defn(node); _defs(node); end
218
+ def _super(node); 'super(' + args_(node) + ')'; end
219
+ def _zsuper(node); 'super'; end
220
+ def _begin(node); "begin\n" + body_(node) + "\nend\n"; end
221
+ def _ensure(node); head_(node) + "\nensure\n" + ensr_(node); end
222
+
223
+ def _sclass(node)
224
+ return 'class << ' + recv_(node) +
225
+ head_body(node) +
226
+ "end\n"
227
+ end
228
+
229
+ class ScopeMethod #:nodoc:
230
+ # this is complex because Ruby gloms several different
231
+ # kinds of variables into one "scope" token, and they
232
+ # don't directly match their layout in the source code
233
+
234
+ def _scopic(ref, node)
235
+ @ref = ref
236
+ @node = node
237
+ @expression = ''
238
+ @previously = false
239
+ @block_arg = false
240
+ @splat_arg = false
241
+ @previous_splat = false
242
+
243
+ if @node[:tbl]
244
+ @expression << '('
245
+ render_argument_list
246
+ @expression << ')'
247
+ end
248
+
249
+ @expression << "\n"
250
+ @expression << @ref.next_(@node)
251
+ return @expression
252
+ end
253
+
254
+ def ulterior_comma(token)
255
+ @expression << ', ' if @index > 0
256
+ @expression << token
257
+ @index = 0
258
+ end
259
+
260
+ def possible_comma(token)
261
+ @expression << ', ' if @index > 0
262
+ @expression << @ref._send(token)
263
+ end
264
+
265
+ def render_argument_list
266
+ @nekst = @node[:next]
267
+ @block = @nekst.last if @nekst && @nekst.first == :block
268
+ @args = @block.first.last if @block && @block.first.first == :args
269
+ @rest = @args[:rest] if @args
270
+ @opt = @args[:opt] if @args
271
+
272
+ @node[:tbl].each_with_index do |_n, _index|
273
+ @n, @index = _n, _index
274
+ render_argument
275
+ break if @block_arg
276
+ end
277
+ end
278
+
279
+ def render_argument
280
+ @splat_arg = @block_arg = false
281
+
282
+ if @rest and @rest.first == :lasgn and
283
+ (@n == nil or @rest.last[:vid] == @n)
284
+ ulterior_comma('*')
285
+ @splat_arg = true
286
+ end
287
+
288
+ if @block and (ba = @block[1]) and
289
+ ba.first == :block_arg and ba.last[:vid] == @n
290
+ ulterior_comma('&')
291
+ @block_arg = true
292
+ end
293
+
294
+ # ERGO Ruby 1.9 changes these rules!!
295
+
296
+ if !@previous_splat or @block_arg
297
+ if @opt and @opt.first == :block and # ERGO why a @block??
298
+ (lasgn = @opt.last.first).first == :lasgn and
299
+ lasgn.last[:vid] == @n
300
+ @previously = true
301
+ possible_comma(@opt.last.first)
302
+ else
303
+ possible_comma(@n)
304
+ @expression << ' = nil' if @previously and !@block_arg and !@splat_arg
305
+ end
306
+
307
+ @previous_splat ||= @splat_arg
308
+ end
309
+ end
310
+ end
311
+
312
+ def _scope(node)
313
+ return ScopeMethod.new._scopic(self, node)
314
+ end
315
+
316
+ def _defs(node)
317
+ return 'def ' + recv_(node, '.') + mid_(node) +
318
+ defn_(node) +
319
+ "end\n"
320
+ end
321
+
322
+ def _rescue(node)
323
+ if node[:else] == false and node[:head] and
324
+ node[:resq] and node[:head].first == :vcall
325
+ return head_(node) + ' rescue ' + resq_(node)
326
+ else
327
+ exp = head_(node) +
328
+ else_(node) +
329
+ "rescue"
330
+ if node[:resq] and node[:resq].first == :resbody
331
+ body = node[:resq].last
332
+ exp << ' ' + args_(body) if body and body[:args]
333
+ end
334
+ return exp + "\n" + resq_(node)
335
+ end
336
+ end
337
+
338
+ def _resbody(node)
339
+ return body_(node)
340
+ # already emitted: head_(node) + ' ' + args_(node)
341
+ end
342
+
343
+ def _yield(node)
344
+ exp = 'yield'
345
+ exp << '(' + head_(node) + ')' if node[:head]
346
+ return exp
347
+ end
348
+
349
+ def _alias(node)
350
+ return "alias #{ lit_(node[:new].last) } #{ lit_(node[:old].last) }"
351
+ end
352
+
353
+ def _valias(node)
354
+ return "alias #{ node[:new] } #{ node[:old] }"
355
+ end
356
+
357
+ ########################################################
358
+ #### control flow
359
+
360
+ def _if(node)
361
+ expression = '( if ' + eval_parenz{cond_(node)} + ' then '
362
+ expression << eval_parenz{body_(node)} if node[:body]
363
+ expression << ' else ' +
364
+ eval_parenz{else_(node)} if node[:else]
365
+ expression << ' end )'
366
+ return expression
367
+ end
368
+
369
+ def _while(node, concept = 'while')
370
+ return '( ' + concept + ' ' + cond_(node) +
371
+ head_body(node) +
372
+ "\nend )"
373
+ end
374
+
375
+ def _for(node)
376
+ return '( for ' + var_(node) + ' in ' + iter_(node) + "\n" +
377
+ body_(node) + "\n" +
378
+ 'end )'
379
+ end
380
+
381
+ def _args(node); return ''; end # _call and _fcall insert the real args
382
+ def _until(node); _while(node, 'until'); end
383
+ def _break(node); 'break'; end
384
+ def _next(node); 'next' ; end
385
+ def _case(node); '( case ' + head_body(node) + "\nend )"; end
386
+ def _when(node); 'when ' + head_body(node) + "\n" + next_(node); end
387
+ def _retry(node); 'retry'; end
388
+ def _redo(node); 'redo'; end
389
+ def head_body(node); head_(node) + "\n" + body_(node); end
390
+
391
+ def _return(node)
392
+ exp = 'return'
393
+ return exp unless stts = node[:stts]
394
+ exp << ' '
395
+
396
+ if stts.first == :array
397
+ exp << '[' + stts_(node) + ']'
398
+ elsif stts.first == :svalue
399
+ exp << stts_(node)
400
+ else
401
+ exp << eval_parenz{stts_(node)}
402
+ end
403
+
404
+ return exp
405
+ end
406
+
407
+ def _postexe(node)
408
+ raise '_postexe called with unexpected arguments' unless node == {} or node.keys == [:node]
409
+ return 'END'
410
+ end
411
+
412
+ # :argscat => [:body, :head],
413
+
414
+ ########################################################
415
+ #### assignments
416
+
417
+ def _dasgn_curr(node)
418
+ expression = vid_(node)
419
+ return expression unless value = node[:value]
420
+ expression << ' = '
421
+ we_b_array = value.first == :array
422
+ expression << nest_if(we_b_array, '[', ']'){ value_(node) }
423
+ return expression
424
+ end
425
+
426
+ def _cdecl(node)
427
+ return _send(node[ node[:vid] == 0 ? :else : :vid ]) + ' = ' + value_(node)
428
+ end
429
+
430
+ def _dasgn(node); _dasgn_curr(node); end
431
+ def _iasgn(node); _dasgn_curr(node); end
432
+ def _gasgn(node); _dasgn_curr(node); end
433
+ def _lasgn(node); _dasgn_curr(node); end
434
+ def _cvasgn(node); _dasgn_curr(node); end
435
+
436
+ def _op_asgn2(node)
437
+ expression = ''
438
+
439
+ if node[:recv].first == :self
440
+ expression << 'self'
441
+ else
442
+ expression << recv_(node)
443
+ end
444
+
445
+ expression << '.'
446
+ expression << vid_(node[:next].last) + ' ||= ' + value_(node)
447
+ return expression
448
+ end
449
+
450
+ ########################################################
451
+ #### operators
452
+
453
+ def _and(node, und = 'and')
454
+ return eval_intermediate( '( ' +
455
+ eval_parenz{ first_(node)} + ' ' + und + ' ' +
456
+ eval_parenz{second_(node)} + ' )' )
457
+ end
458
+
459
+ def _back_ref(node); '$' + node[:nth].chr; end
460
+ def _colon2(node); head_(node, '::') + mid_(node); end
461
+ def _colon3(node); '::' + mid_(node); end
462
+ def _cvar(node); _lvar(node); end
463
+ def _cvdecl(node); vid_(node) + ' = ' + value_(node); end
464
+ def _defined(node); 'defined? ' + head_(node); end
465
+ def _dot2(node); '( ' + beg_(node) + ' .. ' + end_(node) + ' )'; end
466
+ def _dot3(node); '( ' + beg_(node) + ' ... ' + end_(node) + ' )'; end
467
+ def _dregx(node); _dstr(node, '/'); end
468
+ def _dregx_once(node); _dstr(node, '/'); end
469
+ def _dsym(node); ':' + _lit(node[:lit]) + ' ' + rest_(node); end
470
+ def _dvar(node); eval_intermediate(vid_(node)); end
471
+ def _dxstr(node); _dstr(node, '`'); end
472
+ def eval_parenz; eval_intermediate('( ' + yield + ' )'); end
473
+ def _evstr(node); body_(node); end
474
+ def _false(nada); 'false'; end
475
+ def _gvar(node); vid_(node); end
476
+ def _ivar(node); _dvar(node); end
477
+ def _lit(node); node[:lit].inspect; end
478
+ def _lvar(node); eval_intermediate(vid_(node)); end
479
+ def _match(node); node[:lit].inspect; end
480
+ def neg_one(node); node == -1 ? '' : _send(node); end
481
+ def _nil(nada); 'nil' ; end
482
+ def _not(node); '(not(' + body_(node) + '))'; end
483
+ def _nth_ref(node); "$#{ node[:nth] }"; end # ERGO eval it?
484
+ def _op_asgn_and(node); _op_asgn_or(node, ' &&= '); end
485
+ def _or(node); _and(node, 'or'); end
486
+ def _str(node); _lit(node); end
487
+ def _svalue(node); head_(node); end
488
+ def _to_ary(node); head_(node); end
489
+ def _true(nada); 'true' ; end
490
+ def _undef(node); 'undef ' + mid_(node); end
491
+ def _vcall(node); mid_(node); end
492
+ def we_b(node); node.first.first; end
493
+ def _xstr(node); '`' + scrape_literal(node) + '`'; end
494
+ def _zarray(node); return '[]'; end
495
+
496
+ def _flip2(node) # ERGO what the heck is this??
497
+ p node
498
+ p node.keys
499
+ return ''
500
+ end
501
+
502
+ def _masgn(node)
503
+
504
+ #{:value=>
505
+ # [:splat,
506
+ # {:head=>
507
+ # [:fcall,
508
+ # {:mid=>:calc_stack,
509
+ # :args=>
510
+ # [:array,
511
+ # [[:vcall, {:mid=>:insn}],
512
+ # [:vcall, {:mid=>:from}],
513
+ # [:vcall, {:mid=>:after}],
514
+ # [:vcall, {:mid=>:opops}]]]}]}],
515
+ # :args=>false,
516
+ # :head=>
517
+ # [:array,
518
+ # [[:dasgn_curr, {:value=>false, :vid=>:name}],
519
+ # [:dasgn_curr, {:value=>false, :vid=>:pops}],
520
+ # [:dasgn_curr, {:value=>false, :vid=>:rets}],
521
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs1}],
522
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs2}]]]}
523
+
524
+ value, head, args = node.values_at(:value, :head, :args)
525
+
526
+ if value
527
+ return '( ' + head_(node) + ' = *' + head_(value.last) + ' )' if value.first == :splat
528
+
529
+ if head and args
530
+ exp = head_(node)
531
+ return exp + ', * = ' + value_(node) if args == -1
532
+ return exp + ', *' + args_(node) + ' = ' + value_(node)
533
+ end
534
+
535
+ return '( ' + head_(node) + ' = ' + value_(node) + ' )' if args == false
536
+ end
537
+
538
+ if value == false and head == false and args
539
+ return '*' + neg_one(args)
540
+ end
541
+
542
+ if head.kind_of?(Array) and head.first == :array
543
+ return head.last.map{|n|
544
+ nest_if(n.first == :masgn, '(', ')'){ _send(n) }
545
+ }.join(', ')
546
+ end
547
+
548
+ if head == false and args and value
549
+ return '*' + args_(node) + ' = ' + value_(node)
550
+ end
551
+
552
+ return head_(node)
553
+ end
554
+
555
+ def _splat(node)
556
+ if (head = node[:head]) and
557
+ ((we_b_array = head.first == :array) or head.first == :lvar)
558
+ return '*' + nest_if(we_b_array, '[', ']'){ head_(node) }
559
+ end
560
+
561
+ return '*' + head_(node)
562
+ end # ERGO raise if any other key!
563
+
564
+ def _const(node)
565
+ expression = vid_(node)
566
+ q = eval(expression, @block.binding)
567
+ eval_intermediate(expression) unless q.kind_of?(Module)
568
+ return expression
569
+ rescue # ERGO will someone need to see whatever this was?
570
+ return expression
571
+ end
572
+
573
+ def scrape_literal(node, regex = false)
574
+ lit = node[:lit].inspect.gsub(/^"/, '').gsub(/"$/, '')
575
+ lit.gsub!('\\\\', '\\') if regex
576
+ return lit
577
+ end
578
+
579
+ def _dstr(node, delim = '"')
580
+ regex = delim == '/'
581
+ expression = delim + scrape_literal(node, regex)
582
+
583
+ if node[:next] and node[:next].first == :array
584
+ (node[:next].last || []).each do |n|
585
+ expression << if n.first == :str
586
+ scrape_literal(n.last, regex)
587
+ else
588
+ '#{ ' + _send(n) + ' }'
589
+ end
590
+ end
591
+ end
592
+
593
+ return eval_intermediate(expression + delim)
594
+ end
595
+
596
+ def _retry(node)
597
+ raise '_retry called with unexpected arguments' unless node == {} or node.keys == [:node]
598
+ return 'retry'
599
+ end
600
+
601
+ def recv_zero_self(node, plus = '')
602
+ recv = node[:recv]
603
+ return 'self' + plus if recv == 0
604
+ return recv_(node, plus)
605
+ end
606
+
607
+ def _attrasgn(node)
608
+ recv, args = node.values_at(:recv, :args)
609
+
610
+ if args
611
+ if args.first == :array
612
+ if node[:mid].class == Symbol
613
+ if node[:mid] == :'[]='
614
+ return recv_zero_self(node) + '[' +
615
+ _send(args.last.first) + '] = ' +
616
+ _send(args.last.last)
617
+ end
618
+ return recv_zero_self(node, '.') + mid_(node) + '(' + _send(args.last.last) + ')'
619
+ end
620
+ end
621
+
622
+ return recv_zero_self(node) +
623
+ '[' + head_(args.last) + '] = ' +
624
+ body_(args.last)
625
+ end
626
+
627
+ return recv_zero_self(node, '.') + node[:mid].to_s.gsub(/=$/, '')
628
+ end
629
+
630
+ def _op_asgn_or(node, op = ' ||= ')
631
+ # CONSIDER what be :aid?
632
+ #{:value=>[:lasgn, {:value=>[:str, {:lit=>"vm_opts.h"}], :cnt=>2, :vid=>:file}],
633
+ # :aid=>0,
634
+ # :head=>[:lvar, {:cnt=>2, :vid=>:file}]}
635
+ return head_(node) + op + value_(node[:value].last)
636
+ end
637
+
638
+ def fcall_args(node = nil, methodic = false)
639
+ expression = ''
640
+ return expression unless node
641
+ expression << ' ' unless methodic
642
+ return expression + nest_if(methodic, '(', ')'){ _send(node) }
643
+ end
644
+
645
+ def _fcall(node)
646
+ exp = mid_(node) + fcall_args(node[:args], true)
647
+ eval_intermediate(exp) unless %w(lambda proc).include?(exp)
648
+ return exp
649
+ end
650
+
651
+ def _block_pass(node)
652
+ fcall = node[:iter].last
653
+ return eval_intermediate(recv_(fcall, '.') +
654
+ mid_(fcall) + '(' + args_(fcall, ', ') +
655
+ '&' + body_(node) + ')' )
656
+ end
657
+
658
+ def _iter(node)
659
+ var = node[:var]
660
+
661
+ return eval_intermediate(
662
+ iter_(node) +
663
+ '{' +
664
+ nest_if(var != false, '|', '|'){ var_(node) unless var == 0 } +
665
+ nest_if(node[:body] , ' ', ' '){ body_(node) } +
666
+ '}')
667
+ end
668
+
669
+ def _array(node)
670
+ nest_if we_b(node) == :array, '[', ']' do
671
+ node.map{ |z|
672
+ exp = _send(z)
673
+ exp << ', ' unless z.object_id == node.last.object_id
674
+ exp
675
+ }.join
676
+ end
677
+ end
678
+
679
+ def _hash(node)
680
+ return '{}' unless node[:head] and (array = node[:head].last)
681
+ expression = '{ '
682
+
683
+ array.in_groups_of 2 do |key, value|
684
+ expression << _send(key) + ' => ' + _send(value)
685
+ expression << ', ' if value != array.last
686
+ end
687
+
688
+ return expression + ' }'
689
+ end
690
+
691
+ def _match2(node)
692
+ # ERGO should this work like match3?
693
+ return recv_(node) + ' =~ ' + value_(node)
694
+ end
695
+
696
+ def we_b_op(node)
697
+ return node[:mid] && node[:mid].to_s !~ /^[a-z]/i
698
+ end
699
+
700
+ def _match3(node)
701
+ # ERGO do :lit and :value exclude each other?
702
+ return recv_(node) + ' =~ ' + _send(node[:lit] || node[:value])
703
+ end
704
+
705
+ def _block_arg(node) # is this ever called?
706
+ return '' # note that _scope should not take care of this
707
+ end
708
+
709
+ class CallMethod
710
+ def bracket_args
711
+ return false unless @mid == '[]'
712
+ @expression << '[' + @ref.args_(@node) + ']'
713
+ return true
714
+ end
715
+
716
+ def insert_method_call
717
+ @expression << '.'
718
+ @expression << @mid
719
+ @expression << '(' + @ref.args_(@node) + ')'
720
+ @ref.eval_intermediate(@expression) if @methodic
721
+ end
722
+
723
+ def operator_and_arguments
724
+ @mid = @ref.mid_(@node)
725
+
726
+ unless bracket_args
727
+ @methodic = @mid =~ /[a-z]/i
728
+
729
+ if @methodic
730
+ insert_method_call
731
+ else
732
+ @expression << ' '
733
+ @expression << @mid
734
+ @expression << ' '
735
+ nest_args
736
+ end
737
+ end
738
+ end
739
+
740
+ def nest_args
741
+ return unless @args = @node[:args]
742
+
743
+ nest_me = @args.first == :array &&
744
+ @args.last.length == 1 &&
745
+ (call = @args.last.first).first == :call &&
746
+ @ref.we_b_op(call.last)
747
+
748
+ exp = @ref.nest_if(nest_me, '( ', ' )'){ @ref.args_(@node) }
749
+ @ref.eval_intermediate(exp) if nest_me
750
+ @expression << exp
751
+ end
752
+
753
+ def caller(ref, node)
754
+ @ref, @node = ref, node
755
+ @expression = ''
756
+ @recv = @node[:recv]
757
+
758
+ if @recv.first == :block_pass
759
+ @expression << @ref.recv_(@node)
760
+ operator_and_arguments
761
+ else
762
+ nest_me = @recv.first == :call && @ref.we_b_op(@recv.last)
763
+
764
+ exp = if @recv.first == :array
765
+ @ref.nest_if(true, '[ ', ' ]'){ @ref.recv_(node) }
766
+ else
767
+ exp2 = @ref.nest_if(nest_me, '( ', ' )'){ @ref.recv_(node) }
768
+ @ref.eval_intermediate(exp2) if nest_me
769
+ exp2
770
+ end
771
+
772
+ @expression << exp
773
+ operator_and_arguments
774
+ end
775
+ return @expression
776
+ end
777
+ end
778
+
779
+ def _call(node); CallMethod.new.caller(self, node); end
780
+
781
+ def nest_if(condition, before, after, &block)
782
+ exp = ''
783
+ exp << before if condition
784
+ exp << (block.call || '')
785
+ exp << after if condition
786
+ return exp
787
+ end
788
+
789
+ def _op_asgn1(node) # ERGO just look up the list of these?
790
+ return '' unless args = node[:args]
791
+ exp = recv_(node)
792
+
793
+ if [:'-', :'+', :'*', :'**', :'/', :^, :|, :&,
794
+ :'<<', :'>>',
795
+ ].include?(node[:mid]) and
796
+ node[:recv] and args.first == :argscat
797
+
798
+ return exp +
799
+ "[#{ body_(args.last) }] #{ node[:mid] }= " + head_(args.last)
800
+ end
801
+
802
+ raise "unexpected mid value #{ node[:mid].inspect } in opcode for X= " unless node[:mid] == 0
803
+
804
+ if args.first == :argscat and args.last[:body]
805
+ exp << '[' + body_(args.last) + ']'
806
+ exp << ' ||= ' + head_(args.last)
807
+ else
808
+ raise "unexpected arguments in opcode for ||= "
809
+ end
810
+
811
+ return exp
812
+ end
813
+
814
+ def _argscat(node)
815
+ return head_(node) + ', *' +
816
+ nest_if(node[:body].first == :array, '[', ']'){ body_(node) }
817
+ end
818
+
819
+ def diagnose(diagnostic, result, called, options, block, additional_diagnostics)
820
+ @__additional_diagnostics = additional_diagnostics
821
+ @__additional_diagnostics.unshift diagnostic
822
+ self.args = options.fetch(:args, [])
823
+ rf = self
824
+ polarity = 'assert{ '
825
+ lines = rf.split_and_read(called)
826
+
827
+ if lines.first =~ /^\s*(assert|deny)/
828
+ polarity = $1 + '{ '
829
+ end
830
+
831
+ rf.absorb_block_args lines
832
+ rf.block = block
833
+ effect = " - should #{ 'not ' if polarity =~ /deny/ }pass\n"
834
+
835
+ report = rf.magenta(polarity) + rf.bold(rf.result) + rf.magenta(" }") +
836
+ rf.red(arrow_result(result) + effect) +
837
+ rf.format_evaluations
838
+
839
+ return __build_message(report)
840
+ end
841
+
842
+ def arrow_result(result) #:nodoc:
843
+ return "\t--> #{ result.inspect }"
844
+ end
845
+
846
+ end
847
+
848
+ end; end; end
849
+
850
+ unless [].respond_to? :in_groups_of
851
+ class Array
852
+ def in_groups_of(number, fill_with = nil, &block)
853
+ require 'enumerator'
854
+ collection = dup
855
+ collection << fill_with until collection.size.modulo(number).zero?
856
+ collection.each_slice(number, &block)
857
+ end
858
+ end
859
+ end
data/lib/assert2/xhtml.rb CHANGED
@@ -51,7 +51,7 @@ requirements:
51
51
  must match
52
52
  - the specification only requires the attributes and structural
53
53
  elements that its matcher demands; we skip the rest -
54
- such as the <ol> and <li> fields. They can change
54
+ such as the <ol> and <li> elements. They can change
55
55
  freely as our website upgrades
56
56
  - at fault time, the matcher prints out the failing elements
57
57
  and their immediate context.
@@ -127,12 +127,12 @@ class BeHtmlWith
127
127
  builder.doc.children.each do |child|
128
128
  @first_samples = []
129
129
  # TODO warn if child is text
130
- path = build_deep_xpath(child)
131
- next if path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
130
+ @path = build_deep_xpath(child)
131
+ next if @path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
132
132
 
133
- matchers = doc.root.xpath_with_callback path, :refer do |elements, index|
134
- collect_samples(elements, index.to_i)
135
- end
133
+ matchers = @doc.root.xpath_with_callback @path, :refer do |elements, index|
134
+ collect_samples(elements, index.to_i)
135
+ end
136
136
 
137
137
  if matchers.empty?
138
138
  @first_samples << @doc.root if @first_samples.empty? # TODO test the first_samples system
@@ -153,22 +153,54 @@ class BeHtmlWith
153
153
  return '//' + build_xpath(element)
154
154
  end
155
155
 
156
+ def build_deep_xpath_too(element)
157
+ @references = []
158
+ return '//' + build_xpath_too(element)
159
+ end
160
+
156
161
  attr_reader :references
157
162
 
158
163
  def build_xpath(element)
159
164
  path = element.name.sub(/\!$/, '')
160
165
  element_kids = element.children.grep(Nokogiri::XML::Element)
161
- path << "[ refer(., '#{@references.length}')"
166
+ path << '[ '
167
+ count = @references.length
162
168
  @references << element
163
169
 
164
170
  if element_kids.any?
165
- path << ' and ' +
166
- element_kids.map{|child|
171
+ path << element_kids.map{|child|
167
172
  './descendant::' + build_xpath(child)
168
173
  }.join(' and ')
174
+ path << ' and '
175
+ end
176
+
177
+ path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
178
+ return path
179
+ end
180
+
181
+ def build_xpath_too(element)
182
+ path = element.name.sub(/\!$/, '')
183
+ element_kids = element.children.grep(Nokogiri::XML::Element)
184
+ path << '[ '
185
+ count = @references.length
186
+ @references << element
187
+ brackets_owed = 0
188
+
189
+ if element_kids.length > 0
190
+ child = element_kids[0]
191
+ path << './descendant::' + build_xpath_too(child)
192
+ # }.join(' and ')
193
+ # path << ' and '
194
+ end
195
+
196
+ if element_kids.length > 1
197
+ path << element_kids[1..-1].map{|child|
198
+ '[ ./following-sibling::*[ ./descendant-or-self::' + build_xpath_too(child) + ' ] ]'
199
+ }.join #(' and .')
169
200
  end
201
+ path << ' and ' if element_kids.any?
170
202
 
171
- path << ' ]'
203
+ path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
172
204
  return path
173
205
  end
174
206
 
@@ -121,19 +121,18 @@ class BeHtmlWith
121
121
  begin
122
122
  bwock = block || @block || proc{} # TODO what to do with no block? validate?
123
123
  builder = Nokogiri::HTML::Builder.new(&bwock)
124
- puts builder.doc.root.to_html
125
124
  @doc = Nokogiri::HTML(stwing)
126
125
  @reason = nil
127
126
 
128
127
  builder.doc.children.each do |child|
129
128
  @first_samples = []
130
129
  # TODO warn if child is text
131
- path = build_deep_xpath(child)
132
- next if path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
130
+ @path = build_deep_xpath(child)
131
+ next if @path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
133
132
 
134
- matchers = doc.root.xpath_with_callback path, :refer do |elements, index|
135
- collect_samples(elements, index.to_i)
136
- end
133
+ matchers = @doc.root.xpath_with_callback @path, :refer do |elements, index|
134
+ collect_samples(elements, index.to_i)
135
+ end
137
136
 
138
137
  if matchers.empty?
139
138
  @first_samples << @doc.root if @first_samples.empty? # TODO test the first_samples system
@@ -154,22 +153,54 @@ puts builder.doc.root.to_html
154
153
  return '//' + build_xpath(element)
155
154
  end
156
155
 
156
+ def build_deep_xpath_too(element)
157
+ @references = []
158
+ return '//' + build_xpath_too(element)
159
+ end
160
+
157
161
  attr_reader :references
158
162
 
159
163
  def build_xpath(element)
160
164
  path = element.name.sub(/\!$/, '')
161
165
  element_kids = element.children.grep(Nokogiri::XML::Element)
162
- path << "[ refer(., '#{@references.length}')"
166
+ path << '[ '
167
+ count = @references.length
163
168
  @references << element
164
169
 
165
170
  if element_kids.any?
166
- path << ' and ' +
167
- element_kids.map{|child|
171
+ path << element_kids.map{|child|
168
172
  './descendant::' + build_xpath(child)
169
173
  }.join(' and ')
174
+ path << ' and '
175
+ end
176
+
177
+ path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
178
+ return path
179
+ end
180
+
181
+ def build_xpath_too(element)
182
+ path = element.name.sub(/\!$/, '')
183
+ element_kids = element.children.grep(Nokogiri::XML::Element)
184
+ path << '[ '
185
+ count = @references.length
186
+ @references << element
187
+ brackets_owed = 0
188
+
189
+ if element_kids.length > 0
190
+ child = element_kids[0]
191
+ path << './descendant::' + build_xpath_too(child)
192
+ # }.join(' and ')
193
+ # path << ' and '
194
+ end
195
+
196
+ if element_kids.length > 1
197
+ path << element_kids[1..-1].map{|child|
198
+ '[ ./following-sibling::*[ ./descendant-or-self::' + build_xpath_too(child) + ' ] ]'
199
+ }.join #(' and .')
170
200
  end
201
+ path << ' and ' if element_kids.any?
171
202
 
172
- path << ' ]'
203
+ path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
173
204
  return path
174
205
  end
175
206
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assert2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phlip
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-15 00:00:00 -07:00
12
+ date: 2009-03-19 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,6 +25,7 @@ files:
25
25
  - lib/assert2
26
26
  - lib/assert2/xhtml.rb~
27
27
  - lib/assert2/flunk.rb
28
+ - lib/assert2/rubynode_reflector.rb~
28
29
  - lib/assert2/xpath.rb~
29
30
  - lib/assert2/rubynode_reflector.rb
30
31
  - lib/assert2/xpath.rb