rdx 0.9.0.pre

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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.rdx +20 -0
  3. data/README +19 -0
  4. data/bin/rdx +7 -0
  5. data/examples/minimal/.rdx +8 -0
  6. data/examples/minimal/README +10 -0
  7. data/examples/minimal/lib/other_conventions.rb +64 -0
  8. data/examples/minimal/lib/the_basics.rb +94 -0
  9. data/examples/minimal/lib/using_directives.rb +66 -0
  10. data/examples/minimal/rakefile +27 -0
  11. data/examples/ruby-2.0.0-p0/README +7 -0
  12. data/examples/ruby-2.0.0-p0/install/core/.rdx +6 -0
  13. data/examples/ruby-2.0.0-p0/install/core/README +19 -0
  14. data/examples/ruby-2.0.0-p0/install/core/Rakefile +61 -0
  15. data/examples/ruby-2.0.0-p0/install/core/diffs/array.c.diff +166 -0
  16. data/examples/ruby-2.0.0-p0/install/core/diffs/bignum.c.diff +11 -0
  17. data/examples/ruby-2.0.0-p0/install/core/diffs/class.c.diff +36 -0
  18. data/examples/ruby-2.0.0-p0/install/core/diffs/compar.c.diff +11 -0
  19. data/examples/ruby-2.0.0-p0/install/core/diffs/complex.c.diff +301 -0
  20. data/examples/ruby-2.0.0-p0/install/core/diffs/cont.c.diff +65 -0
  21. data/examples/ruby-2.0.0-p0/install/core/diffs/dir.c.diff +147 -0
  22. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/re.rdoc.diff +328 -0
  23. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/security.rdoc.diff +8 -0
  24. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/standard_library.rdoc.diff +0 -0
  25. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax.rdoc.diff +0 -0
  26. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/assignment.rdoc.diff +160 -0
  27. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/calling_methods.rdoc.diff +130 -0
  28. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/control_expressions.rdoc.diff +254 -0
  29. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/exceptions.rdoc.diff +0 -0
  30. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/literals.rdoc.diff +54 -0
  31. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/methods.rdoc.diff +157 -0
  32. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/miscellaneous.rdoc.diff +91 -0
  33. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/modules_and_classes.rdoc.diff +161 -0
  34. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/precedence.rdoc.diff +8 -0
  35. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/refinements.rdoc.diff +146 -0
  36. data/examples/ruby-2.0.0-p0/install/core/diffs/encoding.c.diff +276 -0
  37. data/examples/ruby-2.0.0-p0/install/core/diffs/enum.c.diff +281 -0
  38. data/examples/ruby-2.0.0-p0/install/core/diffs/enumerator.c.diff +479 -0
  39. data/examples/ruby-2.0.0-p0/install/core/diffs/error.c.diff +143 -0
  40. data/examples/ruby-2.0.0-p0/install/core/diffs/eval.c.diff +47 -0
  41. data/examples/ruby-2.0.0-p0/install/core/diffs/eval_jump.c.diff +23 -0
  42. data/examples/ruby-2.0.0-p0/install/core/diffs/file.c.diff +752 -0
  43. data/examples/ruby-2.0.0-p0/install/core/diffs/gc.c.diff +195 -0
  44. data/examples/ruby-2.0.0-p0/install/core/diffs/hash.c.diff +84 -0
  45. data/examples/ruby-2.0.0-p0/install/core/diffs/iseq.c.diff +354 -0
  46. data/examples/ruby-2.0.0-p0/install/core/diffs/load.c.diff +53 -0
  47. data/examples/ruby-2.0.0-p0/install/core/diffs/marshal.c.diff +98 -0
  48. data/examples/ruby-2.0.0-p0/install/core/diffs/math.c.diff +110 -0
  49. data/examples/ruby-2.0.0-p0/install/core/diffs/numeric.c.diff +103 -0
  50. data/examples/ruby-2.0.0-p0/install/core/diffs/object.c.diff +295 -0
  51. data/examples/ruby-2.0.0-p0/install/core/diffs/pack.c.diff +18 -0
  52. data/examples/ruby-2.0.0-p0/install/core/diffs/parse.y.diff +23 -0
  53. data/examples/ruby-2.0.0-p0/install/core/diffs/proc.c.diff +155 -0
  54. data/examples/ruby-2.0.0-p0/install/core/diffs/random.c.diff +126 -0
  55. data/examples/ruby-2.0.0-p0/install/core/diffs/range.c.diff +49 -0
  56. data/examples/ruby-2.0.0-p0/install/core/diffs/rational.c.diff +312 -0
  57. data/examples/ruby-2.0.0-p0/install/core/diffs/re.c.diff +207 -0
  58. data/examples/ruby-2.0.0-p0/install/core/diffs/ruby.c.diff +21 -0
  59. data/examples/ruby-2.0.0-p0/install/core/diffs/signal.c.diff +67 -0
  60. data/examples/ruby-2.0.0-p0/install/core/diffs/sprintf.c.diff +29 -0
  61. data/examples/ruby-2.0.0-p0/install/core/diffs/string.c.diff +73 -0
  62. data/examples/ruby-2.0.0-p0/install/core/diffs/struct.c.diff +20 -0
  63. data/examples/ruby-2.0.0-p0/install/core/diffs/time.c.diff +691 -0
  64. data/examples/ruby-2.0.0-p0/install/core/diffs/transcode.c.diff +435 -0
  65. data/examples/ruby-2.0.0-p0/install/core/diffs/variable.c.diff +62 -0
  66. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_backtrace.c.diff +164 -0
  67. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_eval.c.diff +99 -0
  68. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_method.c.diff +17 -0
  69. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_trace.c.diff +393 -0
  70. data/examples/ruby-2.0.0-p0/install/stdlib/.rdx +6 -0
  71. data/examples/ruby-2.0.0-p0/install/stdlib/README +19 -0
  72. data/examples/ruby-2.0.0-p0/install/stdlib/Rakefile +53 -0
  73. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/abbrev.rb.diff +77 -0
  74. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/base64.rb.diff +42 -0
  75. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/benchmark.rb.diff +144 -0
  76. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/cmath.rb.diff +52 -0
  77. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/forwardable.rb.diff +150 -0
  78. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/mathn.rb.diff +58 -0
  79. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/matrix.rb.diff +657 -0
  80. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/observer.rb.diff +31 -0
  81. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/optparse.rb.diff +147 -0
  82. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/ostruct.rb.diff +78 -0
  83. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/prime.rb.diff +52 -0
  84. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/pstore.rb.diff +110 -0
  85. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/scanf.rb.diff +100 -0
  86. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/securerandom.rb.diff +144 -0
  87. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/set.rb.diff +637 -0
  88. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/shellwords.rb.diff +66 -0
  89. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/singleton.rb.diff +37 -0
  90. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tempfile.rb.diff +104 -0
  91. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/thread.rb.diff +38 -0
  92. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/time.rb.diff +140 -0
  93. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tmpdir.rb.diff +52 -0
  94. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri.rb.diff +39 -0
  95. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri/common.rb.diff +237 -0
  96. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/weakref.rb.diff +36 -0
  97. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/yaml/store.rb.diff +27 -0
  98. data/examples/ruby-2.0.0-p0/rakefile +165 -0
  99. data/lib/rdx.rb +331 -0
  100. data/lib/rdx/assertions.rb +484 -0
  101. data/lib/rdx/binding.rb +151 -0
  102. data/lib/rdx/code_object.rb +598 -0
  103. data/lib/rdx/comment.rb +338 -0
  104. data/lib/rdx/convention.rb +1174 -0
  105. data/lib/rdx/directive.rb +1432 -0
  106. data/lib/rdx/example.rb +679 -0
  107. data/lib/rdx/generator.rb +112 -0
  108. data/lib/rdx/generator/rdoc.rb +1006 -0
  109. data/lib/rdx/options.rb +359 -0
  110. data/lib/rdx/plain_text.rb +65 -0
  111. data/lib/rdx/reporter.rb +421 -0
  112. data/lib/rdx/ruby_lex.rb +324 -0
  113. data/lib/rdx/runner.rb +309 -0
  114. data/lib/rdx/source_file.rb +94 -0
  115. data/lib/rdx/specification.rb +194 -0
  116. data/lib/rdx/statement.rb +248 -0
  117. data/lib/rdx/store.rb +119 -0
  118. data/lib/rdx/task.rb +361 -0
  119. data/lib/rdx/text.rb +688 -0
  120. data/lib/rdx/version.rb +15 -0
  121. data/rakefile +64 -0
  122. metadata +203 -0
