riel 1.0.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.
Files changed (48) hide show
  1. data/README +0 -0
  2. data/lib/riel/ansicolor.rb +93 -0
  3. data/lib/riel/array.rb +20 -0
  4. data/lib/riel/command.rb +30 -0
  5. data/lib/riel/date.rb +16 -0
  6. data/lib/riel/dir.rb +90 -0
  7. data/lib/riel/enumerable.rb +66 -0
  8. data/lib/riel/env.rb +49 -0
  9. data/lib/riel/file.rb +212 -0
  10. data/lib/riel/filetype.rb +189 -0
  11. data/lib/riel/hash.rb +12 -0
  12. data/lib/riel/io.rb +20 -0
  13. data/lib/riel/log.rb +548 -0
  14. data/lib/riel/matchdata.rb +13 -0
  15. data/lib/riel/optproc.rb +369 -0
  16. data/lib/riel/pathname.rb +16 -0
  17. data/lib/riel/rcfile.rb +35 -0
  18. data/lib/riel/regexp.rb +152 -0
  19. data/lib/riel/setdiff.rb +53 -0
  20. data/lib/riel/size_converter.rb +62 -0
  21. data/lib/riel/string.rb +81 -0
  22. data/lib/riel/tempfile.rb +28 -0
  23. data/lib/riel/text.rb +408 -0
  24. data/lib/riel/timer.rb +52 -0
  25. data/lib/riel.rb +13 -0
  26. data/test/riel/array_test.rb +22 -0
  27. data/test/riel/command_test.rb +28 -0
  28. data/test/riel/date_test.rb +17 -0
  29. data/test/riel/dir_test.rb +98 -0
  30. data/test/riel/enumerable_test.rb +27 -0
  31. data/test/riel/env_test.rb +52 -0
  32. data/test/riel/file_test.rb +242 -0
  33. data/test/riel/filetype_test.rb +32 -0
  34. data/test/riel/hash_test.rb +12 -0
  35. data/test/riel/io_test.rb +22 -0
  36. data/test/riel/log_test.rb +184 -0
  37. data/test/riel/matchdata_test.rb +15 -0
  38. data/test/riel/optproc_test.rb +233 -0
  39. data/test/riel/pathname_test.rb +36 -0
  40. data/test/riel/rcfile_test.rb +44 -0
  41. data/test/riel/regexp_test.rb +24 -0
  42. data/test/riel/setdiff_test.rb +26 -0
  43. data/test/riel/size_converter_test.rb +64 -0
  44. data/test/riel/string_test.rb +58 -0
  45. data/test/riel/tempfile_test.rb +16 -0
  46. data/test/riel/text_test.rb +102 -0
  47. data/test/riel/timer_test.rb +43 -0
  48. metadata +134 -0
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'riel/text'
5
+
6
+ class String
7
+
8
+ def ends_with(substr)
9
+ return rindex(substr) == (length - substr.length)
10
+ end
11
+
12
+ # Returns the string, as a number if it is one, and nil otherwise.
13
+ def num
14
+ begin
15
+ Integer(self)
16
+ rescue ArgumentError => ae
17
+ nil
18
+ end
19
+ end
20
+
21
+ # returns a string based on this instance, with the first occurrance of
22
+ # +other+ removed. +other+ may be a string or regular expression.
23
+ def -(other)
24
+ sub(other, '')
25
+ end
26
+
27
+ Infinity = (1.0 / 0)
28
+
29
+ # from (maybe to this)
30
+ RANGE_REGEXP = %r{^ \s* (\d*) (?: \s* (\-|\.\.) \s* (\d*) )? \s* $ }x
31
+
32
+ # splits num into array of ranges.
33
+ # handles
34
+ def self.to_ranges(str, args = Hash.new)
35
+ min = args[:min] || -Infinity
36
+ max = args[:max] || Infinity
37
+ collapse = args[:collapse]
38
+
39
+ ranges = Array.new
40
+ str.split(%r{\s*,\s*}).each do |section|
41
+ md = section.match(RANGE_REGEXP)
42
+ next unless md
43
+
44
+ from = _matchdata_to_number(md, 1, min)
45
+ to = _has_matchdata?(md, 2) ? _matchdata_to_number(md, 3, max) : from
46
+
47
+ prevrange = ranges[-1]
48
+
49
+ if collapse && prevrange && prevrange.include?(from - 1) && prevrange.include?(to - 1)
50
+ ranges[-1] = (prevrange.first .. to)
51
+ else
52
+ ranges << (from .. to)
53
+ end
54
+ end
55
+
56
+ ranges
57
+ end
58
+
59
+ def self._has_matchdata?(md, idx)
60
+ md && md[idx] && !md[idx].empty?
61
+ end
62
+
63
+ def self._matchdata_to_number(md, idx, default)
64
+ _has_matchdata?(md, idx) ? md[idx].to_i : default
65
+ end
66
+
67
+ def to_ranges
68
+ String.to_ranges(self)
69
+ end
70
+
71
+ HIGHLIGHTER = ::Text::ANSIHighlighter.new
72
+
73
+ # returns a highlighted (colored) version of the string, applying the regular
74
+ # expressions in the array, which are paired with the desired color.
75
+ def highlight(re, color)
76
+ gsub(re) do |match|
77
+ HIGHLIGHTER.color(color, match)
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'tempfile'
5
+
6
+
7
+ class Tempfile
8
+
9
+ class << self
10
+
11
+ alias_method :original_open, :open
12
+
13
+ # this works around the behavior (fixed in 1.9) so that open returns
14
+ # the new temporary file instead of nil.
15
+
16
+ def open(*args, &blk)
17
+ tempname = nil
18
+ original_open(*args) do |tf|
19
+ tempname = tf.path
20
+
21
+ blk.call(tf) if blk
22
+ end
23
+
24
+ tempname
25
+ end
26
+ end
27
+
28
+ end
data/lib/riel/text.rb ADDED
@@ -0,0 +1,408 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ module Text
5
+
6
+ # Highlights text using either ANSI terminal codes, or HTML.
7
+
8
+ # Note that the foreground and background sections can have modifiers
9
+ # (attributes).
10
+ #
11
+ # Examples:
12
+ # black
13
+ # blue on white
14
+ # bold green on yellow
15
+ # underscore bold magenta on cyan
16
+ # underscore red on cyan
17
+
18
+ class Highlighter
19
+
20
+ VERSION = "1.0.4"
21
+
22
+ ATTRIBUTES = %w{
23
+ none
24
+ reset
25
+ bold
26
+ underscore
27
+ underline
28
+ blink
29
+ negative # not reverse, which is already defined for strings.
30
+ concealed
31
+ black
32
+ red
33
+ green
34
+ yellow
35
+ blue
36
+ magenta
37
+ cyan
38
+ white
39
+ on_black
40
+ on_red
41
+ on_green
42
+ on_yellow
43
+ on_blue
44
+ on_magenta
45
+ on_cyan
46
+ on_white
47
+ }
48
+
49
+ NONE = Object.new
50
+ HTML = Object.new
51
+ ANSI = Object.new
52
+
53
+ COLORS = %w{ black red green yellow blue magenta cyan white }
54
+ DECORATIONS = %w{ none reset bold underscore underline blink negative concealed }
55
+
56
+ BACKGROUND_COLORS = COLORS.collect { |color| "on_#{color}" }
57
+ FOREGROUND_COLORS = COLORS
58
+
59
+ COLORS_RE = Regexp.new('(?: ' +
60
+ # background will be in capture 0
61
+ 'on(?:\s+|_) ( ' + COLORS.join(' | ') + ' ) | ' +
62
+ # foreground will be in capture 1
63
+ '( ' + (COLORS + DECORATIONS).join(' | ') + ' ) ' +
64
+ ')', Regexp::EXTENDED);
65
+
66
+ DEFAULT_COLORS = [
67
+ "black on yellow",
68
+ "black on green",
69
+ "black on magenta",
70
+ "yellow on black",
71
+ "magenta on black",
72
+ "green on black",
73
+ "cyan on black",
74
+ "blue on yellow",
75
+ "blue on magenta",
76
+ "blue on green",
77
+ "blue on cyan",
78
+ "yellow on blue",
79
+ "magenta on blue",
80
+ "green on blue",
81
+ "cyan on blue",
82
+ ]
83
+
84
+ attr_reader :colors
85
+
86
+ def self.parse_colors(str)
87
+ str.scan(Regexp.new(COLORS_RE)).collect do |color|
88
+ color[0] ? "on_" + color[0] : color[1]
89
+ end
90
+ end
91
+
92
+ # returns a list of all color combinations.
93
+ def self.all_colors
94
+ all_colors = Array.new
95
+ ([ nil ] + DECORATIONS).each do |dec|
96
+ ([ nil ] + FOREGROUND_COLORS).each do |fg|
97
+ ([ nil ] + BACKGROUND_COLORS).each do |bg|
98
+ name = [ dec, fg, bg ].compact.join("_")
99
+ all_colors << name if name && name.length > 0
100
+ end
101
+ end
102
+ end
103
+ all_colors
104
+ end
105
+
106
+ all_colors.each do |name|
107
+ meth = Array.new
108
+ meth << "def #{name}(&blk)"
109
+ meth << " color(\"#{name}\", &blk)"
110
+ meth << "end"
111
+
112
+ self.class_eval meth.join("\n")
113
+ end
114
+
115
+ def initialize(colors)
116
+ @colors = colors
117
+ end
118
+
119
+ def highlight(str)
120
+ # implemented by subclasses
121
+ end
122
+
123
+ def to_s
124
+ (@colors || '').join(' ')
125
+ end
126
+
127
+ def ==(other)
128
+ return @colors.sort == other.colors.sort
129
+ end
130
+
131
+ # Colorizes the given object. If a block is passed, its return value is used
132
+ # and the stream is reset. If a String is provided as the object, it is
133
+ # colorized and the stream is reset. Otherwise, only the code for the given
134
+ # color name is returned.
135
+
136
+ def color(colorstr, obj = self, &blk)
137
+ # ^^^^ this is the Module self
138
+
139
+ colornames = self.class.parse_colors(colorstr)
140
+ result = names_to_code(colornames)
141
+
142
+ if blk
143
+ result << blk.call
144
+ result << names_to_code("reset")
145
+ elsif obj.kind_of?(String)
146
+ result << obj
147
+ result << names_to_code("reset")
148
+ end
149
+ result
150
+ end
151
+
152
+ # returns the code for the given color string, which is in the format:
153
+ # foreground* [on background]?
154
+ #
155
+ # Note that the foreground and background sections can have modifiers
156
+ # (attributes).
157
+ #
158
+ # Examples:
159
+ # black
160
+ # blue on white
161
+ # bold green on yellow
162
+ # underscore bold magenta on cyan
163
+ # underscore red on cyan
164
+
165
+ def code(str)
166
+ fg, bg = str.split(/\s*\bon_?\s*/)
167
+ (fg ? foreground(fg) : "") + (bg ? background(bg) : "")
168
+ end
169
+
170
+ # Returns the code for the given background color(s).
171
+ def background(bgcolor)
172
+ names_to_code("on_" + bgcolor)
173
+ end
174
+
175
+ # Returns the code for the given foreground color(s).
176
+ def foreground(fgcolor)
177
+ fgcolor.split(/\s+/).collect { |fg| names_to_code(fg) }.join("")
178
+ end
179
+
180
+ end
181
+
182
+ # Highlights using ANSI escape sequences.
183
+
184
+ class ANSIHighlighter < Highlighter
185
+
186
+ ATTRIBUTES = Hash[
187
+ 'none' => '0',
188
+ 'reset' => '0',
189
+ 'bold' => '1',
190
+ 'underscore' => '4',
191
+ 'underline' => '4',
192
+ 'blink' => '5',
193
+ 'negative' => '7',
194
+ 'concealed' => '8',
195
+ 'black' => '30',
196
+ 'red' => '31',
197
+ 'green' => '32',
198
+ 'yellow' => '33',
199
+ 'blue' => '34',
200
+ 'magenta' => '35',
201
+ 'cyan' => '36',
202
+ 'white' => '37',
203
+ 'on_black' => '40',
204
+ 'on_red' => '41',
205
+ 'on_green' => '42',
206
+ 'on_yellow' => '43',
207
+ 'on_blue' => '44',
208
+ 'on_magenta' => '45',
209
+ 'on_cyan' => '46',
210
+ 'on_white' => '47',
211
+ ]
212
+
213
+ RESET = "\e[0m"
214
+
215
+ def self.make(str)
216
+ colors = parse_colors(str)
217
+ ANSIHighlighter.new(colors)
218
+ end
219
+
220
+ def initialize(colors = DEFAULT_COLORS)
221
+ super
222
+ @code = nil
223
+ end
224
+
225
+ # Returns the escape sequence for the given names.
226
+
227
+ def names_to_code(names)
228
+ str = ""
229
+ names.each do |name|
230
+ code = ATTRIBUTES[name]
231
+ if code
232
+ str << "\e[#{code}m"
233
+ end
234
+ end
235
+ str
236
+ end
237
+
238
+ def highlight(str)
239
+ @code ||= begin
240
+ @code = @colors.collect do |color|
241
+ names_to_code(color)
242
+ end.join("")
243
+ end
244
+
245
+ @code + str + RESET
246
+ end
247
+
248
+ end
249
+
250
+ # Highlights using HTML. Fonts are highlighted using <span> tags, not <font>.
251
+ # Also note that negative is translated to white on black.
252
+ # According to http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color,
253
+ # valid color keywords are: aqua, black, blue, fuchsia, gray, green, lime,
254
+ # maroon, navy, olive, purple, red, silver, teal, white, and yellow.
255
+ # Thus, no magenta or cyan.
256
+
257
+ class HTMLHighlighter < Highlighter
258
+
259
+ def initialize
260
+ # we need to know what we're resetting from (bold, font, underlined ...)
261
+ @stack = []
262
+ end
263
+
264
+ # Returns the start tag for the given name.
265
+
266
+ def start_style(name)
267
+ case name
268
+ when "negative"
269
+ "<span style=\"color: white; background-color: black\">"
270
+ when /on_(\w+)/
271
+ colval = color_value($1)
272
+ "<span style=\"background-color: #{colval}\">"
273
+ else
274
+ colval = color_value(name)
275
+ "<span style=\"color: #{colval}\">"
276
+ end
277
+ end
278
+
279
+ # Returns the end tag ("</span>").
280
+
281
+ def end_style
282
+ "</span>"
283
+ end
284
+
285
+ def color_value(cname)
286
+ case cname
287
+ when "cyan"
288
+ "#00FFFF"
289
+ when "magenta"
290
+ "#FF00FF"
291
+ else
292
+ cname
293
+ end
294
+ end
295
+
296
+ # Returns the code for the given name.
297
+
298
+ def names_to_code(names)
299
+ str = ""
300
+
301
+ names.each do |name|
302
+ @stack << name
303
+
304
+ case name
305
+ when "none", "reset"
306
+ @stack.pop
307
+ if @stack.length > 0
308
+ begin
309
+ prev = @stack.pop
310
+ case prev
311
+ when "bold"
312
+ str << "</b>"
313
+ when "underscore", "underline"
314
+ str << "</u>"
315
+ when "blink"
316
+ str << "</blink>"
317
+ when "concealed"
318
+ str << " -->"
319
+ else
320
+ str << end_style
321
+ end
322
+ end while @stack.length > 0
323
+ end
324
+ str
325
+ when "bold"
326
+ str << "<b>"
327
+ when "underscore", "underline"
328
+ str << "<u>"
329
+ when "blink"
330
+ str << "<blink>"
331
+ when "concealed"
332
+ str << "<!-- "
333
+ else
334
+ str << start_style(name)
335
+ end
336
+ end
337
+
338
+ str
339
+ end
340
+ end
341
+
342
+ # Does no highlighting.
343
+
344
+ class NonHighlighter < Highlighter
345
+
346
+ def initialize
347
+ super(nil)
348
+ end
349
+
350
+ # Since the NonHighlighter does no highlighting, and thus its name, this
351
+ # returns an empty string.
352
+
353
+ def names_to_code(colorname)
354
+ ""
355
+ end
356
+
357
+ end
358
+
359
+
360
+ # An object that can be highlighted. This is used by the String class.
361
+
362
+ module Highlightable
363
+
364
+ # The highlighter for the class in which this module is included.
365
+
366
+ @@highlighter = ANSIHighlighter.new(Text::Highlighter::DEFAULT_COLORS)
367
+
368
+ Text::Highlighter::all_colors.each do |name|
369
+ meth = Array.new
370
+ meth << "def #{name}(&blk)"
371
+ meth << " @@highlighter.color(\"#{name}\", self, &blk)"
372
+ meth << "end"
373
+
374
+ self.class_eval meth.join("\n")
375
+ end
376
+
377
+ # Sets the highlighter for this class. This can be either by type or by
378
+ # String.
379
+
380
+ def highlighter=(hl)
381
+ $VERBOSE = false
382
+ @@highlighter = case hl
383
+ when Text::Highlighter
384
+ hl
385
+ when Text::Highlighter::NONE, "NONE", nil
386
+ Text::NonHighlighter.new # unless @@highlighter.kind_of?(Text::NonHighlighter)
387
+ when Text::Highlighter::HTML, "HTML"
388
+ Text::HTMLHighlighter.new # unless @@highlighter.kind_of?(Text::HTMLHighlighter)
389
+ when Text::Highlighter::ANSI, "ANSI"
390
+ Text::ANSIHighlighter.new
391
+ else
392
+ Text::NonHighlighter.new
393
+ end
394
+
395
+ end
396
+
397
+ end
398
+
399
+ $HAVE_TEXT_HIGHLIGHT = true
400
+
401
+ end
402
+
403
+ # String is extended to support highlighting.
404
+
405
+ class String
406
+ include Text::Highlightable
407
+ extend Text::Highlightable
408
+ end
data/lib/riel/timer.rb ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'riel/log'
5
+
6
+ class Timer
7
+
8
+ def initialize(what, args = Hash.new)
9
+ if args.kind_of?(Fixnum)
10
+ args = { :level => args }
11
+ end
12
+
13
+ @io = args[:io] || $stdout
14
+ @level = args[:level] || Log::DEBUG
15
+
16
+ stmsg = args.include?(:startmsg) ? args[:startmsg] : "#{what} start time"
17
+ endmsg = args.include?(:endmsg) ? args[:endmsg] : "#{what} end time"
18
+ elmsg = args.include?(:elmsg) ? args[:elmsg] : "#{what} elapsed "
19
+
20
+ sttime = Time.new
21
+ logmsg(stmsg, sttime)
22
+
23
+ yield
24
+
25
+ endtime = Time.new
26
+
27
+ logmsg(stmsg, sttime)
28
+ logmsg(endmsg, endtime)
29
+ logmsg(elmsg, endtime - sttime)
30
+ end
31
+
32
+ def logmsg(msg, value)
33
+ if msg
34
+ if @io
35
+ @io.puts "#{msg}: #{value}"
36
+ else
37
+ Log.log "#{msg}: #{value}", @level
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ def timethis(what)
45
+ sttime = Time.new
46
+ Log.log "#{what} start time: #{sttime}"
47
+ yield
48
+ endtime = Time.new
49
+ Log.log "#{what} start time: #{sttime}"
50
+ Log.log "#{what} end time : #{endtime}"
51
+ Log.log "#{what} elapsed : #{endtime - sttime}"
52
+ end
data/lib/riel.rb ADDED
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module RIEL
5
+ VERSION = '0.0.1'
6
+ end
7
+
8
+ rbfiles = Dir[File.dirname(__FILE__) + "/riel/**/*.rb"]
9
+
10
+ rbfiles.sort.each do |rbfile|
11
+ rootname = rbfile.match(%r{.*/(\w+)\.rb})[1]
12
+ require "riel/" + rootname
13
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'rubyunit'
5
+ require 'riel/array'
6
+
7
+ class ArrayTestCase < RUNIT::TestCase
8
+
9
+ def test_to_s
10
+ a = %w{ this is a test }
11
+ assert_equal "[ this, is, a, test ]", a.to_s
12
+ end
13
+
14
+ def test_rand
15
+ a = %w{ this is a test }
16
+ 10.times do
17
+ r = a.rand
18
+ assert_not_nil r
19
+ assert a.include?(r)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'rubyunit'
5
+ require 'riel/command'
6
+
7
+ class CommandTestCase < RUNIT::TestCase
8
+
9
+ def test_all
10
+ assert_equal [ "/bin/ls\n" ], Command.run("ls", "/bin/ls")
11
+ assert_equal [ "/bin/grep\n", "/bin/ls\n" ], Command.run("ls", "/bin/ls", "/bin/grep" )
12
+
13
+ lnum = 0
14
+ expected = [ "/bin/grep\n", "/bin/ls\n" ]
15
+ lines = Command.run("ls", "/bin/ls", "/bin/grep" ) do |line|
16
+ assert_equals(expected[lnum], line)
17
+ lnum += 1
18
+ end
19
+ assert_equal expected, lines
20
+
21
+ expected = [ "/bin/grep\n", "/bin/ls\n" ]
22
+ lines = Command.run("ls", "/bin/ls", "/bin/grep" ) do |line, lnum|
23
+ assert_equals(expected[lnum], line)
24
+ end
25
+ assert_equal expected, lines
26
+ end
27
+
28
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'rubyunit'
5
+ require 'riel/date'
6
+
7
+ class DateTestCase < RUNIT::TestCase
8
+
9
+ def test
10
+ assert_equal 28, Date.days_in_month(2010, 2)
11
+ assert_equal 31, Date.days_in_month(2010, 1)
12
+ assert_equal 29, Date.days_in_month(2008, 2)
13
+ assert_equal 30, Date.days_in_month(2007, 6)
14
+ assert_equal 31, Date.days_in_month(2010, 12)
15
+ end
16
+
17
+ end