assert2 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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