@@ -0,0 +1,688 @@
1
+
2
+ module RDX
3
+
4
+ #
5
+ # Various utilities for text processing.
6
+ #
7
+ module Text
8
+
9
+ #
10
+ # Contains some useful +Regexp+ patterns.
11
+ #
12
+ module REGEXP
13
+
14
+ # Matches a number token.
15
+ TK_NUMBER = /(?>
16
+ \d+
17
+ (?: _\d+ )*
18
+ )/x
19
+
20
+ # Matches a literal Integer (without tag <tt>0[bodx]?</tt>).
21
+ INT = INT_P = /(?>
22
+ [-+]? #{TK_NUMBER}
23
+ )/ox
24
+
25
+ # Matches the result of Rational#to_s.
26
+ RTNL = %r%(?>
27
+ [-+]? #{TK_NUMBER} / #{TK_NUMBER}
28
+ )%ox
29
+
30
+ # Matches the result of Rational#inspect.
31
+ RTNL_P = %r%(?> \(
32
+ #{RTNL}
33
+ \) )%ox
34
+
35
+ # Matches a literal Float.
36
+ FLT = FLT_P = /(?>
37
+ [-+]? #{TK_NUMBER} \. #{TK_NUMBER}
38
+ (?: [Ee] [-+]? #{TK_NUMBER} )?
39
+ )/ox
40
+
41
+ # Matches a literal Integer or Float.
42
+ INT_FLT = FLT_INT = INT_FLT_P = FLT_INT_P = /(?>
43
+ #{FLT} | #{INT}
44
+ )/ox
45
+
46
+ # Matches a literal Integer, Float and the result of Rational#to_s.
47
+ REAL = /(?>
48
+ #{RTNL} | #{INT_FLT}
49
+ )/ox
50
+
51
+ # Matches a literal Integer, Float and the result of Rational#inspect.
52
+ REAL_P = /(?>
53
+ #{RTNL_P} | #{INT_FLT_P}
54
+ )/ox
55
+
56
+ # Matches the text <tt>[+-]?Infinity</tt> and +NaN+ other than real numbers.
57
+ IPERREAL_P = /(?>
58
+ [-+]? Infinity | NaN | #{REAL_P}
59
+ )/ox
60
+
61
+ # Matches the result of Complex#to_s.
62
+ CMPLX = /(?>
63
+ #{REAL} [-+] #{REAL} i
64
+ )/ox
65
+
66
+ # Matches the result of Complex#inspect.
67
+ CMPLX_P = /(?> \(
68
+ #{IPERREAL_P} [-+] #{IPERREAL_P} \*? i
69
+ \) )/ox
70
+
71
+ # Matches the result of Numeric#to_s.
72
+ NUMERIC = %r%(?>
73
+ #{CMPLX} | #{REAL}
74
+ )%ox
75
+
76
+ # Matches the result of Numeric#inspect
77
+ NUMERIC_P = %r%(?>
78
+ #{CMPLX_P} | #{REAL_P}
79
+ )%ox
80
+
81
+ # Matches an identifier token.
82
+ TK_ID = /(?>
83
+ (?! true | false | nil | self | __FILE__ | __LINE__ | __ENCODING__ )
84
+ [A-Za-z_[^[:ascii:]]]\w*
85
+ )/x
86
+
87
+ # Matches a literal class (constant) name.
88
+ CLASS = CONSTANT = TK_CONST = /(?> (?:
89
+ (?: :: )?+ [A-Z] \w*+
90
+ )++ )/x
91
+
92
+ # Matches an instance variable token.
93
+ TK_IVAR = /(?>
94
+ @ \w+
95
+ )/x
96
+
97
+ # Matches a class variable token.
98
+ TK_CVAR = /(?>
99
+ @@ \w+
100
+ )/x
101
+
102
+ # Matches a global variable token.
103
+ TK_GVAR = /(?>
104
+ $ \w+
105
+ )/x
106
+
107
+ # Matches a fetching token.
108
+ FETCH = Regexp.union TK_CONST, TK_ID, TK_IVAR, TK_CVAR, TK_GVAR
109
+
110
+ # Matches a literal double-quoted string (without interpolation).
111
+ DQ_STRING = /(?> "
112
+ (?:
113
+ [^"#\\]*+
114
+ (?: \\. )?+
115
+ (?: \#(?![{@$]) )?+
116
+ )*+
117
+ " )/x
118
+
119
+ # Matches a literal single-quoted string.
120
+ SQ_STRING = /(?> '
121
+ (?:
122
+ [^'\\]*+
123
+ (?: \\. )?+
124
+ )*+
125
+ ' )/x
126
+
127
+ # Matches the rest of line if it has no more code (spaces and comment).
128
+ END_CODE_IN_LINE = /[^\S\n]*(?:#.*)?$/
129
+
130
+ # Matches the magic comment of the string (eventual shebang line) and captures it.
131
+ MAGIC_COMMENT = %r%\A(?>
132
+ (?: \#! .* \n )? # Optional shebang line
133
+ [^\S\n]* ( \# .*? coding[:=] .* ) # Magic comment
134
+ )%x
135
+
136
+ # Matches the result of Time#to_s (but also invalid ones).
137
+ TIME = %r%(?>
138
+ \d{4,}-\d\d-\d\d \ \d\d:\d\d:\d\d \ [-+]\d{4}
139
+ )%x
140
+
141
+ end
142
+
143
+ extend self
144
+
145
+ #
146
+ # Converts _numeric_str_ to a +Numeric+ using +to_c+, +to_r+, +to_f+
147
+ # or +to_i+ according to the given +String+.
148
+ #
149
+ # RDX::Text.numeric_from_str "12" #=> 12 # Integer
150
+ # RDX::Text.numeric_from_str "1.2" #=> 1.2 # Float
151
+ # RDX::Text.numeric_from_str "1/2" #=> 1/2 # Rational
152
+ # RDX::Text.numeric_from_str "1+2i" #=> 1+2i # Complex
153
+ #
154
+ # If the string isn't valid can return unexpected results.
155
+ #
156
+ def numeric_from_str(numeric_str)
157
+ case numeric_str = numeric_str.to_str
158
+ when /i/
159
+ numeric_str.to_c
160
+ when /\//
161
+ numeric_str.to_r
162
+ when /[.eE]/
163
+ numeric_str.to_f
164
+ else
165
+ numeric_str.to_i
166
+ end
167
+ end
168
+
169
+ #
170
+ # Replaces every potential object id (hexadecimal number) in the given _string_ with
171
+ # _replacement_.
172
+ #
173
+ # RDX::Text.generalize_object_ids "#<Class:0x0007a89f>" #=> "#<Class:0xXXXXXX>"
174
+ # RDX::Text.generalize_object_ids "#<Class:0x00012b40af>" #=> "#<Class:0xXXXXXX>"
175
+ # RDX::Text.generalize_object_ids "An hexadecimal number: 0X14a58ef"
176
+ # #=> "An hexadecimal number: 0xXXXXXX"
177
+ #
178
+ def generalize_object_ids string, replacement='0xXXXXXX'
179
+ return string unless string.respond_to?(:to_str)
180
+ return string if Object.const_defined?(:Encoding) and
181
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
182
+ string = string.to_str.dup
183
+ string.gsub!(/0[Xx]\h+/, replacement)
184
+ string
185
+ end
186
+
187
+ #
188
+ # Replace all occurrences of Numeric#to_s in the given _string_
189
+ # with _replacement_.
190
+ #
191
+ # RDX::Text.generalize_numerics "1.2e+7" #=> "0"
192
+ # RDX::Text.generalize_numerics "12and-7" #=> "0and0"
193
+ # RDX::Text.generalize_numerics "12/7" #=> "0"
194
+ # RDX::Text.generalize_numerics "12 / 7" #=> "0 / 0"
195
+ #
196
+ def generalize_numerics string, replacement='0'
197
+ return string unless string.respond_to?(:to_str)
198
+ return string if Object.const_defined?(:Encoding) and
199
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
200
+ string.to_str.gsub REGEXP::NUMERIC, replacement
201
+ end
202
+
203
+ #
204
+ # Replace all occurrences of Time#to_s in the given _string_
205
+ # with _replacement_.
206
+ #
207
+ # txt = "Now it's: #{Time.now}"
208
+ # RDX::Text.generalize_times txt #=> "Now it's: 0000-01-01 00:00:00 +0000"
209
+ #
210
+ def generalize_times string, replacement=Time.utc(0).getlocal("+00:00").to_s
211
+ return string unless string.respond_to?(:to_str)
212
+ return string if Object.const_defined?(:Encoding) and
213
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
214
+ string.to_str.gsub REGEXP::TIME, replacement
215
+ end
216
+
217
+ #
218
+ # Normalizes all floats in the given _string_ and replace them rounding with
219
+ # at most _digits_ significant digits. If _zero_threshold_ isn't +nil+ determines
220
+ # the threshold under which floats are considered <tt>0.0</tt>.
221
+ #
222
+ # RDX::Text.normalize_floats "12.345678" #=> "+12.3457"
223
+ # RDX::Text.normalize_floats "+12.345678" #=> "+12.3457"
224
+ # RDX::Text.normalize_floats "-123456.0", 3 #=> "-1.23e+05"
225
+ # RDX::Text.normalize_floats "-1.23E5" #=> "-123000.0"
226
+ # RDX::Text.normalize_floats "-1.23E5", 3 #=> "-1.23e+05"
227
+ # RDX::Text.normalize_floats "0.0000001" #=> "+0.0"
228
+ # RDX::Text.normalize_floats "0.0000001", 2, nil
229
+ # #=> "+1.0e-07"
230
+ # RDX::Text.normalize_floats "1_2.0000001" #=> "+12.0"
231
+ # RDX::Text.normalize_floats "1.2345678+1.2_345678i", 3
232
+ # #=> "+1.23+1.23i"
233
+ # RDX::Text.normalize_floats "6.0221412927e23", 4
234
+ # #=> "+6.022e+23"
235
+ #
236
+ def normalize_floats string, digits=6, zero_threshold=10.0**-digits
237
+ return string unless string.respond_to?(:to_str)
238
+ return string if Object.const_defined?(:Encoding) and
239
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
240
+ string.to_str.gsub REGEXP::FLT do |float_str|
241
+ float = float_str.to_f
242
+ float = 0.0 if zero_threshold and float.abs < zero_threshold
243
+ str = "%+.#{digits}g" % float
244
+ str.sub! /(?:e[-+]?\d+)?$/, '.0\&' unless str.include? '.'
245
+ str
246
+ end
247
+ end
248
+
249
+ #
250
+ # Normalizes all occurrences of Time#to_s in the given _string_ to the result
251
+ # of the corresponding +UTC+ time.
252
+ #
253
+ # t0_cst = "1969-12-31 18:00:00 -0600"
254
+ # RDX::Text.normalize_times t0_cst
255
+ # #=> "1970-01-01 00:00:00 UTC"
256
+ #
257
+ def normalize_times string
258
+ return string unless string.respond_to?(:to_str)
259
+ return string if Object.const_defined?(:Encoding) and
260
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
261
+ string.to_str.gsub REGEXP::TIME do |time_str|
262
+ *nums,offset = time_str.split /(?<=\d)[- :]/
263
+ time = Time.new *nums.map(&:to_i), offset.insert(3,':')
264
+ time.getutc.to_s
265
+ end
266
+ end
267
+
268
+ #
269
+ # Normalizes the spaces in the given _output_ string, removing invisible or
270
+ # redundant differences according to the given parameters.
271
+ #
272
+ # RDX::Text.normalize_output_spacing <<EOS
273
+ # This output is indented,
274
+ # contains spaces at the end of this line,\s\s\s
275
+ # four spaces separate these \s\s words,
276
+ # contains tab \t characters
277
+ # and ends with three line terminator\n\n
278
+ # EOS
279
+ # # =>
280
+ # # <<EOS.chomp("\n")
281
+ # # This output is indented,
282
+ # # contains spaces at the end of this line,
283
+ # # four spaces separate these words,
284
+ # # contains tab characters
285
+ # # and ends with three line terminator
286
+ # # EOS
287
+ #
288
+ def normalize_output_spacing output, flush_left=true, squeeze_spaces=true, strip_newlines=true
289
+ return output unless output.respond_to?(:to_str)
290
+ output = remove_trailing_inline_spaces expand_tabs output.to_str
291
+ output = flush_left output if flush_left
292
+ if squeeze_spaces
293
+ output.squeeze! ' '
294
+ output.gsub! /\n{3,}/, "\n\n"
295
+ end
296
+ output = strip_newlines(output) if strip_newlines
297
+ output
298
+ end
299
+
300
+ #
301
+ # Replaces in the given _string_ all the occurrences of _root_ with the _replacement_.
302
+ #
303
+ # dir = path = nil
304
+ # Dir.mktmpdir do |tmpdir|
305
+ # dir = tmpdir
306
+ # fname = 'a_file'
307
+ # path = File.join tmpdir, fname
308
+ # end
309
+ # RDX::Text.normalize_path path, dir, '<tmpdir>' #=> "<tmpdir>/a_file"
310
+ #
311
+ def normalize_path string, root=Dir.pwd, replacement='<relative_path>'
312
+ return string unless string.respond_to?(:to_str)
313
+ string.to_str.gsub root.to_str, replacement.to_str
314
+ end
315
+
316
+ #
317
+ # Normalizes the _output_ string through #normalize_output_spacing, #normalize_floats
318
+ # and #generalize_object_ids in cascade (the details removed are considered negligible
319
+ # for an output).
320
+ #
321
+ def normalize_output output
322
+ normalize_floats generalize_object_ids normalize_output_spacing output
323
+ end
324
+
325
+ #
326
+ # Removes minor details from an exception message _msg_ (the most important is that
327
+ # newlines are converted to regular spaces, so that the message can be written in
328
+ # multiple lines).
329
+ #
330
+ # RDX::Text.normalize_exception_message <<EOS
331
+ # undefined method `a_method'
332
+ # for "a quite long string":String
333
+ # EOS
334
+ # # => %Q{undefined method `a_method' for "a quite long string":string}
335
+ #
336
+ def normalize_exception_message msg
337
+ return msg unless msg.respond_to?(:to_str)
338
+ msg.to_str.strip.chomp('.').gsub(/\s+/,' ').downcase
339
+ end
340
+
341
+ #
342
+ # Splits the given _string_ to an exception class and message according to _mode_,
343
+ # which specifies the format of the string:
344
+ # * +:class+ : _class_
345
+ # * +:message+ : _message_
346
+ # * +:class_message+ : _class_ <tt>:</tt> _message_
347
+ # * +:message_class+ : _message_ <tt>(</tt> _class_ <tt>)</tt>
348
+ # If a field is not detected it's replaced with +nil+.
349
+ #
350
+ # # IRB style (rescued exception):
351
+ # str = "ArgumentError: wrong number of arguments (1 for 0)"
352
+ # RDX::Text.split_exception_string str, :class_message
353
+ # #=> ["ArgumentError", "wrong number of arguments (1 for 0)"]
354
+ # RDX::Text.split_exception_string str, :message_class
355
+ # #=> [nil, "ArgumentError: wrong number of arguments (1 for 0)"]
356
+ #
357
+ # # Command line style (unhandled exception):
358
+ # str = "no implicit conversion of Fixnum into String (TypeError)"
359
+ # RDX::Text.split_exception_string str, :message_class
360
+ # #=> ["TypeError", "no implicit conversion of Fixnum into String"]
361
+ # RDX::Text.split_exception_string str, :class_message
362
+ # #=> [nil, "no implicit conversion of Fixnum into String (TypeError)"]
363
+ #
364
+ def split_exception_string string, mode=:class_message
365
+ string = string.to_str
366
+ case mode
367
+ when :class
368
+ message = nil
369
+ klass = string.match(/\A\s*(#{REGEXP::CLASS})\s*\z/) && $1
370
+ when :message
371
+ message = string
372
+ klass = nil
373
+ when :class_message
374
+ message = string.strip
375
+ klass = message.slice!(/\A(#{REGEXP::CLASS})\s*:\s*/) && $1
376
+ when :message_class
377
+ message = string.strip
378
+ klass = message.slice!(/\s*\((#{REGEXP::CLASS})\)\z/) && $1
379
+ end
380
+ message = nil if message && message.empty?
381
+ [klass,message]
382
+ end
383
+
384
+ #
385
+ # Removes in _string_ the enclosing module (or class) _mod_ from the namespace.
386
+ #
387
+ # mod = Module.new
388
+ # wrapper = class << mod # creates a new lexical scope
389
+ # module Wrapped
390
+ # end
391
+ # self
392
+ # end
393
+ # str = "The wrapped module is: #{wrapper::Wrapped}"
394
+ # #-> "The wrapped module is: #<Class:0x36e7108>::Wrapped"
395
+ # RDX::Text.remove_enclosing_module str, wrapper
396
+ # #=> "The wrapped module is: Wrapped"
397
+ #
398
+ def remove_enclosing_module string, mod
399
+ return string unless string.respond_to?(:to_str)
400
+ return string if Object.const_defined?(:Encoding) and
401
+ !string.encoding.ascii_compatible? || !string.valid_encoding?
402
+ raise unless mod.is_a?(Module)
403
+ string = string.to_str
404
+ mod_str1 = mod.to_s
405
+ # For some reason, in nested constants' names is used Object#to_s and not Module#to_s
406
+ # The result is that for singleton classes we don't get the information about the attached object.
407
+ # So we have to bypass the inheritance chain:
408
+ mod_str2 = Object.instance_method(:to_s).bind(mod).call
409
+ mod_str = Regexp.escape(mod_str1) + '|' + Regexp.escape(mod_str2)
410
+ mod_rgxp = /(?:#{mod_str})::/
411
+ string.gsub(mod_rgxp,'')
412
+ end
413
+ alias remove_enclosing_class remove_enclosing_module
414
+
415
+ #
416
+ # Returns a +Regexp+ that matches _content_ (may be a +String+ or +Regexp+)
417
+ # wrapped into any of the one HTML _tags_ given (does not handle HTML escaping).
418
+ # If one tag is +nil+ the unwrapped _content_ is also allowed.
419
+ #
420
+ # RDX::Text.add_html_tags 'text', 'b' #=> %r%<b>text</b>%
421
+ # RDX::Text.add_html_tags 'dot.', 'u', nil #=> %r%dot\.|<u>dot\.</u>%
422
+ # RDX::Text.add_html_tags 'Fixnum', 'tt', 'code'
423
+ # #=> %r%<tt>Fixnum</tt>|<code>Fixnum</code>%
424
+ # # One very used by RDX:
425
+ # RDX::Text.add_html_tags /[Pp]roduces:?/, 'em', 'i'
426
+ # #=> %r%<em>(?-mix:[Pp]roduces:?)</em>|<i>(?-mix:[Pp]roduces:?)</i>%
427
+ #
428
+ def add_html_tags content, *tags
429
+ content = Regexp.escape(content.to_str) unless content.is_a?(Regexp)
430
+ pattern = []
431
+ pattern << content if tags.compact!
432
+ tags.each do |tag|
433
+ re_tag = Regexp.escape(tag.to_str)
434
+ wrapped = "<#{re_tag}>#{content}</#{re_tag}>"
435
+ pattern << wrapped
436
+ end
437
+ Regexp.compile pattern.join('|')
438
+ end
439
+ alias add_html_tag add_html_tags
440
+
441
+ #
442
+ # Returns a +Regexp+ pattern that matches any of the given _prompts_ at the beginning
443
+ # of a string (for definition). Leading spaces are allowed, one trailing is required to avoid
444
+ # the matching of another prompt.
445
+ #
446
+ # # Perhaps the most important for RDX:
447
+ # re = RDX::Text.build_prompt '=>' # => /\A\s*(?>=>)\s/
448
+ # str = " => 15"
449
+ # str.slice! re; str #=> "15"
450
+ # str = "=>> 24" # another prompt
451
+ # str.slice! re #=> nil
452
+ #
453
+ def build_prompt *prompts
454
+ prompts.flatten!
455
+ prompts.map! do |prompt|
456
+ prompt.is_a?(Regexp) ? prompt.to_s : Regexp.escape(prompt.to_str)
457
+ end
458
+ pat = prompts.uniq.join '|'
459
+ return /\A\s*(?>#{pat})\s/
460
+ end
461
+
462
+ #
463
+ # Assembles a +Regexp+ pattern that matches the given _parts_ in sequence,
464
+ # allowing inline spaces between them.
465
+ # Each part can be:
466
+ # * a +Regexp+
467
+ # * a +String+, which is interpreted literally
468
+ # * a +Symbol+, which represent a REGEXP constant
469
+ # (if begins with <tt>CAPTURE_</tt> the pattern is surrounded capturing parhentesis)
470
+ #
471
+ # str = "=> Float::MIN"
472
+ # re = RDX::Text.build_pattern '=>', :CLASS
473
+ # str.match re # > #<MatchData "=> Float::MIN">
474
+ # re = RDX::Text.build_pattern '=>', :CAPTURE_CLASS
475
+ # str[re,1] #=> "Float::MIN"
476
+ #
477
+ def build_pattern *parts
478
+ parts.map! do |part|
479
+ if part.is_a?(Regexp)
480
+ part
481
+ elsif part.is_a?(Symbol)
482
+ name = part.to_s
483
+ capturing = name.slice! /^CAPTURE_/
484
+ re = REGEXP.const_get(name,false)
485
+ capturing ? /(#{re})/ : re
486
+ else
487
+ Regexp.escape part.to_str
488
+ end
489
+ end
490
+ return Regexp.compile parts.join('[^\S\n]*')
491
+ end
492
+
493
+ #
494
+ # Substitutes the content of each line in _string_ (but not newlines themselves) with _replacement_
495
+ # (which is the empty string by default).
496
+ #
497
+ # RDX::Text.collect_newlines <<EOS
498
+ # useless data
499
+ # where line count matters
500
+ # EOS
501
+ # # => "\n\n"
502
+ # RDX::Text.collect_newlines <<EOS, '#'
503
+ # # This is a useless
504
+ # # ruby comment
505
+ # EOS
506
+ # # => "#\n#\n"
507
+ #
508
+ def collect_newlines string, replacement=nil
509
+ replacement ||= ''
510
+ string.to_str.gsub /^.*$/, replacement
511
+ end
512
+
513
+ #
514
+ # If _string_ is formatted like a C-style comments strips the stars without
515
+ # consuming newlines characters.
516
+ #
517
+ # RDX::Text.strip_stars <<EOS
518
+ # /*
519
+ # * C comment
520
+ # */
521
+ # EOS
522
+ # # => " \n C comment\n \n"
523
+ #--
524
+ # This method is adapted from RDoc::Text#strip_stars
525
+ # rb_cmt = <<EOS
526
+ # ##
527
+ # # Strips /* */ style comments
528
+ # EOS
529
+ # RDX::Text.strip_stars rb_cmt #=> rb_cmt
530
+ #
531
+ def strip_stars string
532
+ string = string.to_str
533
+ return string unless string =~ %r%\A\s*/\*.*\*/\s*\z%m
534
+
535
+ encoding = string.encoding if Object.const_defined? :Encoding
536
+
537
+ # text = text.gsub %r%Document-method:\s+[\w:.#=!?]+%, '' # handled in RDoc::Parser::C
538
+
539
+ space = ' '
540
+ space.force_encoding encoding if encoding
541
+
542
+ string.sub! %r%/\*+% do space*$&.length end
543
+ string.sub! %r%(\*+/)(\s*)\z% do space*$1.length + $2 end
544
+ string.gsub! %r%^[^\S\n]*\*?% do space * $&.length end
545
+
546
+ # empty = ''
547
+ # empty.force_encoding encoding if encoding
548
+ # text.gsub(/^\s+$/, empty)
549
+
550
+ string
551
+ end
552
+
553
+ #
554
+ # If _string_ is formatted like a Ruby-style comment strips hashes
555
+ # (and leading inline spaces before them), without consuming newlines characters.
556
+ # If _multihash_ is +true+ removes consecutive hashes.
557
+ #
558
+ # RDX::Text.strip_hashes <<EOS
559
+ # # A
560
+ # # Ruby
561
+ # # comment
562
+ # EOS
563
+ # # => " A\n Ruby\n comment\n"
564
+ # RDX::Text.strip_hashes "## meta-comment" #=> "# meta-comment"
565
+ # RDX::Text.strip_hashes "## meta-comment", true #=> " meta-comment"
566
+ #--
567
+ # This method is adapted from RDoc::Text#strip_hashes
568
+ #
569
+ def strip_hashes string, multihash=false
570
+ string = string.to_str
571
+ return string if string =~ /^[^\S\n]*+[^#]/
572
+
573
+ empty = ''
574
+ empty.force_encoding string.encoding if Object.const_defined? :Encoding
575
+
576
+ replacement = multihash ? empty : '\1'
577
+ string.gsub /^[^\S\n]*+#(#*)/, replacement
578
+ end
579
+
580
+ #
581
+ # Expands tab characters in _string_ to eight spaces.
582
+ #--
583
+ # Adapted from RDoc::Text#expand_tabs
584
+ #
585
+ def expand_tabs string
586
+ string = string.to_str
587
+ expanded = []
588
+
589
+ string.each_line do |line|
590
+ line.gsub!(/^((?:.{8})*?)([^\t\r\n]{0,7})\t/) do
591
+ r = "#{$1}#{$2}#{' ' * (8 - $2.size)}"
592
+ r.force_encoding string.encoding if Object.const_defined? :Encoding
593
+ r
594
+ end until line !~ /\t/
595
+
596
+ expanded << line
597
+ end
598
+
599
+ expanded.join ''
600
+ end
601
+
602
+ #
603
+ # Flush _string_ left based on the shortest line.
604
+ #
605
+ # RDX::Text.flush_left <<EOS
606
+ # this is
607
+ # indented
608
+ # EOS
609
+ # # =>
610
+ # # <<EOS
611
+ # # this is
612
+ # # indented
613
+ # # EOS
614
+ #--
615
+ # Taken as is from RDoc::Text#flush_left
616
+ #
617
+ def flush_left string
618
+ string = string.to_str
619
+ indent = string.each_line.map{ |ln| ln=~/\S/ }.compact.min
620
+ empty = ''
621
+ empty.force_encoding string.encoding if Object.const_defined? :Encoding
622
+ string.gsub /^ {0,#{indent}}/, empty # unlimited if indent == nil
623
+ end
624
+
625
+ #
626
+ # Removes trailing spaces (invisible) from each line of _string_.
627
+ #
628
+ # RDX::Text.remove_trailing_inline_spaces <<EOS
629
+ # lines ending\t\s
630
+ # with spaces\s\s\s
631
+ # EOS
632
+ # # => <<EOS
633
+ # # lines ending
634
+ # # with spaces
635
+ # # EOS
636
+ #
637
+ def remove_trailing_inline_spaces string
638
+ string.to_str.gsub %r%[^\S\n]+$%, ''
639
+ end
640
+
641
+ #
642
+ # Removes leading and trailing newlines from _string_.
643
+ #
644
+ # RDX::Text.strip_newlines "\nthe text\n\n" #=> "the text"
645
+ #
646
+ def strip_newlines string, leading=true, trailing=true
647
+ string = string.to_str.dup
648
+ string.slice! /\A\n+/ if leading
649
+ string.slice! /\n+\z/ if trailing
650
+ string
651
+ end
652
+
653
+ #
654
+ # Strips stars, hashes, expands tabs, flushes _text_ to the left.
655
+ #
656
+ # RDX::Text.normalize_comment <<EOS
657
+ # #
658
+ # # This is a
659
+ # # typical Ruby comment
660
+ # #
661
+ # EOS
662
+ # # => "\nThis is a\ntypical Ruby comment\n\n"
663
+ #
664
+ # RDX::Text.normalize_comment <<EOS
665
+ # /*
666
+ # * This is a
667
+ # * typical C comment
668
+ # */
669
+ # EOS
670
+ # # => "\nThis is a\ntypical C comment\n\n"
671
+ #
672
+ #--
673
+ # Method adapted from RDoc::Text#normalize_comment
674
+ #
675
+ def normalize_comment string, multihash=false
676
+ string = string.to_str
677
+ return string if string.empty?
678
+ string = strip_stars string
679
+ string = strip_hashes string, multihash
680
+ string = expand_tabs string
681
+ string = flush_left string
682
+ # string = strip_newlines string
683
+ string
684
+ end
685
+
686
+ end
687
+
688
+ end