PageRenderTime 281ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/text/src/test/resources/examples/ruby/pleac.in.rb

https://github.com/rimolive/core
Ruby | 6368 lines | 4278 code | 927 blank | 1163 comment | 310 complexity | cbeb52b0e3a76e39866eae7fd5c5280e MD5 | raw file
Possible License(s): EPL-1.0, MPL-2.0-no-copyleft-exception
  1. # -*- ruby -*-
  2. # Local variables:
  3. # indent-tabs-mode: nil
  4. # ruby-indent-level: 4
  5. # End:
  6. # @@PLEAC@@_NAME
  7. # @@SKIP@@ Ruby
  8. # @@PLEAC@@_WEB
  9. # @@SKIP@@ http://www.ruby-lang.org
  10. # @@PLEAC@@_1.0
  11. string = '\n' # two characters, \ and an n
  12. string = 'Jon \'Maddog\' Orwant' # literal single quotes
  13. string = "\n" # a "newline" character
  14. string = "Jon \"Maddog\" Orwant" # literal double quotes
  15. string = %q/Jon 'Maddog' Orwant/ # literal single quotes
  16. string = %q[Jon 'Maddog' Orwant] # literal single quotes
  17. string = %q{Jon 'Maddog' Orwant} # literal single quotes
  18. string = %q(Jon 'Maddog' Orwant) # literal single quotes
  19. string = %q<Jon 'Maddog' Orwant> # literal single quotes
  20. a = <<"EOF"
  21. This is a multiline here document
  22. terminated by EOF on a line by itself
  23. EOF
  24. # @@PLEAC@@_1.1
  25. value = string[offset,count]
  26. value = string[offset..-1]
  27. string[offset,count] = newstring
  28. string[offset..-1] = newtail
  29. # in Ruby we can also specify intervals by their two offsets
  30. value = string[offset..offs2]
  31. string[offset..offs2] = newstring
  32. leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*")
  33. fivers = string.unpack("A5" * (string.length/5))
  34. chars = string.unpack("A1" * string.length)
  35. string = "This is what you have"
  36. # +012345678901234567890 Indexing forwards (left to right)
  37. # 109876543210987654321- Indexing backwards (right to left)
  38. # note that 0 means 10 or 20, etc. above
  39. first = string[0, 1] # "T"
  40. start = string[5, 2] # "is"
  41. rest = string[13..-1] # "you have"
  42. last = string[-1, 1] # "e"
  43. end_ = string[-4..-1] # "have"
  44. piece = string[-8, 3] # "you"
  45. string[5, 2] = "wasn't" # change "is" to "wasn't"
  46. string[-12..-1] = "ondrous" # "This wasn't wondrous"
  47. string[0, 1] = "" # delete first character
  48. string[-10..-1] = "" # delete last 10 characters
  49. if string[-10..-1] =~ /pattern/
  50. puts "Pattern matches in last 10 characters"
  51. end
  52. string[0, 5].gsub!(/is/, 'at')
  53. a = "make a hat"
  54. a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1]
  55. a = "To be or not to be"
  56. b = a.unpack("x6 A6")
  57. b, c = a.unpack("x6 A2 X5 A2")
  58. puts "#{b}\n#{c}\n"
  59. def cut2fmt(*args)
  60. template = ''
  61. lastpos = 1
  62. for place in args
  63. template += "A" + (place - lastpos).to_s + " "
  64. lastpos = place
  65. end
  66. template += "A*"
  67. return template
  68. end
  69. fmt = cut2fmt(8, 14, 20, 26, 30)
  70. # @@PLEAC@@_1.2
  71. # careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby)
  72. # thus no problem of "defined" later since only nil is false
  73. # the following sets to `c' if `b' is nil or false
  74. a = b || c
  75. # if you need Perl's behaviour (setting to `c' if `b' is 0) the most
  76. # effective way is to use Numeric#nonzero? (thanks to Dave Thomas!)
  77. a = b.nonzero? || c
  78. # you will still want to use defined? in order to test
  79. # for scope existence of a given object
  80. a = defined?(b) ? b : c
  81. dir = ARGV.shift || "/tmp"
  82. # @@PLEAC@@_1.3
  83. v1, v2 = v2, v1
  84. alpha, beta, production = %w(January March August)
  85. alpha, beta, production = beta, production, alpha
  86. # @@PLEAC@@_1.4
  87. num = char[0]
  88. char = num.chr
  89. # Ruby also supports having a char from character constant
  90. num = ?r
  91. char = sprintf("%c", num)
  92. printf("Number %d is character %c\n", num, num)
  93. ascii = string.unpack("C*")
  94. string = ascii.pack("C*")
  95. hal = "HAL"
  96. ascii = hal.unpack("C*")
  97. # We can't use Array#each since we can't mutate a Fixnum
  98. ascii.collect! { |i|
  99. i + 1 # add one to each ASCII value
  100. }
  101. ibm = ascii.pack("C*")
  102. puts ibm
  103. # @@PLEAC@@_1.5
  104. array = string.split('')
  105. array = string.unpack("C*")
  106. string.scan(/./) { |b|
  107. # do something with b
  108. }
  109. string = "an apple a day"
  110. print "unique chars are: ", string.split('').uniq.sort, "\n"
  111. sum = 0
  112. for ascval in string.unpack("C*") # or use Array#each for a pure OO style :)
  113. sum += ascval
  114. end
  115. puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary
  116. # @@INCLUDE@@ include/ruby/slowcat.rb
  117. # @@PLEAC@@_1.6
  118. revbytes = string.reverse
  119. revwords = string.split(" ").reverse.join(" ")
  120. revwords = string.split(/(\s+)/).reverse.join
  121. # using the fact that IO is Enumerable, you can directly "select" it
  122. long_palindromes = File.open("/usr/share/dict/words").
  123. select { |w| w.chomp!; w.reverse == w && w.length > 5 }
  124. # @@PLEAC@@_1.7
  125. while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) }
  126. end
  127. # @@PLEAC@@_1.8
  128. 'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) }
  129. rows, cols = 24, 80
  130. text = %q(I am #{rows} high and #{cols} long)
  131. text.gsub!(/\#{(\w+)}/) { eval("#{$1}") }
  132. puts text
  133. 'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i }
  134. # @@PLEAC@@_1.9
  135. e = "bo peep".upcase
  136. e.downcase!
  137. e.capitalize!
  138. "thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize }
  139. # @@PLEAC@@_1.10
  140. "I have #{n+1} guanacos."
  141. print "I have ", n+1, " guanacos."
  142. # @@PLEAC@@_1.11
  143. var = <<'EOF'.gsub(/^\s+/, '')
  144. your text
  145. goes here
  146. EOF
  147. # @@PLEAC@@_1.12
  148. string = "Folding and splicing is the work of an editor,\n"+
  149. "not a mere collection of silicon\n"+
  150. "and\n"+
  151. "mobile electrons!"
  152. def wrap(str, max_size)
  153. all = []
  154. line = ''
  155. for l in str.split
  156. if (line+l).length >= max_size
  157. all.push(line)
  158. line = ''
  159. end
  160. line += line == '' ? l : ' ' + l
  161. end
  162. all.push(line).join("\n")
  163. end
  164. print wrap(string, 20)
  165. #=> Folding and
  166. #=> splicing is the
  167. #=> work of an editor,
  168. #=> not a mere
  169. #=> collection of
  170. #=> silicon and mobile
  171. #=> electrons!
  172. # @@PLEAC@@_1.13
  173. string = %q(Mom said, "Don't do that.")
  174. string.gsub(/['"]/) { '\\'+$& }
  175. string.gsub(/['"]/, '\&\&')
  176. string.gsub(/[^A-Z]/) { '\\'+$& }
  177. "is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta?
  178. # @@PLEAC@@_1.14
  179. string.strip!
  180. # @@PLEAC@@_1.15
  181. def parse_csv(text)
  182. new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/)
  183. new << nil if text[-1] == ?,
  184. new.flatten.compact
  185. end
  186. line = %q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped">
  187. fields = parse_csv(line)
  188. fields.each_with_index { |v,i|
  189. print "#{i} : #{v}\n";
  190. }
  191. # @@PLEAC@@_1.16
  192. # Use the soundex.rb Library from Michael Neumann.
  193. # http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb
  194. require 'Soundex'
  195. code = Text::Soundex.soundex(string)
  196. codes = Text::Soundex.soundex(array)
  197. # substitution function for getpwent():
  198. # returns an array of user entries,
  199. # each entry contains the username and the full name
  200. def login_names
  201. result = []
  202. File.open("/etc/passwd") { |file|
  203. file.each_line { |line|
  204. next if line.match(/^#/)
  205. cols = line.split(":")
  206. result.push([cols[0], cols[4]])
  207. }
  208. }
  209. result
  210. end
  211. puts "Lookup user: "
  212. user = STDIN.gets
  213. user.chomp!
  214. exit unless user
  215. name_code = Text::Soundex.soundex(user)
  216. splitter = Regexp.new('(\w+)[^,]*\b(\w+)')
  217. for username, fullname in login_names do
  218. firstname, lastname = splitter.match(fullname)[1,2]
  219. if name_code == Text::Soundex.soundex(username)
  220. || name_code == Text::Soundex.soundex(firstname)
  221. || name_code == Text::Soundex.soundex(lastname)
  222. then
  223. puts "#{username}: #{firstname} #{lastname}"
  224. end
  225. end
  226. # @@PLEAC@@_1.17
  227. # @@INCLUDE@@ include/ruby/fixstyle.rb
  228. # @@PLEAC@@_1.18
  229. # @@INCLUDE@@ include/ruby/psgrep.rb
  230. # @@PLEAC@@_2.1
  231. # Matz tells that you can use Integer() for strict checked conversion.
  232. Integer("abc")
  233. #=> `Integer': invalid value for Integer: "abc" (ArgumentError)
  234. Integer("567")
  235. #=> 567
  236. # You may use Float() for floating point stuff
  237. Integer("56.7")
  238. #=> `Integer': invalid value for Integer: "56.7" (ArgumentError)
  239. Float("56.7")
  240. #=> 56.7
  241. # You may also use a regexp for that
  242. if string =~ /^[+-]?\d+$/
  243. p 'is an integer'
  244. else
  245. p 'is not'
  246. end
  247. if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
  248. p 'is a decimal number'
  249. else
  250. p 'is not'
  251. end
  252. # @@PLEAC@@_2.2
  253. # equal(num1, num2, accuracy) : returns true if num1 and num2 are
  254. # equal to accuracy number of decimal places
  255. def equal(i, j, a)
  256. sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j)
  257. end
  258. wage = 536 # $5.36/hour
  259. week = 40 * wage # $214.40
  260. printf("One week's wage is: \$%.2f\n", week/100.0)
  261. # @@PLEAC@@_2.3
  262. num.round # rounds to integer
  263. a = 0.255
  264. b = sprintf("%.2f", a)
  265. print "Unrounded: #{a}\nRounded: #{b}\n"
  266. printf "Unrounded: #{a}\nRounded: %.2f\n", a
  267. print "number\tint\tfloor\tceil\n"
  268. a = [ 3.3 , 3.5 , 3.7, -3.3 ]
  269. for n in a
  270. printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :)
  271. n, n.to_i, n.floor, n.ceil)
  272. end
  273. # @@PLEAC@@_2.4
  274. def dec2bin(n)
  275. [n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '')
  276. end
  277. def bin2dec(n)
  278. [("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0]
  279. end
  280. # @@PLEAC@@_2.5
  281. for i in x .. y
  282. # i is set to every integer from x to y, inclusive
  283. end
  284. x.step(y,7) { |i|
  285. # i is set to every integer from x to y, stepsize = 7
  286. }
  287. print "Infancy is: "
  288. (0..2).each { |i|
  289. print i, " "
  290. }
  291. print "\n"
  292. # @@PLEAC@@_2.6
  293. # We can add conversion methods to the Integer class,
  294. # this makes a roman number just a representation for normal numbers.
  295. class Integer
  296. @@romanlist = [["M", 1000],
  297. ["CM", 900],
  298. ["D", 500],
  299. ["CD", 400],
  300. ["C", 100],
  301. ["XC", 90],
  302. ["L", 50],
  303. ["XL", 40],
  304. ["X", 10],
  305. ["IX", 9],
  306. ["V", 5],
  307. ["IV", 4],
  308. ["I", 1]]
  309. def to_roman
  310. remains = self
  311. roman = ""
  312. for sym, num in @@romanlist
  313. while remains >= num
  314. remains -= num
  315. roman << sym
  316. end
  317. end
  318. roman
  319. end
  320. def Integer.from_roman(roman)
  321. ustr = roman.upcase
  322. sum = 0
  323. for entry in @@romanlist
  324. sym, num = entry[0], entry[1]
  325. while sym == ustr[0, sym.length]
  326. sum += num
  327. ustr.slice!(0, sym.length)
  328. end
  329. end
  330. sum
  331. end
  332. end
  333. roman_fifteen = 15.to_roman
  334. puts "Roman for fifteen is #{roman_fifteen}"
  335. i = Integer.from_roman(roman_fifteen)
  336. puts "Converted back, #{roman_fifteen} is #{i}"
  337. # check
  338. for i in (1..3900)
  339. r = i.to_roman
  340. j = Integer.from_roman(r)
  341. if i != j
  342. puts "error: #{i} : #{r} - #{j}"
  343. end
  344. end
  345. # @@PLEAC@@_2.7
  346. random = rand(y-x+1)+x
  347. chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*)
  348. password = (1..8).collect { chars[rand(chars.size)] }.pack("C*")
  349. # @@PLEAC@@_2.8
  350. srand # uses a combination of the time, the process id, and a sequence number
  351. srand(val) # for repeatable behaviour
  352. # @@PLEAC@@_2.9
  353. # from the randomr lib:
  354. # http://raa.ruby-lang.org/project/randomr/
  355. ----> http://raa.ruby-lang.org/project/randomr/
  356. require 'random/mersenne_twister'
  357. mers = Random::MersenneTwister.new 123456789
  358. puts mers.rand(0) # 0.550321932544541
  359. puts mers.rand(10) # 2
  360. # using online sources of random data via the realrand package:
  361. # http://raa.ruby-lang.org/project/realrand/
  362. # **Note**
  363. # The following online services are used in this package:
  364. # http://www.random.org - source: atmospheric noise
  365. # http://www.fourmilab.ch/hotbits - source: radioactive decay timings
  366. # http://random.hd.org - source: entropy from local and network noise
  367. # Please visit the sites and respect the rules of each service.
  368. require 'random/online'
  369. generator1 = Random::RandomOrg.new
  370. puts generator1.randbyte(5).join(",")
  371. puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times.
  372. generator2 = Random::FourmiLab.new
  373. puts generator2.randbyte(5).join(",")
  374. # randnum is not supported.
  375. generator3 = Random::EntropyPool.new
  376. puts generator3.randbyte(5).join(",")
  377. # randnum is not supported.
  378. # @@PLEAC@@_2.10
  379. def gaussian_rand
  380. begin
  381. u1 = 2 * rand() - 1
  382. u2 = 2 * rand() - 1
  383. w = u1*u1 + u2*u2
  384. end while (w >= 1)
  385. w = Math.sqrt((-2*Math.log(w))/w)
  386. [ u2*w, u1*w ]
  387. end
  388. mean = 25
  389. sdev = 2
  390. salary = gaussian_rand[0] * sdev + mean
  391. printf("You have been hired at \$%.2f\n", salary)
  392. # @@PLEAC@@_2.11
  393. def deg2rad(d)
  394. (d/180.0)*Math::PI
  395. end
  396. def rad2deg(r)
  397. (r/Math::PI)*180
  398. end
  399. # @@PLEAC@@_2.12
  400. sin_val = Math.sin(angle)
  401. cos_val = Math.cos(angle)
  402. tan_val = Math.tan(angle)
  403. # AFAIK Ruby's Math module doesn't provide acos/asin
  404. # While we're at it, let's also define missing hyperbolic functions
  405. module Math
  406. def Math.asin(x)
  407. atan2(x, sqrt(1 - x**2))
  408. end
  409. def Math.acos(x)
  410. atan2(sqrt(1 - x**2), x)
  411. end
  412. def Math.atan(x)
  413. atan2(x, 1)
  414. end
  415. def Math.sinh(x)
  416. (exp(x) - exp(-x)) / 2
  417. end
  418. def Math.cosh(x)
  419. (exp(x) + exp(-x)) / 2
  420. end
  421. def Math.tanh(x)
  422. sinh(x) / cosh(x)
  423. end
  424. end
  425. # The support for Complex numbers is not built-in
  426. y = Math.acos(3.7)
  427. #=> in `sqrt': square root for negative number (ArgumentError)
  428. # There is an implementation of Complex numbers in 'complex.rb' in current
  429. # Ruby distro, but it doesn't support atan2 with complex args, so it doesn't
  430. # solve this problem.
  431. # @@PLEAC@@_2.13
  432. log_e = Math.log(val)
  433. log_10 = Math.log10(val)
  434. def log_base(base, val)
  435. Math.log(val)/Math.log(base)
  436. end
  437. answer = log_base(10, 10_000)
  438. puts "log10(10,000) = #{answer}"
  439. # @@PLEAC@@_2.14
  440. require 'matrix.rb'
  441. a = Matrix[[3, 2, 3], [5, 9, 8]]
  442. b = Matrix[[4, 7], [9, 3], [8, 1]]
  443. c = a * b
  444. a.row_size
  445. a.column_size
  446. c.det
  447. a.transpose
  448. # @@PLEAC@@_2.15
  449. require 'complex.rb'
  450. require 'rational.rb'
  451. a = Complex(3, 5) # 3 + 5i
  452. b = Complex(2, -2) # 2 - 2i
  453. puts "c = #{a*b}"
  454. c = a * b
  455. d = 3 + 4*Complex::I
  456. printf "sqrt(#{d}) = %s\n", Math.sqrt(d)
  457. # @@PLEAC@@_2.16
  458. number = hexadecimal.hex
  459. number = octal.oct
  460. print "Gimme a number in decimal, octal, or hex: "
  461. num = gets.chomp
  462. exit unless defined?(num)
  463. num = num.oct if num =~ /^0/ # does both oct and hex
  464. printf "%d %x %o\n", num, num, num
  465. print "Enter file permission in octal: "
  466. permissions = gets.chomp
  467. raise "Exiting ...\n" unless defined?(permissions)
  468. puts "The decimal value is #{permissions.oct}"
  469. # @@PLEAC@@_2.17
  470. def commify(n)
  471. n.to_s =~ /([^\.]*)(\..*)?/
  472. int, dec = $1.reverse, $2 ? $2 : ""
  473. while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
  474. end
  475. int.reverse + dec
  476. end
  477. # @@PLEAC@@_2.18
  478. printf "It took %d hour%s\n", time, time == 1 ? "" : "s"
  479. # dunno if an equivalent to Lingua::EN::Inflect exists...
  480. # @@PLEAC@@_2.19
  481. #-----------------------------
  482. #!/usr/bin/ruby
  483. # bigfact - calculating prime factors
  484. def factorize(orig)
  485. factors = {}
  486. factors.default = 0 # return 0 instead nil if key not found in hash
  487. n = orig
  488. i = 2
  489. sqi = 4 # square of i
  490. while sqi <= n do
  491. while n.modulo(i) == 0 do
  492. n /= i
  493. factors[i] += 1
  494. # puts "Found factor #{i}"
  495. end
  496. # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
  497. sqi += 2 * i + 1
  498. i += 1
  499. end
  500. if (n != 1) && (n != orig)
  501. factors[n] += 1
  502. end
  503. factors
  504. end
  505. def printfactorhash(orig, factorcount)
  506. print format("%-10d ", orig)
  507. if factorcount.length == 0
  508. print "PRIME"
  509. else
  510. # sorts after number, because the hash keys are numbers
  511. factorcount.sort.each { |factor,exponent|
  512. print factor
  513. if exponent > 1
  514. print "**", exponent
  515. end
  516. print " "
  517. }
  518. end
  519. puts
  520. end
  521. for arg in ARGV
  522. n = arg.to_i
  523. mfactors = factorize(n)
  524. printfactorhash(n, mfactors)
  525. end
  526. #-----------------------------
  527. # @@PLEAC@@_3.0
  528. puts Time.now
  529. print "Today is day ", Time.now.yday, " of the current year.\n"
  530. print "Today is day ", Time.now.day, " of the current month.\n"
  531. # @@PLEAC@@_3.1
  532. day, month, year = Time.now.day, Time.now.month, Time.now.year
  533. # or
  534. day, month, year = Time.now.to_a[3..5]
  535. tl = Time.now.localtime
  536. printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day)
  537. Time.now.localtime.strftime("%Y-%m-%d")
  538. # @@PLEAC@@_3.2
  539. Time.local(year, month, day, hour, minute, second).tv_sec
  540. Time.gm(year, month, day, hour, minute, second).tv_sec
  541. # @@PLEAC@@_3.3
  542. sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a
  543. # @@PLEAC@@_3.4
  544. when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds)
  545. then_ = now - difference
  546. # @@PLEAC@@_3.5
  547. bree = 361535725
  548. nat = 96201950
  549. difference = bree - nat
  550. puts "There were #{difference} seconds between Nat and Bree"
  551. seconds = difference % 60
  552. difference = (difference - seconds) / 60
  553. minutes = difference % 60
  554. difference = (difference - minutes) / 60
  555. hours = difference % 24
  556. difference = (difference - hours) / 24
  557. days = difference % 7
  558. weeks = (difference - days) / 7
  559. puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})"
  560. # @@PLEAC@@_3.6
  561. monthday, weekday, yearday = date.mday, date.wday, date.yday
  562. # AFAIK the week number is not just a division since week boundaries are on sundays
  563. weeknum = d.strftime("%U").to_i + 1
  564. year = 1981
  565. month = "jun" # or `6' if you want to emulate a broken language
  566. day = 16
  567. t = Time.mktime(year, month, day)
  568. print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n"
  569. # @@PLEAC@@_3.7
  570. yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/
  571. epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec
  572. # dunno an equivalent to Date::Manip#ParseDate
  573. # @@PLEAC@@_3.8
  574. string = Time.at(epoch_secs)
  575. Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009
  576. time = Time.mktime(1973, "jan", 18, 3, 45, 50)
  577. print "In localtime it gives: ", time.localtime, "\n"
  578. # @@PLEAC@@_3.9
  579. # Ruby provides micro-seconds in Time object
  580. Time.now.usec
  581. # Ruby gives the seconds in floating format when substracting two Time objects
  582. before = Time.now
  583. line = gets
  584. elapsed = Time.now - before
  585. puts "You took #{elapsed} seconds."
  586. # On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are:
  587. # This Ruby version: average 0.00321 sec
  588. # Cookbook's Perl version: average 0.00981 sec
  589. size = 500
  590. number_of_times = 100
  591. total_time = 0
  592. number_of_times.times {
  593. # populate array
  594. array = []
  595. size.times { array << rand }
  596. # sort it
  597. begin_ = Time.now
  598. array.sort!
  599. time = Time.now - begin_
  600. total_time += time
  601. }
  602. printf "On average, sorting %d random numbers takes %.5f seconds\n",
  603. size, (total_time/Float(number_of_times))
  604. # @@PLEAC@@_3.10
  605. sleep(0.005) # Ruby is definitely not as broken as Perl :)
  606. # (may be interrupted by sending the process a SIGALRM)
  607. # @@PLEAC@@_3.11
  608. #!/usr/bin/ruby -w
  609. # hopdelta - feed mail header, produce lines
  610. # showing delay at each hop.
  611. require 'time'
  612. class MailHopDelta
  613. def initialize(mail)
  614. @head = mail.gsub(/\n\s+/,' ')
  615. @topline = %w-Sender Recipient Time Delta-
  616. @start_from = mail.match(/^From.*\@([^\s>]*)/)[1]
  617. @date = Time.parse(mail.match(/^Date:\s+(.*)/)[1])
  618. end
  619. def out(line)
  620. "%-20.20s %-20.20s %-20.20s %s" % line
  621. end
  622. def hop_date(day)
  623. day.strftime("%I:%M:%S %Y/%m/%d")
  624. end
  625. def puts_hops
  626. puts out(@topline)
  627. puts out(['Start', @start_from, hop_date(@date),''])
  628. @head.split(/\n/).reverse.grep(/^Received:/).each do |hop|
  629. hop.gsub!(/\bon (.*?) (id.*)/,'; \1')
  630. whence = hop.match(/;\s+(.*)$/)[1]
  631. unless whence
  632. warn "Bad received line: #{hop}"
  633. next
  634. end
  635. from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/
  636. by = $1 if hop =~ /by\s+(\S+\.\S+)/
  637. next unless now = Time.parse(whence).localtime
  638. delta = now - @date
  639. puts out([from, by, hop_date(now), hop_time(delta)])
  640. @date = now
  641. end
  642. end
  643. def hop_time(secs)
  644. sign = secs < 0 ? -1 : 1
  645. days, secs = secs.abs.divmod(60 * 60 * 24)
  646. hours,secs = secs.abs.divmod(60 * 60)
  647. mins, secs = secs.abs.divmod(60)
  648. rtn = "%3ds" % [secs * sign]
  649. rtn << "%3dm" % [mins * sign] if mins != 0
  650. rtn << "%3dh" % [hours * sign] if hours != 0
  651. rtn << "%3dd" % [days * sign] if days != 0
  652. rtn
  653. end
  654. end
  655. $/ = ""
  656. mail = MailHopDelta.new(ARGF.gets).puts_hops
  657. # @@PLEAC@@_4.0
  658. single_level = [ "this", "that", "the", "other" ]
  659. # Ruby directly supports nested arrays
  660. double_level = [ "this", "that", [ "the", "other" ] ]
  661. still_single_level = [ "this", "that", [ "the", "other" ] ].flatten
  662. # @@PLEAC@@_4.1
  663. a = [ "quick", "brown", "fox" ]
  664. a = %w(Why are you teasing me?)
  665. lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1')
  666. The boy stood on the burning deck,
  667. It was as hot as glass.
  668. END_OF_HERE_DOC
  669. bigarray = IO.readlines("mydatafile").collect { |l| l.chomp }
  670. name = "Gandalf"
  671. banner = %Q(Speak, #{name}, and welcome!)
  672. host_info = `host #{his_host}`
  673. %x(ps #{$$})
  674. banner = 'Costs only $4.95'.split(' ')
  675. rax = %w! ( ) < > { } [ ] !
  676. # @@PLEAC@@_4.2
  677. def commify_series(arr)
  678. return '' if not arr
  679. case arr.size
  680. when 0 then ''
  681. when 1 then arr[0]
  682. when 2 then arr.join(' and ')
  683. else arr[0..-2].join(', ') + ', and ' + arr[-1]
  684. end
  685. end
  686. array = [ "red", "yellow", "green" ]
  687. print "I have ", array, " marbles\n"
  688. # -> I have redyellowgreen marbles
  689. # But unlike Perl:
  690. print "I have #{array} marbles\n"
  691. # -> I have redyellowgreen marbles
  692. # So, needs:
  693. print "I have #{array.join(' ')} marbles\n"
  694. # -> I have red yellow green marbles
  695. #!/usr/bin/ruby
  696. # communify_series - show proper comma insertion in list output
  697. def commify_series(arr)
  698. return '' if not arr
  699. sepchar = arr.find { |p| p =~ /,/ } ? '; ' : ', '
  700. case arr.size
  701. when 0 then ''
  702. when 1 then arr[0]
  703. when 2 then arr.join(' and ')
  704. else arr[0..-2].join(sepchar) + sepchar + 'and ' + arr[-1]
  705. end
  706. end
  707. lists = [
  708. [ 'just one thing' ],
  709. %w(Mutt Jeff),
  710. %w(Peter Paul Mary),
  711. [ 'To our parents', 'Mother Theresa', 'God' ],
  712. [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
  713. [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
  714. [ 'recycle tired, old phrases',
  715. 'ponder big, happy thoughts',
  716. 'sleep and dream peacefully' ],
  717. ]
  718. for list in lists do
  719. puts "The list is: #{commify_series(list)}."
  720. end
  721. # @@PLEAC@@_4.3
  722. # (note: AFAIK Ruby doesn't allow gory change of Array length)
  723. # grow the array by assigning nil to past the end of array
  724. ary[new_size-1] = nil
  725. # shrink the array by slicing it down
  726. ary.slice!(new_size..-1)
  727. # init the array with given size
  728. Array.new(number_of_elems)
  729. # assign to an element past the original end enlarges the array
  730. ary[index_new_last_elem] = value
  731. def what_about_that_array(a)
  732. print "The array now has ", a.size, " elements.\n"
  733. # Index of last element is not really interesting in Ruby
  734. print "Element #3 is `#{a[3]}'.\n"
  735. end
  736. people = %w(Crosby Stills Nash Young)
  737. what_about_that_array(people)
  738. # @@PLEAC@@_4.4
  739. # OO style
  740. bad_users.each { |user|
  741. complain(user)
  742. }
  743. # or, functional style
  744. for user in bad_users
  745. complain(user)
  746. end
  747. for var in ENV.keys.sort
  748. puts "#{var}=#{ENV[var]}"
  749. end
  750. for user in all_users
  751. disk_space = get_usage(user)
  752. if (disk_space > MAX_QUOTA)
  753. complain(user)
  754. end
  755. end
  756. for l in IO.popen("who").readlines
  757. print l if l =~ /^gc/
  758. end
  759. # we can mimic the obfuscated Perl way
  760. while fh.gets # $_ is set to the line just read
  761. chomp # $_ has a trailing \n removed, if it had one
  762. split.each { |w| # $_ is split on whitespace
  763. # but $_ is not set to each chunk as in Perl
  764. print w.reverse
  765. }
  766. end
  767. # ...or use a cleaner way
  768. for l in fh.readlines
  769. l.chomp.split.each { |w| print w.reverse }
  770. end
  771. # same drawback as in problem 1.4, we can't mutate a Numeric...
  772. array.collect! { |v| v - 1 }
  773. a = [ .5, 3 ]; b = [ 0, 1 ]
  774. for ary in [ a, b ]
  775. ary.collect! { |v| v * 7 }
  776. end
  777. puts "#{a.join(' ')} #{b.join(' ')}"
  778. # we can mutate Strings, cool; we need a trick for the scalar
  779. for ary in [ [ scalar ], array, hash.values ]
  780. ary.each { |v| v.strip! } # String#strip rules :)
  781. end
  782. # @@PLEAC@@_4.5
  783. # not relevant in Ruby since we have always references
  784. for item in array
  785. # do somethingh with item
  786. end
  787. # @@PLEAC@@_4.6
  788. unique = list.uniq
  789. # generate a list of users logged in, removing duplicates
  790. users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq
  791. puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series
  792. # @@PLEAC@@_4.7
  793. a - b
  794. # [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5]
  795. # @@PLEAC@@_4.8
  796. union = a | b
  797. intersection = a & b
  798. difference = a - b
  799. # @@PLEAC@@_4.9
  800. array1.concat(array2)
  801. # if you will assign to another object, better use:
  802. new_ary = array1 + array2
  803. members = [ "Time", "Flies" ]
  804. initiates = [ "An", "Arrow" ]
  805. members += initiates
  806. members = [ "Time", "Flies" ]
  807. initiates = [ "An", "Arrow" ]
  808. members[2,0] = [ "Like", initiates ].flatten
  809. members[0] = "Fruit"
  810. members[3,2] = "A", "Banana"
  811. # @@PLEAC@@_4.10
  812. reversed = ary.reverse
  813. ary.reverse_each { |e|
  814. # do something with e
  815. }
  816. descending = ary.sort.reverse
  817. descending = ary.sort { |a,b| b <=> a }
  818. # @@PLEAC@@_4.11
  819. # remove n elements from front of ary (shift n)
  820. front = ary.slice!(0, n)
  821. # remove n elements from the end of ary (pop n)
  822. end_ = ary.slice!(-n .. -1)
  823. # let's extend the Array class, to make that useful
  824. class Array
  825. def shift2()
  826. slice!(0 .. 1) # more symetric with pop2...
  827. end
  828. def pop2()
  829. slice!(-2 .. -1)
  830. end
  831. end
  832. friends = %w(Peter Paul Mary Jim Tim)
  833. this, that = friends.shift2
  834. beverages = %w(Dew Jolt Cola Sprite Fresca)
  835. pair = beverages.pop2
  836. # @@PLEAC@@_4.12
  837. # use Enumerable#detect (or the synonym Enumerable#find)
  838. highest_eng = employees.detect { |emp| emp.category == 'engineer' }
  839. # @@PLEAC@@_4.13
  840. # use Enumerable#select (or the synonym Enumerable#find_all)
  841. bigs = nums.select { |i| i > 1_000_000 }
  842. pigs = users.keys.select { |k| users[k] > 1e7 }
  843. matching = `who`.select { |u| u =~ /^gnat / }
  844. engineers = employees.select { |e| e.position == 'Engineer' }
  845. secondary_assistance = applicants.select { |a|
  846. a.income >= 26_000 && a.income < 30_000
  847. }
  848. # @@PLEAC@@_4.14
  849. # normally you would have an array of Numeric (Float or
  850. # Fixnum or Bignum), so you would use:
  851. sorted = unsorted.sort
  852. # if you have strings representing Integers or Floats
  853. # you may specify another sort method:
  854. sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f }
  855. # let's use the list of my own PID's
  856. `ps ux`.split("\n")[1..-1].
  857. select { |i| i =~ /^#{ENV['USER']}/ }.
  858. collect { |i| i.split[1] }.
  859. sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i }
  860. puts "Select a process ID to kill:"
  861. pid = gets.chomp
  862. raise "Exiting ... \n" unless pid && pid =~ /^\d+$/
  863. Process.kill('TERM', pid.to_i)
  864. sleep 2
  865. Process.kill('KILL', pid.to_i)
  866. descending = unsorted.sort { |a,b| b.to_f <=> a.to_f }
  867. # @@PLEAC@@_4.15
  868. ordered = unordered.sort { |a,b| compare(a,b) }
  869. precomputed = unordered.collect { |e| [compute, e] }
  870. ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] }
  871. ordered = ordered_precomputed.collect { |e| e[1] }
  872. ordered = unordered.collect { |e| [compute, e] }.
  873. sort { |a,b| a[0] <=> b[0] }.
  874. collect { |e| e[1] }
  875. for employee in employees.sort { |a,b| a.name <=> b.name }
  876. print employee.name, " earns \$ ", employee.salary, "\n"
  877. end
  878. # Beware! `0' is true in Ruby.
  879. # For chaining comparisons, you may use Numeric#nonzero?, which
  880. # returns num if num is not zero, nil otherwise
  881. sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age }
  882. users = []
  883. # getpwent is not wrapped in Ruby... let's fallback
  884. IO.readlines('/etc/passwd').each { |u| users << u.split(':') }
  885. users.sort! { |a,b| a[0] <=> b[0] }
  886. for user in users
  887. puts user[0]
  888. end
  889. sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] }
  890. sorted = strings.sort { |a,b| a.length <=> b.length }
  891. # let's show only the compact version
  892. ordered = strings.collect { |e| [e.length, e] }.
  893. sort { |a,b| a[0] <=> b[0] }.
  894. collect { |e| e[1] }
  895. ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }.
  896. sort { |a,b| a[0] <=> b[0] }.
  897. collect { |e| e[1] }
  898. print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }.
  899. sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }.
  900. collect { |e| e[0] }
  901. # @@PLEAC@@_4.16
  902. circular.unshift(circular.pop) # the last shall be first
  903. circular.push(circular.shift) # and vice versa
  904. def grab_and_rotate(l)
  905. l.push(ret = l.shift)
  906. ret
  907. end
  908. processes = [1, 2, 3, 4, 5]
  909. while (1)
  910. process = grab_and_rotate(processes)
  911. puts "Handling process #{process}"
  912. sleep 1
  913. end
  914. # @@PLEAC@@_4.17
  915. def fisher_yates_shuffle(a)
  916. (a.size-1).downto(1) { |i|
  917. j = rand(i+1)
  918. a[i], a[j] = a[j], a[i] if i != j
  919. }
  920. end
  921. def naive_shuffle(a)
  922. for i in 0...a.size
  923. j = rand(a.size)
  924. a[i], a[j] = a[j], a[i]
  925. end
  926. end
  927. # @@PLEAC@@_4.18
  928. #!/usr/bin/env ruby
  929. # example 4-2 words
  930. # words - gather lines, present in colums
  931. # class to encapsulate the word formatting from the input
  932. class WordFormatter
  933. def initialize(cols)
  934. @cols = cols
  935. end
  936. # helper to return the length of the longest word in the wordlist
  937. def maxlen(wordlist)
  938. max = 1
  939. for word in wordlist
  940. if word.length > max
  941. max = word.length
  942. end
  943. end
  944. max
  945. end
  946. # process the wordlist and print it formmated into columns
  947. def output(wordlist)
  948. collen = maxlen(wordlist) + 1
  949. columns = @cols / collen
  950. columns = 1 if columns == 0
  951. rows = (wordlist.length + columns - 1) / columns
  952. # now process each item, picking out proper piece for this position
  953. 0.upto(rows * columns - 1) { |item|
  954. target = (item % columns) * rows + (item / columns)
  955. eol = ((item+1) % columns == 0)
  956. piece = wordlist[target] || ""
  957. piece = piece.ljust(collen) unless eol
  958. print piece
  959. puts if eol
  960. }
  961. # no need to finish it up, because eol is always true for the last element
  962. end
  963. end
  964. # get nr of chars that fit in window or console, see PLEAC 15.4
  965. # not portable -- linux only (?)
  966. def getWinCharWidth()
  967. buf = "\0" * 8
  968. $stdout.ioctl(0x5413, buf)
  969. ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("$4")
  970. ws_col || 80
  971. rescue
  972. 80
  973. end
  974. # main program
  975. cols = getWinCharWidth()
  976. formatter = WordFormatter.new(cols)
  977. words = readlines()
  978. words.collect! { |line|
  979. line.chomp
  980. }
  981. formatter.output(words)
  982. # @@PLEAC@@_4.19
  983. # In ruby, Fixnum's are automatically converted to Bignum's when
  984. # needed, so there is no need for an extra module
  985. def factorial(n)
  986. s = 1
  987. while n > 0
  988. s *= n
  989. n -= 1
  990. end
  991. s
  992. end
  993. puts factorial(500)
  994. #---------------------------------------------------------
  995. # Example 4-3. tsc-permute
  996. # tsc_permute: permute each word of input
  997. def permute(items, perms)
  998. unless items.length > 0
  999. puts perms.join(" ")
  1000. else
  1001. for i in items
  1002. newitems = items.dup
  1003. newperms = perms.dup
  1004. newperms.unshift(newitems.delete(i))
  1005. permute(newitems, newperms)
  1006. end
  1007. end
  1008. end
  1009. # In ruby the main program must be after all definitions it is using
  1010. permute(ARGV, [])
  1011. #---------------------------------------------------------
  1012. # mjd_permute: permute each word of input
  1013. def factorial(n)
  1014. s = 1
  1015. while n > 0
  1016. s *= n
  1017. n -= 1
  1018. end
  1019. s
  1020. end
  1021. # we use a class with a class variable store the private cache
  1022. # for the results of the factorial function.
  1023. class Factorial
  1024. @@fact = [ 1 ]
  1025. def Factorial.compute(n)
  1026. if @@fact[n]
  1027. @@fact[n]
  1028. else
  1029. @@fact[n] = n * Factorial.compute(n - 1)
  1030. end
  1031. end
  1032. end
  1033. #---------------------------------------------------------
  1034. # Example 4-4- mjd-permute
  1035. # n2pat(n, len): produce the N-th pattern of length len
  1036. # We must use a lower case letter as parameter N, otherwise it is
  1037. # handled as constant Length is the length of the resulting
  1038. # array, not the index of the last element (length -1) like in
  1039. # the perl example.
  1040. def n2pat(n, length)
  1041. pat = []
  1042. i = 1
  1043. while i <= length
  1044. pat.push(n % i)
  1045. n /= i
  1046. i += 1
  1047. end
  1048. pat
  1049. end
  1050. # pat2perm(pat): turn pattern returned by n2pat() into
  1051. # permutation of integers.
  1052. def pat2perm(pat)
  1053. source = (0 .. pat.length - 1).to_a
  1054. perm = []
  1055. perm.push(source.slice!(pat.pop)) while pat.length > 0
  1056. perm
  1057. end
  1058. def n2perm(n, len)
  1059. pat2perm(n2pat(n,len))
  1060. end
  1061. # In ruby the main program must be after all definitions
  1062. while gets
  1063. data = split
  1064. # the perl solution has used $#data, which is length-1
  1065. num_permutations = Factorial.compute(data.length())
  1066. 0.upto(num_permutations - 1) do |i|
  1067. # in ruby we can not use an array as selector for an array
  1068. # but by exchanging the two arrays, we can use the collect method
  1069. # which returns an array with the result of all block invocations
  1070. permutation = n2perm(i, data.length).collect {
  1071. |j| data[j]
  1072. }
  1073. puts permutation.join(" ")
  1074. end
  1075. end
  1076. # @@PLEAC@@_5.0
  1077. age = { "Nat", 24,
  1078. "Jules", 25,
  1079. "Josh", 17 }
  1080. age["Nat"] = 24
  1081. age["Jules"] = 25
  1082. age["Josh"] = 17
  1083. food_color = {
  1084. "Apple" => "red",
  1085. "Banana" => "yellow",
  1086. "Lemon" => "yellow",
  1087. "Carrot" => "orange"
  1088. }
  1089. # In Ruby, you cannot avoid the double or simple quoting
  1090. # while manipulatin hashes
  1091. # @@PLEAC@@_5.1
  1092. hash[key] = value
  1093. food_color["Raspberry"] = "pink"
  1094. puts "Known foods:", food_color.keys
  1095. # @@PLEAC@@_5.2
  1096. # does hash have a value for key ?
  1097. if (hash.has_key?(key))
  1098. # it exists
  1099. else
  1100. # it doesn't
  1101. end
  1102. [ "Banana", "Martini" ].each { |name|
  1103. print name, " is a ", food_color.has_key?(name) ? "food" : "drink", "\n"
  1104. }
  1105. age = {}
  1106. age['Toddler'] = 3
  1107. age['Unborn'] = 0
  1108. age['Phantasm'] = nil
  1109. for thing in ['Toddler', 'Unborn', 'Phantasm', 'Relic']
  1110. print "#{thing}: "
  1111. print "Has-key " if age.has_key?(thing)
  1112. print "True " if age[thing]
  1113. print "Nonzero " if age[thing] && age[thing].nonzero?
  1114. print "\n"
  1115. end
  1116. #=>
  1117. # Toddler: Has-key True Nonzero
  1118. # Unborn: Has-key True
  1119. # Phantasm: Has-key
  1120. # Relic:
  1121. # You use Hash#has_key? when you use Perl's exists -> it checks
  1122. # for existence of a key in a hash.
  1123. # All Numeric are "True" in ruby, so the test doesn't have the
  1124. # same semantics as in Perl; you would use Numeric#nonzero? to
  1125. # achieve the same semantics (false if 0, true otherwise).
  1126. # @@PLEAC@@_5.3
  1127. food_color.delete("Banana")
  1128. # @@PLEAC@@_5.4
  1129. hash.each { |key, value|
  1130. # do something with key and value
  1131. }
  1132. hash.each_key { |key|
  1133. # do something with key
  1134. }
  1135. food_color.each { |food, color|
  1136. puts "#{food} is #{color}"
  1137. }
  1138. food_color.each_key { |food|
  1139. puts "#{food} is #{food_color[food]}"
  1140. }
  1141. # IMO this demonstrates that OO style is by far more readable
  1142. food_color.keys.sort.each { |food|
  1143. puts "#{food} is #{food_color[food]}."
  1144. }
  1145. #-----------------------------
  1146. #!/usr/bin/ruby
  1147. # countfrom - count number of messages from each sender
  1148. # Default value is 0
  1149. from = Hash.new(0)
  1150. while gets
  1151. /^From: (.*)/ and from[$1] += 1
  1152. end
  1153. # More useful to sort by number of received mail by person
  1154. from.sort {|a,b| b[1]<=>a[1]}.each { |v|
  1155. puts "#{v[1]}: #{v[0]}"
  1156. }
  1157. #-----------------------------
  1158. # @@PLEAC@@_5.5
  1159. # You may use the built-in 'inspect' method this way:
  1160. p hash
  1161. # Or do it the Cookbook way:
  1162. hash.each { |k,v| puts "#{k} => #{v}" }
  1163. # Sorted by keys
  1164. hash.sort.each { |e| puts "#{e[0]} => #{e[1]}" }
  1165. # Sorted by values
  1166. hash.sort{|a,b| a[1]<=>b[1]}.each { |e| puts "#{e[0]} => #{e[1]}" }
  1167. # @@PLEAC@@_5.7
  1168. ttys = Hash.new
  1169. for i in `who`
  1170. user, tty = i.split
  1171. (ttys[user] ||= []) << tty # see problems_ruby for more infos
  1172. end
  1173. ttys.keys.sort.each { |k|
  1174. puts "#{k}: #{commify_series(ttys[k])}" # from 4.2
  1175. }
  1176. # @@PLEAC@@_5.8
  1177. surname = { "Mickey" => "Mantle", "Babe" => "Ruth" }
  1178. puts surname.index("Mantle")
  1179. # If you really needed to 'invert' the whole hash, use Hash#invert
  1180. #-----------------------------
  1181. #!/usr/bin/ruby -w
  1182. # foodfind - find match for food or color
  1183. given = ARGV.shift or raise "usage: foodfind food_or_color"
  1184. color = {
  1185. "Apple" => "red",
  1186. "Banana" => "yellow",
  1187. "Lemon" => "yellow",
  1188. "Carrot" => "orange",
  1189. }
  1190. if (color.has_key?(given))
  1191. puts "#{given} is a food with color #{color[given]}."
  1192. end
  1193. if (color.has_value?(given))
  1194. puts "#{color.index(given)} is a food with color #{given}."
  1195. end
  1196. #-----------------------------
  1197. # @@PLEAC@@_5.9
  1198. # Sorted by keys (Hash#sort gives an Array of pairs made of each key,value)
  1199. food_color.sort.each { |f|
  1200. puts "#{f[0]} is #{f[1]}."
  1201. }
  1202. # Sorted by values
  1203. food_color.sort { |a,b| a[1] <=> b[1] }.each { |f|
  1204. puts "#{f[0]} is #{f[1]}."
  1205. }
  1206. # Sorted by length of values
  1207. food_color.sort { |a,b| a[1].length <=> b[1].length }.each { |f|
  1208. puts "#{f[0]} is #{f[1]}."
  1209. }
  1210. # @@PLEAC@@_5.10
  1211. merged = a.clone.update(b) # because Hash#update changes object in place
  1212. drink_color = { "Galliano" => "yellow", "Mai Tai" => "blue" }
  1213. ingested_color = drink_color.clone.update(food_color)
  1214. substance_color = {}
  1215. for i in [ food_color, drink_color ]
  1216. i.each_key { |k|
  1217. if substance_color.has_key?(k)
  1218. puts "Warning: #{k} seen twice. Using the first definition."
  1219. next
  1220. end
  1221. substance_color[k] = 1
  1222. }
  1223. end
  1224. # @@PLEAC@@_5.11
  1225. common = hash1.keys & hash2.keys
  1226. this_not_that = hash1.keys - hash2.keys
  1227. # @@PLEAC@@_5.12
  1228. # no problem here, Ruby handles any kind of object for key-ing
  1229. # (it takes Object#hash, which defaults to Object#id)
  1230. # @@PLEAC@@_5.13
  1231. # AFAIK, not possible in Ruby
  1232. # @@PLEAC@@_5.14
  1233. # Be careful, the following is possible only because Fixnum objects are
  1234. # special (documentation says: there is effectively only one Fixnum object
  1235. # instance for any given integer value).
  1236. count = Hash.new(0)
  1237. array.each { |e|
  1238. count[e] += 1
  1239. }
  1240. # @@PLEAC@@_5.15
  1241. father = {
  1242. "Cain" , "Adam",
  1243. "Abel" , "Adam",
  1244. "Seth" , "Adam",
  1245. "Enoch" , "Cain",
  1246. "Irad" , "Enoch",
  1247. "Mehujael" , "Irad",
  1248. "Methusael" , "Mehujael",
  1249. "Lamech" , "Methusael",
  1250. "Jabal" , "Lamech",
  1251. "Jubal" , "Lamech",
  1252. "Tubalcain" , "Lamech",
  1253. "Enos" , "Seth",
  1254. }
  1255. while gets
  1256. chomp
  1257. begin
  1258. print $_, " "
  1259. end while $_ = father[$_]
  1260. puts
  1261. end
  1262. children = {}
  1263. father.each { |k,v|
  1264. (children[v] ||= []) << k
  1265. }
  1266. while gets
  1267. chomp
  1268. puts "#{$_} begat #{(children[$_] || ['Nobody']).join(', ')}.\n"
  1269. end
  1270. includes = {}
  1271. files.each { |f|
  1272. begin
  1273. for l in IO.readlines(f)
  1274. next unless l =~ /^\s*#\s*include\s*<([^>]+)>/
  1275. (includes[$1] ||= []) << f
  1276. end
  1277. rescue SystemCallError
  1278. $stderr.puts "#$! (skipping)"
  1279. end
  1280. }
  1281. include_free = includes.values.flatten.uniq - includes.keys
  1282. # @@PLEAC@@_5.16
  1283. # dutree - print sorted intented rendition of du output
  1284. #% dutree
  1285. #% dutree /usr
  1286. #% dutree -a
  1287. #% dutree -a /bin
  1288. # The DuNode class collects all information about a directory,
  1289. # and provides some convenience methods
  1290. class DuNode
  1291. attr_reader :name
  1292. attr_accessor :size
  1293. attr_accessor :kids
  1294. def initialize(name)
  1295. @name = name
  1296. @kids = []
  1297. @size = 0
  1298. end
  1299. # support for sorting nodes with side
  1300. def size_compare(node2)
  1301. @size <=> node2.size
  1302. end
  1303. def basename
  1304. @name.sub(/.*\//, "")
  1305. end
  1306. #returns substring before last "/", nil if not there
  1307. def parent
  1308. p = @name.sub(/\/[^\/]+$/,"")
  1309. if p == @name
  1310. nil
  1311. else
  1312. p
  1313. end
  1314. end
  1315. end
  1316. # The DuTree does the acdtual work of
  1317. # getting the input, parsing it, builging up a tree
  1318. # and format it for output
  1319. class Dutree
  1320. attr_reader :topdir
  1321. def initialize
  1322. @nodes = Hash.new
  1323. @dirsizes = Hash.new(0)
  1324. @kids = Hash.new([])
  1325. end
  1326. # get a node by name, create it if it does not exist yet
  1327. def get_create_node(name)
  1328. if @nodes.has_key?(name)
  1329. @nodes[name]
  1330. else
  1331. node = DuNode.new(name)
  1332. @nodes[name] = node
  1333. node
  1334. end
  1335. end
  1336. # run du, read in input, save sizes and kids
  1337. # stores last directory read in instance variable topdir
  1338. def input(arguments)
  1339. name = ""
  1340. cmd = "du " + arguments.join(" ")
  1341. IO.popen(cmd) { |pipe|
  1342. pipe.each { |line|
  1343. size, name = line.chomp.split(/\s+/, 2)
  1344. node = get_create_node(name)
  1345. node.size = size.to_i
  1346. @nodes[name] = node
  1347. parent = node.parent
  1348. if parent
  1349. get_create_node(parent).kids.push(node)
  1350. end
  1351. }
  1352. }
  1353. @topdir = @nodes[name]
  1354. end
  1355. # figure out how much is taken in each directory
  1356. # that isn't stored in the subdirectories. Add a new
  1357. # fake kid called "." containing that much.
  1358. def get_dots(node)
  1359. cursize = node.size
  1360. for kid in node.kids
  1361. cursize -= kid.size
  1362. get_dots(kid)
  1363. end
  1364. if node.size != cursize
  1365. newnode = get_create_node(node.name + "/.")
  1366. newnode.size = cursize
  1367. node.kids.push(newnode)
  1368. end
  1369. end
  1370. # recursively output everything
  1371. # passing padding and number width as well
  1372. # on recursive calls
  1373. def output(node, prefix="", width=0)
  1374. line = sprintf("%#{width}d %s", node.size, node.basename)
  1375. puts(prefix + line)
  1376. prefix += line.sub(/\d /, "| ")
  1377. prefix.gsub!(/[^|]/, " ")
  1378. if node.kids.length > 0 # not a bachelor node
  1379. kids = node.kids
  1380. kids.sort! { |a,b|
  1381. b.size_compare(a)
  1382. }
  1383. width = kids[0].size.to_s.length
  1384. for kid in kids
  1385. output(kid, prefix, width)
  1386. end
  1387. end
  1388. end
  1389. end
  1390. tree = Dutree.new
  1391. tree.input(ARGV)
  1392. tree.get_dots(tree.topdir)
  1393. tree.output(tree.topdir)
  1394. # @@PLEAC@@_6.0
  1395. # The verbose version are match, sub, gsub, sub! and gsub!;
  1396. # pattern needs to be a Regexp object; it yields a MatchData
  1397. # object.
  1398. pattern.match(string)
  1399. string.sub(pattern, replacement)
  1400. string.gsub(pattern, replacement)
  1401. # As usual in Ruby, sub! does the same as sub but also modifies
  1402. # the object, the same for gsub!/gsub.
  1403. # Sugared syntax yields the position of the match (or nil if no
  1404. # match). Note that the object at the right of the operator needs
  1405. # not to be a Regexp object (it can be a String). The "dont
  1406. # match" operator yields true or false.
  1407. meadow =~ /sheep/ # position of the match, nil if no match
  1408. meadow !~ /sheep/ # true if doesn't match, false if it does
  1409. # There is no sugared version for the substitution
  1410. meadow =~ /\bovines?\b/i and print "Here be sheep!"
  1411. string = "good food"
  1412. string.sub!(/o*/, 'e')
  1413. # % echo ababacaca | ruby -ne 'puts $& if /(a|ba|b)+(a|ac)+/'
  1414. # ababa
  1415. # The "global" (or "multiple") match is handled by String#scan
  1416. scan (/(\d+)/) {
  1417. puts "Found number #{$1}"
  1418. }
  1419. # String#scan yields an Array if not used with a block
  1420. numbers = scan(/\d+/)
  1421. digits = "123456789"
  1422. nonlap = digits.scan(/(\d\d\d)/)
  1423. yeslap = digits.scan(/(?=(\d\d\d))/)
  1424. puts "Non-overlapping: #{nonlap.join(' ')}"
  1425. puts "Overlapping: #{yeslap.join(' ')}";
  1426. # Non-overlapping: 123 456 789
  1427. # Overlapping: 123 234 345 456 567 678 789
  1428. string = "And little lambs eat ivy"
  1429. string =~ /l[^s]*s/
  1430. puts "(#$`) (#$&) (#$')"
  1431. # (And ) (little lambs) ( eat ivy)
  1432. # @@PLEAC@@_6.1
  1433. # Ruby doesn't have the same problem:
  1434. dst = src.sub('this', 'that')
  1435. progname = $0.sub('^.*/', '')
  1436. bindirs = %w(/usr/bin /bin /usr/local/bin)
  1437. libdirs = bindirs.map { |l| l.sub('bin', 'lib') }
  1438. # @@PLEAC@@_6.3
  1439. /\S+/ # as many non-whitespace bytes as possible
  1440. /[A-Za-z'-]+/ # as many letters, apostrophes, and hyphens
  1441. /\b([A-Za-z]+)\b/ # usually best
  1442. /\s([A-Za-z]+)\s/ # fails at ends or w/ punctuation
  1443. # @@PLEAC@@_6.4
  1444. require 'socket'
  1445. str = 'www.ruby-lang.org and www.rubygarden.org'
  1446. re = /
  1447. ( # capture the hostname in $1
  1448. (?: # these parens for grouping only
  1449. (?! [-_] ) # lookahead for neither underscore nor dash
  1450. [\w-] + # hostname component
  1451. \. # and the domain dot
  1452. ) + # now repeat that whole thing a bunch of times
  1453. [A-Za-z] # next must be a letter
  1454. [\w-] + # now trailing domain part
  1455. ) # end of $1 capture
  1456. /x # /x for nice formatting
  1457. str.gsub! re do # pass a block to execute replacement
  1458. host = TCPsocket.gethostbyname($1)
  1459. "#{$1} [#{host[3]}]"
  1460. end
  1461. puts str
  1462. #-----------------------------
  1463. # to match whitespace or #-characters in an extended re you need to escape
  1464. # them.
  1465. foo = 42
  1466. str = 'blah #foo# blah'
  1467. str.gsub! %r/ # replace
  1468. \# # a pound sign
  1469. (\w+) # the variable name
  1470. \# # another pound sign
  1471. /x do
  1472. eval $1 # with the value of a local variable
  1473. end
  1474. puts str # => blah 42 blah
  1475. # @@PLEAC@@_6.5
  1476. # The 'g' modifier doesn't exist in Ruby, a regexp can't be used
  1477. # directly in a while loop; instead, use String#scan { |match| .. }
  1478. fish = 'One fish two fish red fish blue fish'
  1479. WANT = 3
  1480. count = 0
  1481. fish.scan(/(\w+)\s+fish\b/i) {
  1482. if (count += 1) == WANT
  1483. puts "The third fish is a #{$1} one."
  1484. end
  1485. }
  1486. if fish =~ /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i
  1487. puts "The third fish is a #{$1} one."
  1488. end
  1489. pond = 'One fish two fish red fish blue fish'
  1490. # String#scan without a block gives an array of matches, each match
  1491. # being an array of all the specified groups
  1492. colors = pond.scan(/(\w+)\s+fish\b/i).flatten # get all matches
  1493. color = colors[2] # then the one we want
  1494. # or without a temporary array
  1495. color = pond.scan(/(\w+)\s+fish\b/i).flatten[2] # just grab element 3
  1496. puts "The third fish in the pond is #{color}."
  1497. count = 0
  1498. fishes = 'One fish two fish red fish blue fish'
  1499. evens = fishes.scan(/(\w+)\s+fish\b/i).select { (count+=1) % 2 == 0 }
  1500. print "Even numbered fish are #{evens.join(' ')}."
  1501. count = 0
  1502. fishes.gsub(/
  1503. \b # makes next \w more efficient
  1504. ( \w+ ) # this is what we\'ll be changing
  1505. (
  1506. \s+ fish \b
  1507. )
  1508. /x) {
  1509. if (count += 1) == 4
  1510. 'sushi' + $2
  1511. else
  1512. $1 + $2
  1513. end
  1514. }
  1515. pond = 'One fish two fish red fish blue fish swim here.'
  1516. puts "Last fish is #{pond.scan(/\b(\w+)\s+fish\b/i).flatten[-1]}"
  1517. /
  1518. A # find some pattern A
  1519. (?! # mustn\'t be able to find
  1520. .* # something
  1521. A # and A
  1522. )
  1523. $ # through the end of the string
  1524. /x
  1525. # The "s" perl modifier is "m" in Ruby (not very nice since there is
  1526. # also an "m" in perl..)
  1527. pond = "One fish two fish red fish blue fish swim here."
  1528. if (pond =~ /
  1529. \b ( \w+) \s+ fish \b
  1530. (?! .* \b fish \b )
  1531. /mix)
  1532. puts "Last fish is #{$1}."
  1533. else
  1534. puts "Failed!"
  1535. end
  1536. # @@PLEAC@@_6.6
  1537. #-----------------------------
  1538. #!/usr/bin/ruby -w
  1539. # killtags - very bad html killer
  1540. $/ = nil; # each read is whole file
  1541. while file = gets() do
  1542. file.gsub!(/<.*?>/m,''); # strip tags (terribly)
  1543. puts file # print file to STDOUT
  1544. end
  1545. #-----------------------------
  1546. #!/usr/bin/ruby -w
  1547. #headerfy - change certain chapter headers to html
  1548. $/ = ''
  1549. while file = gets() do
  1550. pattern = /
  1551. \A # start of record
  1552. ( # capture in $1
  1553. Chapter # text string
  1554. \s+ # mandatory whitespace
  1555. \d+ # decimal number
  1556. \s* # optional whitespace
  1557. : # a real colon
  1558. . * # anything not a newline till end of line
  1559. )
  1560. /x
  1561. puts file.gsub(pattern,'<H1>\1</H1>')
  1562. end
  1563. #-----------------------------
  1564. #% ruby -00pe "gsub!(/\A(Chapter\s+\d+\s*:.*)/,'<H1>\1</H1>')" datafile
  1565. #!/usr/bin/ruby -w
  1566. #-----------------------------
  1567. for file in ARGV
  1568. file = File.open(ARGV.shift)
  1569. while file.gets('') do # each read is a paragraph
  1570. print "chunk #{$.} in $ARGV has <<#{$1}>>\n" while /^START(.*?)^END/m
  1571. end # /m activates the multiline mode
  1572. end
  1573. #-----------------------------
  1574. # @@PLEAC@@_6.7
  1575. #-----------------------------
  1576. $/ = nil;
  1577. file = File.open("datafile")
  1578. chunks = file.gets.split(/pattern/)
  1579. #-----------------------------
  1580. # .Ch, .Se and .Ss divide chunks of STDIN
  1581. chunks = gets(nil).split(/^\.(Ch|Se|Ss)$/)
  1582. print "I read #{chunks.size} chunks.\n"
  1583. #-----------------------------
  1584. # @@PLEAC@@_6.8
  1585. while gets
  1586. if ~/BEGIN/ .. ~/END/
  1587. # line falls between BEGIN and END inclusive
  1588. end
  1589. end
  1590. while gets
  1591. if ($. == firstnum) .. ($. == lastnum)
  1592. # operate between firstnum and lastnum line number
  1593. end
  1594. end
  1595. # in ruby versions prior to 1.8, the above two conditional
  1596. # expressions could be shortened to:
  1597. # if /BEGIN/ .. /END/
  1598. # and
  1599. # if firstnum .. lastnum
  1600. # but these now only work this way from the command line
  1601. #-----------------------------
  1602. while gets
  1603. if ~/BEGIN/ ... ~/END/
  1604. # line falls between BEGIN and END on different lines
  1605. end
  1606. end
  1607. while gets
  1608. if ($. == first) ... ($. == last)
  1609. # operate between first and last line number on different lines
  1610. end
  1611. end
  1612. #-----------------------------
  1613. # command-line to print lines 15 through 17 inclusive (see below)
  1614. ruby -ne 'print if 15 .. 17' datafile
  1615. # print out all <XMP> .. </XMP> displays from HTML doc
  1616. while gets
  1617. print if ~%r#<XMP>#i .. ~%r#</XMP>#i;
  1618. end
  1619. # same, but as shell command
  1620. # ruby -ne 'print if %r#<XMP>#i .. %r#</XMP>#i' document.html
  1621. #-----------------------------
  1622. # ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
  1623. # print if $top .. $bottom' /etc/passwd # FAILS
  1624. # ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
  1625. # print if $. == $top .. $. == $bottom' /etc/passwd # works
  1626. # ruby -ne 'print if 3 .. 5' /etc/passwd # also works
  1627. #-----------------------------
  1628. print if ~/begin/ .. ~/end/;
  1629. print if ~/begin/ ... ~/end/;
  1630. #-----------------------------
  1631. while gets
  1632. $in_header = $. == 1 .. ~/^$/ ? true : false
  1633. $in_body = ~/^$/ .. ARGF.eof ? true : false
  1634. end
  1635. #-----------------------------
  1636. seen = {}
  1637. ARGF.each do |line|
  1638. next unless line =~ /^From:?\s/i .. line =~ /^$/;
  1639. line.scan(%r/([^<>(),;\s]+\@[^<>(),;\s]+)/).each do |addr|
  1640. puts addr unless seen[addr]
  1641. seen[addr] ||= 1
  1642. end
  1643. end
  1644. # @@PLEAC@@_6.9
  1645. def glob2pat(globstr)
  1646. patmap = {
  1647. '*' => '.*',
  1648. '?' => '.',
  1649. '[' => '[',
  1650. ']' => ']',
  1651. }
  1652. globstr.gsub!(/(.)/) { |c| patmap[c] || Regexp::escape(c) }
  1653. '^' + globstr + '$'
  1654. end
  1655. # @@PLEAC@@_6.10
  1656. # avoid interpolating patterns like this if the pattern
  1657. # isn't going to change:
  1658. pattern = ARGV.shift
  1659. ARGF.each do |line|
  1660. print line if line =~ /#{pattern}/
  1661. end
  1662. # the above creates a new regex each iteration. Instead,
  1663. # use the /o modifier so the regex is compiled only once
  1664. pattern = ARGV.shift
  1665. ARGF.each do |line|
  1666. print line if line =~ /#{pattern}/o
  1667. end
  1668. #-----------------------------
  1669. #!/usr/bin/ruby
  1670. # popgrep1 - grep for abbreviations of places that say "pop"
  1671. # version 1: slow but obvious way
  1672. popstates = %w(CO ON MI WI MN)
  1673. ARGF.each do |line|
  1674. popstates.each do |state|
  1675. if line =~ /\b#{state}\b/
  1676. print line
  1677. last
  1678. end
  1679. end
  1680. end
  1681. #-----------------------------
  1682. #!/usr/bin/ruby
  1683. # popgrep2 - grep for abbreviations of places that say "pop"
  1684. # version 2: eval strings; fast but hard to quote
  1685. popstates = %w(CO ON MI WI MN)
  1686. code = "ARGF.each do |line|\n"
  1687. popstates.each do |state|
  1688. code += "\tif line =~ /\\b#{state}\\b/; print(line); next; end\n"
  1689. end
  1690. code += "end\n"
  1691. print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
  1692. eval code
  1693. # CODE IS
  1694. # ---
  1695. # ARGF.each do |line|
  1696. # if line =~ /\bCO\b/; print(line); next; end
  1697. # if line =~ /\bON\b/; print(line); next; end
  1698. # if line =~ /\bMI\b/; print(line); next; end
  1699. # if line =~ /\bWI\b/; print(line); next; end
  1700. # if line =~ /\bMN\b/; print(line); next; end
  1701. # end
  1702. #
  1703. # ---
  1704. ## alternatively, the same idea as above but compiling
  1705. ## to a case statement: (not in perlcookbook)
  1706. #!/usr/bin/ruby -w
  1707. # popgrep2.5 - grep for abbreviations of places that say "pop"
  1708. # version 2.5: eval strings; fast but hard to quote
  1709. popstates = %w(CO ON MI WI MN)
  1710. code = "ARGF.each do |line|\n case line\n"
  1711. popstates.each do |state|
  1712. code += " when /\\b#{state}\\b/ : print line\n"
  1713. end
  1714. code += " end\nend\n"
  1715. print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
  1716. eval code
  1717. # CODE IS
  1718. # ---
  1719. # ARGF.each do |line|
  1720. # case line
  1721. # when /\bCO\b/ : print line
  1722. # when /\bON\b/ : print line
  1723. # when /\bMI\b/ : print line
  1724. # when /\bWI\b/ : print line
  1725. # when /\bMN\b/ : print line
  1726. # end
  1727. # end
  1728. #
  1729. # ---
  1730. # Note: (above) Ruby 1.8+ allows the 'when EXP : EXPR' on one line
  1731. # with the colon separator.
  1732. #-----------------------------
  1733. #!/usr/bin/ruby
  1734. # popgrep3 - grep for abbreviations of places that say "pop"
  1735. # version3: build a match_any function
  1736. popstates = %w(CO ON MI WI MN)
  1737. expr = popstates.map{|e|"line =~ /\\b#{e}\\b/"}.join('||')
  1738. eval "def match_any(line); #{expr};end"
  1739. ARGF.each do |line|
  1740. print line if match_any(line)
  1741. end
  1742. #-----------------------------
  1743. ## building a match_all function is a trivial
  1744. ## substitution of && for ||
  1745. ## here is a generalized example:
  1746. #!/usr/bin/ruby -w
  1747. ## grepauth - print lines that mention both foo and bar
  1748. class MultiMatch
  1749. def initialize(*patterns)
  1750. _any = build_match('||',patterns)
  1751. _all = build_match('&&',patterns)
  1752. eval "def match_any(line);#{_any};end\n"
  1753. eval "def match_all(line);#{_all};end\n"
  1754. end
  1755. def build_match(sym,args)
  1756. args.map{|e|"line =~ /#{e}/"}.join(sym)
  1757. end
  1758. end
  1759. mm = MultiMatch.new('foo','bar')
  1760. ARGF.each do |line|
  1761. print line if mm.match_all(line)
  1762. end
  1763. #-----------------------------
  1764. #!/usr/bin/ruby
  1765. # popgrep4 - grep for abbreviations of places that say "pop"
  1766. # version4: pretty fast, but simple: compile all re's first:
  1767. popstates = %w(CO ON MI WI MN)
  1768. popstates = popstates.map{|re| %r/\b#{re}\b/}
  1769. ARGF.each do |line|
  1770. popstates.each do |state_re|
  1771. if line =~ state_re
  1772. print line
  1773. break
  1774. end
  1775. end
  1776. end
  1777. ## speeds trials on the jargon file(412): 26006 lines, 1.3MB
  1778. ## popgrep1 => 7.040s
  1779. ## popgrep2 => 0.656s
  1780. ## popgrep2.5 => 0.633s
  1781. ## popgrep3 => 0.675s
  1782. ## popgrep4 => 1.027s
  1783. # unless speed is criticial, the technique in popgrep4 is a
  1784. # reasonable balance between speed and logical simplicity.
  1785. # @@PLEAC@@_6.11
  1786. begin
  1787. print "Pattern? "
  1788. pat = $stdin.gets.chomp
  1789. Regexp.new(pat)
  1790. rescue
  1791. warn "Invalid Pattern"
  1792. retry
  1793. end
  1794. # @@PLEAC@@_6.13
  1795. # uses the 'amatch' extension found on:
  1796. # http://raa.ruby-lang.org/project/amatch/
  1797. require 'amatch'
  1798. matcher = Amatch.new('balast')
  1799. #$relative, $distance = 0, 1
  1800. File.open('/usr/share/dict/words').each_line do |line|
  1801. print line if matcher.search(line) <= 1
  1802. end
  1803. __END__
  1804. #CODE
  1805. ballast
  1806. ballasts
  1807. balustrade
  1808. balustrades
  1809. blast
  1810. blasted
  1811. blaster
  1812. blasters
  1813. blasting
  1814. blasts
  1815. # @@PLEAC@@_6.14
  1816. str.scan(/\G(\d)/).each do |token|
  1817. puts "found #{token}"
  1818. end
  1819. #-----------------------------
  1820. n = " 49 here"
  1821. n.gsub!(/\G /,'0')
  1822. puts n
  1823. #-----------------------------
  1824. str = "3,4,5,9,120"
  1825. str.scan(/\G,?(\d+)/).each do |num|
  1826. puts "Found number: #{num}"
  1827. end
  1828. #-----------------------------
  1829. # Ruby doesn't have the String.pos or a /c re modifier like Perl
  1830. # But it does have StringScanner in the standard library (strscn)
  1831. # which allows similar functionality:
  1832. require 'strscan'
  1833. text = 'the year 1752 lost 10 days on the 3rd of September'
  1834. sc = StringScanner.new(text)
  1835. while sc.scan(/.*?(\d+)/)
  1836. print "found: #{sc[1]}\n"
  1837. end
  1838. if sc.scan(/\S+/)
  1839. puts "Found #{sc[0]} after last number"
  1840. end
  1841. #-----------------------------
  1842. # assuming continuing from above:
  1843. puts "The position in 'text' is: #{sc.pos}"
  1844. sc.pos = 30
  1845. puts "The position in 'text' is: #{sc.pos}"
  1846. # @@PLEAC@@_6.15
  1847. #-----------------------------
  1848. # greedy pattern
  1849. str.gsub!(/<.*>/m,'') # not good
  1850. # non-greedy (minimal) pattern
  1851. str.gsub!(/<.*?>/m,'') # not great
  1852. #-----------------------------
  1853. #<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b>
  1854. #-----------------------------
  1855. %r{ <b><i>(.*?)</i></b> }mx
  1856. #-----------------------------
  1857. %r/BEGIN((?:(?!BEGIN).)*)END/
  1858. #-----------------------------
  1859. %r{ <b><i>( (?: (?!</b>|</i>). )* ) </i></b> }mx
  1860. #-----------------------------
  1861. %r{ <b><i>( (?: (?!</[ib]>). )* ) </i></b> }mx
  1862. #-----------------------------
  1863. %r{
  1864. <b><i>
  1865. [^<]* # stuff not possibly bad, and not possibly the end.
  1866. (?:
  1867. # at this point, we can have '<' if not part of something bad
  1868. (?! </?[ib]> ) # what we can't have
  1869. < # okay, so match the '<'
  1870. [^<]* # and continue with more safe stuff
  1871. ) *
  1872. </i></b>
  1873. }mx
  1874. # @@PLEAC@@_6.16
  1875. #-----------------------------
  1876. $/ = ""
  1877. ARGF.each do |para|
  1878. para.scan %r/
  1879. \b # start at word boundary
  1880. (\S+) # find chunk of non-whitespace
  1881. \b # until a word boundary
  1882. (
  1883. \s+ # followed by whitespace
  1884. \1 # and that same chunk again
  1885. \b # and a word boundary
  1886. ) + # one or more times
  1887. /xi do
  1888. puts "dup word '#{$1}' at paragraph #{$.}"
  1889. end
  1890. end
  1891. #-----------------------------
  1892. astr = 'nobody'
  1893. bstr = 'bodysnatcher'
  1894. if "#{astr} #{bstr}" =~ /^(\w+)(\w+) \2(\w+)$/
  1895. print "#{$2} overlaps in #{$1}-#{$2}-#{$3}"
  1896. end
  1897. #-----------------------------
  1898. #!/usr/bin/ruby -w
  1899. # prime_pattern -- find prime factors of argument using patterns
  1900. ARGV << 180
  1901. cap = 'o' * ARGV.shift
  1902. while cap =~ /^(oo+?)\1+$/
  1903. print $1.size, " "
  1904. cap.gsub!(/#{$1}/,'o')
  1905. end
  1906. puts cap.size
  1907. #-----------------------------
  1908. #diophantine
  1909. # solve for 12x + 15y + 16z = 281, maximizing x
  1910. if ('o' * 281).match(/^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/)
  1911. x, y, z = $1.size, $2.size, $3.size
  1912. puts "One solution is: x=#{x}; y=#{y}; z=#{z}"
  1913. else
  1914. puts "No solution."
  1915. end
  1916. # => One solution is: x=17; y=3; z=2
  1917. #-----------------------------
  1918. # using different quantifiers:
  1919. ('o' * 281).match(/^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/)
  1920. # => One solution is: x=17; y=3; z=2
  1921. ('o' * 281).match(/^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/)
  1922. # => One solution is: x=0; y=7; z=11
  1923. ('o' * 281).match(/^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/)
  1924. # => One solution is: x=1; y=3; z=14
  1925. # @@PLEAC@@_6.17
  1926. # alpha OR beta
  1927. %r/alpha|beta/
  1928. # alpha AND beta
  1929. %r/(?=.*alpha)(?=.*beta)/m
  1930. # alpha AND beta, no overlap
  1931. %r/alpha.*beta|beta.*alpha/m
  1932. # NOT beta
  1933. %r/^(?:(?!beta).)*$/m
  1934. # NOT bad BUT good
  1935. %r/(?=(?:(?!BAD).)*$)GOOD/m
  1936. #-----------------------------
  1937. if !(string =~ /pattern/) # ugly
  1938. something()
  1939. end
  1940. if string !~ /pattern/ # preferred
  1941. something()
  1942. end
  1943. #-----------------------------
  1944. if string =~ /pat1/ && string =~ /pat2/
  1945. something()
  1946. end
  1947. #-----------------------------
  1948. if string =~ /pat1/ || string =~ /pat2/
  1949. something()
  1950. end
  1951. #-----------------------------
  1952. #!/usr/bin/ruby -w
  1953. # minigrep - trivial grep
  1954. pat = ARGV.shift
  1955. ARGF.each do |line|
  1956. print line if line =~ /#{pat}/o
  1957. end
  1958. #-----------------------------
  1959. "labelled" =~ /^(?=.*bell)(?=.*lab)/m
  1960. #-----------------------------
  1961. $string =~ /bell/ && $string =~ /lab/
  1962. #-----------------------------
  1963. $murray_hill = "blah bell blah "
  1964. if $murray_hill =~ %r{
  1965. ^ # start of string
  1966. (?= # zero-width lookahead
  1967. .* # any amount of intervening stuff
  1968. bell # the desired bell string
  1969. ) # rewind, since we were only looking
  1970. (?= # and do the same thing
  1971. .* # any amount of intervening stuff
  1972. lab # and the lab part
  1973. )
  1974. }mx # /m means . can match newline
  1975. print "Looks like Bell Labs might be in Murray Hill!\n";
  1976. end
  1977. #-----------------------------
  1978. "labelled" =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/
  1979. #-----------------------------
  1980. $brand = "labelled";
  1981. if $brand =~ %r{
  1982. (?: # non-capturing grouper
  1983. ^ .*? # any amount of stuff at the front
  1984. bell # look for a bell
  1985. .*? # followed by any amount of anything
  1986. lab # look for a lab
  1987. ) # end grouper
  1988. | # otherwise, try the other direction
  1989. (?: # non-capturing grouper
  1990. ^ .*? # any amount of stuff at the front
  1991. lab # look for a lab
  1992. .*? # followed by any amount of anything
  1993. bell # followed by a bell
  1994. ) # end grouper
  1995. }mx # /m means . can match newline
  1996. print "Our brand has bell and lab separate.\n";
  1997. end
  1998. #-----------------------------
  1999. $map =~ /^(?:(?!waldo).)*$/s
  2000. #-----------------------------
  2001. $map = "the great baldo"
  2002. if $map =~ %r{
  2003. ^ # start of string
  2004. (?: # non-capturing grouper
  2005. (?! # look ahead negation
  2006. waldo # is he ahead of us now?
  2007. ) # is so, the negation failed
  2008. . # any character (cuzza /s)
  2009. ) * # repeat that grouping 0 or more
  2010. $ # through the end of the string
  2011. }mx # /m means . can match newline
  2012. print "There's no waldo here!\n";
  2013. end
  2014. =begin
  2015. 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04
  2016. USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
  2017. tchrist tty1 5:16pm 36days 24:43 0.03s xinit
  2018. tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh
  2019. tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh
  2020. gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh
  2021. =end
  2022. #% w | minigrep '^(?!.*ttyp).*tchrist'
  2023. #-----------------------------
  2024. %r{
  2025. ^ # anchored to the start
  2026. (?! # zero-width look-ahead assertion
  2027. .* # any amount of anything (faster than .*?)
  2028. ttyp # the string you don't want to find
  2029. ) # end look-ahead negation; rewind to start
  2030. .* # any amount of anything (faster than .*?)
  2031. tchrist # now try to find Tom
  2032. }x
  2033. #-----------------------------
  2034. #% w | grep tchrist | grep -v ttyp
  2035. #-----------------------------
  2036. #% grep -i 'pattern' files
  2037. #% minigrep '(?i)pattern' files
  2038. #-----------------------------
  2039. # @@PLEAC@@_6.20
  2040. ans = $stdin.gets.chomp
  2041. re = %r/^#{Regexp.quote(ans)}/
  2042. case
  2043. when "SEND" =~ re : puts "Action is send"
  2044. when "STOP" =~ re : puts "Action is stop"
  2045. when "ABORT" =~ re : puts "Action is abort"
  2046. when "EDIT" =~ re : puts "Action is edit"
  2047. end
  2048. #-----------------------------
  2049. require 'abbrev'
  2050. table = Abbrev.abbrev %w-send stop abort edit-
  2051. loop do
  2052. print "Action: "
  2053. ans = $stdin.gets.chomp
  2054. puts "Action for #{ans} is #{table[ans.downcase]}"
  2055. end
  2056. #-----------------------------
  2057. # dummy values are defined for 'file', 'PAGER', and
  2058. # the 'invoke_editor' and 'deliver_message' methods
  2059. # do not do anything interesting in this example.
  2060. #!/usr/bin/ruby -w
  2061. require 'abbrev'
  2062. file = 'pleac_ruby.data'
  2063. PAGER = 'less'
  2064. def invoke_editor
  2065. puts "invoking editor"
  2066. end
  2067. def deliver_message
  2068. puts "delivering message"
  2069. end
  2070. actions = {
  2071. 'edit' => self.method(:invoke_editor),
  2072. 'send' => self.method(:deliver_message),
  2073. 'list' => proc {system(PAGER, file)},
  2074. 'abort' => proc {puts "See ya!"; exit},
  2075. "" => proc {puts "Unknown Command"}
  2076. }
  2077. dtable = Abbrev.abbrev(actions.keys)
  2078. loop do
  2079. print "Action: "
  2080. ans = $stdin.gets.chomp.delete(" \t")
  2081. actions[ dtable[ans.downcase] || "" ].call
  2082. end
  2083. # @@PLEAC@@_6.19
  2084. #-----------------------------
  2085. # basically, the Perl Cookbook categorizes this as an
  2086. # unsolvable problem ...
  2087. #-----------------------------
  2088. 1 while addr.gsub!(/\([^()]*\)/,'')
  2089. #-----------------------------
  2090. Dear someuser@host.com,
  2091. Please confirm the mail address you gave us Wed May 6 09:38:41
  2092. MDT 1998 by replying to this message. Include the string
  2093. "Rumpelstiltskin" in that reply, but spelled in reverse; that is,
  2094. start with "Nik...". Once this is done, your confirmed address will
  2095. be entered into our records.
  2096. # @@PLEAC@@_6.21
  2097. #-----------------------------
  2098. #% gunzip -c ~/mail/archive.gz | urlify > archive.urlified
  2099. #-----------------------------
  2100. #% urlify ~/mail/*.inbox > ~/allmail.urlified
  2101. #-----------------------------
  2102. #!/usr/bin/ruby -w
  2103. # urlify - wrap HTML links around URL-like constructs
  2104. urls = '(https?|telnet|gopher|file|wais|ftp)';
  2105. ltrs = '\w';
  2106. gunk = '/#~:.?+=&%@!\-';
  2107. punc = '.:?\-';
  2108. any = "#{ltrs}#{gunk}#{punc}";
  2109. ARGF.each do |line|
  2110. line.gsub! %r/
  2111. \b # start at word boundary
  2112. ( # begin $1 {
  2113. #{urls} : # need resource and a colon
  2114. [#{any}] +? # followed by on or more
  2115. # of any valid character, but
  2116. # be conservative and take only
  2117. # what you need to....
  2118. ) # end $1 }
  2119. (?= # look-ahead non-consumptive assertion
  2120. [#{punc}]* # either 0 or more punctuation
  2121. [^#{any}] # followed by a non-url char
  2122. | # or else
  2123. $ # then end of the string
  2124. )
  2125. /iox do
  2126. %Q|<A HREF="#{$1}">#{$1}</A>|
  2127. end
  2128. print line
  2129. end
  2130. # @@PLEAC@@_6.23
  2131. %r/^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/i
  2132. #-----------------------------
  2133. str.sub!(/(\S+)(\s+)(\S+)/, '\3\2\1')
  2134. #-----------------------------
  2135. %r/(\w+)\s*=\s*(.*)\s*$/ # keyword is $1, value is $2
  2136. #-----------------------------
  2137. %r/.{80,}/
  2138. #-----------------------------
  2139. %r|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)|
  2140. #-----------------------------
  2141. str.gsub!(%r|/usr/bin|,'/usr/local/bin')
  2142. #-----------------------------
  2143. str.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/){ $1.hex.chr }
  2144. #-----------------------------
  2145. str.gsub!(%r{
  2146. /\* # Match the opening delimiter
  2147. .*? # Match a minimal number of characters
  2148. \*/ # Match the closing delimiter
  2149. }xm,'')
  2150. #-----------------------------
  2151. str.sub!(/^\s+/, '')
  2152. str.sub!(/\s+$/, '')
  2153. # but really, in Ruby we'd just do:
  2154. str.strip!
  2155. #-----------------------------
  2156. str.gsub!(/\\n/,"\n")
  2157. #-----------------------------
  2158. str.sub!(/^.*::/, '')
  2159. #-----------------------------
  2160. %r/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\.
  2161. ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/x
  2162. #-----------------------------
  2163. str.sub!(%r|^.*/|, '')
  2164. #-----------------------------
  2165. cols = ( (ENV['TERMCAP'] || " ") =~ /:co#(\d+):/ ) ? $1 : 80;
  2166. #-----------------------------
  2167. name = " #{$0} #{ARGV}".gsub(%r| /\S+/|, ' ')
  2168. #-----------------------------
  2169. require 'rbconfig'
  2170. include Config
  2171. raise "This isn't Linux" unless CONFIG['target_os'] =~ /linux/i;
  2172. #-----------------------------
  2173. str.gsub!(%r/\n\s+/, ' ')
  2174. #-----------------------------
  2175. nums = str.scan(/(\d+\.?\d*|\.\d+)/)
  2176. #-----------------------------
  2177. capwords = str.scan(%r/(\b[^\Wa-z0-9_]+\b)/)
  2178. #-----------------------------
  2179. lowords = str.scan(%r/(\b[^\WA-Z0-9_]+\b)/)
  2180. #-----------------------------
  2181. icwords = str.scan(%r/(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)/)
  2182. #-----------------------------
  2183. links = str.scan(%r/<A[^>]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/mi)
  2184. #-----------------------------
  2185. initial = str =~ /^\S+\s+(\S)\S*\s+\S/ ? $1 : ""
  2186. #-----------------------------
  2187. str.gsub!(%r/"([^"]*)"/, %q-``\1''-)
  2188. #-----------------------------
  2189. $/ = ""
  2190. sentences = []
  2191. ARGF.each do |para|
  2192. para.gsub!(/\n/, ' ')
  2193. para.gsub!(/ {3,}/,' ')
  2194. sentences << para.scan(/(\S.*?[!?.])(?= |\Z)/)
  2195. end
  2196. #-----------------------------
  2197. %r/(\d{4})-(\d\d)-(\d\d)/ # YYYY in $1, MM in $2, DD in $3
  2198. #-----------------------------
  2199. %r/ ^
  2200. (?:
  2201. 1 \s (?: \d\d\d \s)? # 1, or 1 and area code
  2202. | # ... or ...
  2203. \(\d\d\d\) \s # area code with parens
  2204. | # ... or ...
  2205. (?: \+\d\d?\d? \s)? # optional +country code
  2206. \d\d\d ([\s\-]) # and area code
  2207. )
  2208. \d\d\d (\s|\1) # prefix (and area code separator)
  2209. \d\d\d\d # exchange
  2210. $
  2211. /x
  2212. #-----------------------------
  2213. %r/\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/i
  2214. #-----------------------------
  2215. lines = []
  2216. lines << $1 while input.sub!(/^([^\012\015]*)(\012\015?|\015\012?)/,'')
  2217. # @@PLEAC@@_7.0
  2218. # An IO object being Enumerable, we can use 'each' directly on it
  2219. File.open("/usr/local/widgets/data").each { |line|
  2220. puts line if line =~ /blue/
  2221. }
  2222. logfile = File.new("/var/log/rubylog.txt", "w")
  2223. mysub($stdin, logfile)
  2224. # The method IO#readline is similar to IO#gets
  2225. # but throws an exception when it reaches EOF
  2226. f = File.new("bla.txt")
  2227. begin
  2228. while (line = f.readline)
  2229. line.chomp
  2230. $stdout.print line if line =~ /blue/
  2231. end
  2232. rescue EOFError
  2233. f.close
  2234. end
  2235. while $stdin.gets # reads from STDIN
  2236. unless (/\d/)
  2237. $stderr.puts "No digit found." # writes to STDERR
  2238. end
  2239. puts "Read: #{$_}" # writes to STDOUT
  2240. end
  2241. logfile = File.new("/tmp/log", "w")
  2242. logfile.close
  2243. # $defout (or its synonym '$>') is the destination of output
  2244. # for Kernel#print, Kernel#puts, and family functions
  2245. logfile = File.new("log.txt", "w")
  2246. old = $defout
  2247. $defout = logfile # switch to logfile for output
  2248. puts "Countdown initiated ..."
  2249. $defout = old # return to original output
  2250. puts "You have 30 seconds to reach minimum safety distance."
  2251. # @@PLEAC@@_7.1
  2252. source = File.new(path, "r") # open file "path" for reading only
  2253. sink = File.new(path, "w") # open file "path" for writing only
  2254. source = File.open(path, File::RDONLY) # open file "path" for reading only
  2255. sink = File.open(path, File::WRONLY) # open file "path" for writing only
  2256. file = File.open(path, "r+") # open "path" for reading and writing
  2257. file = File.open(path, flags) # open "path" with the flags "flags" (see examples below for flags)
  2258. # open file "path" read only
  2259. file = File.open(path, "r")
  2260. file = File.open(path, File::RDONLY)
  2261. # open file "path" write only, create it if it does not exist
  2262. # truncate it to zero length if it exists
  2263. file = File.open(path, "w")
  2264. file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
  2265. file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT, 0666) # with permission 0666
  2266. # open file "path" write only, fails if file exists
  2267. file = File.open(path, File::WRONLY|File::EXCL|File::CREAT)
  2268. file = File.open(path, File::WRONLY|File::EXCL|File::CREAT, 0666)
  2269. # open file "path" for appending
  2270. file = File.open(path, "a")
  2271. file = File.open(path, File::WRONLY|File::APPEND|File::CREAT)
  2272. file = File.open(path, File::WRONLY|File::APPEND|File::CREAT, 0666)
  2273. # open file "path" for appending only when file exists
  2274. file = File.open(path, File::WRONLY|File::APPEND)
  2275. # open file "path" for reading and writing
  2276. file = File.open(path, "r+")
  2277. file = File.open(path, File::RDWR)
  2278. # open file for reading and writing, create a new file if it does not exist
  2279. file = File.open(path, File::RDWR|File::CREAT)
  2280. file = File.open(path, File::RDWR|File::CREAT, 0600)
  2281. # open file "path" reading and writing, fails if file exists
  2282. file = File.open(path, File::RDWR|File::EXCL|File::CREAT)
  2283. file = File.open(path, File::RDWR|File::EXCL|File::CREAT, 0600)
  2284. # @@PLEAC@@_7.2
  2285. # No problem with Ruby since the filename doesn't contain characters with
  2286. # special meaning; like Perl's sysopen
  2287. File.open(filename, 'r')
  2288. # @@PLEAC@@_7.3
  2289. File.expand_path('~root/tmp')
  2290. #=> "/root/tmp"
  2291. File.expand_path('~rpcuser')
  2292. #=> "/var/lib/nfs"
  2293. # To expand ~/.. it explicitely needs the environment variable HOME
  2294. File.expand_path('~/tmp')
  2295. #=> "/home/gc/tmp"
  2296. # @@PLEAC@@_7.4
  2297. # The exception raised in Ruby reports the filename
  2298. File.open('afile')
  2299. # @@PLEAC@@_7.5
  2300. # Standard Ruby distribution provides the following useful extension
  2301. require 'tempfile'
  2302. # With the Tempfile class, the file is automatically deleted on garbage
  2303. # collection, so you won't need to remove it, later on.
  2304. tf = Tempfile.new('tmp') # a name is required to create the filename
  2305. # If you need to pass the filename to an external program you can use
  2306. # File#path, but don't forget to File#flush in order to flush anything
  2307. # living in some buffer somewhere.
  2308. tf.flush
  2309. system("/usr/bin/dowhatever #{tf.path}")
  2310. fh = Tempfile.new('tmp')
  2311. fh.sync = true # autoflushes
  2312. 10.times { |i| fh.puts i }
  2313. fh.rewind
  2314. puts 'Tmp file has: ', fh.readlines
  2315. # @@PLEAC@@_7.6
  2316. while (DATA.gets) do
  2317. # process the line
  2318. end
  2319. __END__
  2320. # your data goes here
  2321. # __DATA__ doesn't exist in Ruby
  2322. #CODE
  2323. # get info about the script (size, date of last modification)
  2324. kilosize = DATA.stat.size / 1024
  2325. last_modif = DATA.stat.mtime
  2326. puts "<P>Script size is #{kilosize}"
  2327. puts "<P>Last script update: #{last_modif}"
  2328. __END__
  2329. # DO NOT REMOVE THE PRECEEDING LINE.
  2330. # Everything else in this file will be ignored.
  2331. #CODE
  2332. # @@PLEAC@@_7.7
  2333. while line = gets do
  2334. # do something with line.
  2335. end
  2336. # or
  2337. while gets do
  2338. # do something with $_
  2339. end
  2340. # or more rubyish
  2341. $stdin.each do |line|
  2342. # do stuff with line
  2343. end
  2344. # ARGF may makes this more easy
  2345. # this is skipped if ARGV.size==0
  2346. ARGV.each do |filename|
  2347. # closing and exception handling are done by the block
  2348. open(filename) do |fd|
  2349. fd.each do |line|
  2350. # do stuff with line
  2351. end
  2352. end rescue abort("can't open %s" % filename)
  2353. end
  2354. # globbing is done in the Dir module
  2355. ARGV = Dir["*.[Cch]"] if ARGV.empty?
  2356. # note: optparse is the preferred way to handle this
  2357. if (ARGV[0] == '-c')
  2358. chop_first += 1
  2359. ARGV.shift
  2360. end
  2361. # processing numerical options
  2362. if ARGV[0] =~ /^-(\d+)$/
  2363. columns = $1
  2364. ARGV.shift
  2365. end
  2366. # again, better to use optparse:
  2367. require 'optparse'
  2368. nostdout = 0
  2369. append = 0
  2370. unbuffer = 0
  2371. ignore_ints = 0
  2372. ARGV.options do |opt|
  2373. opt.on('-n') { nostdout +=1 }
  2374. opt.on('-a') { append +=1 }
  2375. opt.on('-u') { unbuffer +=1 }
  2376. opt.on('-i') { ignore_ints +=1 }
  2377. opt.parse!
  2378. end or abort("usage: " + __FILE__ + " [-ainu] [filenames]")
  2379. # no need to do undef $/, we have File.read
  2380. str = File.read(ARGV[0])
  2381. # again we have File.read
  2382. str = File.read(ARGV[0])
  2383. # not sure what this should do:
  2384. # I believe open the file, print filename, lineno and line:
  2385. ARGF.each_with_index do |line, idx|
  2386. print ARGF.filename, ":", idx, ";", line
  2387. end
  2388. # print all the lines in every file passed via command line that contains login
  2389. ARGF.each do |line|
  2390. puts line if line =~ /login/
  2391. end
  2392. #
  2393. # even this would fit
  2394. #%ruby -ne "print if /f/" 2.log
  2395. #
  2396. ARGF.each { |l| puts l.downcase! }
  2397. #------------------
  2398. #!/usr/bin/ruby -p
  2399. # just like perl's -p
  2400. $_.downcase!
  2401. #
  2402. # I don't know who should I trust.
  2403. # perl's version splits on \w+ while python's on \w.
  2404. chunks = 0
  2405. File.read(ARGV[0]).split.each do |word|
  2406. next if word =~ /^#/
  2407. break if ["__DATA__", "__END__"].member? word
  2408. chunks += 1
  2409. end
  2410. print "Found ", chunks, " chunks\n"
  2411. # @@PLEAC@@_7.8
  2412. old = File.open(old_file)
  2413. new = File.open(new_file, "w")
  2414. while old.gets do
  2415. # change $_, then...
  2416. new.print $_
  2417. end
  2418. old.close
  2419. new.close
  2420. File.rename(old_file, "old.orig")
  2421. File.rename(new_file, old_file)
  2422. while old.gets do
  2423. if $. == 20 then # we are at the 20th line
  2424. new.puts "Extra line 1"
  2425. new.puts "Extra line 2"
  2426. end
  2427. new.print $_
  2428. end
  2429. while old.gets do
  2430. next if 20..30 # skip the 20th line to the 30th
  2431. # Ruby (and Perl) permit to write if 20..30
  2432. # instead of if (20 <= $.) and ($. <= 30)
  2433. new.print $_
  2434. end
  2435. # @@PLEAC@@_7.9
  2436. #% ruby -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ...
  2437. #
  2438. #-----------------------------
  2439. ##!/usr/bin/ruby -i.orig -p
  2440. # filter commands go here
  2441. #-----------------------------
  2442. #% ruby -pi.orig -e 'gsub!(/DATE/){Time.now)'
  2443. # effectively becomes:
  2444. ARGV << 'I'
  2445. oldfile = ""
  2446. while gets
  2447. if ARGF.filename != oldfile
  2448. newfile = ARGF.filename
  2449. File.rename(newfile, newfile + ".orig")
  2450. $stdout = File.open(newfile,'w')
  2451. oldfile = newfile
  2452. end
  2453. gsub!(/DATE/){Time.now}
  2454. print
  2455. end
  2456. $stdout = STDOUT
  2457. #-----------------------------
  2458. #% ruby -i.old -pe 'gsub!(%r{\bhisvar\b}, 'hervar')' *.[Cchy]
  2459. #-----------------------------
  2460. # set up to iterate over the *.c files in the current directory,
  2461. # editing in place and saving the old file with a .orig extension
  2462. $-i = '.orig' # set up -i mode
  2463. ARGV.replace(Dir['*.[Cchy]'])
  2464. while gets
  2465. if $. == 1
  2466. print "This line should appear at the top of each file\n"
  2467. end
  2468. gsub!(/\b(p)earl\b/i, '\1erl') # Correct typos, preserving case
  2469. print
  2470. ARGF.close if ARGF.eof
  2471. end
  2472. # @@PLEAC@@_7.10
  2473. File.open('itest', 'r+') do |f| # open file for update
  2474. lines = f.readlines # read into array of lines
  2475. lines.each do |it| # modify lines
  2476. it.gsub!(/foo/, 'QQQ')
  2477. end
  2478. f.pos = 0 # back to start
  2479. f.print lines # write out modified lines
  2480. f.truncate(f.pos) # truncate to new length
  2481. end # file is automatically closed
  2482. #-----------------------------
  2483. File.open('itest', 'r+') do |f|
  2484. out = ""
  2485. f.each do |line|
  2486. out << line.gsub(/DATE/) {Time.now}
  2487. end
  2488. f.pos = 0
  2489. f.print out
  2490. f.truncate(f.pos)
  2491. end
  2492. # @@PLEAC@@_7.11
  2493. File.open('infile', 'r+') do |f|
  2494. f.flock File::LOCK_EX
  2495. # update file
  2496. end
  2497. #-----------------------------
  2498. File::LOCK_SH # shared lock (for reading)
  2499. File::LOCK_EX # exclusive lock (for writing)
  2500. File::LOCK_NB # non-blocking request
  2501. File::LOCK_UN # free lock
  2502. #-----------------------------
  2503. unless f.flock File::LOCK_EX | File::LOCK_NB
  2504. warn "can't get immediate lock: blocking ..."
  2505. f.flock File::LOCK_EX
  2506. end
  2507. #-----------------------------
  2508. File.open('numfile', File::RDWR|File::CREAT) do |f|
  2509. f.flock(File::LOCK_EX)
  2510. num = f.gets.to_i || 0
  2511. f.pos = 0
  2512. f.truncate 0
  2513. f.puts num + 1q
  2514. end
  2515. # @@PLEAC@@_7.12
  2516. output_handle.sync = true
  2517. # Please note that like in Perl, $stderr is already unbuffered
  2518. #-----------------------------
  2519. #!/usr/bin/ruby -w
  2520. # seeme - demo stdio output buffering
  2521. $stdout.sync = ARGV.size > 0
  2522. print "Now you don't see it..."
  2523. sleep 2
  2524. puts "now you do"
  2525. #-----------------------------
  2526. $stderr.sync = true
  2527. afile.sync = false
  2528. #-----------------------------
  2529. # assume 'remote_con' is an interactive socket handle,
  2530. # but 'disk_file' is a handle to a regular file.
  2531. remote_con.sync = true # unbuffer for clarity
  2532. disk_file.sync = false # buffered for speed
  2533. #-----------------------------
  2534. require 'socket'
  2535. sock = TCPSocket.new('www.ruby-lang.org', 80)
  2536. sock.sync = true
  2537. sock.puts "GET /en/ HTTP/1.0 \n\n"
  2538. resp = sock.read
  2539. print "DOC IS: #{resp}\n"
  2540. # @@PLEAC@@_7.13
  2541. #-----------------------------
  2542. # assumes fh1, fh2, fh2 are oen IO objects
  2543. nfound = select([$stdin, fh1, fh2, fh3], nil, nil, 0)
  2544. nfound[0].each do |file|
  2545. case file
  2546. when fh1
  2547. # do something with fh1
  2548. when fh2
  2549. # do something with fh2
  2550. when fh3
  2551. # do something with fh3
  2552. end
  2553. end
  2554. #-----------------------------
  2555. input_files = []
  2556. # repeat next line for all in-files to poll
  2557. input_files << fh1
  2558. if nfound = select(input_files, nil, nil, 0)
  2559. # input ready on files in nfound[0]
  2560. end
  2561. # @@PLEAC@@_8.0
  2562. #-----------------------------
  2563. # datafile is a file or IO object
  2564. datafile.readlines.each { |line|
  2565. line.chomp!
  2566. size = line.length
  2567. puts size
  2568. }
  2569. #-----------------------------
  2570. datafile.readlines.each { |line|
  2571. puts line.chomp!.length
  2572. }
  2573. #-----------------------------
  2574. lines = datafile.readlines
  2575. #-----------------------------
  2576. whole_file = file.read
  2577. #-----------------------------
  2578. # ruby -040 -e 'word = gets; puts "First word is #{word}"'
  2579. #-----------------------------
  2580. # ruby -ne 'BEGIN { $/="%%\n" }; $_.chomp; puts $_ if( $_=~/Unix/i)' fortune.dat
  2581. #-----------------------------
  2582. handle.print "one", "two", "three" # "onetwothree"
  2583. puts "Baa baa black sheep." # sent to $stdout
  2584. #-----------------------------
  2585. buffer = handle.read(4096)
  2586. rv = buffer.length
  2587. #-----------------------------
  2588. handle.truncate(length)
  2589. open("/tmp#{$$}.pid", 'w') { |handle| handle.truncate(length) }
  2590. #-----------------------------
  2591. pos = datafile.pos # tell is an alias of pos
  2592. puts "I'm #{pos} bytes from the start of datafile"
  2593. #-----------------------------
  2594. logfile.seek(0, IO::SEEK_END)
  2595. datafile.seek(pos) # IO::SEEK_SET is the default
  2596. out.seek(-20, IO::SEEK_CUR)
  2597. #-----------------------------
  2598. written = datafile.syswrite(mystring)
  2599. raise RunTimeError unless written == mystring.length
  2600. block = infile.sysread(256) # no equivalent to perl offset parameter in sysread
  2601. puts "only read #{block.length} bytes" if 256 != block.length
  2602. #-----------------------------
  2603. pos = handle.sysseek(0, IO::SEEK_CUR) # don't change position
  2604. # @@PLEAC@@_8.1
  2605. while (line = fh.gets)
  2606. line.chomp!
  2607. nextline = nil
  2608. line.gsub!(/\\$/) { |match| nextline = fh.gets; '' }
  2609. if (nextline != nil)
  2610. line += nextline
  2611. redo
  2612. end
  2613. # process full record in line here
  2614. end
  2615. #-----------------------------
  2616. # DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
  2617. # $(TEXINFOS) $(INFOS) $(MANS) $(DATA)
  2618. # DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
  2619. # $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \
  2620. # $(EXTRA_DIST)
  2621. #-----------------------------
  2622. line.gsub!(/\\\s*$/, '') {
  2623. # as before
  2624. }
  2625. # @@PLEAC@@_8.2
  2626. #-----------------------------
  2627. count = `wc -l < #{filename}`
  2628. fail "wc failed: #{$?}" if $? != 0
  2629. count.chomp!
  2630. #-----------------------------
  2631. count = 0
  2632. File.open(file, 'r') { |fh|
  2633. count += 1 while fh.gets
  2634. }
  2635. # count now holds the number of lines read
  2636. #-----------------------------
  2637. count = 0
  2638. while (chunk = file.sysread(2**16))
  2639. count += chunk.count("\n")
  2640. end rescue EOFError
  2641. #-----------------------------
  2642. File.open(filename,'r') { |fh|
  2643. count += 1 while fh.gets
  2644. }
  2645. # count now holds the number of lines read
  2646. #-----------------------------
  2647. # As ruby doesn't quite have an equivalent to using a for
  2648. # statement as in perl, I threw this in
  2649. count = File.readlines(filename).size
  2650. #-----------------------------
  2651. 1 while file.gets
  2652. count = $.
  2653. #-----------------------------
  2654. $/ = ''
  2655. open(filename, 'r') { |fh|
  2656. 1 while fh.gets
  2657. para_count = $.
  2658. } rescue fail("can't open #{filename}: $!")
  2659. #-----------------------------
  2660. # ^^PLEAC^^_8.3
  2661. #-----------------------------
  2662. while (gets)
  2663. split.each { |chunk|
  2664. # do something with chunk
  2665. }
  2666. end
  2667. #-----------------------------
  2668. while (gets)
  2669. gsub(/(\w[\w'-]*)/) { |word|
  2670. # do something with word
  2671. }
  2672. end
  2673. #-----------------------------
  2674. # Make a word frequency count
  2675. # normally hashes can be created using {} or just Hash.new
  2676. # but we want the default value of an entry to be 0 instead
  2677. # of nil. (nil can't be incremented)
  2678. seen = Hash.new(0)
  2679. while (gets)
  2680. gsub(/(\w[\w'-]*)/) { |word|
  2681. seen[word.downcase] += 1
  2682. }
  2683. end
  2684. # output hash in a descending numeric sort of its values
  2685. seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
  2686. printf("%5d %s\n", v, k )
  2687. end
  2688. #-----------------------------
  2689. # Line frequency count
  2690. seen = Hash.new(0)
  2691. while (gets)
  2692. seen[$_.downcase] += 1
  2693. end
  2694. seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
  2695. printf("%5d %s\n", v, k )
  2696. end
  2697. #-----------------------------
  2698. # @@PLEAC@@_8.4
  2699. #-----------------------------
  2700. # instead of file handle FILE, we can just
  2701. # use a string containing the filename
  2702. File.readlines(file).each { |line|
  2703. # do something with line
  2704. }
  2705. #-----------------------------
  2706. File.readlines(file).reverse_each { |line|
  2707. # do something with line
  2708. }
  2709. #-----------------------------
  2710. # the variable lines might have been created
  2711. # this way
  2712. # lines = File.readlines(file)
  2713. #
  2714. # normally one would use the reverse_each, but
  2715. # if you insist on using a numerical index to
  2716. # iterate over the lines array...
  2717. (lines.size - 1).downto(0) { |i|
  2718. line = lines[i]
  2719. }
  2720. #-----------------------------
  2721. # the second readlines argument is a the
  2722. # record separator $/, just like perl, a blank
  2723. # separator splits the records into paragraphs
  2724. File.readlines(file, '').each { |paragraph|
  2725. # do something with paragraph
  2726. puts "->Paragraph #{paragraph}"
  2727. }
  2728. #-----------------------------
  2729. # @@PLEAC@@_8.6
  2730. $/ = "%\n";
  2731. srand;
  2732. File.open('/usr/share/fortune/humorists').each do |line|
  2733. adage = line if rand($.) < 1
  2734. end
  2735. puts adage;
  2736. # @@PLEAC@@_8.10
  2737. begin
  2738. fh = File.open(file, "r+")
  2739. addr = fh.tell unless fh.eof while fh.gets
  2740. fh.truncate(addr)
  2741. rescue SystemCallError
  2742. $stderr.puts "#$!"
  2743. end
  2744. # @@PLEAC@@_9.0
  2745. entry = File.stat("/usr/bin/vi")
  2746. entry = File.stat("/usr/bin")
  2747. entry = File.stat(INFILE)
  2748. entry = File.stat("/usr/bin/vi")
  2749. ctime = entry.ctime
  2750. size = entry.size
  2751. f = File.open(filename, "r")
  2752. ## There is no -T equivalent in Ruby, but we can still test emptiness
  2753. if test(?s, filename)
  2754. puts "#{filename} doesn't have text in it."
  2755. exit
  2756. end
  2757. Dir.new("/usr/bin").each do |filename|
  2758. puts "Inside /usr/bin is something called #{filename}"
  2759. end
  2760. # @@PLEAC@@_9.1
  2761. file = File.stat("filename")
  2762. readtime, writetime = file.atime, file.mtime
  2763. file.utime(readtime, writetime)
  2764. SECONDS_PER_DAY = 60 * 60 * 24
  2765. file = File.stat("filename")
  2766. atime, mtime = file.atime, file.mtime
  2767. atime -= 7 * SECONDS_PER_DAY
  2768. mtime -= 7 * SECONDS_PER_DAY
  2769. File.utime(atime, mtime, file)
  2770. mtime = File.stat(file).mtime
  2771. File.utime(Time.new, mtime, file)
  2772. File.utime(Time.new, File.stat("testfile").mtime, file)
  2773. #-----------------------------
  2774. #!/usr/bin/ruby -w
  2775. ## uvi - vi a file without changing it's access times
  2776. if ARGV.length != 1
  2777. puts "usage: uvi filename"
  2778. exit
  2779. end
  2780. file = ARGV[0]
  2781. atime, mtime = File.stat(file).atime, File.stat(file).mtime
  2782. system(ENV["EDITOR"] || "vi", file)
  2783. File.utime(atime, mtime, file)
  2784. #-----------------------------
  2785. # @@PLEAC@@_9.2
  2786. File.unlink(FILENAME)
  2787. err_flg = false
  2788. filenames.each do |file|
  2789. begin
  2790. File.unlink(file)
  2791. rescue
  2792. err_flg = $!
  2793. end
  2794. end
  2795. err_flg and raise "Couldn't unlink all of #{filenames.join(" ")}: #{err_flg}"
  2796. File.unlink(file)
  2797. count = filenames.length
  2798. filenames.each do |file|
  2799. begin
  2800. File.unlink(file)
  2801. rescue
  2802. count -= 1
  2803. end
  2804. end
  2805. if count != filenames.length
  2806. STDERR.puts "could only delete #{count} of #{filenames.length} files"
  2807. end
  2808. # @@PLEAC@@_9.3
  2809. require "ftools"
  2810. File.copy(oldfile, newfile)
  2811. infile = File.open(oldfile, "r")
  2812. outfile = File.open(newfile, "w")
  2813. blksize = infile.stat.blksize
  2814. # This doesn't handle partial writes or ^Z
  2815. # like the Perl version does.
  2816. while (line = infile.read(blksize))
  2817. outfile.write(line)
  2818. end
  2819. infile.close
  2820. outfile.close
  2821. system("cp #{oldfile} #{newfile}") # unix
  2822. system("copy #{oldfile} #{newfile}") # dos, vms
  2823. require "ftools"
  2824. File.copy("datafile.dat", "datafile.bak")
  2825. File.move("datafile.new", "datafile.dat")
  2826. # @@PLEAC@@_9.4
  2827. $seen = {} # must use global var to be seen inside of method below
  2828. def do_my_thing(filename)
  2829. dev, ino = File.stat(filename).dev, File.stat(filename).ino
  2830. unless $seen[[dev, ino]]
  2831. # do something with $filename because we haven't
  2832. # seen it before
  2833. end
  2834. $seen[[dev, ino]] = $seen[[dev, ino]].to_i + 1
  2835. end
  2836. files.each do |filename|
  2837. dev, ino = File.stat(filename).dev, File.stat(filename).ino
  2838. if !$seen.has_key?([dev, ino])
  2839. $seen[[dev, ino]] = []
  2840. end
  2841. $seen[[dev, ino]].push(filename)
  2842. end
  2843. $seen.keys.sort.each do |devino|
  2844. ino, dev = devino
  2845. if $seen[devino].length > 1
  2846. # $seen[devino] is a list of filenames for the same file
  2847. end
  2848. end
  2849. # @@PLEAC@@_9.5
  2850. Dir.open(dirname) do |dir|
  2851. dir.each do |file|
  2852. # do something with dirname/file
  2853. puts file
  2854. end
  2855. end
  2856. # Dir.close is automatic
  2857. # No -T equivalent in Ruby
  2858. dir.each do |file|
  2859. next if file =~ /^\.\.?$/
  2860. # ...
  2861. end
  2862. def plainfiles(dir)
  2863. dh = Dir.open(dir)
  2864. dh.entries.grep(/^[^.]/).
  2865. map {|file| "#{dir}/#{file}"}.
  2866. find_all {|file| test(?f, file)}.
  2867. sort
  2868. end
  2869. # @@PLEAC@@_9.6
  2870. list = Dir.glob("*.c")
  2871. dir = Dir.open(path)
  2872. files = dir.entries.grep(/\.c$/)
  2873. dir.close
  2874. files = Dir.glob("*.c")
  2875. files = Dir.open(path).entries.grep(/\.[ch]$/i)
  2876. dir = Dir.new(path)
  2877. files = dir.entries.grep(/\.[ch]$/i)
  2878. begin
  2879. d = Dir.open(dir)
  2880. rescue Errno::ENOENT
  2881. raise "Couldn't open #{dir} for reading: #{$!}"
  2882. end
  2883. files = []
  2884. d.each do |file|
  2885. puts file
  2886. next unless file =~ /\.[ch]$/i
  2887. filename = "#{dir}/#{file}"
  2888. # There is no -T equivalent in Ruby, but we can still test emptiness
  2889. files.push(filename) if test(?s, filename)
  2890. end
  2891. dirs.entries.grep(/^\d+$/).
  2892. map { |file| [file, "#{path}/#{file}"]} .
  2893. select { |file| test(?d, file[1]) }.
  2894. sort { |a,b| a[0] <=> b[0] }.
  2895. map { |file| file[1] }
  2896. # @@PLEAC@@_9.7
  2897. require 'find'
  2898. Find.find(dirlist) do |file|
  2899. # do whatever
  2900. end
  2901. require 'find'
  2902. argv = ARGV.empty? ? %w{.} : ARGV
  2903. Find.find(*argv) do |file|
  2904. print file, (test(?d, file) ? "/\n" : "\n")
  2905. end
  2906. require 'find'
  2907. argv = ARGV.empty? ? %w{.} : ARGV
  2908. sum = 0
  2909. Find.find(*argv) do |file|
  2910. size = test(?s, file) || 0
  2911. sum += size
  2912. end
  2913. puts "#{argv.join(' ')} contains #{sum} bytes"
  2914. require 'find'
  2915. argv = ARGV.empty? ? %w{.} : ARGV
  2916. saved_size, saved_name = -1, ""
  2917. Find.find(*argv) do |file|
  2918. size = test(?s, file) || 0
  2919. next unless test(?f, file) && size > saved_size
  2920. saved_size = size
  2921. saved_name = file
  2922. end
  2923. puts "Biggest file #{saved_name} in #{argv.join(' ')} is #{saved_size}"
  2924. require 'find'
  2925. argv = ARGV.empty? ? %w{.} : ARGV
  2926. age, name = nil
  2927. Find.find(*argv) do |file|
  2928. mtime = File.stat(file).mtime
  2929. next if age && age > mtime
  2930. age = mtime
  2931. name = file
  2932. end
  2933. puts "#{name} #{age}"
  2934. #-----------------------------
  2935. #!/usr/bin/ruby -w
  2936. # fdirs - find all directories
  2937. require 'find'
  2938. argv = ARGV.empty? ? %w{.} : ARGV
  2939. File.find(*argv) { |file| puts file if test(?d, file) }
  2940. #-----------------------------
  2941. # @@PLEAC@@_9.8
  2942. require 'fileutils'
  2943. puts "Usage #{$0} dir ..." if ARGV.empty?
  2944. ARGV.each do |dir|
  2945. FileUtils.rmtree(dir)
  2946. end
  2947. # @@PLEAC@@_9.9
  2948. require 'ftools'
  2949. names.each do |file|
  2950. newname = file
  2951. begin
  2952. File.move(file, newname)
  2953. rescue Errno::EPERM
  2954. $stderr.puts "Couldn't rename #{file} to #{newname}: #{$!}"
  2955. end
  2956. end
  2957. require 'ftools'
  2958. op = ARGV.empty? ? (raise "Usage: rename expr [files]\n") : ARGV.shift
  2959. argv = ARGV.empty? ? $stdin.readlines.map { |f| f.chomp } : ARGV
  2960. argv.each do |file|
  2961. was = file
  2962. file = eval("file.#{op}")
  2963. File.move(was, file) unless was == file
  2964. end
  2965. # @@PLEAC@@_9.10
  2966. base = File.basename(path)
  2967. dir = File.dirname(path)
  2968. # ruby has no fileparse equivalent
  2969. dir, base = File.split(path)
  2970. ext = base.scan(/\..*$/).to_s
  2971. path = '/usr/lib/libc.a'
  2972. file = File.basename(path)
  2973. dir = File.dirname(path)
  2974. puts "dir is #{dir}, file is #{file}"
  2975. # dir is /usr/lib, file is libc.a
  2976. path = '/usr/lib/libc.a'
  2977. dir, filename = File.split(path)
  2978. name, ext = filename.split(/(?=\.)/)
  2979. puts "dir is #{dir}, name is #{name}, ext is #{ext}"
  2980. # NOTE: The Ruby code prints
  2981. # dir is /usr/lib, name is libc, extension is .a
  2982. # while the Perl code prints a '/' after the directory name
  2983. # dir is /usr/lib/, name is libc, extension is .a
  2984. # No fileparse_set_fstype() equivalent in ruby
  2985. def extension(path)
  2986. ext = path.scan(/\..*$/).to_s
  2987. ext.sub(/^\./, "")
  2988. end
  2989. # @@PLEAC@@_9.11
  2990. #-----------------------------
  2991. #!/usr/bin/ruby -w
  2992. # symirror - build spectral forest of symlinks
  2993. require 'find'
  2994. require 'fileutils'
  2995. raise "usage: #{$0} realdir mirrordir" unless ARGV.size == 2
  2996. srcdir,dstdir = ARGV
  2997. srcmode = File::stat(srcdir).mode
  2998. Dir.mkdir(dstdir, srcmode & 07777) unless test(?d, dstdir)
  2999. # fix relative paths
  3000. Dir.chdir(srcdir) {srcdir = Dir.pwd}
  3001. Dir.chdir(dstdir) {dstdir = Dir.pwd}
  3002. Find.find(srcdir) do |srcfile|
  3003. if test(?d, srcfile)
  3004. dest = srcfile.sub(/^#{srcdir}/, dstdir)
  3005. dmode = File::stat(srcfile).mode & 07777
  3006. Dir.mkdir(dest, dmode) unless test(?d, dest)
  3007. a = Dir["#{srcfile}/*"].reject{|f| test(?d, f)}
  3008. FileUtils.ln_s(a, dest)
  3009. end
  3010. end
  3011. # @@PLEAC@@_9.12
  3012. # we use the Getopt/Declare library here for convenience:
  3013. # http://raa.ruby-lang.org/project/getoptdeclare/
  3014. #-----------------------------
  3015. #!/usr/bin/ruby -w
  3016. # lst - list sorted directory contents (depth first)
  3017. require 'find'
  3018. require 'etc'
  3019. require "Getopt/Declare"
  3020. # Note: in the option-spec below there must by at least one hard
  3021. # tab in between each -option and its description. For example
  3022. # -i <tab> read from stdin
  3023. opts = Getopt::Declare.new(<<'EOPARAM')
  3024. ============
  3025. Input Format:
  3026. -i read from stdin
  3027. ============
  3028. Output Format:
  3029. -l long listing
  3030. -r reverse listing
  3031. ============
  3032. Sort on: (one of)
  3033. -m mtime (modify time - default)
  3034. {$sort_criteria = :mtime}
  3035. -u atime (access time)
  3036. {$sort_criteria = :atime}
  3037. -c ctime (inode change time)
  3038. {$sort_criteria = :ctime}
  3039. -s size
  3040. {$sort_criteria = :size}
  3041. [mutex: -m -u -c -s]
  3042. EOPARAM
  3043. $sort_criteria ||= :mtime
  3044. files = {}
  3045. DIRS = opts['-i'] ? $stdin.readlines.map{|f|f.chomp!} : ARGV
  3046. DIRS.each do |dir|
  3047. Find.find(dir) do |ent|
  3048. files[ent] = File::stat(ent)
  3049. end
  3050. end
  3051. entries = files.keys.sort_by{|f| files[f].send($sort_criteria)}
  3052. entries = entries.reverse unless opts['-r']
  3053. entries.each do |ent|
  3054. unless opts['-l']
  3055. puts ent
  3056. next
  3057. end
  3058. stats = files[ent]
  3059. ftime = stats.send($sort_criteria == :size ? :mtime : $sort_criteria)
  3060. printf "%6d %04o %6d %8s %8s %8d %s %s\n",
  3061. stats.ino,
  3062. stats.mode & 07777,
  3063. stats.nlink,
  3064. ETC::PASSWD[stats.uid].name,
  3065. ETC::GROUP[stats.gid].name,
  3066. stats.size,
  3067. ftime.strftime("%a %b %d %H:%M:%S %Y"),
  3068. ent
  3069. end
  3070. # @@PLEAC@@_10.0
  3071. def hello
  3072. $greeted += 1 # in Ruby, a variable beginning with $ is global (can be any type of course)
  3073. puts "hi there!"
  3074. end
  3075. # We need to initialize $greeted before it can be used, because "+=" is waiting a Numeric object
  3076. $greeted = 0
  3077. hello # note that appending () is optional to function calls with no parameters
  3078. # @@PLEAC@@_10.1
  3079. # In Ruby, parameters are named anyway
  3080. def hypotenuse(side1, side2)
  3081. Math.sqrt(side1**2 + side2**2) # the sqrt function comes from the Math module
  3082. end
  3083. diag = hypotenuse(3, 4)
  3084. puts hypotenuse(3, 4)
  3085. a = [3, 4]
  3086. print hypotenuse(*a) # the star operator will magically convert an Array into a "tuple"
  3087. both = men + women
  3088. # In Ruby, all objects are references, so the same problem arises; we then return a new object
  3089. nums = [1.4, 3.5, 6.7]
  3090. def int_all(n)
  3091. n.collect { |v| v.to_i }
  3092. end
  3093. ints = int_all(nums)
  3094. nums = [1.4, 3.5, 6.7]
  3095. def trunc_em(n)
  3096. n.collect! { |v| v.to_i } # the bang-version of collect modifies the object
  3097. end
  3098. trunc_em(nums)
  3099. # Ruby has two chomp version:
  3100. # ``chomp'' chomps the record separator and returns what's expected
  3101. # ``chomp!'' does the same but also modifies the parameter object
  3102. # @@PLEAC@@_10.2
  3103. def somefunc
  3104. variable = something # variable is local by default
  3105. end
  3106. name, age = ARGV
  3107. start = fetch_time
  3108. a, b = pair # will succeed if pair is an Array object (like ARGV is)
  3109. c = fetch_time
  3110. # In ruby, run_check can't access a, b, or c until they are
  3111. # explicitely defined global (using leading $), even if they are
  3112. # both defined in the same scope
  3113. def check_x(x)
  3114. y = "whatever"
  3115. run_check
  3116. if $condition
  3117. puts "got $x"
  3118. end
  3119. end
  3120. # The following will keep a reference to the array, though the
  3121. # results will be slightly different from perl: the last element
  3122. # of $global_array will be itself an array
  3123. def save_array(ary)
  3124. $global_array << ary
  3125. end
  3126. # The following gives the same results as in Perl for $global_array,
  3127. # though it doesn't illustrate anymore the way to keep a reference
  3128. # to an object: $global_array is extended with the elements of ary
  3129. def save_array(ary)
  3130. $global_array += ary
  3131. end
  3132. # @@PLEAC@@_10.3
  3133. # In Ruby, AFAIK a method cannot access "local variables" defined
  3134. # upper scope; mostly because everything is an object, so you'll
  3135. # do the same by defining an attribute or a static attribute
  3136. # In Ruby the BEGIN also exists:
  3137. BEGIN { puts "hello from BEGIN" }
  3138. puts "hello from main"
  3139. BEGIN { puts "hello from 2nd BEGIN" }
  3140. # gives:
  3141. # hello from BEGIN
  3142. # hello from 2nd BEGIN
  3143. # hello from main
  3144. # In Ruby, it can be written as a static method and a static
  3145. # variable
  3146. class Counter
  3147. @@counter = 0
  3148. def Counter.next_counter; @@counter += 1; end
  3149. end
  3150. # There is no need of BEGIN since the variable will get
  3151. # initialized when parsing
  3152. class Counter
  3153. @@counter = 42
  3154. def Counter.next_counter; @@counter += 1; end
  3155. def Counter.prev_counter; @@counter -= 1; end
  3156. end
  3157. # @@PLEAC@@_10.4
  3158. # You can either get the whole trace as an array of strings, each
  3159. # string telling which file, line and method is calling:
  3160. caller
  3161. # ...or only the last caller
  3162. caller[0]
  3163. # We need to extract just the method name of the backtrace:
  3164. def whoami; caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
  3165. def whowasi; caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
  3166. # @@PLEAC@@_10.5
  3167. # In Ruby, every value is a reference on an object, thus there is
  3168. # no such problem
  3169. array_diff(array1, array2)
  3170. def add_vecpair(a1, a2)
  3171. results = []
  3172. a1.each_index { |i| results << (a1[i] + a2[i]) }
  3173. results
  3174. end
  3175. a = [1, 2]
  3176. b = [5, 8]
  3177. c = add_vecpair(a, b)
  3178. p c
  3179. # Add this to the beginning of the function to check if we were
  3180. # given two arrays
  3181. a1.type == Array && a2.type == Array or
  3182. raise "usage: add_vecpair array1 array2 (was used with: #{a1.type} #{a2.type})"
  3183. # @@PLEAC@@_10.6
  3184. # There is no return context in Ruby
  3185. # @@PLEAC@@_10.7
  3186. # Like in Perl, we need to fake with a hash, but it's dirty :-(
  3187. def thefunc(param_args)
  3188. args = { 'INCREMENT' => '10s', 'FINISH' => '0', 'START' => 0 }
  3189. args.update(param_args)
  3190. if (args['INCREMENT'] =~ /m$/ )
  3191. # .....
  3192. end
  3193. end
  3194. thefunc({ 'INCREMENT' => '20s', 'START' => '+5m', 'FINISH' => '+30m' })
  3195. thefunc({})
  3196. # @@PLEAC@@_10.8
  3197. # there is no "undef" direct equivalent but there is the slice equiv:
  3198. a, c = func.indexes(0, 2)
  3199. # @@PLEAC@@_10.9
  3200. # Ruby has no such limitation:
  3201. def somefunc
  3202. ary = []
  3203. hash = {}
  3204. # ...
  3205. return ary, hash
  3206. end
  3207. arr, dict = somefunc
  3208. array_of_hashes = fn
  3209. h1, h2, h3 = fn
  3210. # @@PLEAC@@_10.10
  3211. return
  3212. # or (equivalent)
  3213. return nil
  3214. # @@PLEAC@@_10.11
  3215. # You can't prototype in Ruby regarding types :-(
  3216. # Though, you can force the number of arguments:
  3217. def func_with_no_arg; end
  3218. def func_with_no_arg(); end
  3219. def func_with_one_arg(a1); end
  3220. def func_with_two_args(a1, a2); end
  3221. def func_with_any_number_of_args(*args); end
  3222. # @@PLEAC@@_10.12
  3223. raise "some message" # raise exception
  3224. begin
  3225. val = func
  3226. rescue Exception => msg
  3227. $stderr.puts "func raised an exception: #{msg}"
  3228. end
  3229. # In Ruby the rescue statement uses an exception class, every
  3230. # exception which is not matched is still continuing
  3231. begin
  3232. val = func
  3233. rescue FullMoonError
  3234. ...
  3235. end
  3236. # @@PLEAC@@_10.13
  3237. # Saving Global Values
  3238. # Of course we can just save the value and restore it later:
  3239. def print_age
  3240. puts "Age is #{$age}"
  3241. end
  3242. $age = 18 # global variable
  3243. print_age()
  3244. if condition
  3245. safeage = $age
  3246. $age = 23
  3247. print_age()
  3248. $age = safeage
  3249. end
  3250. # We can also use a method that saves the global variable and
  3251. # restores it automatically when the block is left:
  3252. def local(var)
  3253. eval("save = #{var.id2name}")
  3254. begin
  3255. result = yield
  3256. ensure
  3257. # we want to call this even if we got an exception
  3258. eval("#{var.id2name} = save")
  3259. end
  3260. result
  3261. end
  3262. condition = true
  3263. $age = 18
  3264. print_age()
  3265. if condition
  3266. local(:$age) {
  3267. $age = 23
  3268. print_age()
  3269. }
  3270. end
  3271. print_age()
  3272. # There is no need to use local() for filehandles or directory
  3273. # handles in ruby because filehandles are normal objects.
  3274. # @@PLEAC@@_10.14
  3275. # In Ruby you may redefine a method [but not overload it :-(]
  3276. # just by defining again with the same name.
  3277. def foo; puts 'foo'; end
  3278. def foo; puts 'bar'; end
  3279. foo
  3280. #=> bar
  3281. # You can also take a reference to an existing method before
  3282. # redefining a new one, using the `alias' keyword
  3283. def foo; puts 'foo'; end
  3284. alias foo_orig foo
  3285. def foo; puts 'bar'; end
  3286. foo_orig
  3287. foo
  3288. #=> foo
  3289. #=> bar
  3290. # AFAIK, there is no direct way to create a new method whose name
  3291. # comes from a variable, so use "eval"
  3292. colors = %w(red blue green yellow orange purple violet)
  3293. colors.each { |c|
  3294. eval <<-EOS
  3295. def #{c}(*a)
  3296. "<FONT COLOR='#{c}'>" + a.to_s + "</FONT>"
  3297. end
  3298. EOS
  3299. }
  3300. # @@PLEAC@@_10.15
  3301. def method_missing(name, *args)
  3302. "<FONT COLOR='#{name}'>" + args.join(' ') + "</FONT>"
  3303. end
  3304. puts chartreuse("stuff")
  3305. # @@PLEAC@@_10.16
  3306. def outer(arg)
  3307. x = arg + 35
  3308. inner = proc { x * 19 }
  3309. x + inner.call()
  3310. end
  3311. # @@PLEAC@@_10.17
  3312. #!/usr/bin/ruby -w
  3313. # mailsort - sort mbox by different criteria
  3314. require 'English'
  3315. require 'Date'
  3316. # Objects of class Mail represent a single mail.
  3317. class Mail
  3318. attr_accessor :no
  3319. attr_accessor :subject
  3320. attr_accessor :fulltext
  3321. attr_accessor :date
  3322. def initialize
  3323. @fulltext = ""
  3324. @subject = ""
  3325. end
  3326. def append(para)
  3327. @fulltext << para
  3328. end
  3329. # this is called if you call puts(mail)
  3330. def to_s
  3331. @fulltext
  3332. end
  3333. end
  3334. # represents a list of mails.
  3335. class Mailbox < Array
  3336. Subjectpattern = Regexp.new('Subject:\s*(?:Re:\s*)*(.*)\n')
  3337. Datepattern = Regexp.new('Date:\s*(.*)\n')
  3338. # reads mails from open file and stores them
  3339. def read(file)
  3340. $INPUT_RECORD_SEPARATOR = '' # paragraph reads
  3341. msgno = -1
  3342. file.each { |para|
  3343. if para =~ /^From/
  3344. mail = Mail.new
  3345. mail.no = (msgno += 1)
  3346. md = Subjectpattern.match(para)
  3347. if md
  3348. mail.subject = md[1]
  3349. end
  3350. md = Datepattern.match(para)
  3351. if md
  3352. mail.date = DateTime.parse(md[1])
  3353. else
  3354. mail.date = DateTime.now
  3355. end
  3356. self.push(mail)
  3357. end
  3358. mail.append(para) if mail
  3359. }
  3360. end
  3361. def sort_by_subject_and_no
  3362. self.sort_by { |m|
  3363. [m.subject, m.no]
  3364. }
  3365. end
  3366. # sorts by a list of attributs of mail, given as symbols
  3367. def sort_by_attributs(*attrs)
  3368. # you can sort an Enumerable by an array of
  3369. # values, they would be compared
  3370. # from ary[0] to ary[n]t, say:
  3371. # ['b',1] > ['a',10] > ['a',9]
  3372. self.sort_by { |elem|
  3373. attrs.map { |attr|
  3374. elem.send(attr)
  3375. }
  3376. }
  3377. end
  3378. end
  3379. mailbox = Mailbox.new
  3380. mailbox.read(ARGF)
  3381. # print only subjects sorted by subject and number
  3382. for m in mailbox.sort_by_subject_and_no
  3383. puts(m.subject)
  3384. end
  3385. # print complete mails sorted by date, then subject, then number
  3386. for m in mailbox.sort_by_attributs(:date, :subject)
  3387. puts(m)
  3388. end
  3389. # @@PLEAC@@_11.7
  3390. def mkcounter(count)
  3391. start = count
  3392. bundle = {
  3393. "NEXT" => proc { count += 1 },
  3394. "PREV" => proc { count -= 1 },
  3395. "RESET" => proc { count = start }
  3396. }
  3397. bundle["LAST"] = bundle["PREV"]
  3398. return bundle
  3399. end
  3400. c1 = mkcounter(20)
  3401. c2 = mkcounter(77)
  3402. puts "next c1: #{c1["NEXT"].call}" # 21
  3403. puts "next c2: #{c2["NEXT"].call}" # 78
  3404. puts "next c1: #{c1["NEXT"].call}" # 22
  3405. puts "last c1: #{c1["PREV"].call}" # 21
  3406. puts "last c1: #{c1["LAST"].call}" # 20
  3407. puts "old c2: #{c2["RESET"].call}" # 77
  3408. # @@PLEAC@@_11.15
  3409. class Binary_tree
  3410. def initialize(val)
  3411. @value = val
  3412. @left = nil
  3413. @right = nil
  3414. end
  3415. # insert given value into proper point of
  3416. # provided tree. If no tree provided,
  3417. # use implicit pass by reference aspect of @_
  3418. # to fill one in for our caller.
  3419. def insert(val)
  3420. if val < @value then
  3421. if @left then
  3422. @left.insert(val)
  3423. else
  3424. @left = Binary_tree.new(val)
  3425. end
  3426. elsif val > @value then
  3427. if @right then
  3428. @right.insert(val)
  3429. else
  3430. @right = Binary_tree.new(val)
  3431. end
  3432. else
  3433. puts "double"
  3434. # do nothing, no double values
  3435. end
  3436. end
  3437. # recurse on left child,
  3438. # then show current value,
  3439. # then recurse on right child.
  3440. def in_order
  3441. @left.in_order if @left
  3442. print @value, " "
  3443. @right.in_order if @right
  3444. end
  3445. # show current value,
  3446. # then recurse on left child,
  3447. # then recurse on right child.
  3448. def pre_order
  3449. print @value, " "
  3450. @left.pre_order if @left
  3451. @right.pre_order if @right
  3452. end
  3453. # recurse on left child,
  3454. # then recurse on right child,
  3455. # then show current value.
  3456. def post_order
  3457. @left.post_order if @left
  3458. @right.post_order if @right
  3459. print @value, " "
  3460. end
  3461. # find out whether provided value is in the tree.
  3462. # if so, return the node at which the value was found.
  3463. # cut down search time by only looking in the correct
  3464. # branch, based on current value.
  3465. def search(val)
  3466. if val == @value then
  3467. return self
  3468. elsif val < @value then
  3469. return @left.search(val) if @left
  3470. return nil
  3471. else
  3472. return @right.search(val) if @right
  3473. return nil
  3474. end
  3475. end
  3476. end
  3477. # first generate 20 random inserts
  3478. test = Binary_tree.new(0)
  3479. for a in 0..20
  3480. test.insert(rand(1000))
  3481. end
  3482. # now dump out the tree all three ways
  3483. print "Pre order: "; test.pre_order; puts ""
  3484. print "In order: "; test.in_order; puts ""
  3485. print "Post order: "; test.post_order; puts ""
  3486. print "search?"
  3487. while gets
  3488. print test.search($_.to_i)
  3489. print "\nsearch?"
  3490. end
  3491. # @@PLEAC@@_12.0
  3492. # class and module names need to have the first letter capitalized
  3493. module Alpha
  3494. NAME = 'first'
  3495. end
  3496. module Omega
  3497. NAME = 'last'
  3498. end
  3499. puts "Alpha is #{Alpha::NAME}, Omega is #{Omega::NAME}"
  3500. # ruby doesn't differentiate beteen compile-time and run-time
  3501. require 'getoptlong.rb'
  3502. require 'getoptlong' # assumes the .rb
  3503. require 'cards/poker.rb'
  3504. require 'cards/poker' # assumes the .rb
  3505. load 'cards/poker' # require only loads the file once
  3506. module Cards
  3507. module Poker
  3508. @card_deck = Array.new # or @card_deck = []
  3509. def shuffle
  3510. end
  3511. end
  3512. end
  3513. # @@PLEAC@@_12.1
  3514. # a module exports all of its functions
  3515. module Your_Module
  3516. def self.function
  3517. # this would be called as Your_Module.function
  3518. end
  3519. def Your_Module.another
  3520. # this is the same as above, but more specific
  3521. end
  3522. end
  3523. # @@PLEAC@@_12.2
  3524. begin
  3525. require 'nonexistent'
  3526. rescue LoadError
  3527. puts "Couldn't load #{$!}" # $! contains the last error string
  3528. end
  3529. # @@PLEAC@@_12.4
  3530. # module variables are private unless access functions are defined
  3531. module Alpha
  3532. @aa = 10
  3533. @bb = 11
  3534. def self.put_aa
  3535. puts @aa
  3536. end
  3537. def self.bb=(val)
  3538. @bb = val
  3539. end
  3540. end
  3541. Alpha.bb = 12
  3542. # Alpha.aa = 10 # error, no aa=method
  3543. # @@PLEAC@@_12.5
  3544. # caller provides a backtrace of the call stack
  3545. module MyModule
  3546. def find_caller
  3547. caller
  3548. end
  3549. def find_caller2(i)
  3550. caller(i) # an argument limits the size of the stack returned
  3551. end
  3552. end
  3553. # @@PLEAC@@_12.6
  3554. BEGIN {
  3555. $logfile = '/tmp/mylog' unless defined? $logfile
  3556. $LF = File.open($logfile, 'a')
  3557. }
  3558. module Logger
  3559. def self.logmsg(msg)
  3560. $LF.puts msg
  3561. end
  3562. logmsg('startup')
  3563. end
  3564. END {
  3565. Logger::logmsg('shutdown')
  3566. $LF.close
  3567. }
  3568. # @@PLEAC@@_12.7
  3569. #-----------------------------
  3570. # results may be different on your system
  3571. # % ruby -e "$LOAD_PATH.each_index { |i| printf("%d %s\n", i, $LOAD_PATH[i] }
  3572. #0 /usr/local/lib/site_ruby/1.6
  3573. #1 /usr/local/lib/site_ruby/1.6/i386-linux
  3574. #2 /usr/local/lib/site_ruby/
  3575. #3 /usr/lib/ruby/1.6
  3576. #4 /usr/lib/ruby/1.6/i136-linux
  3577. #5 .
  3578. #-----------------------------
  3579. # syntax for sh, bash, ksh, or zsh
  3580. #$ export RUBYLIB=$HOME/rubylib
  3581. # syntax for csh or tcsh
  3582. # % setenv RUBYLIB ~/rubylib
  3583. #-----------------------------
  3584. $LOAD_PATH.unshift "/projects/spectre/lib";
  3585. # @@PLEAC@@_12.8
  3586. # equivalents in ruby are mkmf, SWIG, or Ruby/DL depending on usage
  3587. # @@PLEAC@@_12.9
  3588. # no equivalent in ruby
  3589. # @@PLEAC@@_12.10
  3590. # no equivalent in ruby
  3591. # @@PLEAC@@_12.11
  3592. module FineTime
  3593. def self.time
  3594. # to be defined later
  3595. end
  3596. end
  3597. module FineTime
  3598. def self.time
  3599. "its a fine time"
  3600. end
  3601. end
  3602. puts FineTime.time #=> "its a fine time"
  3603. # @@PLEAC@@_12.12
  3604. def even_only(n)
  3605. raise "#{n} is not even" if (n & 1) != 0 # one way to test
  3606. # ...
  3607. end
  3608. def even_only(n)
  3609. $stderr.puts "#{n} is not even" if (n & 1) != 0
  3610. # ...
  3611. end
  3612. # @@PLEAC@@_12.17
  3613. # The library archive for ruby is called Ruby Application archive,
  3614. # or shorter RAA, and can be found at http://raa.ruby-lang.org.
  3615. # A typical library is installed like this:
  3616. # % gunzip some-module-4.54.tar.gz
  3617. # % tar xf some-module-4.54.tar
  3618. # % cd some-module-4.54.tar
  3619. # % ruby install.rb config
  3620. # % ruby install.rb setup
  3621. # get superuser previleges here if needed for next step
  3622. # % ruby install.rb install
  3623. # Some modules use a different process,
  3624. # you should find details in the documentation
  3625. # Here is an example of such a different process
  3626. # % ruby extconf.rb
  3627. # % make
  3628. # % make install
  3629. # If you want the module installed in your own directory:
  3630. # For ruby version specific libraries
  3631. # % ruby install.rb config --site-ruby=~/lib
  3632. # For version independent libraries
  3633. # % ruby install.rb config --site-ruby-common=~/lib
  3634. # Information about possible options for config
  3635. # % ruby install.rb --help
  3636. # If you have your own complete distribution
  3637. # % ruby install.rb --prefix=path=~/ruby-private
  3638. # @@PLEAC@@_13.0
  3639. # Classes and objects in Ruby are rather straigthforward
  3640. class Person
  3641. # Class variables (also called static attributes) are prefixed by @@
  3642. @@person_counter=0
  3643. # object constructor
  3644. def initialize(age, name, alive = true) # Default arg like in C++
  3645. @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@'
  3646. @@person_counter += 1
  3647. # There is no '++' operator in Ruby. The '++'/'--' operators are in fact
  3648. # hidden assignments which affect variables, not objects. You cannot accomplish
  3649. # assignment via method. Since everything in Ruby is object, '++' and '--'
  3650. # contradict Ruby OO ideology. Instead '-=' and '+=' are used.
  3651. end
  3652. attr_accessor :name, :age # This creates setter and getter methods for @name
  3653. # and @age. See 13.3 for detailes.
  3654. # methods modifying the receiver object usually have the '!' suffix
  3655. def die!
  3656. @alive = false
  3657. puts "#{@name} has died at the age of #{@age}."
  3658. @alive
  3659. end
  3660. def kill(anotherPerson)
  3661. print @name, ' is killing ', anotherPerson.name, ".\n"
  3662. anotherPerson.die!
  3663. end
  3664. # methods used as queries
  3665. # usually have the '?' suffix
  3666. def alive?
  3667. @alive && true
  3668. end
  3669. def year_of_birth
  3670. Time.now.year - @age
  3671. end
  3672. # Class method (also called static method)
  3673. def Person.number_of_people
  3674. @@person_counter
  3675. end
  3676. end
  3677. # Using the class:
  3678. # Create objects of class Person
  3679. lecter = Person.new(47, 'Hannibal')
  3680. starling = Person.new(29, 'Clarice', true)
  3681. pazzi = Person.new(40, 'Rinaldo', true)
  3682. # Calling a class method
  3683. print "There are ", Person.number_of_people, " Person objects\n"
  3684. print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
  3685. lecter.kill(pazzi)
  3686. print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
  3687. print starling.name , ' was born in ', starling.year_of_birth, "\n"
  3688. # @@PLEAC@@_13.1
  3689. # If you don't need any initialisation in the constructor,
  3690. # you don't need to write a constructor.
  3691. class MyClass
  3692. end
  3693. class MyClass
  3694. def initialize
  3695. @start = Time.new
  3696. @age = 0
  3697. end
  3698. end
  3699. class MyClass
  3700. def initialize(inithash)
  3701. @start = Time.new
  3702. @age = 0
  3703. for key, value in inithash
  3704. instance_variable_set("@#{key}", value)
  3705. end
  3706. end
  3707. end
  3708. # @@PLEAC@@_13.2
  3709. # Objects are destroyed by the garbage collector.
  3710. # The time of destroying is not predictable.
  3711. # The ruby garbage collector can handle circular references,
  3712. # so there is no need to write destructor for that.
  3713. # There is no direct support for destructor.
  3714. # You can call a custom function, or more specific a proc object, when the
  3715. # garbage collector is about to destruct the object, but it is unpredictable
  3716. # when this occurs.
  3717. # Also if such a finalizer object has a reference to the orignal object,
  3718. # this may prevent the original object to get garbage collected.
  3719. # Because of this problem the finalize method below is
  3720. # a class method and not a instance method.
  3721. # So if you need to free resources for an object, like
  3722. # closing a socket or kill a spawned subprocess,
  3723. # you should do it explicitly.
  3724. class MyClass
  3725. def initialize
  3726. ObjectSpace.define_finalizer(self,
  3727. self.class.method(:finalize).to_proc)
  3728. end
  3729. def MyClass.finalize(id)
  3730. puts "Object #{id} dying at #{Time.new}"
  3731. end
  3732. end
  3733. # test code
  3734. 3.times {
  3735. MyClass.new
  3736. }
  3737. ObjectSpace.garbage_collect
  3738. # @@PLEAC@@_13.3
  3739. # You can write getter and setter methods in a natural way:
  3740. class Person
  3741. def name
  3742. @name
  3743. end
  3744. def name=(name)
  3745. @name = name
  3746. end
  3747. end
  3748. # But there is a better and shorter way
  3749. class Person
  3750. attr_reader :age
  3751. attr_writer :name
  3752. # attr_reader and attr_writer are actually methods in class Class
  3753. # which set getter and setter methods for you.
  3754. end
  3755. # There is also attr_accessor to create both setters and getters
  3756. class Person
  3757. attr_accessor :age, :name
  3758. end
  3759. # @@PLEAC@@_13.4
  3760. class Person
  3761. # Class variables (also called static attributes) are prefixed by @@
  3762. @@person_counter = 0
  3763. def Person.population
  3764. @@person_counter
  3765. end
  3766. def initialize
  3767. @@person_counter += 1
  3768. ObjectSpace.define_finalizer(self,
  3769. self.class.method(:finalize).to_proc)
  3770. end
  3771. def Person.finalize(id)
  3772. @@person_counter -= 1
  3773. end
  3774. end
  3775. people = []
  3776. 10.times {
  3777. people.push(Person.new)
  3778. }
  3779. printf("There are %d people alive", Person.population)
  3780. FixedArray.class_max_bounds = 100
  3781. alpha = FixedArray.new
  3782. puts "Bound on alpha is #{alpha.max_bounds}"
  3783. beta = FixedArray.new
  3784. beta.max_bounds = 50 # calls the instance method
  3785. beta.class.class_max_bounds = 50 # alternative, calls the class method
  3786. puts "Bound on alpha is #{alpha.max_bounds}"
  3787. class FixedArray
  3788. @@bounds = 7
  3789. def max_bounds
  3790. @@max_bounds
  3791. end
  3792. # instance method, which sets the class variable
  3793. def max_bounds=(value)
  3794. @@max_bounds = value
  3795. end
  3796. # class method. This can only be called on a class,
  3797. # but not on the instances
  3798. def FixedArray.class_max_bounds=(value)
  3799. @@max_bounds = value
  3800. end
  3801. end
  3802. # @@PLEAC@@_13.5
  3803. PersonStruct = Struct.new("Person", :name, :age, :peers)
  3804. # creates a class "Person::Struct", which is accessiable with the
  3805. # constant "PersonStruct"
  3806. p = PersonStruct.new
  3807. p = Struct::Person.new # alternative using the classname
  3808. p.name = "Jason Smythe"
  3809. p.age = 13
  3810. p.peers = ["Wilbur", "Ralph", "Fred"]
  3811. p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol
  3812. p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field
  3813. p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field
  3814. puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}"
  3815. # The fields of a struct have no special type, like other ruby variables
  3816. # you can put any objects in. Therefore the discussions how to specify
  3817. # the types of the fields do not apply to ruby.
  3818. FamilyStruct = Struct.new("Family", :head, :address, :members)
  3819. folks = FamilyStruct.new
  3820. folks.head = PersonStruct.new
  3821. dad = folks.head
  3822. dad.name = "John"
  3823. dad.age = 34
  3824. # supply of own accessor method for the struct for error checking
  3825. class PersonStruct
  3826. def age=(value)
  3827. if !value.kind_of?(Integer)
  3828. raise(ArgumentError, "Age #{value} isn't an Integer")
  3829. elsif value > 150
  3830. raise(ArgumentError, "Age #{value} is unreasonable")
  3831. end
  3832. @age = value
  3833. end
  3834. end
  3835. # @@PLEAC@@_13.6
  3836. # The ruby Object class defines a dup and a clone method.
  3837. # The dup method is recommended for prototype object creation.
  3838. # The default implementation makes a shallow copy,
  3839. # but each class can override it, for example to make a deep copy.
  3840. # If you want to call 'new' directly on the instances,
  3841. # you can create a instance method "new", which returns a new duplicate.
  3842. # This method is distinct from the class method new.
  3843. #
  3844. class A
  3845. def new
  3846. dup
  3847. end
  3848. end
  3849. ob1 = A.new
  3850. # later on
  3851. ob2 = ob1.new
  3852. # @@PLEAC@@_13.7
  3853. methname = 'flicker'
  3854. obj.send(methname, 10) # calls obj.flicker(10)
  3855. # call three methods on the object, by name
  3856. ['start', 'run', 'stop'].each do |method_string|
  3857. obj.send(method_string)
  3858. end
  3859. # Another way is to create a Method object
  3860. method_obj = obj.method('flicker')
  3861. # And then call it
  3862. method_obj.call(10)
  3863. # @@PLEAC@@_13.8
  3864. # All classes in Ruby inherit from class Object
  3865. # and thus all objects share methods defined in this class
  3866. # the class of the object
  3867. puts any_object.type
  3868. # Ruby classes are actually objects of class Class and they
  3869. # respond to methods defined in Object class as well
  3870. # the superclass of this class
  3871. puts any_object.class.superclass
  3872. # ask an object whether it is an instance of particular class
  3873. n = 4.7
  3874. puts n.instance_of?(Float) # true
  3875. puts n.instance_of?(Numeric) # false
  3876. # ask an object whether it is an instance of class, one of the
  3877. # superclasses of the object, or modules included in it
  3878. puts n.kind_of?(Float) # true (the class)
  3879. puts n.kind_of?(Numeric) # true (an ancestor class)
  3880. puts n.kind_of?(Comparable) # true (a mixin module)
  3881. puts n.kind_of?(String) # false
  3882. # ask an object whether it can respond to a particular method
  3883. puts n.respond_to?('+') # true
  3884. puts n.respond_to?('length') # false
  3885. # all methods an object can respond to
  3886. 'just a string'.methods.each { |m| puts m }
  3887. # @@PLEAC@@_13.9
  3888. # Actually any class in Ruby is inheritable
  3889. class Person
  3890. attr_accessor :age, :name
  3891. def initialize
  3892. @name
  3893. @age
  3894. end
  3895. end
  3896. #-----------------------------
  3897. dude = Person.new
  3898. dude.name = 'Jason'
  3899. dude.age = 23
  3900. printf "%s is age %d.\n", dude.name, dude.age
  3901. #-----------------------------
  3902. # Inheriting from Person
  3903. class Employee < Person
  3904. attr_accessor :salary
  3905. end
  3906. #-----------------------------
  3907. empl = Employee.new
  3908. empl.name = 'Jason'
  3909. empl.age = 23
  3910. empl.salary = 200
  3911. printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary
  3912. #-----------------------------
  3913. # Any built-in class can be inherited the same way
  3914. class WeirdString < String
  3915. def initialize(obj)
  3916. super obj
  3917. end
  3918. def +(anotherObj) # + method in this class is overridden
  3919. # to return the sum of string lengths
  3920. self.length + anotherObj.length # 'self' can be omitted
  3921. end
  3922. end
  3923. #-----------------------------
  3924. a = WeirdString.new('hello')
  3925. b = WeirdString.new('bye')
  3926. puts a + b # the overridden +
  3927. #=> 8
  3928. puts a.length # method from the superclass, String
  3929. #=> 5
  3930. # @@PLEAC@@_13.11
  3931. # In ruby you can override the method_missing method
  3932. # to have a solution similar to perls AUTOLOAD.
  3933. class Person
  3934. def initialize
  3935. @ok_fields = %w(name age peers parent)
  3936. end
  3937. def valid_attribute?(name)
  3938. @ok_fields.include?(name)
  3939. end
  3940. def method_missing(namesymbol, *params)
  3941. name = namesymbol.to_s
  3942. return if name =~ /^A-Z/
  3943. if name.to_s[-1] == ('='[0]) # we have a setter
  3944. isSetter = true
  3945. name.sub!(/=$/, '')
  3946. end
  3947. if valid_attribute?(name)
  3948. if isSetter
  3949. instance_variable_set("@#{name}", *params)
  3950. else
  3951. instance_variable_get("@#{name}", *params)
  3952. end
  3953. else
  3954. # if no annestor is responsible,
  3955. # the Object class will throw a NoMethodError exception
  3956. super(namesymbol, *params)
  3957. end
  3958. end
  3959. def new
  3960. kid = Person.new
  3961. kid.parent = self
  3962. kid
  3963. end
  3964. end
  3965. dad = Person.new
  3966. dad.name = "Jason"
  3967. dad.age = 23
  3968. kid = dad.new
  3969. kid.name = "Rachel"
  3970. kid.age = 2
  3971. puts "Kid's parent is #{kid.parent.name}"
  3972. puts dad
  3973. puts kid
  3974. class Employee < Person
  3975. def initialize
  3976. super
  3977. @ok_fields.push("salary", "boss")
  3978. end
  3979. def ok_fields
  3980. @ok_fields
  3981. end
  3982. end
  3983. # @@PLEAC@@_13.13
  3984. # The ruby garbage collector pretends to cope with circular structures.
  3985. # You can test it with this code:
  3986. class RingNode
  3987. attr_accessor :next
  3988. attr_accessor :prev
  3989. attr_reader :name
  3990. def initialize(aName)
  3991. @name = aName
  3992. ObjectSpace.define_finalizer(self,
  3993. self.class.method(:finalize).to_proc)
  3994. end
  3995. def RingNode.finalize(id)
  3996. puts "Node #{id} dying"
  3997. end
  3998. def RingNode.show_all_objects
  3999. ObjectSpace.each_object {|id|
  4000. puts id.name if id.class == RingNode
  4001. }
  4002. end
  4003. end
  4004. def create_test
  4005. a = RingNode.new("Node A")
  4006. b = RingNode.new("Node B")
  4007. c = RingNode.new("Node C")
  4008. a.next = b
  4009. b.next = c
  4010. c.next = a
  4011. a.prev = c
  4012. c.prev = b
  4013. b.prev = a
  4014. a = nil
  4015. b = nil
  4016. c = nil
  4017. end
  4018. create_test
  4019. RingNode.show_all_objects
  4020. ObjectSpace.garbage_collect
  4021. puts "After garbage collection"
  4022. RingNode.show_all_objects
  4023. # @@PLEAC@@_13.14
  4024. class String
  4025. def <=>(other)
  4026. self.casecmp other
  4027. end
  4028. end
  4029. # There is no way to directly overload the '""' (stringify)
  4030. # operator in Ruby. However, by convention, classes which
  4031. # can reasonably be converted to a String will define a
  4032. # 'to_s' method as in the TimeNumber class defined below.
  4033. # The 'puts' method will automatcally call an object's
  4034. # 'to_s' method as is demonstrated below.
  4035. # Furthermore, if a class defines a to_str method, an object of that
  4036. # class can be used most any place where the interpreter is looking
  4037. # for a String value.
  4038. #---------------------------------------
  4039. # NOTE: Ruby has a builtin Time class which would usually be used
  4040. # to manipulate time objects, the following is supplied for
  4041. # educational purposes to demonstrate operator overloading.
  4042. #
  4043. class TimeNumber
  4044. attr_accessor :hours,:minutes,:seconds
  4045. def initialize( hours, minutes, seconds)
  4046. @hours = hours
  4047. @minutes = minutes
  4048. @seconds = seconds
  4049. end
  4050. def to_s
  4051. return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds)
  4052. end
  4053. def to_str
  4054. to_s
  4055. end
  4056. def +( other)
  4057. seconds = @seconds + other.seconds
  4058. minutes = @minutes + other.minutes
  4059. hours = @hours + other.hours
  4060. if seconds >= 60
  4061. seconds %= 60
  4062. minutes += 1
  4063. end
  4064. if minutes >= 60
  4065. minutes %= 60
  4066. hours += 1
  4067. end
  4068. return TimeNumber.new(hours, minutes, seconds)
  4069. end
  4070. def -(other)
  4071. raise NotImplementedError
  4072. end
  4073. def *(other)
  4074. raise NotImplementedError
  4075. end
  4076. def /( other)
  4077. raise NotImplementedError
  4078. end
  4079. end
  4080. t1 = TimeNumber.new(0, 58, 59)
  4081. sec = TimeNumber.new(0, 0, 1)
  4082. min = TimeNumber.new(0, 1, 0)
  4083. puts t1 + sec + min + min
  4084. #-----------------------------
  4085. # StrNum class example: Ruby's builtin String class already has the
  4086. # capabilities outlined in StrNum Perl example, however the '*' operator
  4087. # on Ruby's String class acts differently: It creates a string which
  4088. # is the original string repeated N times.
  4089. #
  4090. # Using Ruby's String class as is in this example:
  4091. x = "Red"; y = "Black"
  4092. z = x+y
  4093. r = z*3 # r is "RedBlackRedBlackRedBlack"
  4094. puts "values are #{x}, #{y}, #{z}, and #{r}"
  4095. print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n"
  4096. # prints:
  4097. # values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
  4098. # Red is GE Black
  4099. #-----------------------------
  4100. class FixNum
  4101. REGEX = /(\.\d*)/
  4102. DEFAULT_PLACES = 0
  4103. attr_accessor :value, :places
  4104. def initialize(value, places = nil)
  4105. @value = value
  4106. if places
  4107. @places = places
  4108. else
  4109. m = REGEX.match(value.to_s)
  4110. if m
  4111. @places = m[0].length - 1
  4112. else
  4113. @places = DEFAULT_PLACES
  4114. end
  4115. end
  4116. end
  4117. def +(other)
  4118. FixNum.new(@value + other.value, max(@places, other.places))
  4119. end
  4120. def *(other)
  4121. FixNum.new(@value * other.value, max(@places, other.places))
  4122. end
  4123. def /(other)
  4124. puts "Divide: #{@value.to_f/other.value.to_f}"
  4125. result = FixNum.new(@value.to_f/other.value.to_f)
  4126. result.places = max(result.places,other.places)
  4127. result
  4128. end
  4129. def to_s
  4130. sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #.
  4131. end
  4132. def to_str
  4133. to_s
  4134. end
  4135. def to_i #convert to int
  4136. @value.to_i
  4137. end
  4138. def to_f #convert to float`
  4139. @value.to_f
  4140. end
  4141. private
  4142. def max(a,b)
  4143. a > b ? a : b
  4144. end
  4145. end
  4146. def demo()
  4147. x = FixNum.new(40)
  4148. y = FixNum.new(12, 0)
  4149. puts "sum of #{x} and #{y} is #{x+y}"
  4150. puts "product of #{x} and #{y} is #{x*y}"
  4151. z = x/y
  4152. puts "#{z} has #{z.places} places"
  4153. unless z.places
  4154. z.places = 2
  4155. end
  4156. puts "div of #{x} by #{y} is #{z}"
  4157. puts "square of that is #{z*z}"
  4158. end
  4159. if __FILE__ == $0
  4160. demo()
  4161. end
  4162. # @@PLEAC@@_14.1
  4163. # There are dbm, sdbm, gdbm modules
  4164. # and the bdb module for accessing the berkeley db
  4165. # sdbm seem to be available on the most systems,
  4166. # so we use it here
  4167. #
  4168. require "sdbm"
  4169. SDBM.open("filename", 0666) { |dbobj|
  4170. # raises exception if open error
  4171. # the returned sdbm-dbobj has most of the methods of a hash
  4172. v = dbobj["key"]
  4173. dbobj["key"] = "newvalue"
  4174. if dbobj.has_key?("key")
  4175. # ...
  4176. end
  4177. dbobj.delete("key2")
  4178. }
  4179. # database is open only inside the block.
  4180. # It is also possible to use a open .. close pair:
  4181. dbobj = SDBM.open("filename", 0666)
  4182. #.. do something with dbobj
  4183. dbobj.close
  4184. #!/usr/bin/ruby -w
  4185. # userstats - generate statistics on who is logged in
  4186. # call with usernames as argument to display the totals
  4187. # for the given usernames, call with "ALL" to display all users
  4188. require "sdbm"
  4189. filename = '/tmp/userstats.db'
  4190. SDBM.open(filename, 0666) { |dbobj|
  4191. if ARGV.length > 0
  4192. if ARGV[0] == "ALL"
  4193. # ARGV is constant, so we need the variable userlist
  4194. userlist = dbobj.keys().sort()
  4195. else
  4196. userlist = ARGV
  4197. end
  4198. userlist.each { |user|
  4199. print "#{user}\t#{dbobj[user]}\n"
  4200. }
  4201. else
  4202. who = `who`
  4203. who.split("\n").each { |line|
  4204. md = /^(\S+)/.match(line)
  4205. raise "Bad line from who: #{line}" unless md
  4206. # sdbm stores only strings, so "+=" doesn't work,
  4207. # we need to convert them expicitly back to integer.
  4208. if dbobj.has_key?(md[0])
  4209. dbobj[md[0]] = dbobj[md[0]].to_i + 1
  4210. else
  4211. dbobj[md[0]] = "1"
  4212. end
  4213. }
  4214. end
  4215. }
  4216. # @@PLEAC@@_14.2
  4217. # using open and clear
  4218. dbobj = SDBM.open("filename", 0666)
  4219. dbobj.clear()
  4220. dbobj.close()
  4221. # deleting file and recreating it
  4222. # the filenames depend on the flavor of dbm you use,
  4223. # for example sdbm has two files named filename.pag and filename.dir,
  4224. # so you need to delete both files
  4225. begin
  4226. File.delete("filename")
  4227. # raises Exception if not exist
  4228. dbobj = SDBM.open("filename", 0666)
  4229. rescue
  4230. # add error handling here
  4231. end
  4232. # @@PLEAC@@_14.3
  4233. # sdbm2gdbm: converts sdbm database to a gdbm database
  4234. require "sdbm"
  4235. require "gdbm"
  4236. unless ARGV.length == 2
  4237. fail "usage: sdbm2gdbm infile outfile"
  4238. end
  4239. infile = ARGV[0]
  4240. outfile = ARGV[1]
  4241. sdb = SDBM.open(infile)
  4242. gdb = GDBM.open(outfile, 0666)
  4243. sdb.each { |key, val|
  4244. gdb[key] = val
  4245. }
  4246. gdb.close
  4247. sdb.close
  4248. # @@PLEAC@@_14.4
  4249. #!/usr/bin/ruby -w
  4250. # dbmmerge: merges two dbm databases
  4251. require "sdbm"
  4252. unless ARGV.length == 3
  4253. fail "usage: dbmmerge indb1 indb2 outdb"
  4254. end
  4255. infile1 = ARGV[0]
  4256. infile2 = ARGV[0]
  4257. outfile = ARGV[2]
  4258. in1 = SDBM.open(infile1, nil)
  4259. in2 = SDBM.open(infile2, nil)
  4260. outdb = SDBM.open(outfile, 0666)
  4261. [in1, in2].each { |indb|
  4262. indb.each { |key, val|
  4263. if outdb.has_key?(key)
  4264. # decide which value to set.
  4265. # set outdb[key] if necessary
  4266. else
  4267. outdb[key] = val
  4268. end
  4269. }
  4270. }
  4271. in1.close
  4272. in2.close
  4273. outdb.close
  4274. # @@PLEAC@@_14.7
  4275. # we write a tie method that extends the Array class.
  4276. # It reads the file into the memory, executes the code block
  4277. # in which you can manipulate the array as needed, and writes
  4278. # the array back to the file after the end of the block execution
  4279. class Array
  4280. def tie(filename, flags)
  4281. File.open(filename, flags) { |f|
  4282. f.each_line { |line|
  4283. self.push(line.chomp)
  4284. }
  4285. yield
  4286. f.rewind
  4287. each { |line|
  4288. if line
  4289. f.puts(line)
  4290. else
  4291. f.puts ""
  4292. end
  4293. }
  4294. }
  4295. end
  4296. end
  4297. array = Array.new
  4298. array.tie("/tmp/textfile.txt", File::RDWR|File::CREAT) {
  4299. array[4] = "a new line 4"
  4300. }
  4301. # The tied array can be manipulated like a normal array,
  4302. # so there is no need for a special API, and the recno_demo program
  4303. # to demonstrate is API is useless
  4304. # tied array demo: show how to use array with a tied file
  4305. filename = "db_file.txt"
  4306. lines = Array.new
  4307. File.unlink(filename) if File.exists?(filename)
  4308. lines.tie(filename, File::RDWR | File::CREAT) {
  4309. # first create a textfile to play with
  4310. lines[0] = "zero"
  4311. lines[1] = "one"
  4312. lines[2] = "two"
  4313. lines[3] = "three"
  4314. lines[4] = "four"
  4315. # print the records in order.
  4316. # Opposed to perl, the tied array behaves exactly as a normal array
  4317. puts "\nOriginal"
  4318. for i in 0..(lines.length-1)
  4319. puts "#{i}: #{lines[i]}"
  4320. end
  4321. #use push and pop
  4322. a = lines.pop
  4323. lines.push("last")
  4324. puts("The last line was [#{a}]")
  4325. #use shift and unshift
  4326. a = lines.shift
  4327. lines.unshift("first")
  4328. puts("The first line was [#{a}]")
  4329. # add record after record 2
  4330. i = 2
  4331. lines.insert(i + 1, "Newbie")
  4332. # add record before record one
  4333. i = 1
  4334. lines.insert(i, "New One")
  4335. # delete record 3
  4336. lines.delete_at(3)
  4337. #now print the records in reverse order
  4338. puts "\nReverse"
  4339. (lines.length - 1).downto(0){ |i|
  4340. puts "#{i}: #{lines[i]}"
  4341. }
  4342. }
  4343. # @@PLEAC@@_14.8
  4344. # example to store complex data in a database
  4345. # uses marshall from the standard library
  4346. require "sdbm"
  4347. db = SDBM.open("pleac14-8-database", 0666)
  4348. # convert the Objects into strings and back by using the Marshal module.
  4349. # Most normal objects can be converted out of the box,
  4350. # but not special things like procedure objects,
  4351. # IO instance variables, singleton objects
  4352. db["Tom Christiansen"] = Marshal.dump(["book author", "tchrist@perl.com"])
  4353. db["Tom Boutell"] = Marshal.dump(["shareware author",
  4354. "boutell@boutell.com"])
  4355. name1 = "Tom Christiansen"
  4356. name2 = "Tom Boutell"
  4357. tom1 = Marshal.load(db[name1])
  4358. tom2 = Marshal.load(db[name2])
  4359. puts "Two Toming: #{tom1} #{tom2}"
  4360. if tom1[0] == tom2[0] && tom1[1] == tom2[1]
  4361. puts "You're having runtime fun with one Tom made two."
  4362. else
  4363. puts "No two Toms are ever alike"
  4364. end
  4365. # To change parts of an entry, get the whole entry, change the parts,
  4366. # and save the whole entry back
  4367. entry = Marshal.load(db["Tom Boutell"])
  4368. entry[0] = "Poet Programmer"
  4369. db["Tom Boutell"] = Marshal.dump(entry)
  4370. db.close
  4371. # @@PLEAC@@_14.9
  4372. # example to make data persistent
  4373. # uses Marshal from the standard lib
  4374. # Stores the data in a simple file,
  4375. # see 14.8 on how to store it in a dbm file
  4376. # The BEGIN block is executed before the rest of the script
  4377. # we use global variables here because local variables
  4378. # will go out of scope and are not accessible from the main script
  4379. BEGIN {
  4380. $persistent_store = "persitence.dat"
  4381. begin
  4382. File.open($persistent_store) do |f|
  4383. $stringvariable1 = Marshal.load(f)
  4384. $arrayvariable2 = Marshal.load(f)
  4385. end
  4386. rescue
  4387. puts "Can not open #{$persistent_store}"
  4388. # Initialisation if this script runs the first time
  4389. $stringvariable1 = ""
  4390. $arrayvariable2 = []
  4391. end
  4392. }
  4393. END {
  4394. File.open($persistent_store, "w+") do |f|
  4395. Marshal.dump($stringvariable1, f)
  4396. Marshal.dump($arrayvariable2, f)
  4397. end
  4398. }
  4399. # simple test program
  4400. puts $stringvariable1
  4401. puts $arrayvariable2
  4402. $stringvariable1 = "Hello World"
  4403. $arrayvariable2.push(5)
  4404. puts $stringvariable1
  4405. puts $arrayvariable2
  4406. # @@PLEAC@@_14.10
  4407. #!/usr/bin/ruby -w
  4408. # Ruby has a dbi module with an architecture similar
  4409. # to the Perl dbi module: the dbi module provides an unified
  4410. # interface and uses specialized drivers for each dbms vendor
  4411. #
  4412. begin
  4413. DBI.connect("DBI:driver:driverspecific", "username", "auth") {
  4414. |dbh|
  4415. dbh.do(SQL1)
  4416. dbh.prepare(SQL2){ |sth|
  4417. sth.execute
  4418. sth.fetch {|row|
  4419. # ...
  4420. }
  4421. } # end of block finishes the statement handle
  4422. } # end of block closes the database connection
  4423. rescue DBI::DatabaseError => e
  4424. puts "dbi error occurred"
  4425. puts "Error code: #{e.err}"
  4426. puts "Error message: #{e.errstr}"
  4427. end
  4428. #!/usr/bin/ruby -w
  4429. # dbusers - example for mysql which creates a table,
  4430. # fills it with values, retrieves the values back,
  4431. # and finally destroys the table.
  4432. require "dbi"
  4433. # replacement for the User::pwnt module
  4434. def getpwent
  4435. result = []
  4436. File.open("/etc/passwd") {|file|
  4437. file.each_line {|line|
  4438. next if line.match(/^#/)
  4439. cols = line.split(":")
  4440. result.push([cols[2], cols[0]])
  4441. }
  4442. }
  4443. result
  4444. end
  4445. begin
  4446. DBI.connect("DBI:Mysql:pleacdatabase", "pleac", "pleacpassword") {
  4447. |conn|
  4448. conn.do("CREATE TABLE users (uid INT, login CHAR(8))")
  4449. users = getpwent
  4450. conn.prepare("INSERT INTO users VALUES (?,?)") {|sth|
  4451. users.each {|entry|
  4452. sth.execute(entry[0], entry[1])
  4453. }
  4454. }
  4455. conn.execute("SELECT uid, login FROM users WHERE uid < 50") {|sth|
  4456. sth.fetch {|row|
  4457. puts row.collect {|col|
  4458. if col.nil?
  4459. "(null)"
  4460. else
  4461. col
  4462. end
  4463. }.join(", ")
  4464. }
  4465. }
  4466. conn.do("DROP TABLE users")
  4467. }
  4468. rescue DBI::DatabaseError => e
  4469. puts "dbi error occurred"
  4470. puts "Error code: #{e.err}"
  4471. puts "Error message: #{e.errstr}"
  4472. end
  4473. # @@PLEAC@@_15.1
  4474. # This test program demonstrates parsing program arguments.
  4475. # It uses the optparse library, which is included with ruby 1.8
  4476. # It handles classic unix style and gnu style options
  4477. require 'optparse'
  4478. @debugmode = false
  4479. @verbose = false
  4480. ARGV.options do |opts|
  4481. opts.banner = "Usage: ruby #{$0} [OPTIONS] INPUTFILES"
  4482. opts.on("-h", "--help", "show this message") {
  4483. puts opts
  4484. exit
  4485. }
  4486. # The OptionParser#on method is called with a specification of short
  4487. # options, of long options, a data type spezification and user help
  4488. # messages for this option.
  4489. # The method analyses the given parameter and decides what it is,
  4490. # so you can leave out the long option if you don't need it
  4491. opts.on("-v", "--[no-]verbose=[FLAG]", TrueClass, "run verbosly") {
  4492. |@verbose| # sets @verbose to true or false
  4493. }
  4494. opts.on("-D", "--DEBUG", TrueClass, "turns on debug mode" ){
  4495. |@debugmode| # sets @debugmode to true
  4496. }
  4497. opts.on("-c", "--count=NUMBER", Integer, "how many times we do it" ){
  4498. |@count| # sets @count to given integer
  4499. }
  4500. opts.on("-o", "--output=FILE", String, "file to write output to"){
  4501. |@outputfile| # sets @outputfile to given string
  4502. }
  4503. opts.parse!
  4504. end
  4505. # example to use the options in the main program
  4506. puts "Verbose is on" if @verbose
  4507. puts "Debugmode is on" if @debugmode
  4508. puts "Outfile is #{@outputfile}" if defined? @outputfile
  4509. puts "Count is #{@count}" if defined? @count
  4510. ARGV.each { |param|
  4511. puts "Got parameter #{param}"
  4512. }
  4513. # @@PLEAC@@_15.4
  4514. buf = "\0" * 8
  4515. $stdout.ioctl(0x5413, buf)
  4516. ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("S4")
  4517. raise "You must have at least 20 characters" unless ws_col >= 20
  4518. max = 0
  4519. values = (1..5).collect { rand(20) } # generate an array[5] of rand values
  4520. for i in values
  4521. max = i if max < i
  4522. end
  4523. ratio = Float(ws_col-12)/max # chars per unit
  4524. for i in values
  4525. printf "%8.1f %s\n", i, "*" * (ratio*i)
  4526. end
  4527. # gives, for example:
  4528. # 15.0 *******************************
  4529. # 10.0 *********************
  4530. # 5.0 **********
  4531. # 14.0 *****************************
  4532. # 18.0 **************************************
  4533. # @@PLEAC@@_16.1
  4534. output = `program args` # collect output into one multiline string
  4535. output = `program args`.split # collect output into array, one line per
  4536. element
  4537. readme = IO.popen("ls")
  4538. output = ""
  4539. while readme.gets do
  4540. output += $_
  4541. end
  4542. readme.close
  4543. `fsck -y /dev/rsd1a` # BAD AND SCARY in Perl because it's managed by the shell
  4544. # I donna in Ruby ...
  4545. # so the "clean and secure" version
  4546. readme, writeme = IO.pipe
  4547. pid = fork {
  4548. # child
  4549. $stdout = writeme
  4550. readme.close
  4551. exec('find', '..')
  4552. }
  4553. # parent
  4554. Process.waitpid(pid, 0)
  4555. writeme.close
  4556. while readme.gets do
  4557. # do something with $_
  4558. end
  4559. # @@PLEAC@@_16.2
  4560. status = system("xemacs #{myfile}")
  4561. status = system("xemacs", myfile)
  4562. system("cmd1 args | cmd2 | cmd3 >outfile")
  4563. system("cmd args <infile >outfile 2>errfile")
  4564. # stop if the command fails
  4565. raise "$program exited funny: #{$?}" unless system("cmd", "args1", "args2")
  4566. # get the value of the signal sent to the child
  4567. # even if it is a SIGINT or SIGQUIT
  4568. system(arglist)
  4569. raise "program killed by signal #{$?}" if ($? & 127) != 0
  4570. pid = fork {
  4571. trap("SIGINT", "IGNORE")
  4572. exec("sleep", "10")
  4573. }
  4574. trap ("SIGINT") {
  4575. puts "Tsk tsk, no process interruptus"
  4576. }
  4577. Process.waitpid(pid, 0)
  4578. # Ruby doesn't permit to lie to the program called by a 'system'.
  4579. # (ie specify what return argv[0] in C, $0 in Perl/Ruby ...)
  4580. # A (dirty) way is to create a link (under Unix), run this link and
  4581. # erase it. Somebody has a best idea ?
  4582. # @@PLEAC@@_16.3
  4583. exec("archive *.data")
  4584. exec("archive", "accounting.data")
  4585. exec("archive accounting.data")
  4586. # @@PLEAC@@_16.4
  4587. # read the output of a program
  4588. IO.popen("ls") {|readme|
  4589. while readme.gets do
  4590. # ...
  4591. end
  4592. }
  4593. # or
  4594. readme = IO.popen("ls")
  4595. while readme.gets do
  4596. # ...
  4597. end
  4598. readme.close
  4599. # "write" in a program
  4600. IO.popen("cmd args","w") {|pipe|
  4601. pipe.puts("data")
  4602. pipe.puts("foo")
  4603. }
  4604. # close wait for the end of the process
  4605. read = IO.popen("sleep 10000") # child goes to sleep
  4606. read.close # and the parent goes to lala land
  4607. writeme = IO.popen("cmd args", "w")
  4608. writeme.puts "hello" # program will get hello\n on STDIN
  4609. writeme.close # program will get EOF on STDIN
  4610. # send in a pager (eg less) all output
  4611. $stdout = IO.popen("/usr/bin/less","w")
  4612. print "huge string\n" * 10000
  4613. # @@PLEAC@@_16.5
  4614. #-----------------------------
  4615. def head(lines = 20)
  4616. pid = open("|-","w")
  4617. if pid == nil
  4618. return
  4619. else
  4620. while gets() do
  4621. pid.print
  4622. lines -= 1
  4623. break if lines == 0
  4624. end
  4625. end
  4626. exit
  4627. end
  4628. head(100)
  4629. while gets() do
  4630. print
  4631. end
  4632. #-----------------------------
  4633. 1: > Welcome to Linux, version 2.0.33 on a i686
  4634. 2: >
  4635. 3: > "The software required `Windows 95 or better',
  4636. 4: > so I installed Linux."
  4637. #-----------------------------
  4638. > 1: Welcome to Linux, Kernel version 2.0.33 on a i686
  4639. > 2:
  4640. > 3: "The software required `Windows 95 or better',
  4641. > 4: so I installed Linux."
  4642. #-----------------------------
  4643. #!/usr/bin/ruby
  4644. # qnumcat - demo additive output filters
  4645. def number()
  4646. pid = open("|-","w")
  4647. if pid == nil
  4648. return
  4649. else
  4650. while gets() do pid.printf("%d: %s", $., $_); end
  4651. end
  4652. exit
  4653. end
  4654. def quote()
  4655. pid = open("|-","w")
  4656. if pid == nil
  4657. return
  4658. else
  4659. while gets() do pid.print "> #{$_}" end
  4660. end
  4661. exit
  4662. end
  4663. number()
  4664. quote()
  4665. while gets() do
  4666. print
  4667. end
  4668. $stdout.close
  4669. exit
  4670. # @@PLEAC@@_16.6
  4671. ARGV.map! { |arg|
  4672. arg =~ /\.(gz|Z)$/ ? "|gzip -dc #{arg}" : arg
  4673. }
  4674. for file in ARGV
  4675. fh = open(file)
  4676. while fh.gets() do
  4677. # .......
  4678. end
  4679. end
  4680. #-----------------------------
  4681. ARGV.map! { |arg|
  4682. arg =~ %r#^\w+://# ? "|GET #{arg}" : arg #
  4683. }
  4684. for file in ARGV
  4685. fh = open(file)
  4686. while fh.gets() do
  4687. # .......
  4688. end
  4689. end
  4690. #-----------------------------
  4691. pwdinfo = (`domainname` =~ /^(\(none\))?$/) ? '/etc/passwd' : '|ypcat passwd';
  4692. pwd = open(pwdinfo);
  4693. #-----------------------------
  4694. puts "File, please? ";
  4695. file = gets().chomp();
  4696. fh = open(file);
  4697. # @@PLEAC@@_16.7
  4698. output = `cmd 2>&1` # with backticks
  4699. # or
  4700. ph = open("|cmd 2>&1") # with an open pipe
  4701. while ph.gets() { } # plus a read
  4702. #-----------------------------
  4703. output = `cmd 2>/dev/null` # with backticks
  4704. # or
  4705. ph = open("|cmd 2>/dev/null") # with an open pipe
  4706. while ph.gets() { } # plus a read
  4707. #-----------------------------
  4708. output = `cmd 2>&1 1>/dev/null` # with backticks
  4709. # or
  4710. ph = open("|cmd 2>&1 1>/dev/null") # with an open pipe
  4711. while ph.gets() { } # plus a read
  4712. #-----------------------------
  4713. output = `cmd 3>&1 1>&2 2>&3 3>&-` # with backticks
  4714. # or
  4715. ph = open("|cmd 3>&1 1>&2 2>&3 3>&-") # with an open pipe
  4716. while ph.gets() { } # plus a read
  4717. #-----------------------------
  4718. system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr")
  4719. #-----------------------------
  4720. output = `cmd 3>&1 1>&2 2>&3 3>&-`
  4721. #-----------------------------
  4722. fd3 = fd1
  4723. fd1 = fd2
  4724. fd2 = fd3
  4725. fd3 = nil
  4726. #-----------------------------
  4727. system("prog args 1>tmpfile 2>&1")
  4728. system("prog args 2>&1 1>tmpfile")
  4729. #-----------------------------
  4730. # system ("prog args 1>tmpfile 2>&1")
  4731. fd1 = "tmpfile" # change stdout destination first
  4732. fd2 = fd1 # now point stderr there, too
  4733. #-----------------------------
  4734. # system("prog args 2>&1 1>tmpfile")
  4735. fd2 = fd1 # stderr same destination as stdout
  4736. fd1 = "tmpfile" # but change stdout destination
  4737. #-----------------------------
  4738. # It is often better not to rely on the shell,
  4739. # because of portability, possible security problems
  4740. # and bigger resource usage. So, it is often better to use the open3 library.
  4741. # See below for an example.
  4742. # opening stdin, stdout, stderr
  4743. require "open3"
  4744. stdin, stdout, stderr = Open3.popen('cmd')
  4745. # @@PLEAC@@_16.8
  4746. #-----------------------------
  4747. # Contrary to perl, we don't need to use a module in Ruby
  4748. fh = Kernel.open("|" + program, "w+")
  4749. fh.puts "here's your input\n"
  4750. output = fh.gets()
  4751. fh.close()
  4752. #-----------------------------
  4753. Kernel.open("|program"),"w+") # RIGHT !
  4754. #-----------------------------
  4755. # Ruby has already object methods for I/O handles
  4756. #-----------------------------
  4757. begin
  4758. fh = Kernel.open("|" + program_and_options, "w+")
  4759. rescue
  4760. if ($@ ~= /^open/)
  4761. $stderr.puts "open failed : #{$!} \n #{$@} \n"
  4762. break
  4763. end
  4764. raise # reraise unforseen exception
  4765. end
  4766. # @@PLEAC@@_16.13
  4767. #% kill -l
  4768. #HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE
  4769. #ALRM TERM CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM
  4770. #PROF WINCH POLL PWR
  4771. #-----------------------------
  4772. #% ruby -e 'puts Signal.list.keys.join(" ")'
  4773. #PWR USR1 BUS USR2 TERM SEGV KILL POLL STOP SYS TRAP IOT HUP INT #
  4774. #WINCH XCPU TTIN CLD TSTP FPE IO TTOU PROF CHLD CONT PIPE ABRT
  4775. #VTALRM QUIT ILL XFSZ URG ALRM
  4776. #-----------------------------
  4777. # After that, the perl script create an hash equivalent to Signal.list,
  4778. # and an array. The array can be obtained by :
  4779. signame = []
  4780. Signal.list.each { |name, i| signame[i] = name }
  4781. # @@PLEAC@@_16.14
  4782. Process.kill(9, pid) # send $pid a signal 9
  4783. Process.kill(-1, Process.getpgrp()) # send whole job a signal 1
  4784. Process.kill("USR1", $$) # send myself a SIGUSR1
  4785. Process.kill("HUP", pid1, pid2, pid3) # send a SIGHUP to processes in @pids
  4786. #-----------------------------
  4787. begin
  4788. Process.kill(0, minion)
  4789. puts "#{minion} is alive!"
  4790. rescue Errno::EPERM # changed uid
  4791. puts "#{minion} has escaped my control!";
  4792. rescue Errno::ESRCH
  4793. puts "#{minion} is deceased."; # or zombied
  4794. rescue
  4795. puts "Odd; I couldn't check the status of #{minion} : #{$!}"
  4796. end
  4797. # @@PLEAC@@_16.15
  4798. Kernel.trap("QUIT", got_sig_quit) # got_sig_quit = Proc.new { puts "Quit\n" }
  4799. trap("PIPE", "got_sig_quit") # def got_sig_pipe ...
  4800. trap("INT") { ouch++ } # increment ouch for every SIGINT
  4801. #-----------------------------
  4802. trap("INT", "IGNORE") # ignore the signal INT
  4803. #-----------------------------
  4804. trap("STOP", "DEFAULT") # restore default STOP signal handling
  4805. # @@PLEAC@@_16.16
  4806. # the signal handler
  4807. def ding
  4808. trap("INT", "ding")
  4809. puts "\aEnter your name!"
  4810. end
  4811. # prompt for name, overriding SIGINT
  4812. def get_name
  4813. save = trap("INT", "ding")
  4814. puts "Kindly Stranger, please enter your name: "
  4815. name = gets().chomp()
  4816. trap("INT", save)
  4817. name
  4818. end
  4819. # @@PLEAC@@_16.21
  4820. # implemented thanks to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/1760
  4821. require 'timeout'
  4822. # we'll do something vastly more useful than cookbook to demonstrate timeouts
  4823. begin
  4824. timeout(5) {
  4825. waitsec = rand(10)
  4826. puts "Let's see if a sleep of #{waitsec} seconds is longer than 5 seconds..."
  4827. system("sleep #{waitsec}")
  4828. }
  4829. puts "Timeout didn't occur"
  4830. rescue Timeout::Error
  4831. puts "Timed out!"
  4832. end
  4833. # @@PLEAC@@_17.1
  4834. # A basic TCP client connection
  4835. require 'socket'
  4836. begin
  4837. t = TCPSocket.new('www.ruby-lang.org', 'www')
  4838. rescue
  4839. puts "error: #{$!}"
  4840. else
  4841. # ... do something with the socket
  4842. t.print "GET / HTTP/1.0\n\n"
  4843. answer = t.gets(nil)
  4844. # and terminate the connection when we're done
  4845. t.close
  4846. end
  4847. # Using the evil low level socket API
  4848. require 'socket'
  4849. # create a socket
  4850. s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  4851. # build the address of the remote machine
  4852. sockaddr_server = [Socket::AF_INET, 80,
  4853. Socket.gethostbyname('www.ruby-lang.org')[3],
  4854. 0, 0].pack("snA4NN")
  4855. # connect
  4856. begin
  4857. s.connect(sockaddr_server)
  4858. rescue
  4859. puts "error: #{$!}"
  4860. else
  4861. # ... do something with the socket
  4862. s.print "GET / HTTP/1.0\n\n"
  4863. # and terminate the connection when we're done
  4864. s.close
  4865. end
  4866. # TCP connection with management of error (DNS)
  4867. require 'socket'
  4868. begin
  4869. client = TCPSocket.new('does not exists', 'www')
  4870. rescue
  4871. puts "error: #{$!}"
  4872. end
  4873. # TCP connection with a time out
  4874. require 'socket'
  4875. require 'timeout'
  4876. begin
  4877. timeout(1) do #the server has one second to answer
  4878. client = TCPSocket.new('www.host.com', 'www')
  4879. end
  4880. rescue
  4881. puts "error: #{$!}"
  4882. end
  4883. # @@PLEAC@@_17.12
  4884. require 'socket'
  4885. class Preforker
  4886. attr_reader (:child_count)
  4887. def initialize(prefork, max_clients_per_child, port, client_handler)
  4888. @prefork = prefork
  4889. @max_clients_per_child = max_clients_per_child
  4890. @port = port
  4891. @child_count = 0
  4892. @reaper = proc {
  4893. trap('CHLD', @reaper)
  4894. pid = Process.wait
  4895. @child_count -= 1
  4896. }
  4897. @huntsman = proc {
  4898. trap('CHLD', 'IGNORE')
  4899. trap('INT', 'IGNORE')
  4900. Process.kill('INT', 0)
  4901. exit
  4902. }
  4903. @client_handler=client_handler
  4904. end
  4905. def child_handler
  4906. trap('INT', 'EXIT')
  4907. @client_handler.setUp
  4908. # wish: sigprocmask UNblock SIGINT
  4909. @max_clients_per_child.times {
  4910. client = @server.accept or break
  4911. @client_handler.handle_request(client)
  4912. client.close
  4913. }
  4914. @client_handler.tearDown
  4915. end
  4916. def make_new_child
  4917. # wish: sigprocmask block SIGINT
  4918. @child_count += 1
  4919. pid = fork do
  4920. child_handler
  4921. end
  4922. # wish: sigprocmask UNblock SIGINT
  4923. end
  4924. def run
  4925. @server = TCPserver.open(@port)
  4926. trap('CHLD', @reaper)
  4927. trap('INT', @huntsman)
  4928. loop {
  4929. (@prefork - @child_count).times { |i|
  4930. make_new_child
  4931. }
  4932. sleep .1
  4933. }
  4934. end
  4935. end
  4936. #-----------------------------
  4937. #!/usr/bin/ruby
  4938. require 'Preforker'
  4939. class ClientHandler
  4940. def setUp
  4941. end
  4942. def tearDown
  4943. end
  4944. def handle_request(client)
  4945. # do stuff
  4946. end
  4947. end
  4948. server = Preforker.new(1, 100, 3102, ClientHandler.new)
  4949. server.run
  4950. # @@PLEAC@@_18.2
  4951. require 'net/ftp'
  4952. begin
  4953. ftp = Net::FTP::new("ftp.host.com")
  4954. ftp.login(username,password)
  4955. ftp.chdir(directory)
  4956. ftp.get(filename)
  4957. ftp.put(filename)
  4958. rescue Net::FTPError
  4959. $stderr.print "FTP failed: " + $!
  4960. ensure
  4961. ftp.close() if ftp
  4962. end
  4963. # A better solution for a local use could be :
  4964. Net::FTP::new("ftp.host.com") do |ftp|
  4965. ftp.login(username,password)
  4966. ftp.chdir(directory)
  4967. ftp.get(filename)
  4968. ftp.put(filename)
  4969. end
  4970. # If you have only one file to get, there is a simple solution :
  4971. require 'open-uri'
  4972. open("ftp://www.ruby-lang.org/path/filename") do |fh|
  4973. # read from filehandle fh
  4974. end
  4975. #--------------------------------------------
  4976. # to wait a defined time for the connection,
  4977. # use the timeout module
  4978. require 'timeout'
  4979. begin
  4980. timeout(30){
  4981. ftp = Net::FTP::new("ftp.host.com")
  4982. ftp.debug_mode = true
  4983. }
  4984. rescue Net::FTPError
  4985. $stderr.puts "Couldn't connect."
  4986. rescue Timeout::Error
  4987. $stderr.puts "Timeout while connecting to server."
  4988. end
  4989. begin
  4990. ftp.login()
  4991. rescue Net::FTPError
  4992. $stderr.print "Couldn't authentificate.\n"
  4993. end
  4994. begin
  4995. ftp.login(username)
  4996. rescue Net::FTPError
  4997. $stderr.print "Still couldn't authenticate.\n"
  4998. end
  4999. begin
  5000. ftp.login(username, password)
  5001. rescue Net::FTPError
  5002. $stderr.print "Couldn't authenticate, even with explicit
  5003. username and password.\n"
  5004. end
  5005. begin
  5006. ftp.login(username, password, account)
  5007. rescue Net::FTPError
  5008. $stderr.print "No dice. It hates me.\n"
  5009. end
  5010. #-----------------------------
  5011. ftp.put(localfile, remotefile)
  5012. #-----------------------------
  5013. # Sending data from STDIN is not directly supported
  5014. # by the ftp library module. A possible way to do it is to use the
  5015. # storlines method directly to send raw commands to the ftp server.
  5016. #-----------------------------
  5017. ftp.get(remotefile, localfile)
  5018. #-----------------------------
  5019. ftp.get(remotefile) { |data| puts data }
  5020. #-----------------------------
  5021. ftp.chdir("/pub/ruby")
  5022. print "I'm in the directory ", ftp.pwd(), "\n"
  5023. #-----------------------------
  5024. ftp.mkdir("/pub/ruby/new_dir")
  5025. #-----------------------------
  5026. lines = ftp.ls("/pub/ruby/")
  5027. # => ["drwxr-xr-x 2 matz users 4096 July 17 1998 1.0", ... ]
  5028. latest = ftp.dir("/pub/ruby/*.tgz").sort.last
  5029. ftp.nlst("/pub/ruby")
  5030. # => ["/pub/ruby/1.0", ... ]
  5031. #-----------------------------
  5032. ftp.quit()
  5033. # @@PLEAC@@_18.6
  5034. require 'net/telnet'
  5035. t = Net::Telnet::new( "Timeout" => 10,
  5036. "Prompt" => /%/,
  5037. "Host" => host )
  5038. t.login(username, password)
  5039. files = t.cmd("ls")
  5040. t.print("top")
  5041. process_string = t.waitfor(/\d+ processes/)
  5042. t.close
  5043. #-----------------------------
  5044. /[$%#>] \z/n
  5045. #-----------------------------
  5046. # In case of an error, the telnet module throws an exception.
  5047. # For control of the behavior in case of an error,
  5048. # you just need to catch the exceptions and do your custom
  5049. # error handling.
  5050. #-----------------------------
  5051. begin
  5052. telnet.login(username, password)
  5053. rescue TimeoutError
  5054. fail "Login failed !\n"
  5055. end
  5056. #-----------------------------
  5057. telnet.waitfor('/--more--/')
  5058. #-----------------------------
  5059. telnet.waitfor(String => 'greasy smoke', Timeout => 30)
  5060. # @@PLEAC@@_18.7
  5061. require 'ping'
  5062. puts "#{host} is alive.\n" if Ping.pingecho(host);
  5063. #-----------------------------
  5064. # the ping module only use TCP ping, not ICMP even if we are root
  5065. if Ping.pingecho("kingkong.com")
  5066. puts "The giant ape lives!\n";
  5067. else
  5068. puts "All hail mighty Gamera, friend of children!\n";
  5069. end
  5070. # @@PLEAC@@_19.0
  5071. #-----------------------------
  5072. # http://www.perl.com/CPAN/
  5073. # http://www.perl.com:8001/bad/mojo.html
  5074. # ftp://gatekeeper.dec.com/pub/misc/netlib.tar.Z
  5075. # ftp://anonymous@myplace:gatekeeper.dec.com/pub/misc/netlib.tar.Z
  5076. # file:///etc/motd
  5077. #-----------------------------
  5078. # http://mox.perl.com/cgi-bin/program?name=Johann&born=1685
  5079. #-----------------------------
  5080. # http://mox.perl.com/cgi-bin/program
  5081. #-----------------------------
  5082. # @@PLEAC@@_19.1
  5083. #!/usr/local/bin/ruby -w
  5084. # hiweb - load CGI class to decode information given by web server
  5085. require 'cgi'
  5086. cgi = CGI.new('html3')
  5087. # get a parameter from a form
  5088. value = cgi.params['PARAM_NAME'][0]
  5089. # output a document
  5090. cgi.out {
  5091. cgi.html {
  5092. cgi.head { cgi.title { "Howdy there!" } } +
  5093. cgi.body { cgi.p { "You typed: " + cgi.tt {
  5094. CGI.escapeHTML(value) } } }
  5095. }
  5096. }
  5097. require 'cgi'
  5098. cgi = CGI.new
  5099. who = cgi.param["Name"][0] # first param in list
  5100. phone = cgi.param["Number"][0]
  5101. picks = cgi.param["Choices"] # complete list
  5102. print cgi.header( 'type' => 'text/plain',
  5103. 'expires' => Time.now + (3 * 24 * 60 * 60) )
  5104. # @@PLEAC@@_19.3
  5105. #!/usr/local/bin/ruby -w
  5106. # webwhoami - show web user's id
  5107. require 'etc'
  5108. print "Content-Type: text/plain\n\n"
  5109. print "Running as " + Etc.getpwuid.name + "\n"
  5110. # % ruby -wc cgi-script # just check syntax
  5111. # % ruby -w cgi-script # params from stdin
  5112. # (offline mode: enter name=value pairs on standard input)
  5113. # name=joe
  5114. # number=10
  5115. # ^D
  5116. # % ruby -w cgi-script name=joe number=10 # run with mock form input
  5117. # % ruby -d cgi-script name=joe number=10 # ditto, under the debugger
  5118. # POST method script in csh
  5119. # % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10)
  5120. # POST method script in sh
  5121. # % HTTP_METHOD=POST perl -w cgi-script name=joe number=10
  5122. # @@PLEAC@@_19.4
  5123. # ruby has several security levels, the level "1" is similar to perls taint mode.
  5124. # It can be switched on by providing the -T command line parameter
  5125. # or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible
  5126. # harmful operations further.
  5127. #!/usr/bin/ruby -T
  5128. $SAFE = 1
  5129. File.open(ARGV[0], "w")
  5130. # ruby warns with:
  5131. # taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError)
  5132. $SAFE = 1
  5133. file = ARGV[0]
  5134. unless /^([\w.-]+)$/.match(file)
  5135. raise "filename #{file} has invalid characters"
  5136. end
  5137. file = $1
  5138. # In ruby, even the back reference from a regular expression stays tainted.
  5139. # you need to explicitly untaint the variable:
  5140. file.untaint
  5141. File.open(file, "w")
  5142. # Race condition exists like in perl:
  5143. unless File.exists(filename) # Wrong because of race condition
  5144. File.open(filename, "w")
  5145. end
  5146. # @@PLEAC@@_19.8
  5147. url = "http://pleac.sourceforge.net/pleac_ruby/"
  5148. print "Location: #{url}\r\n\r\n"
  5149. exit
  5150. #!/usr/bin/ruby
  5151. require 'cgi'
  5152. cgi = CGI.new
  5153. oreo = CGI::Cookie.new('name' => 'filling',
  5154. 'value' => 'vanilla creme',
  5155. 'expires' => Time.now + (3 * 30 * 24 * 60 * 60),
  5156. 'domain' => '.pleac.sourceforge.net')
  5157. whither = 'http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html'
  5158. cgi.out('cookie' => oreo,
  5159. 'Location' => whither){""}
  5160. #!/usr/bin/ruby
  5161. # os_snipe - redirect to a Jargon File entry about current OS
  5162. dir = 'http://www.elsewhere.org/jargon/html/entry'
  5163. agent = ENV['HTTP_USER_AGENT']
  5164. page = case
  5165. when agent =~ /Mac/: 'Macintrash.html'
  5166. when agent =~ /Win(dows )?NT/: 'evil_and_rude.html'
  5167. when agent =~ /Win|MSIE|WebTV/: 'Microsloth_Windows.html'
  5168. when agent =~ /Linux/: 'Linux.html'
  5169. when agent =~ /HP-UX/: 'HP-SUX.html'
  5170. when agent =~ /SunOS/: 'ScumOS.html'
  5171. else 'Appendix_B.html'
  5172. end
  5173. print "Location: #{dir}/#{page}\n\n"
  5174. require 'cgi'
  5175. cgi = CGI.new
  5176. cgi.out('status' => '204 No response'){""}
  5177. # this produces:
  5178. # Status: 204 No response
  5179. # Content-Type: text/html
  5180. # Content-Length: 0
  5181. # <blank line here>
  5182. # @@PLEAC@@_19.10
  5183. preference_value = cgi.cookies["preference name"][0]
  5184. packed_cookie = CGI::Cookie.new("name" => "preference name",
  5185. "value" => "whatever you'd like",
  5186. "expires" => Time.local(Time.now.year + 2,
  5187. Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
  5188. cgi.header("cookie" => [packed_cookie])
  5189. #!/usr/local/bin/ruby -w
  5190. # ic_cookies - sample CGI script that uses a cookie
  5191. require 'cgi'
  5192. cgi = CGI.new('html3')
  5193. cookname = "favorite ice cream"
  5194. favorite = cgi.params["flavor"][0]
  5195. tasty = cgi.cookies[cookname][0] || 'mint'
  5196. unless favorite
  5197. cgi.out {
  5198. cgi.html {
  5199. cgi.head { cgi.title { "Ice Cookies" } } +
  5200. cgi.body {
  5201. cgi.h1 { "Hello Ice Cream" } +
  5202. cgi.hr +
  5203. cgi.form {
  5204. cgi.p { "Please select a flavor: " +
  5205. cgi.text_field("flavor", tasty ) }
  5206. } +
  5207. cgi.hr
  5208. }
  5209. }
  5210. }
  5211. else
  5212. cookie = CGI::Cookie.new( "name" => cookname,
  5213. "value" => favorite,
  5214. "expires" => Time.local(Time.now.year + 2,
  5215. Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
  5216. cgi.out("cookie" => [cookie]) {
  5217. cgi.html {
  5218. cgi.head { cgi.title { "Ice Cookies" } } +
  5219. cgi.body {
  5220. cgi.h1 { "Hello Ice Cream" } +
  5221. cgi.p { "You chose as your favorite flavor `#{favorite}'." }
  5222. }
  5223. }
  5224. }
  5225. end
  5226. # @@PLEAC@@_20.9
  5227. def templatefile(filename, fillings)
  5228. aFile = File.new(filename, "r")
  5229. text = aFile.read()
  5230. aFile.close()
  5231. pattern = Regexp.new('%%(.*?)%%')
  5232. text.gsub!(pattern) {
  5233. fillings[$1] || ""
  5234. }
  5235. text
  5236. end
  5237. fields = {
  5238. 'username' => whats_his_name,
  5239. 'count' => login_count,
  5240. 'total' => minutes_used
  5241. }
  5242. puts templatefile('simple.template', fields)
  5243. # @@INCOMPLETE@@
  5244. # An example using databases is missing