text-format-1.0.0/0000755000175000017500000000000011600332303013275 5ustar ondrejondrejtext-format-1.0.0/metaconfig0000644000175000017500000000000011600332303015322 0ustar ondrejondrejtext-format-1.0.0/tests/0000755000175000017500000000000011600332303014437 5ustar ondrejondrejtext-format-1.0.0/tests/testall.rb0000644000175000017500000000073511600332303016441 0ustar ondrejondrej#!/usr/bin/env ruby #-- # Ruwiki version 0.8.0 # Copyright © 2002 - 2003, Digikata and HaloStatue # Alan Chen (alan@digikata.com) # Austin Ziegler (ruwiki@halostatue.ca) # # Licensed under the same terms as Ruby. # # $Id: testall.rb,v 1.1 2005/01/17 16:15:04 austin Exp $ #++ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0 puts "Checking for test cases:" Dir['tc*.rb'].each do |testcase| puts "\t#{testcase}" require testcase end puts " " text-format-1.0.0/tests/tc_text_format.rb0000644000175000017500000005275211600332303020021 0ustar ondrejondrej$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0 require 'text/format' require 'test/unit' class TestText__Format < Test::Unit::TestCase attr_accessor :format_o GETTYSBURG = <<-'EOS' Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation or any nation so conceived and so dedicated can long endure. We are met on a great battlefield of that war. We have come to dedicate a portion of that field as a final resting-place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this. But in a larger sense, we cannot dedicate, we cannot consecrate, we cannot hallow this ground. The brave men, living and dead who struggled here have consecrated it far above our poor power to add or detract. The world will little note nor long remember what we say here, but it can never forget what they did here. It is for us the living rather to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us--that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion--that we here highly resolve that these dead shall not have died in vain, that this nation under God shall have a new birth of freedom, and that government of the people, by the people, for the people shall not perish from the earth. -- Pres. Abraham Lincoln, 19 November 1863 EOS FIVE_COL = "Four \nscore\nand s\neven \nyears\nago o\nur fa\nthers\nbroug\nht fo\nrth o\nn thi\ns con\ntinen\nt a n\new na\ntion,\nconce\nived \nin li\nberty\nand d\nedica\nted t\no the\npropo\nsitio\nn tha\nt all\nmen a\nre cr\neated\nequal\n. Now\nwe ar\ne eng\naged \nin a \ngreat\ncivil\nwar, \ntesti\nng wh\nether\nthat \nnatio\nn or \nany n\nation\nso co\nnceiv\ned an\nd so \ndedic\nated \ncan l\nong e\nndure\n. We \nare m\net on\na gre\nat ba\nttlef\nield \nof th\nat wa\nr. We\nhave \ncome \nto de\ndicat\ne a p\nortio\nn of \nthat \nfield\nas a \nfinal\nresti\nng-pl\nace f\nor th\nose w\nho he\nre ga\nve th\neir l\nives \nthat \nthat \nnatio\nn mig\nht li\nve. I\nt is \naltog\nether\nfitti\nng an\nd pro\nper t\nhat w\ne sho\nuld d\no thi\ns. Bu\nt in \na lar\nger s\nense,\nwe ca\nnnot \ndedic\nate, \nwe ca\nnnot \nconse\ncrate\n, we \ncanno\nt hal\nlow t\nhis g\nround\n. The\nbrave\nmen, \nlivin\ng and\ndead \nwho s\ntrugg\nled h\nere h\nave c\nonsec\nrated\nit fa\nr abo\nve ou\nr poo\nr pow\ner to\nadd o\nr det\nract.\nThe w\norld \nwill \nlittl\ne not\ne nor\nlong \nremem\nber w\nhat w\ne say\nhere,\nbut i\nt can\nnever\nforge\nt wha\nt the\ny did\nhere.\nIt is\nfor u\ns the\nlivin\ng rat\nher t\no be \ndedic\nated \nhere \nto th\ne unf\ninish\ned wo\nrk wh\nich t\nhey w\nho fo\nught \nhere \nhave \nthus \nfar s\no nob\nly ad\nvance\nd. It\nis ra\nther \nfor u\ns to \nbe he\nre de\ndicat\ned to\nthe g\nreat \ntask \nremai\nning \nbefor\ne us-\n-that\nfrom \nthese\nhonor\ned de\nad we\ntake \nincre\nased \ndevot\nion t\no tha\nt cau\nse fo\nr whi\nch th\ney ga\nve th\ne las\nt ful\nl mea\nsure \nof de\nvotio\nn--th\nat we\nhere \nhighl\ny res\nolve \nthat \nthese\ndead \nshall\nnot h\nave d\nied i\nn vai\nn, th\nat th\nis na\ntion \nunder\nGod s\nhall \nhave \na new\nbirth\nof fr\needom\n, and\nthat \ngover\nnment\nof th\ne peo\nple, \nby th\ne peo\nple, \nfor t\nhe pe\nople \nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- Pr\nes. A\nbraha\nm Lin\ncoln,\n19 No\nvembe\nr 186\n3 \n" FIVE_CNF = "Four \nscore\nand s\neven \nyears\nago o\nur f\\\nathe\\\nrs b\\\nroug\\\nht f\\\north \non t\\\nhis c\nonti\\\nnent \na new\nnati\\\non, c\nonce\\\nived \nin l\\\niber\\\nty a\\\nnd d\\\nedic\\\nated \nto t\\\nhe p\\\nropo\\\nsiti\\\non t\\\nhat a\nll m\\\nen a\\\nre c\\\nreat\\\ned e\\\nqual.\nNow w\ne are\nenga\\\nged i\nn a g\nreat \ncivil\nwar, \ntest\\\ning w\nheth\\\ner t\\\nhat n\nation\nor a\\\nny n\\\nation\nso c\\\nonce\\\nived \nand s\no de\\\ndica\\\nted c\nan l\\\nong e\nndur\\\ne. We\nare m\net on\na gr\\\neat b\nattl\\\nefie\\\nld of\nthat \nwar. \nWe h\\\nave c\nome t\no de\\\ndica\\\nte a \nport\\\nion o\nf th\\\nat f\\\nield \nas a \nfinal\nrest\\\ning-\\\nplace\nfor t\nhose \nwho h\nere g\nave t\nheir \nlives\nthat \nthat \nnati\\\non m\\\night \nlive.\nIt is\nalto\\\ngeth\\\ner f\\\nitti\\\nng a\\\nnd p\\\nroper\nthat \nwe s\\\nhould\ndo t\\\nhis. \nBut i\nn a l\narger\nsens\\\ne, we\ncann\\\not d\\\nedic\\\nate, \nwe c\\\nannot\ncons\\\necra\\\nte, w\ne ca\\\nnnot \nhall\\\now t\\\nhis g\nroun\\\nd. T\\\nhe b\\\nrave \nmen, \nlivi\\\nng a\\\nnd d\\\nead w\nho s\\\ntrug\\\ngled \nhere \nhave \ncons\\\necra\\\nted i\nt far\nabove\nour p\noor p\nower \nto a\\\ndd or\ndetr\\\nact. \nThe w\norld \nwill \nlitt\\\nle n\\\note n\nor l\\\nong r\nemem\\\nber w\nhat w\ne say\nhere,\nbut i\nt can\nnever\nforg\\\net w\\\nhat t\nhey d\nid h\\\nere. \nIt is\nfor u\ns the\nlivi\\\nng r\\\nather\nto be\ndedi\\\ncated\nhere \nto t\\\nhe u\\\nnfin\\\nished\nwork \nwhich\nthey \nwho f\nought\nhere \nhave \nthus \nfar s\no no\\\nbly a\ndvan\\\nced. \nIt is\nrath\\\ner f\\\nor us\nto be\nhere \ndedi\\\ncated\nto t\\\nhe g\\\nreat \ntask \nrema\\\nining\nbefo\\\nre u\\\ns--t\\\nhat f\nrom t\nhese \nhono\\\nred d\nead w\ne ta\\\nke i\\\nncre\\\nased \ndevo\\\ntion \nto t\\\nhat c\nause \nfor w\nhich \nthey \ngave \nthe l\nast f\null m\neasu\\\nre of\ndevo\\\ntion\\\n--th\\\nat we\nhere \nhigh\\\nly r\\\nesol\\\nve t\\\nhat t\nhese \ndead \nshall\nnot h\nave d\nied i\nn va\\\nin, t\nhat t\nhis n\nation\nunder\nGod s\nhall \nhave \na new\nbirth\nof f\\\nreed\\\nom, a\nnd t\\\nhat g\nover\\\nnment\nof t\\\nhe p\\\neopl\\\ne, by\nthe p\neopl\\\ne, f\\\nor t\\\nhe p\\\neople\nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- P\\\nres. \nAbra\\\nham L\ninco\\\nln, 1\n9 No\\\nvemb\\\ner 1\\\n863 \n" FIVE_CNT = "Four \nscore\nand \nseven\nyears\nago \nour f\nathe\\\nrs b\\\nroug\\\nht f\\\north \non t\\\nhis c\nonti\\\nnent \na new\nnati\\\non, c\nonce\\\nived \nin l\\\niber\\\nty a\\\nnd d\\\nedic\\\nated \nto t\\\nhe p\\\nropo\\\nsiti\\\non t\\\nhat \nall \nmen \nare c\nreat\\\ned e\\\nqual.\nNow \nwe a\\\nre e\\\nngag\\\ned in\na gr\\\neat \ncivil\nwar, \ntest\\\ning w\nheth\\\ner t\\\nhat n\nation\nor a\\\nny n\\\nation\nso c\\\nonce\\\nived \nand \nso d\\\nedic\\\nated \ncan \nlong \nendu\\\nre. \nWe a\\\nre m\\\net on\na gr\\\neat b\nattl\\\nefie\\\nld of\nthat \nwar. \nWe h\\\nave \ncome \nto d\\\nedic\\\nate a\nport\\\nion \nof t\\\nhat \nfield\nas a \nfinal\nrest\\\ning-\\\nplace\nfor \nthose\nwho \nhere \ngave \ntheir\nlives\nthat \nthat \nnati\\\non m\\\night \nlive.\nIt is\nalto\\\ngeth\\\ner f\\\nitti\\\nng a\\\nnd p\\\nroper\nthat \nwe s\\\nhould\ndo t\\\nhis. \nBut \nin a \nlarg\\\ner s\\\nense,\nwe c\\\nannot\ndedi\\\ncate,\nwe c\\\nannot\ncons\\\necra\\\nte, \nwe c\\\nannot\nhall\\\now t\\\nhis g\nroun\\\nd. T\\\nhe b\\\nrave \nmen, \nlivi\\\nng a\\\nnd d\\\nead \nwho s\ntrug\\\ngled \nhere \nhave \ncons\\\necra\\\nted \nit f\\\nar a\\\nbove \nour \npoor \npower\nto a\\\ndd or\ndetr\\\nact. \nThe \nworld\nwill \nlitt\\\nle n\\\note \nnor \nlong \nreme\\\nmber \nwhat \nwe s\\\nay h\\\nere, \nbut \nit c\\\nan n\\\never \nforg\\\net w\\\nhat \nthey \ndid \nhere.\nIt is\nfor \nus t\\\nhe l\\\niving\nrath\\\ner to\nbe d\\\nedic\\\nated \nhere \nto t\\\nhe u\\\nnfin\\\nished\nwork \nwhich\nthey \nwho f\nought\nhere \nhave \nthus \nfar \nso n\\\nobly \nadva\\\nnced.\nIt is\nrath\\\ner f\\\nor us\nto be\nhere \ndedi\\\ncated\nto t\\\nhe g\\\nreat \ntask \nrema\\\nining\nbefo\\\nre u\\\ns--t\\\nhat \nfrom \nthese\nhono\\\nred \ndead \nwe t\\\nake i\nncre\\\nased \ndevo\\\ntion \nto t\\\nhat \ncause\nfor \nwhich\nthey \ngave \nthe \nlast \nfull \nmeas\\\nure \nof d\\\nevot\\\nion-\\\n-that\nwe h\\\nere h\nighly\nreso\\\nlve \nthat \nthese\ndead \nshall\nnot \nhave \ndied \nin v\\\nain, \nthat \nthis \nnati\\\non u\\\nnder \nGod \nshall\nhave \na new\nbirth\nof f\\\nreed\\\nom, \nand \nthat \ngove\\\nrnme\\\nnt of\nthe p\neopl\\\ne, by\nthe p\neopl\\\ne, f\\\nor t\\\nhe p\\\neople\nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- P\\\nres. \nAbra\\\nham L\ninco\\\nln, \n19 N\\\novem\\\nber \n1863 \n" # Tests both abbreviations and abbreviations= def test_abbreviations abbr = [" Pres. Abraham Lincoln\n", " Pres. Abraham Lincoln\n"] @format_o = Text::Format.new assert_equal([], @format_o.abbreviations) @format_o.abbreviations = [ 'foo', 'bar' ] assert_equal([ 'foo', 'bar' ], @format_o.abbreviations) assert_equal(abbr[0], @format_o.format(abbr[0])) @format_o.extra_space = true assert_equal(abbr[1], @format_o.format(abbr[0])) @format_o.abbreviations = [ "Pres" ] assert_equal([ "Pres" ], @format_o.abbreviations) assert_equal(abbr[0], @format_o.format(abbr[0])) @format_o.extra_space = false assert_equal(abbr[0], @format_o.format(abbr[0])) end # Tests both body_indent and body_indent= def test_body_indent @format_o = Text::Format.new assert_equal(0, @format_o.body_indent) @format_o.body_indent = 7 assert_equal(7, @format_o.body_indent) @format_o.body_indent = -3 assert_equal(3, @format_o.body_indent) @format_o.body_indent = "9" assert_equal(9, @format_o.body_indent) @format_o.body_indent = "-2" assert_equal(2, @format_o.body_indent) assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[1]) end # Tests both columns and columns= def test_columns @format_o = Text::Format.new assert_equal(72, @format_o.columns) @format_o.columns = 7 assert_equal(7, @format_o.columns) @format_o.columns = -3 assert_equal(3, @format_o.columns) @format_o.columns = "9" assert_equal(9, @format_o.columns) @format_o.columns = "-2" assert_equal(2, @format_o.columns) @format_o.columns = 40 assert_equal(40, @format_o.columns) assert_match(/this continent$/, @format_o.format(GETTYSBURG).split("\n")[1]) end # Tests both extra_space and extra_space= def test_extra_space @format_o = Text::Format.new assert_equal(false, @format_o.extra_space) @format_o.extra_space = true assert_equal(true, @format_o.extra_space) end # Tests both first_indent and first_indent= def test_first_indent @format_o = Text::Format.new assert_equal(4, @format_o.first_indent) @format_o.first_indent = 7 assert_equal(7, @format_o.first_indent) @format_o.first_indent = -3 assert_equal(3, @format_o.first_indent) @format_o.first_indent = "9" assert_equal(9, @format_o.first_indent) @format_o.first_indent = "-2" assert_equal(2, @format_o.first_indent) assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[0]) end def test_format_style @format_o = Text::Format.new assert_equal(Text::Format::LEFT_ALIGN, @format_o.format_style) assert_match(/^November 1863$/, @format_o.format(GETTYSBURG).split("\n")[-1]) @format_o.format_style = Text::Format::RIGHT_ALIGN assert_equal(Text::Format::RIGHT_ALIGN, @format_o.format_style) assert_match(/^ +November 1863$/, @format_o.format(GETTYSBURG).split("\n")[-1]) @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(Text::Format::RIGHT_FILL, @format_o.format_style) assert_match(/^November 1863 +$/, @format_o.format(GETTYSBURG).split("\n")[-1]) @format_o.format_style = Text::Format::JUSTIFY assert_equal(Text::Format::JUSTIFY, @format_o.format_style) assert_match(/^of freedom, and that government of the people, by the people, for the$/, @format_o.format(GETTYSBURG).split("\n")[-3]) assert_raises(ArgumentError) { @format_o.format_style = 33 } end def test_tag_paragraph @format_o = Text::Format.new assert_equal(false, @format_o.tag_paragraph) @format_o.tag_paragraph = true assert_equal(true, @format_o.tag_paragraph) assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]), Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG])) end def test_tag_text @format_o = Text::Format.new assert_equal([], @format_o.tag_text) assert_equal(@format_o.format(GETTYSBURG), Text::Format.new.format(GETTYSBURG)) @format_o.tag_paragraph = true @format_o.tag_text = ["Gettysburg Address", "---"] assert_not_equal(@format_o.format(GETTYSBURG), Text::Format.new.format(GETTYSBURG)) assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]), Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG])) assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG, GETTYSBURG]), Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG, GETTYSBURG])) end def test_justify? @format_o = Text::Format.new assert_equal(false, @format_o.justify?) @format_o.format_style = Text::Format::RIGHT_ALIGN assert_equal(false, @format_o.justify?) @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(false, @format_o.justify?) @format_o.format_style = Text::Format::JUSTIFY assert_equal(true, @format_o.justify?) # The format testing is done in _format_style end def test_left_align? @format_o = Text::Format.new assert_equal(true, @format_o.left_align?) @format_o.format_style = Text::Format::RIGHT_ALIGN assert_equal(false, @format_o.left_align?) @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(false, @format_o.left_align?) @format_o.format_style = Text::Format::JUSTIFY assert_equal(false, @format_o.left_align?) # The format testing is done in _format_style end def test_left_margin @format_o = Text::Format.new assert_equal(0, @format_o.left_margin) @format_o.left_margin = -3 assert_equal(3, @format_o.left_margin) @format_o.left_margin = "9" assert_equal(9, @format_o.left_margin) @format_o.left_margin = "-2" assert_equal(2, @format_o.left_margin) @format_o.left_margin = 7 assert_equal(7, @format_o.left_margin) ft = @format_o.format(GETTYSBURG).split("\n") assert_match(/^ {11}Four score/, ft[0]) assert_match(/^ {7}November/, ft[-1]) end def test_hard_margins @format_o = Text::Format.new assert_equal(false, @format_o.hard_margins) @format_o.hard_margins = true @format_o.columns = 5 @format_o.first_indent = 0 @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(true, @format_o.hard_margins) assert_equal(FIVE_COL, @format_o.format(GETTYSBURG)) @format_o.split_rules |= Text::Format::SPLIT_CONTINUATION assert_equal(Text::Format::SPLIT_CONTINUATION_FIXED, @format_o.split_rules) assert_equal(FIVE_CNF, @format_o.format(GETTYSBURG)) @format_o.split_rules = Text::Format::SPLIT_CONTINUATION assert_equal(Text::Format::SPLIT_CONTINUATION, @format_o.split_rules) assert_equal(FIVE_CNT, @format_o.format(GETTYSBURG)) end # Tests both nobreak and nobreak_regex, since one is only useful # with the other. def test_nobreak @format_o = Text::Format.new assert_equal(false, @format_o.nobreak) assert_equal(true, @format_o.nobreak_regex.empty?) @format_o.nobreak = true @format_o.nobreak_regex = { %r{this} => %r{continent} } @format_o.columns = 77 assert_equal(true, @format_o.nobreak) assert_equal({ %r{this} => %r{continent} }, @format_o.nobreak_regex) assert_match(/^this continent/, @format_o.format(GETTYSBURG).split("\n")[1]) end def test_right_align? @format_o = Text::Format.new assert_equal(false, @format_o.right_align?) @format_o.format_style = Text::Format::RIGHT_ALIGN assert_equal(true, @format_o.right_align?) @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(false, @format_o.right_align?) @format_o.format_style = Text::Format::JUSTIFY assert_equal(false, @format_o.right_align?) # The format testing is done in _format_style end def test_right_fill? @format_o = Text::Format.new assert_equal(false, @format_o.right_fill?) @format_o.format_style = Text::Format::RIGHT_ALIGN assert_equal(false, @format_o.right_fill?) @format_o.format_style = Text::Format::RIGHT_FILL assert_equal(true, @format_o.right_fill?) @format_o.format_style = Text::Format::JUSTIFY assert_equal(false, @format_o.right_fill?) # The format testing is done in _format_style end def test_right_margin @format_o = Text::Format.new assert_equal(0, @format_o.right_margin) @format_o.right_margin = -3 assert_equal(3, @format_o.right_margin) @format_o.right_margin = "9" assert_equal(9, @format_o.right_margin) @format_o.right_margin = "-2" assert_equal(2, @format_o.right_margin) @format_o.right_margin = 7 assert_equal(7, @format_o.right_margin) ft = @format_o.format(GETTYSBURG).split("\n") assert_match(/^ {4}Four score.*forth on$/, ft[0]) assert_match(/^November/, ft[-1]) end def test_tabstop @format_o = Text::Format.new assert_equal(8, @format_o.tabstop) @format_o.tabstop = 7 assert_equal(7, @format_o.tabstop) @format_o.tabstop = -3 assert_equal(3, @format_o.tabstop) @format_o.tabstop = "9" assert_equal(9, @format_o.tabstop) @format_o.tabstop = "-2" assert_equal(2, @format_o.tabstop) end def test_text @format_o = Text::Format.new assert_equal([], @format_o.text) @format_o.text = "Test Text" assert_equal("Test Text", @format_o.text) @format_o.text = ["Line 1", "Line 2"] assert_equal(["Line 1", "Line 2"], @format_o.text) end def test_new @format_o = Text::Format.new { |fo| fo.text = "Test 1, 2, 3" } assert_equal("Test 1, 2, 3", @format_o.text) @format_o = Text::Format.new(:columns => 79) assert_equal(79, @format_o.columns) @format_o = Text::Format.new(:columns => 80) { |fo| fo.text = "Test 4, 5, 6" } assert_equal("Test 4, 5, 6", @format_o.text) assert_equal(80, @format_o.columns) @format_o = Text::Format.new(:text => "Test A, B, C") assert_equal("Test A, B, C", @format_o.text) @format_o = Text::Format.new(:text => "Test X, Y, Z") { |fo| fo.columns = -5 } assert_equal("Test X, Y, Z", @format_o.text) assert_equal(5, @format_o.columns) end def test_center @format_o = Text::Format.new ct = @format_o.center(GETTYSBURG.split("\n")).split("\n") assert_match(/^ Four score and seven years ago our fathers brought forth on this/, ct[0]) assert_match(/^ not perish from the earth./, ct[-3]) end def test_expand @format_o = Text::Format.new assert_equal(" ", @format_o.expand("\t ")) @format_o.tabstop = 4 assert_equal(" ", @format_o.expand("\t ")) end def test_unexpand @format_o = Text::Format.new assert_equal("\t ", @format_o.unexpand(" ")) @format_o.tabstop = 4 assert_equal("\t ", @format_o.unexpand(" ")) end def test_space_only assert_equal("", Text::Format.new.format(" ")) assert_equal("", Text::Format.new.format("\n")) assert_equal("", Text::Format.new.format(" ")) assert_equal("", Text::Format.new.format(" \n")) assert_equal("", Text::Format.new.paragraphs("\n")) assert_equal("", Text::Format.new.paragraphs(" ")) assert_equal("", Text::Format.new.paragraphs(" ")) assert_equal("", Text::Format.new.paragraphs(" \n")) assert_equal("", Text::Format.new.paragraphs(["\n"])) assert_equal("", Text::Format.new.paragraphs([" "])) assert_equal("", Text::Format.new.paragraphs([" "])) assert_equal("", Text::Format.new.paragraphs([" \n"])) end def test_splendiferous h = nil test = "This is a splendiferous test" @format_o = Text::Format.new(:columns => 6, :left_margin => 0, :indent => 0, :first_indent => 0) assert_match(/^splendiferous$/, @format_o.format(test)) @format_o.hard_margins = true assert_match(/^lendif$/, @format_o.format(test)) h = Object.new @format_o.split_rules = Text::Format::SPLIT_HYPHENATION class << h def hyphenate_to(word, size) return [nil, word] if size < 2 [word[0 ... size], word[size .. -1]] end end @format_o.hyphenator = h assert_match(/^ferous$/, @format_o.format(test)) h = Object.new class << h def hyphenate_to(word, size, formatter) return [nil, word] if word.size < formatter.columns [word[0 ... size], word[size .. -1]] end end @format_o.hyphenator = h assert_match(/^ferous$/, @format_o.format(test)) end def test_encephelogram hy = nil begin require 'text/hyphen' hy = Text::Hyphen.new rescue LoadError begin require 'rubygems' require 'text/hyphen' hy = Text::Hyphen.new rescue LoadError begin require 'tex/hyphen' hy = TeX::Hyphen.new rescue LoadError print 'S' return true end end end tx = "something pancakes electroencephalogram" fo = Text::Format.new(:body_indent => 15, :columns => 30, :hard_margins => true, :split_rules => Text::Format::SPLIT_HYPHENATION, :hyphenator => hy, :text => tx) res = fo.paragraphs exp = <<-EOS something pancakes elec- troencephalo- gram EOS exp.chomp! assert_equal(exp, res) end end text-format-1.0.0/Rakefile0000644000175000017500000000605411600332303014747 0ustar ondrejondrej#! /usr/bin/env rake $LOAD_PATH.unshift('lib') require 'rubygems' require 'rake/gempackagetask' require 'text/format' require 'archive/tar/minitar' require 'zlib' DISTDIR = "text-format-#{Text::Format::VERSION}" TARDIST = "../#{DISTDIR}.tar.gz" DATE_RE = %r<(\d{4})[./-]?(\d{2})[./-]?(\d{2})(?:[\sT]?(\d{2})[:.]?(\d{2})[:.]?(\d{2})?)?> if ENV['RELEASE_DATE'] year, month, day, hour, minute, second = DATE_RE.match(ENV['RELEASE_DATE']).captures year ||= 0 month ||= 0 day ||= 0 hour ||= 0 minute ||= 0 second ||= 0 ReleaseDate = Time.mktime(year, month, day, hour, minute, second) else ReleaseDate = nil end task :test do |t| require 'test/unit/testsuite' require 'test/unit/ui/console/testrunner' runner = Test::Unit::UI::Console::TestRunner $LOAD_PATH.unshift('tests') $stderr.puts "Checking for test cases:" if t.verbose Dir['tests/tc_*.rb'].each do |testcase| $stderr.puts "\t#{testcase}" if t.verbose load testcase end suite = Test::Unit::TestSuite.new("Text::Format") ObjectSpace.each_object(Class) do |testcase| suite << testcase.suite if testcase < Test::Unit::TestCase end runner.run(suite) end spec = eval(File.read("text-format.gemspec")) spec.version = Text::Format::VERSION desc "Build the RubyGem for Text::Format" task :gem => [ :test ] Rake::GemPackageTask.new(spec) do |g| g.need_tar = false g.need_zip = false g.package_dir = ".." end desc "Build a Text::Format .tar.gz distribution." task :tar => [ TARDIST ] file TARDIST => [ :test ] do |t| current = File.basename(Dir.pwd) Dir.chdir("..") do begin files = Dir["#{current}/**/*"].select { |dd| dd !~ %r{(?:/CVS/?|~$)} } files.map! do |dd| ddnew = dd.gsub(/^#{current}/, DISTDIR) mtime = ReleaseDate || File.stat(dd).mtime if File.directory?(dd) { :name => ddnew, :mode => 0755, :dir => true, :mtime => mtime } else if dd =~ %r{bin/} mode = 0755 else mode = 0644 end data = File.read(dd) { :name => ddnew, :mode => mode, :data => data, :size => data.size, :mtime => mtime } end end ff = File.open(t.name.gsub(%r{^\.\./}o, ''), "wb") gz = Zlib::GzipWriter.new(ff) tw = Archive::Tar::Minitar::Writer.new(gz) files.each do |entry| if entry[:dir] tw.mkdir(entry[:name], entry) else tw.add_file_simple(entry[:name], entry) { |os| os.write(entry[:data]) } end end ensure tw.close if tw gz.close if gz end end end task TARDIST => [ :test ] desc "Build the RDoc documentation for Text::Format" task :docs do require 'rdoc/rdoc' rdoc_options = %w(--title Text::Format --main README --line-numbers) files = FileList[*%w(README ChangeLog Install bin/**/*.rb lib/**/*.rb)] rdoc_options += files.to_a RDoc::RDoc.new.document(rdoc_options) end desc "Build everything." task :default => [ :tar, :gem ] text-format-1.0.0/pre-setup.rb0000644000175000017500000000222211600332303015544 0ustar ondrejondrejrequire 'rdoc/rdoc' ## # Build the rdoc documentation. Also, try to build the RI documentation. # def build_rdoc(options) RDoc::RDoc.new.document(options) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build RDoc documentation\n#{e.message}" end def build_ri(files) RDoc::RDoc.new(files) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build Ri documentation\n#{e.message}" end def run_tests(test_list) return if test_list.empty? require 'test/unit/ui/console/testrunner' $:.unshift "lib" test_list.each do |test| next if File.directory?(test) require test end tests = [] ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) } tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) } tests.delete_if { |o| o == Test::Unit::TestCase } tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) } $:.shift end rdoc = %w(--main README --line-numbers --title Text::Format) ri = %w(--ri-site --merge) dox = %w(README ChangeLog lib) build_rdoc rdoc + dox build_ri ri + dox #run_tests [] text-format-1.0.0/ToDo0000644000175000017500000000053311600332303014066 0ustar ondrejondrejText::Format To Do ================== * Margin markers: the ability to place markers in the margin when formatting. * Line numbering: the ability to number lines in the margin when formatting, including numering lines by step (every fifth line, etc.). * Email attribution quoting reformatting. * Proportional width support for GUI formatting. text-format-1.0.0/lib/0000755000175000017500000000000011600332303014043 5ustar ondrejondrejtext-format-1.0.0/lib/text/0000755000175000017500000000000011600332303015027 5ustar ondrejondrejtext-format-1.0.0/lib/text/format.rb0000644000175000017500000011257211600332303016654 0ustar ondrejondrej# :title: Text::Format # :main: Text::Format #-- # Text::Format for Ruby # Version 1.0.0 # # Copyright (c) 2002 - 2005 Austin Ziegler # # $Id: format.rb,v 1.5 2005/04/20 01:43:55 austin Exp $ #++ unless defined?(Text) module Text; end end # = Introduction # # Text::Format provides the ability to nicely format fixed-width text with # knowledge of the writeable space (number of columns), margins, and # indentation settings. # # Copyright:: Copyright (c) 2002 - 2005 by Austin Ziegler # Version:: 1.0.0 # Based On:: Perl # Text::Format[http://search.cpan.org/author/GABOR/Text-Format0.52/lib/Text/Format.pm], # Copyright (c) 1998 Gábor Egressy # Licence:: Ruby's, Perl Artistic, or GPL version 2 (or later) # class Text::Format VERSION = '1.0.0' SPACES_RE = %r{\s+}mo.freeze NEWLINE_RE = %r{\n}o.freeze TAB = "\t".freeze NEWLINE = "\n".freeze # Global common English abbreviations. More can be added with # #abbreviations. ABBREV = %w(Mr Mrs Ms Jr Sr Dr) # Formats text flush to the left margin with a visual and physical # ragged right margin. # # >A paragraph that is< # >left aligned.< LEFT_ALIGN = :left # Formats text flush to the right margin with a visual ragged left # margin. The actual left margin is padded with spaces from the # beginning of the line to the start of the text such that the right # margin will be flush. # # >A paragraph that is< # > right aligned.< RIGHT_ALIGN = :right # Formats text flush to the left margin with a visual ragged right # margin. The line is padded with spaces from the end of the text to the # right margin. # # >A paragraph that is< # >right filled. < RIGHT_FILL = :fill # Formats the text flush to both the left and right margins. The last # line will not be justified if it consists of a single word (it will be # treated as +RIGHT_FILL+ in this case). Spacing between words is # increased to ensure that the textg is flush with both margins. # # |A paragraph that| # |is justified.| # # |A paragraph that is| # |justified. | JUSTIFY = :justify # When #hard_margins is enabled, a word that extends over the right # margin will be split at the number of characters needed. This is # similar to how characters wrap on a terminal. This is the default # split mechanism when #hard_margins is enabled. # # repre # senta # ion SPLIT_FIXED = 1 # When #hard_margins is enabled, a word that extends over the right # margin will be split at one less than the number of characters needed # with a C-style continuation character (\). If the word cannot be split # using the rules of SPLIT_CONTINUATION, and the word will not fit # wholly into the next line, then SPLIT_FIXED will be used. # # repr\ # esen\ # tati\ # on SPLIT_CONTINUATION = 2 # When #hard_margins is enabled, a word that extends over the right # margin will be split according to the hyphenator specified by the # #hyphenator object; if there is no hyphenation library supplied, then # the hyphenator of Text::Format itself is used, which is the same as # SPLIT_CONTINUATION. See #hyphenator for more information about # hyphenation libraries. The example below is valid with either # TeX::Hyphen or Text::Hyphen. If the word cannot be split using the # hyphenator's rules, and the word will not fit wholly into the next # line, then SPLIT_FIXED will be used. # # rep- # re- # sen- # ta- # tion # SPLIT_HYPHENATION = 4 # When #hard_margins is enabled, a word that extends over the right # margin will be split at one less than the number of characters needed # with a C-style continuation character (\). If the word cannot be split # using the rules of SPLIT_CONTINUATION, then SPLIT_FIXED will be used. SPLIT_CONTINUATION_FIXED = SPLIT_CONTINUATION | SPLIT_FIXED # When #hard_margins is enabled, a word that extends over the right # margin will be split according to the hyphenator specified by the # #hyphenator object; if there is no hyphenation library supplied, then # the hyphenator of Text::Format itself is used, which is the same as # SPLIT_CONTINUATION. See #hyphenator for more information about # hyphenation libraries. The example below is valid with either # TeX::Hyphen or Text::Hyphen. If the word cannot be split using the # hyphenator's rules, then SPLIT_FIXED will be used. SPLIT_HYPHENATION_FIXED = SPLIT_HYPHENATION | SPLIT_FIXED # Attempts to split words according to the rules of the supplied # hyphenator (e.g., SPLIT_HYPHENATION); if the word cannot be split # using these rules, then the rules of SPLIT_CONTINUATION will be # followed. In all cases, if the word cannot be split using either # SPLIT_HYPHENATION or SPLIT_CONTINUATION, and the word will not fit # wholly into the next line, then SPLIT_FIXED will be used. SPLIT_HYPHENATION_CONTINUATION = SPLIT_HYPHENATION | SPLIT_CONTINUATION # Attempts to split words according to the rules of the supplied # hyphenator (e.g., SPLIT_HYPHENATION); if the word cannot be split # using these rules, then the rules of SPLIT_CONTINUATION will be # followed. In all cases, if the word cannot be split using either # SPLIT_HYPHENATION or SPLIT_CONTINUATION, then SPLIT_FIXED will be # used. SPLIT_ALL = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED # Words forcibly split by Text::Format will be stored as split words. # This class represents a word forcibly split. class SplitWord # The word that was split. attr_reader :word # The first part of the word that was split. attr_reader :first # The remainder of the word that was split. attr_reader :rest def initialize(word, first, rest) @word = word @first = first @rest = rest end end # Indicates punctuation characters that terminates a sentence, as some # English typesetting rules indicate that sentences should be followed # by two spaces. This is an archaic rule, but is supported with # #extra_space. This is the default set of terminal punctuation # characters. Additional terminal punctuation may be added to the # formatting object through #terminal_punctuation. TERMINAL_PUNCTUATION = %q(.?!) # Indicates quote characters that may follow terminal punctuation under # the current formatting rules. This satisfies the English formatting # rule that indicates that sentences terminated inside of quotes should # have the punctuation inside of the quoted text, not outside of the # terminal quote. Additional terminal quotes may be added to the # formatting object through #terminal_quotes. See TERMINAL_PUNCTUATION # for more information. TERMINAL_QUOTES = %q('") # This method returns the regular expression used to detect the end of a # sentence under the current definition of TERMINAL_PUNCTUATION, # #terminal_punctuation, TERMINAL_QUOTES, and #terminal_quotes. def __sentence_end_re %r{[#{TERMINAL_PUNCTUATION}#{self.terminal_punctuation}][#{TERMINAL_QUOTES}#{self.terminal_quotes}]?$} end private :__sentence_end_re # Returns a regular expression for a set of characters (at least one # non-whitespace followed by at least one space) of the specified size # followed by one or more of any character. RE_BREAK_SIZE = lambda { |size| %r[((?:\S+\s+){#{size}})(.+)] } # Compares the formatting rules, excepting #hyphenator, of two # Text::Format objects. Generated results (e.g., #split_words) are not # compared. def ==(o) (@text == o.text) and (@columns == o.columns) and (@left_margin == o.left_margin) and (@right_margin == o.right_margin) and (@hard_margins == o.hard_margins) and (@split_rules == o.split_rules) and (@first_indent == o.first_indent) and (@body_indent == o.body_indent) and (@tag_text == o.tag_text) and (@tabstop == o.tabstop) and (@format_style == o.format_style) and (@extra_space == o.extra_space) and (@tag_paragraph == o.tag_paragraph) and (@nobreak == o.nobreak) and (@terminal_punctuation == o.terminal_punctuation) and (@terminal_quotes == o.terminal_quotes) and (@abbreviations == o.abbreviations) and (@nobreak_regex == o.nobreak_regex) end # The default text to be manipulated. Note that value is optional, but # if the formatting functions are called without values, this text is # what will be formatted. # # *Default*:: [] # Used in:: All methods attr_accessor :text # The total width of the format area. The margins, indentation, and text # are formatted into this space. Any value provided is silently # converted to a positive integer. # # COLUMNS # <--------------------------------------------------------------> # <-----------><------><---------------------------><------------> # left margin indent text is formatted into here right margin # # *Default*:: 72 # Used in:: #format, #paragraphs, #center attr_accessor :columns def columns=(col) #:nodoc: @columns = col.to_i.abs end # The number of spaces used for the left margin. The value provided is # silently converted to a positive integer value. # # columns # <--------------------------------------------------------------> # <-----------><------><---------------------------><------------> # LEFT MARGIN indent text is formatted into here right margin # # *Default*:: 0 # Used in:: #format, #paragraphs, #center attr_accessor :left_margin def left_margin=(left) #:nodoc: @left_margin = left.to_i.abs end # The number of spaces used for the right margin. The value provided is # silently converted to a positive integer value. # # columns # <--------------------------------------------------------------> # <-----------><------><---------------------------><------------> # left margin indent text is formatted into here RIGHT MARGIN # # *Default*:: 0 # Used in:: #format, #paragraphs, #center attr_accessor :right_margin def right_margin=(right) #:nodoc: @right_margin = right.to_i.abs end # The number of spaces to indent the first line of a paragraph. The # value provided is silently converted to a positive integer value. # # columns # <--------------------------------------------------------------> # <-----------><------><---------------------------><------------> # left margin INDENT text is formatted into here right margin # # *Default*:: 4 # Used in:: #format, #paragraphs attr_accessor :first_indent def first_indent=(first) #:nodoc: @first_indent = first.to_i.abs end # The number of spaces to indent all lines after the first line of a # paragraph. The value provided is silently converted to a positive # integer value. # # columns # <--------------------------------------------------------------> # <-----------><------><---------------------------><------------> # left margin INDENT text is formatted into here right margin # # *Default*:: 0 # Used in:: #format, #paragraphs attr_accessor :body_indent def body_indent=(body) #:nodoc: @body_indent = body.to_i.abs end # Normally, words larger than the format area will be placed on a line # by themselves. Setting this value to +true+ will force words larger # than the format area to be split into one or more "words" each at most # the size of the format area. The first line and the original word will # be placed into #split_words. Note that this will cause the output to # look *similar* to a #format_style of JUSTIFY. (Lines will be filled as # much as possible.) # # *Default*:: +false+ # Used in:: #format, #paragraphs attr_accessor :hard_margins # An array of words split during formatting if #hard_margins is set to # +true+. # #split_words << Text::Format::SplitWord.new(word, first, rest) attr_reader :split_words # The object responsible for hyphenating. It must respond to # #hyphenate_to(word, size) or #hyphenate_to(word, size, formatter) and # return an array of the word split into two parts (e.g., [part1, # part2]; if there is a hyphenation mark to be applied, # responsibility belongs to the hyphenator object. The size is the # MAXIMUM size permitted, including any hyphenation marks. # # If the #hyphenate_to method has an arity of 3, the current formatter # (+self+) will be provided to the method. This allows the hyphenator to # make decisions about the hyphenation based on the formatting rules. # # #hyphenate_to should return [nil, word] if the word cannot be # hyphenated. # # *Default*:: +self+ (SPLIT_CONTINUATION) # Used in:: #format, #paragraphs attr_accessor :hyphenator def hyphenator=(h) #:nodoc: h ||= self raise ArgumentError, "#{h.inspect} is not a valid hyphenator." unless h.respond_to?(:hyphenate_to) arity = h.method(:hyphenate_to).arity raise ArgumentError, "#{h.inspect} must have exactly two or three arguments." unless arity.between?(2, 3) @hyphenator = h @hyphenator_arity = arity end # Specifies the split mode; used only when #hard_margins is set to # +true+. Allowable values are: # # * +SPLIT_FIXED+ # * +SPLIT_CONTINUATION+ # * +SPLIT_HYPHENATION+ # * +SPLIT_CONTINUATION_FIXED+ # * +SPLIT_HYPHENATION_FIXED+ # * +SPLIT_HYPHENATION_CONTINUATION+ # * +SPLIT_ALL+ # # *Default*:: Text::Format::SPLIT_FIXED # Used in:: #format, #paragraphs attr_accessor :split_rules def split_rules=(s) #:nodoc: raise ArgumentError, "Invalid value provided for #split_rules." if ((s < SPLIT_FIXED) or (s > SPLIT_ALL)) @split_rules = s end # Indicates whether sentence terminators should be followed by a single # space (+false+), or two spaces (+true+). See #abbreviations for more # information. # # *Default*:: +false+ # Used in:: #format, #paragraphs attr_accessor :extra_space # Defines the current abbreviations as an array. This is only used if # extra_space is turned on. # # If one is abbreviating "President" as "Pres." (abbreviations = # ["Pres"]), then the results of formatting will be as illustrated in # the table below: # # abbreviations # extra_space | #include?("Pres") | not #include?("Pres") # ------------+-------------------+---------------------- # true | Pres. Lincoln | Pres. Lincoln # false | Pres. Lincoln | Pres. Lincoln # ------------+-------------------+---------------------- # extra_space | #include?("Mrs") | not #include?("Mrs") # true | Mrs. Lincoln | Mrs. Lincoln # false | Mrs. Lincoln | Mrs. Lincoln # # Note that abbreviations should not have the terminal period as part of # their definitions. # # This automatic abbreviation handling *will* cause some issues with # uncommon sentence structures. The two sentences below will not be # formatted correctly: # # You're in trouble now, Mr. # Just wait until your father gets home. # # Under no circumstances (because Mr is a predefined abbreviation) will # this ever be separated by two spaces. # # *Default*:: [] # Used in:: #format, #paragraphs attr_accessor :abbreviations # Specifies additional punctuation characters that terminate a sentence, # as some English typesetting rules indicate that sentences should be # followed by two spaces. This is an archaic rule, but is supported with # #extra_space. This is added to the default set of terminal punctuation # defined in TERMINAL_PUNCTUATION. # # *Default*:: "" # Used in:: #format, #paragraphs attr_accessor :terminal_punctuation # Specifies additional quote characters that may follow # terminal punctuation under the current formatting rules. This # satisfies the English formatting rule that indicates that sentences # terminated inside of quotes should have the punctuation inside of the # quoted text, not outside of the terminal quote. This is added to the # default set of terminal quotes defined in TERMINAL_QUOTES. # # *Default*:: "" # Used in:: #format, #paragraphs attr_accessor :terminal_quotes # Indicates whether the formatting of paragraphs should be done with # tagged paragraphs. Useful only with #tag_text. # # *Default*:: +false+ # Used in:: #format, #paragraphs attr_accessor :tag_paragraph # The text to be placed before each paragraph when #tag_paragraph is # +true+. When #format is called, only the first element (#tag_text[0]) # is used. When #paragraphs is called, then each successive element # (#tag_text[n]) will be used once, with corresponding paragraphs. If # the tag elements are exhausted before the text is exhausted, then the # remaining paragraphs will not be tagged. Regardless of indentation # settings, a blank line will be inserted between all paragraphs when # #tag_paragraph is +true+. # # The Text::Format package provides three number generators, # Text::Format::Alpha, Text::Format::Number, and Text::Format::Roman to # assist with the numbering of paragraphs. # # *Default*:: [] # Used in:: #format, #paragraphs attr_accessor :tag_text # Indicates whether or not the non-breaking space feature should be # used. # # *Default*:: +false+ # Used in:: #format, #paragraphs attr_accessor :nobreak # A hash which holds the regular expressions on which spaces should not # be broken. The hash is set up such that the key is the first word and # the value is the second word. # # For example, if +nobreak_regex+ contains the following hash: # # { %r{Mrs?\.?} => %r{\S+}, %r{\S+} => %r{(?:[SJ])r\.?} } # # Then "Mr. Jones", "Mrs Jones", and "Jones Jr." would not be broken. If # this simple matching algorithm indicates that there should not be a # break at the current end of line, then a backtrack is done until there # are two words on which line breaking is permitted. If two such words # are not found, then the end of the line will be broken *regardless*. # If there is a single word on the current line, then no backtrack is # done and the word is stuck on the end. # # *Default*:: {} # Used in:: #format, #paragraphs attr_accessor :nobreak_regex # Indicates the number of spaces that a single tab represents. Any value # provided is silently converted to a positive integer. # # *Default*:: 8 # Used in:: #expand, #unexpand, # #paragraphs attr_accessor :tabstop def tabstop=(tabs) #:nodoc: @tabstop = tabs.to_i.abs end # Specifies the format style. Allowable values are: # *+LEFT_ALIGN+ # *+RIGHT_ALIGN+ # *+RIGHT_FILL+ # *+JUSTIFY+ # # *Default*:: Text::Format::LEFT_ALIGN # Used in:: #format, #paragraphs attr_accessor :format_style def format_style=(fs) #:nodoc: raise ArgumentError, "Invalid value provided for format_style." unless [LEFT_ALIGN, RIGHT_ALIGN, RIGHT_FILL, JUSTIFY].include?(fs) @format_style = fs end # Indicates that the format style is left alignment. # # *Default*:: +true+ # Used in:: #format, #paragraphs def left_align? @format_style == LEFT_ALIGN end # Indicates that the format style is right alignment. # # *Default*:: +false+ # Used in:: #format, #paragraphs def right_align? @format_style == RIGHT_ALIGN end # Indicates that the format style is right fill. # # *Default*:: +false+ # Used in:: #format, #paragraphs def right_fill? @format_style == RIGHT_FILL end # Indicates that the format style is full justification. # # *Default*:: +false+ # Used in:: #format, #paragraphs def justify? @format_style == JUSTIFY end # The formatting object itself can be used as a #hyphenator, where the # default implementation of #hyphenate_to implements the conditions # necessary to properly produce SPLIT_CONTINUATION. def hyphenate_to(word, size) if (size - 2) < 0 [nil, word] else [word[0 .. (size - 2)] + "\\", word[(size - 1) .. -1]] end end # Splits the provided word so that it is in two parts, word[0 .. # (size - 1)] and word[size .. -1]. def split_word_to(word, size) [word[0 .. (size - 1)], word[size .. -1]] end # Formats text into a nice paragraph format. The text is separated into # words and then reassembled a word at a time using the settings of this # Format object. # # If +text+ is +nil+, then the value of #text will be worked on. def format_one_paragraph(text = nil) text ||= @text text = text[0] if text.kind_of?(Array) # Convert the provided paragraph to a list of words. words = text.split(SPACES_RE).reverse.reject { |ww| ww.nil? or ww.empty? } text = [] # Find the maximum line width and the initial indent string. # TODO 20050114 - allow the left and right margins to be specified as # strings. If they are strings, then we need to use the sizes of the # strings. Also: allow the indent string to be set manually and # indicate whether the indent string will have a following space. max_line_width = @columns - @first_indent - @left_margin - @right_margin indent_str = ' ' * @first_indent first_line = true if words.empty? line = [] line_size = 0 extra_space = false else line = [ words.pop ] line_size = line[-1].size extra_space = __add_extra_space?(line[-1]) end while next_word = words.pop next_word.strip! unless next_word.nil? new_line_size = (next_word.size + line_size) + 1 if extra_space if (line[-1] !~ __sentence_end_re) extra_space = false end end # Increase the width of the new line if there's a sentence # terminator and we are applying extra_space. new_line_size += 1 if extra_space # Will the word fit onto the current line? If so, simply append it # to the end of the line. if new_line_size <= max_line_width if line.empty? line << next_word else if extra_space line << " #{next_word}" else line << " #{next_word}" end end else # Forcibly wrap the line if nonbreaking spaces are turned on and # there is a condition where words must be wrapped. If we have # returned more than one word, readjust the word list. line, next_word = __wrap_line(line, next_word) if @nobreak if next_word.kind_of?(Array) if next_word.size > 1 words.push(*(next_word.reverse)) next_word = words.pop else next_word = next_word[0] end next_word.strip! unless next_word.nil? end # Check to see if the line needs to be hyphenated. If a word has a # hyphen in it (e.g., "fixed-width"), then we can ALWAYS wrap at # that hyphenation, even if #hard_margins is not turned on. More # elaborate forms of hyphenation will only be performed if # #hard_margins is turned on. If we have returned more than one # word, readjust the word list. line, new_line_size, next_word = __hyphenate(line, line_size, next_word, max_line_width) if next_word.kind_of?(Array) if next_word.size > 1 words.push(*(next_word.reverse)) next_word = words.pop else next_word = next_word[0] end next_word.strip! unless next_word.nil? end text << __make_line(line, indent_str, max_line_width, next_word.nil?) unless line.nil? if first_line first_line = false max_line_width = @columns - @body_indent - @left_margin - @right_margin indent_str = ' ' * @body_indent end if next_word.nil? line = [] new_line_size = 0 else line = [ next_word ] new_line_size = next_word.size end end line_size = new_line_size extra_space = __add_extra_space?(next_word) unless next_word.nil? end loop do break if line.nil? or line.empty? line, line_size, ww = __hyphenate(line, line_size, ww, max_line_width)#if @hard_margins text << __make_line(line, indent_str, max_line_width, ww.nil?) line = ww ww = nil end if (@tag_paragraph and (not text.empty?)) if @tag_cur.nil? or @tag_cur.empty? @tag_cur = @tag_text[0] end fchar = /(\S)/o.match(text[0])[1] white = text[0].index(fchar) unless @tag_cur.nil? if ((white - @left_margin - 1) > @tag_cur.size) then white = @tag_cur.size + @left_margin text[0].gsub!(/^ {#{white}}/, "#{' ' * @left_margin}#{@tag_cur}") else text.unshift("#{' ' * @left_margin}#{@tag_cur}\n") end end end text.join('') end alias format format_one_paragraph # Considers each element of text (provided or internal) as a paragraph. # If #first_indent is the same as #body_indent, then paragraphs will be # separated by a single empty line in the result; otherwise, the # paragraphs will follow immediately after each other. Uses #format to # do the heavy lifting. # # If +to_wrap+ responds to #split, then it will be split into an array # of elements by calling #split with the value of +split_on+. The # default value of split_on is $/, or the default record separator, # repeated twice (e.g., /\n\n/). def paragraphs(to_wrap = nil, split_on = /(#{$/}){2}/o) to_wrap = @text if to_wrap.nil? if to_wrap.respond_to?(:split) to_wrap = to_wrap.split(split_on) else to_wrap = [to_wrap].flatten end if ((@first_indent == @body_indent) or @tag_paragraph) then p_end = NEWLINE else p_end = '' end cnt = 0 ret = [] to_wrap.each do |tw| @tag_cur = @tag_text[cnt] if @tag_paragraph @tag_cur = '' if @tag_cur.nil? line = format(tw) ret << "#{line}#{p_end}" if (not line.nil?) and (line.size > 0) cnt += 1 end ret[-1].chomp! unless ret.empty? ret.join('') end # Centers the text, preserving empty lines and tabs. def center(to_center = nil) to_center = @text if to_center.nil? to_center = [to_center].flatten tabs = 0 width = @columns - @left_margin - @right_margin centered = [] to_center.each do |tc| s = tc.strip tabs = s.count(TAB) tabs = 0 if tabs.nil? ct = ((width - s.size - (tabs * @tabstop) + tabs) / 2) ct = (width - @left_margin - @right_margin) - ct centered << "#{s.rjust(ct)}\n" end centered.join('') end # Replaces all tab characters in the text with #tabstop spaces. def expand(to_expand = nil) to_expand = @text if to_expand.nil? tmp = ' ' * @tabstop changer = lambda do |text| res = text.split(NEWLINE_RE) res.collect! { |ln| ln.gsub!(/\t/o, tmp) } res.join(NEWLINE) end if to_expand.kind_of?(Array) to_expand.collect { |te| changer[te] } else changer[to_expand] end end # Replaces all occurrences of #tabstop consecutive spaces with a tab # character. def unexpand(to_unexpand = nil) to_unexpand = @text if to_unexpand.nil? tmp = / {#{@tabstop}}/ changer = lambda do |text| res = text.split(NEWLINE_RE) res.collect! { |ln| ln.gsub!(tmp, TAB) } res.join(NEWLINE) end if to_unexpand.kind_of?(Array) to_unexpand.collect { |tu| changer[tu] } else changer[to_unexpand] end end # Return +true+ if the word may have an extra space added after it. This # will only be the case if #extra_space is +true+ and the word is not an # abbreviation. def __add_extra_space?(word) return false unless @extra_space word = word.gsub(/\.$/o, '') unless word.nil? return false if ABBREV.include?(word) return false if @abbreviations.include?(word) true end private :__add_extra_space? def __make_line(line, indent, width, last = false) #:nodoc: line_size = line.inject(0) { |ls, el| ls + el.size } lmargin = " " * @left_margin fill = " " * (width - line_size) if right_fill? and (line_size <= width) unless last if justify? and (line.size > 1) spaces = width - line_size word_spaces = spaces / (line.size / 2) spaces = spaces % (line.size / 2) if word_spaces > 0 line.reverse.each do |word| next if (word =~ /^\S/o) word.sub!(/^/o, " " * word_spaces) next unless (spaces > 0) word.sub!(/^/o, " ") spaces -= 1 end end end line = "#{lmargin}#{indent}#{line.join('')}#{fill}\n" unless line.empty? if right_align? and (not line.nil?) line.sub(/^/o, " " * (@columns - @right_margin - (line.size - 1))) else line end end # private :__make_line def __hyphenate(line, line_size, next_word, width) #:nodoc: return [ line, line_size, next_word ] if line.nil? or line.empty? rline = line.dup rsize = line_size rnext = [] rnext << next_word.dup unless next_word.nil? loop do break if rnext.nil? or rline.nil? if rsize == width break elsif rsize > width word = rline.pop size = width - rsize + word.size if (size < 1) rnext.unshift word next end first = rest = nil # TODO: Add the check to see if the word contains a hyphen to # split on automatically. # Does the word already have a hyphen in it? If so, try to use # that to split the word. # if word.index('-') < size # first = word[0 ... word.index("-")] # rest = word[word.index("-") .. -1] # end if @hard_margins if first.nil? and (@split_rules & SPLIT_HYPHENATION) == SPLIT_HYPHENATION if @hyphenator_arity == 2 first, rest = @hyphenator.hyphenate_to(word, size) else first, rest = @hyphenator.hyphenate_to(word, size, self) end end if first.nil? and (@split_rules & SPLIT_CONTINUATION) == SPLIT_CONTINUATION first, rest = self.hyphenate_to(word, size) end if first.nil? if (@split_rules & SPLIT_FIXED) == SPLIT_FIXED first, rest = split_word_to(word, size) elsif (not rest.nil? and (rest.size > size)) first, rest = split_word_to(word, size) end end else first = word if first.nil? end if first.nil? rest = word else rsize = rsize - word.size + first.size if rline.empty? rline << first else rsize += 1 rline << " #{first}" end @split_words << SplitWord.new(word, first, rest) end rnext.unshift rest unless rest.nil? break else break if rnext.empty? word = rnext.shift.dup size = width - rsize - 1 if (size <= 0) rnext.unshift word break end first = rest = nil # TODO: Add the check to see if the word contains a hyphen to # split on automatically. # Does the word already have a hyphen in it? If so, try to use # that to split the word. # if word.index('-') < size # first = word[0 ... word.index("-")] # rest = word[word.index("-") .. -1] # end if @hard_margins if (@split_rules & SPLIT_HYPHENATION) == SPLIT_HYPHENATION if @hyphenator_arity == 2 first, rest = @hyphenator.hyphenate_to(word, size) else first, rest = @hyphenator.hyphenate_to(word, size, self) end end if first.nil? and (@split_rules & SPLIT_CONTINUATION) == SPLIT_CONTINUATION first, rest = self.hyphenate_to(word, size) end if first.nil? if (@split_rules & SPLIT_FIXED) == SPLIT_FIXED first, rest = split_word_to(word, size) elsif (not rest.nil? and (rest.size > width)) first, rest = split_word_to(word, size) end end else first = word if first.nil? end # The word was successfully split. Does it fit? unless first.nil? if (rsize + first.size) < width @split_words << SplitWord.new(word, first, rest) rsize += first.size + 1 rline << " #{first}" else rest = word end else rest = word unless rest.nil? end rnext.unshift rest break end end [ rline, rsize, rnext ] end private :__hyphenate # The line must be broken. Typically, this is done by moving the last # word on the current line to the next line. However, it may be possible # that certain combinations of words may not be broken (see # #nobreak_regex for more information). Therefore, it may be necessary # to move multiple words from the current line to the next line. This # function does this. def __wrap_line(line, next_word) no_break = false word_index = line.size - 1 @nobreak_regex.each_pair do |first, second| if line[word_index] =~ first and next_word =~ second no_break = true end end # If the last word and the next word aren't to be broken, and the line # has more than one word in it, then we need to go back by words to # ensure that we break as allowed. if no_break and word_index.nonzero? word_index -= 1 while word_index.nonzero? no_break = false @nobreak_regex.each_pair { |first, second| if line[word_index] =~ first and line[word_index + 1] =~ second no_break = true end } break unless no_break word_index -= 1 end if word_index.nonzero? words = line.slice!(word_index .. -1) words << next_word end end [line, words] end private :__wrap_line # Create a Text::Format object. Accepts an optional hash of construction # options (this will be changed to named paramters in Ruby 2.0). After # the initial object is constructed (with either the provided or default # values), the object will be yielded (as +self+) to an optional block # for further construction and operation. def initialize(options = {}) #:yields self: @text = options[:text] || [] @columns = options[:columns] || 72 @tabstop = options[:tabstop] || 8 @first_indent = options[:first_indent] || 4 @body_indent = options[:body_indent] || 0 @format_style = options[:format_style] || LEFT_ALIGN @left_margin = options[:left_margin] || 0 @right_margin = options[:right_margin] || 0 @extra_space = options[:extra_space] || false @tag_paragraph = options[:tag_paragraph] || false @tag_text = options[:tag_text] || [] @abbreviations = options[:abbreviations] || [] @terminal_punctuation = options[:terminal_punctuation] || "" @terminal_quotes = options[:terminal_quotes] || "" @nobreak = options[:nobreak] || false @nobreak_regex = options[:nobreak_regex] || {} @hard_margins = options[:hard_margins] || false @split_rules = options[:split_rules] || SPLIT_FIXED @hyphenator = options[:hyphenator] || self @hyphenator_arity = @hyphenator.method(:hyphenate_to).arity @tag_cur = "" @split_words = [] yield self if block_given? end end text-format-1.0.0/lib/text/format/0000755000175000017500000000000011600332303016317 5ustar ondrejondrejtext-format-1.0.0/lib/text/format/alpha.rb0000644000175000017500000000367311600332303017742 0ustar ondrejondrej # Provides a numbering object that will produce letters. Accepts four # options for numbering that will control how the letters are presented # when given as #[](index). This numbering object will only provide 26 # values ("a" .. "z") unless :wrap is +true+. # # :transform:: The symbol representing the method to be called on # the letter. This must be a method that does not # require any arguments. # :postfix:: The value that will be appended to the letter # presented by #[]. Defaults to +nil+. # :prefix:: The value that will be prepended to the letter # presented by #[]. Defaults to +nil+. # :wrap:: If +true+, then indexes will be wrapped from "z" # to "a". # # a1 = Text::Format::Alpha.new(:postfix => ".") # puts a1[0] # => "a." # puts a1[1] # => "b. # puts a1[27] # => "" # # a2 = Text::Format::Alpha.new(:prefix => "A.") # puts a2[0] # => "A.a" # puts a2[1] # => "A.b" # puts a2[27] # => "" # # a3 = Text::Format::Alpha.new(:transform => :upcase) # puts a3[0] # => "A" # puts a3[1] # => "B" # puts a3[27] # => "" # # a4 = Text::Format::Alpha.new(:wrap => true) # puts a4[0] # => "a" # puts a4[27] # => "b" class Text::Format::Alpha def [](index) if @wrap ltr = (?a + (index % 26)).chr elsif index.between?(0, 25) ltr = (?a + index).chr else ltr = nil end if ltr if @transform "#{@prefix}#{ltr.send(transform)}#{@postfix}" else "#{@prefix}#{ltr}#{@postfix}" end else "" end end def initialize(options = {}) #:yields self: @transform = options[:transform] || nil @wrap = options[:wrap] || false @postfix = options[:postfix] || nil @prefix = options[:prefix] || nil end end text-format-1.0.0/lib/text/format/number.rb0000644000175000017500000000237511600332303020143 0ustar ondrejondrej # Provides a numbering object that will produce numbers. Accepts three # parameters for numbering that will control how the numbers are presented # when given as #[](index). # # :offset:: The number to add to the index in order to produce # the proper index. This is because #tag_text indexes # from 0, not 1. This defaults to 1. # :postfix:: The value that will be appended to the number # presented by #[]. Defaults to +nil+. # :prefix:: The value that will be prepended to the number # presented by #[]. Defaults to +nil+. # # n1 = Text::Format::Number.new(:postfix => ".") # puts n1[0] # => "1." # puts n1[1] # => "2. # # n2 = Text::Format::Number.new(:prefix => "2.") # puts n2[0] # => "2.1" # puts n2[1] # => "2.2" # # n3 = Text::Format::Number.new(:offset => 3) # puts n3[0] # => "3" # puts n3[1] # => "4" class Text::Format::Number def [](index) "#{@prefix}#{index + @offset}#{@postfix}" end def initialize(options = {}) #:yields self: @offset = options[:offset].to_i || 1 @postfix = options[:postfix] || nil @prefix = options[:prefix] || nil end end text-format-1.0.0/lib/text/format/roman.rb0000644000175000017500000000523211600332303017762 0ustar ondrejondrej # Provides a numbering object that will produce numbers. Accepts three # parameters for numbering that will control how the numbers are presented # when given as #[](index). # # :offset:: The number to add to the index in order to produce # the proper index. This is because #tag_text indexes # from 0, not 1. This defaults to 1. Any value less # than 1 will be set to 1 (because Romans did not know # about zero or negative numbers). # :lower:: Renders the Roman numerals in lowercase if +true+. # Defaults to +false+. # :postfix:: The value that will be appended to the number # presented by #[]. Defaults to +nil+. # :prefix:: The value that will be prepended to the number # presented by #[]. Defaults to +nil+. # # r1 = Text::Format::Roman.new(:postfix => ".") # puts r1[0] # => "I." # puts r1[8] # => "IX. # # r2 = Text::Format::Roman.new(:prefix => "M.") # puts r2[0] # => "M.I" # puts r2[8] # => "M.IX" # # r3 = Text::Format::Roman.new(:offset => 3) # puts r3[0] # => "III" # puts r3[9] # => "XII" # # r4 = Text::Format::Roman.new(:offset => 0) # puts r4[0] # => "I" # puts r4[8] # => "IX" # # r5 = Text::Format::Roman.new(:lower => true) # puts r5[0] # => "i" # puts r5[8] # => "ix" class Text::Format::Roman def [](index) roman = "" index += @offset # Do 1,000s roman << "M" * (index / 1000) index %= 1000 # Do 900s roman << "CM" * (index / 900) index %= 900 # Do 500s roman << "D" * (index / 500) index %= 500 # Do 400s roman << "CD" * (index / 400) index %= 400 # Do 100s roman << "C" * (index / 100) index %= 100 # Do 90s roman << "XC" * (index / 90) index %= 90 # Do 50s roman << "L" * (index / 50) index %= 50 # Do 40s roman << "XL" * (index / 40) index %= 40 # Do 10s roman << "X" * (index / 10) index %= 10 # Do 9s roman << "IX" * (index / 9) index %= 9 # Do 5s roman << "V" * (index / 5) index %= 5 # Do 4s roman << "IV" * (index / 4) index %= 4 # Do 1s roman << "I" * index roman.downcase! if @lower "#{@prefix}#{roman}#{@postfix}" end def initialize(options = {}) @offset = options[:offset].to_i || 1 @lower = options[:lower] || false @postfix = options[:postfix] || nil @prefix = options[:prefix] || nil @offset = 1 if @offset < 1 end end text-format-1.0.0/Changelog0000644000175000017500000001060611600332303015112 0ustar ondrejondrej== Text::Format 1.0.0 * Changed installer: added a .gem package. * Changed installer: moving to a variant of setup.rb by Minero Aoki. * Fixed significant problems with #hard_margin wrapping and fallback issues, eliminating all known possibilities for an infinite loop in wrapping. Some of the formatting changes involved with this result in different and more subtle wrapping and splitting of words; please read the full documentation for details. * Clarified the API for #hyphenate_to (delineated the return value required if the hyphenator cannot hyphenate the word to the specified size). * Changed a number of public and private API calls to work better. As long as the constants provided by Text::Format have been used (and not direct access to the constant values), there will be no issues presented by most of these changes. * Changed the initialization of the Text::Format object. The documentation has also been updated to be correct. Note that this will mean that some uses of Text::Format will not work, as Text::Format.new now yields +self+ if a block is given instead of evaluating the block with Object#instance_eval. * Added text numbering generators (Text::Format::Alpha, Text::Format::Number, and Text::Format::Roman) to work with #tag_paragraphs and #tag_text to generate numbered paragraphs. * #nobreak_regex must be a hash of regular expressions, not strings that are converted to regular expressions. This Perlism has finally been removed. * The performance has been improved; the number of times that lines are joined together and then split apart has been reduced. * Changed the dependency to Text::Hyphen from TeX::Hyphen. * Added auto-split capabilities to #paragraphs. See the updated documentation. == Text::Format 0.64 * Fixed a bug where a NoMethod exception would be raised if #paragraphs was called with either (" ") or ([" "]). == Text::Format 0.63 * Fixed a bug where a crash would occur when a hyphenator returned nil instead of "". == Text::Format 0.62 * Modified the API for hyphenators. Previously, a hyphenator could only be defined as an object containing a method #hyphenate_to with the signature: #hyphenate_to(word, size) Now, the #hyphenate_to method may be the above signature or: #hyphenate_to(word, size, formatter) So that the hyphenator may access information about the formatting object, if necessary. Thanks to Tim Bates for suggesting a case where this would be useful. * Fixed a bug for strings matching /\A\s*\Z/ raising a NameError. * Fixed a test case that failed uner 1.6.8. The following no longer works: l, m1, m2 = /((?:\S+\s+){11})(.+)/.match(line) This has been replaced with an explicit use of l[1] and l[2]. Thanks to Tim Bates for finding this problem. * Changed installer to Phil Thomson's install-package wrapper. == Text::Format 0.61 * Fixed a problem with the installer. Note that Text::Format is no longer case sensitive for require purposes. It will be required as: require 'text/format' Versions earlier than 0.60 were case-sensitive. Please be aware of this if you are installing Text::Format over an older version. It may not replace the existing library in the way that you expect. == Text::Format 0.60 * Added Symbol equivalents for the Hash initialization. Hash initialization has been modified so that values are set as follows (Symbols are highest priority; strings are middle; defaults are lowest): @columns = arg[:columns] || arg['columns'] || @columns * Fixed a problem with Text::Format::RIGHT_FILL handling where a single word is larger than #columns. * Removed Comparable mixin (<=> doesn't make sense; == does). * Added #hard_margins, #split_rules, #hyphenator, and #split_words. Text formatted with #hard_margins will have words larger than #columns split forcibly. Words forcibly split will be placed into #split_words. See the documentation for important information on how this feature works. == Text::Format 0.52.2 * Fixed the ordering of #<=> in cases of Boolean values. * Fixed #expand and #unexpand Array handling. * Added a Changelog. == Text::Format 0.52.1 * Fixed a problem when tabs aren't counted properly. * Changed #abbreviations from Hash to Array to better suit Ruby's capabilities. * Fixed problems with the way that Array arguments are handled in calls to the major object types. == Text::Format 0.52 * Initial release $Id: Changelog,v 1.6 2005/06/24 19:49:09 austin Exp $ text-format-1.0.0/Install0000644000175000017500000000026511600332303014631 0ustar ondrejondrejInstalling this package is as simple as: % ruby setup.rb Alternatively, you can use the RubyGem version of Text::Format available as text-format-1.0.0.gem from the usual sources. text-format-1.0.0/metadata.yml0000644000175000017500000000337311600332303015606 0ustar ondrejondrej--- !ruby/object:Gem::Specification rubygems_version: 0.8.10 specification_version: 1 name: text-format version: !ruby/object:Gem::Version version: 1.0.0 date: 2005-06-24 summary: Text::Format formats fixed-width text nicely. require_paths: - lib email: austin@rubyforge.org homepage: http://rubyforge.org/projects/text-format rubyforge_project: text-format description: "Text::Format is provides the ability to nicely format fixed-width text with knowledge of the writeable space (number of columns), margins, and indentation settings. Text::Format can work with either TeX::Hyphen or Text::Hyphen to hyphenate words when formatting." autorequire: text/format default_executable: bindir: bin has_rdoc: true required_ruby_version: !ruby/object:Gem::Version::Requirement requirements: - - ">" - !ruby/object:Gem::Version version: 0.0.0 version: platform: ruby authors: - Austin Ziegler files: - Changelog - Install - lib - metaconfig - pre-setup.rb - Rakefile - README - setup.rb - tests - ToDo - lib/text - lib/text/format - lib/text/format.rb - lib/text/format/alpha.rb - lib/text/format/number.rb - lib/text/format/roman.rb - tests/tc_text_format.rb - tests/testall.rb test_files: - tests/tc_text_format.rb rdoc_options: - "--title" - Text::Format - "--main" - README - "--line-numbers" extra_rdoc_files: - README - Changelog - Install executables: [] extensions: [] requirements: [] dependencies: - !ruby/object:Gem::Dependency name: text-hyphen version_requirement: version_requirements: !ruby/object:Gem::Version::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.0.0 version: text-format-1.0.0/setup.rb0000644000175000017500000007137211600332303014774 0ustar ondrejondrej# # setup.rb # # Copyright (c) 2000-2004 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted windows stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end # # Config # if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) require arg.split(/=/, 2)[1] $".push 'rbconfig.rb' else require 'rbconfig' end def multipackage_install? FileTest.directory?(File.dirname($0) + '/packages') end class ConfigItem def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default.dup.freeze @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value @value end def eval(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < ConfigItem def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val setup_rb_error "config: --#{@name} accepts only yes/no for argument" end (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no' end end class PathItem < ConfigItem def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < ConfigItem def config_type 'program' end end class SelectItem < ConfigItem def initialize(name, template, default, desc) super @ok = template.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class PackageSelectionItem < ConfigItem def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class ConfigTable_class def initialize(items) @items = items @table = {} items.each do |i| @table[i.name] = i end ALIASES.each do |ali, name| @table[ali] = @table[name] end @script_extensions = ['rb'] end attr_accessor :script_extensions include Enumerable def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or raise ArgumentError, "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def new dup() end def savefile '.config' end def load begin t = dup() File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) t[k] = v.strip end t rescue Errno::ENOENT setup_rb_error $!.message + "#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value end } end def [](key) lookup(key).eval(self) end def []=(key, val) lookup(key).set val end end c = ::Config::CONFIG rubypath = c['bindir'] + '/' + c['ruby_install_name'] major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V < 1.6.3 _stdruby = c['rubylibdir'] _siteruby = c['sitedir'] _siterubyver = c['sitelibdir'] _siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 _stdruby = "$prefix/lib/ruby/#{version}" _siteruby = c['sitedir'] _siterubyver = "$siteruby/#{version}" _siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 _stdruby = "$prefix/lib/ruby/#{version}" _siteruby = "$prefix/lib/ruby/#{version}/site_ruby" _siterubyver = _siteruby _siterubyverarch = "$siterubyver/#{c['arch']}" end libdir = '-* dummy libdir *-' stdruby = '-* dummy rubylibdir *-' siteruby = '-* dummy site_ruby *-' siterubyver = '-* dummy site_ruby version *-' parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\ .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\ .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\ .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\ .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver') } libdir = parameterize.call(c['libdir']) stdruby = parameterize.call(_stdruby) siteruby = parameterize.call(_siteruby) siterubyver = parameterize.call(_siterubyver) siterubyverarch = parameterize.call(_siterubyverarch) if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end common_conf = [ PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', libdir, 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for man pages'), PathItem.new('stdruby', 'path', stdruby, 'the directory for standard ruby libraries'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] class ConfigTable_class # open again ALIASES = { 'std-ruby' => 'stdruby', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } end multipackage_conf = [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] if multipackage_install? ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf) else ConfigTable = ConfigTable_class.new(common_conf) end module MetaConfigAPI def eval_file_ifexist(fname) instance_eval File.read(fname), fname, 1 if File.file?(fname) end def config_names ConfigTable.map {|i| i.name } end def config?(name) ConfigTable.key?(name) end def bool_config?(name) ConfigTable.lookup(name).config_type == 'bool' end def path_config?(name) ConfigTable.lookup(name).config_type == 'path' end def value_config?(name) case ConfigTable.lookup(name).config_type when 'bool', 'path' true else false end end def add_config(item) ConfigTable.add item end def add_bool_config(name, default, desc) ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) ConfigTable.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) ConfigTable.lookup(name).default = default end def remove_config(name) ConfigTable.remove(name) end def add_script_extension(ext) ConfigTable.script_extensions << ext end end # # File Operations # module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # does not check '/'... it's too abnormal case dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(fname) $stderr.puts "rm -f #{fname}" if verbose? return if no_harm? if File.exist?(fname) or File.symlink?(fname) File.chmod 0777, fname File.unlink fname end end def rm_rf(dn) $stderr.puts "rm -rf #{dn}" if verbose? return if no_harm? Dir.chdir dn Dir.foreach('.') do |fn| next if fn == '.' next if fn == '..' if File.dir?(fn) verbose_off { rm_rf fn } else verbose_off { rm_f fn } end end Dir.chdir '..' Dir.rmdir dn end def move_file(src, dest) File.unlink dest if File.exist?(dest) begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(str) $stderr.puts str if verbose? system str or raise RuntimeError, "'system #{str}' failed" end def ruby(str) command config('rubyprog') + ' ' + str end def make(task = '') command config('makeprog') + ' ' + task end def extdir?(dir) File.exist?(dir + '/MANIFEST') end def all_files_in(dirname) Dir.open(dirname) {|d| return d.select {|ent| File.file?("#{dirname}/#{ent}") } } end REJECT_DIRS = %w( CVS SCCS RCS CVS.adm .svn ) def all_dirs_in(dirname) Dir.open(dirname) {|d| return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS } end end # # Main Installer # module HookUtils def run_hook(name) try_run_hook "#{curr_srcdir()}/#{name}" or try_run_hook "#{curr_srcdir()}/#{name}.rb" end def try_run_hook(fname) return false unless File.file?(fname) begin instance_eval File.read(fname), fname, 1 rescue setup_rb_error "hook #{fname} failed:\n" + $!.message end true end end module HookScriptAPI def get_config(key) @config[key] end alias config get_config def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # #abstract srcdir_root #abstract objdir_root #abstract relpath def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file? srcfile(path) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.3.1' Copyright = 'Copyright (c) 2000-2004 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke instance().invoke end @singleton = nil def ToplevelInstaller.instance @singleton ||= new(File.dirname($0)) @singleton end include MetaConfigAPI def initialize(ardir_root) @config = nil @options = { 'verbose' => true } @ardir = File.expand_path(ardir_root) end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' @config = load_config('config') parsearg_config init_installers exec_config exec_setup exec_install else @config = load_config(task) __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" end def load_config(task) case task when 'config' ConfigTable.new when 'clean', 'distclean' if File.exist?(ConfigTable.savefile) then ConfigTable.load else ConfigTable.new end else ConfigTable.load end end def init_installers @installer = Installer.new(@config, @options, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/ while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg return arg when '-q', '--quiet' @options['verbose'] = false when '--verbose' @options['verbose'] = true when '-h', '--help' print_usage $stdout exit 0 when '-v', '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def parsearg_no_options unless ARGV.empty? setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/ @options['config-opt'] = [] while i = ARGV.shift if /\A--?\z/ =~ i @options['config-opt'] = ARGV.dup break end m = re.match(i) or setup_rb_error "config: unknown option #{i}" name, value = *m.to_a[1,2] @config[name] = value end end def parsearg_install @options['no-harm'] = false @options['install-prefix'] = '' while a = ARGV.shift case a when /\A--no-harm\z/ @options['no-harm'] = true when /\A--prefix=(.*)\z/ path = $1 path = File.expand_path(path) unless path[0,1] == '/' @options['install-prefix'] = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, '-h,--help', 'print this message' out.printf fmt, '-v,--version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' ConfigTable.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '$prefix' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_show ConfigTable.each do |i| printf "%-20s %s\n", i.name, i.value end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end class ToplevelInstallerMulti < ToplevelInstaller include HookUtils include HookScriptAPI include FileOperations def initialize(ardir) super @packages = all_dirs_in("#{@ardir}/packages") raise 'no package exists' if @packages.empty? end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" @packages.each do |name| eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig" end end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, @options, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # multi-package metaconfig API # attr_reader :packages def declare_packages(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_clean rm_f ConfigTable.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f ConfigTable.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose'] Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def verbose? @options['verbose'] end def no_harm? @options['no-harm'] end end class Installer FILETYPES = %w( bin lib ext data ) include HookScriptAPI include HookUtils include FileOperations def initialize(config, opt, srcroot, objroot) @config = config @options = opt @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # configs/options # def no_harm? @options['no-harm'] end def verbose? @options['verbose'] end def verbose_off begin save, @options['verbose'] = @options['verbose'], false yield ensure @options['verbose'] = save end end # # TASK config # def exec_config exec_task_traverse 'config' end def config_dir_bin(rel) end def config_dir_lib(rel) end def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end def extconf opt = @options['config-opt'].join(' ') command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}" end def config_dir_data(rel) end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) all_files_in(curr_srcdir()).each do |fname| adjust_shebang "#{curr_srcdir()}/#{fname}" end end def adjust_shebang(path) return if no_harm? tmpfile = File.basename(path) + '.tmp' begin File.open(path, 'rb') {|r| first = r.gets return unless File.basename(config('rubypath')) == 'ruby' return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby' $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose? File.open(tmpfile, 'wb') {|w| w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath')) w.write r.read } move_file tmpfile, File.basename(path) } ensure File.unlink tmpfile if File.exist?(tmpfile) end end def setup_dir_lib(rel) end def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end def setup_dir_data(rel) end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files ruby_extentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @options['install-prefix'] list.each do |fname| install fname, dest, mode, @options['install-prefix'] end end def ruby_scripts collect_filenames_auto().select {|n| /\.(#{ConfigTable.script_extensions.join('|')})\z/ =~ n } end # picked up many entries from cvs-1.11.1/src/ignore.c reject_patterns = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) mapping = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } REJECT_PATTERNS = Regexp.new('\A(?:' + reject_patterns.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] } }.join('|') + ')\z') def collect_filenames_auto mapdir((existfiles() - hookfiles()).reject {|fname| REJECT_PATTERNS =~ fname }) end def existfiles all_files_in(curr_srcdir()) | all_files_in('.') end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def mapdir(filelist) filelist.map {|fname| if File.exist?(fname) # objdir fname else # srcdir File.join(curr_srcdir(), fname) end } end def ruby_extentions(dir) Dir.open(dir) {|d| ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname } if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end return ents } end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f ConfigTable.savefile rm_f 'InstalledFiles' end def clean_dir_bin(rel) end def clean_dir_lib(rel) end def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end def clean_dir_data(rel) end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f ConfigTable.savefile rm_f 'InstalledFiles' end def distclean_dir_bin(rel) end def distclean_dir_lib(rel) end def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end # # lib # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if config('without-ext') == 'yes' and type == 'ext' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') all_dirs_in(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end end if $0 == __FILE__ begin if multipackage_install? ToplevelInstallerMulti.invoke else ToplevelInstaller.invoke end rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end text-format-1.0.0/README0000644000175000017500000000117211600332303014156 0ustar ondrejondrejText::Format 1.0.0 ================== Text::Format is provides the ability to nicely format fixed-width text with knowledge of the writeable space (number of columns), margins, and indentation settings. Text::Format can work with either TeX::Hyphen or Text::Hyphen to hyphenate words when formatting. This is release 1.0, containing both feature enhancements and bug fixes over the previous version, 0.64. Text::Format is originally based on the Perl library of the same name by Gábor Egressy. It is copyright 2002 - 2005 by Austin Ziegler and is licenced under Ruby's licence. It is also available under the Perl Artistic licence.