%#-- %# Copyright protects this work. %# See LICENSE file for details. %#++ % require 'inochi/util/tempdir' % yaml_addr = "http://yaml.kwiki.org/?YamlInFiveMinutes" %|part "Usage" %|section "Command-line interface" When you run this command: <%= $program %> --help You will see this output:
<%= verbatim `ruby bin/#{$program} --help` %>%|tip "Merging files with **kdiff3**" Instead of merging files by hand, you can transfer wanted changes between files semi-automatically using [kdiff3](http://kdiff3.sourceforge.net). Simply follow these instructions: 1. Create a file named merge2 with the following content: #!/bin/sh old_file=$1 shift new_file=$1 shift output_file=$1 shift kdiff3 --merge "$old_file" "$new_file" --output "$output_file" 2. Make the file executable: chmod +x merge2 3. Place the file in a directory that is listed in your `PATH` environment variable. 4. Run <%= $project %> like this: <%= $program %> -m merge2 Now **kdiff3** will be invoked to help you transfer your changes. When you are finished transferring changes, save the file and quit **kdiff3**. If you do not want to transfer any changes, simply quit **kdiff3** _without_ saving the file. %|section "Ruby library interface" The `Inochi` module has several class methods which provide a common configuration for various aspects of your project. These aspects, and their interactions with the `Inochi` module, are as follows: * Your project's main library invokes the `Inochi.init()` method. * Your project's main executable invokes the `Inochi.main()` method. * Your project's rakefile invokes the `Inochi.rake()` method. * Your project's user manual invokes the `Inochi.book()` method. %|section "General walkthrough", "Tutorial" This tutorial shows how <%= $project %> is used to manage a hypothetical `WordCount` project throughout the various stages of its life. %|section "Have a brilliant idea" It is 4am on Sunday morning. Unwilling to sleep, you have spent the past few hours programming obsessively.. Though your eyes grow heavy and your stomach churns from hunger, your mind charges forth with haste. > Push on! Keep on! Until at last, pushed far beyond its limit, your body overpowers your will and drags you into black unconsciousness. *BEEP* *BEEP* *B*--- Half-asleep and violent from the sudden disturbance, you silence the bleeting alarm clock with vengeance. It is 2pm on Sunday afternoon. Red beams of sunlight slip through the gaps in your curtains. It is a beautiful day, outside. *Outside*--- you think, > What am I doing to myself? > > I've got to get *outside*. > > I've got to get *away*... > > Away from this computer... this... mental prison in which I toil night after night, like a souless machine. Venturing into the courtyard outside your quarters, you find peace. A warm breeze graces you, sweeping your hair gently as a mother would. The bright sunlight penetrates your mind's eye as your thoughts fade... Thoughts of tests to write, units to refactor, bugs to fix, options to document. They melt and mix and flow into nothingness. All is clear. No thoughts. No more. > No! You collapse heavily onto the grassy earth beneath you. Breathing deeply, you sink into yourself and whisper > It's okay. > > Just, let go. and fall asleep. You awaken that evening relaxed and refreshed. A brilliant idea for a new project enters your mind: the project will be a tool that counts the number of words in text file. And, the project can be accessed from Ruby via the `WordCount` module. *However*, you must go to work the next morning, so there isn't much time. What can you do? Let's see how <%= $project %> can help us meet this challenge. %|cd TempDir.new.path % main_executable = 'bin/word_count' %|section "Generate your project" Give life to your new project:
# inochi WordCount %= verbatim `ruby #{$install}/bin/inochi WordCount`Enter the word_count directory:
# cd word_count % cd "word_count"%|paragraph "View Rake tasks"
# rake -T %= verbatim `rake -T`%|paragraph "Run unit tests"
# rake test %= verbatim `rake test`%|paragraph "Run project executable"
% command = main_executable # ruby <%= command %> %= verbatim `ruby #{command}`See usage information:
% command = "#{main_executable} --help" # ruby <%= command %> %= verbatim `ruby #{command}`See project & version information:
% command = "#{main_executable} --version" # ruby <%= command %> %= verbatim `ruby #{command}`%|paragraph "Show user manual" Build the user manual (please disregard any "unclosed span" warnings):
# rake doc:manLaunch the user manual:
% command = "#{main_executable} --manual" # ruby <%= command %>The manual will now appear in your default web browser. %|section "Configure your project" <%= xref "Ruby library interface" %> lists and documents the interactions between your project and <%= $project %>. These points of interaction are illustrated in the following sections. %|section "Project information" % license_file = 'LICENSE' Open the <%= license_file %> file, which contains the open source [ISC license](http://www.opensource.org/licenses/isc-license.txt) by default, and add a copyright notice with your name and (optional) email address:
%= verbatim File.read(license_file)% main_library = 'lib/word_count.rb' Open the main project library file <%= main_library %> and fill in the blanks:
%= verbatim File.read(main_library)
%|section "Project executable"
Open the <%= main_executable %> file and fill in the blanks:
%= verbatim File.read(main_executable)
%|section "Rake tasks"
% rake_file = 'rakefile'
Open the <%= rake_file %> and fill in the blanks:
%= verbatim File.read(rake_file)
%|section "User manual"
<%
whole = 'doc/index.erb'
parts = File.read(whole).
scan(/^[[:blank:]]*%\+[[:blank:]]*(.*)\s*/).
flatten.map {|s| "doc/" + eval(s) }
files = [whole, *parts]
%>
The user manual's source file <%= whole %> subdivides its content into several smaller files, according to topic, for easier editing and maintenance. These files are processed by the [<%= ERBook::PROJECT %>](<%= ERBook::WEBSITE %>) program's [XHTML format](<%= ERBook::DOCSITE %>#xhtml) to produce the doc/index.html file.
Open these source files and fill in the blanks:
%|files.each |f|
%|paragraph "#{f}"
<%= verbatim File.read(f) %>
%|section "Implement your project"
Add the following code to the bottom of lib/word_count.rb, the main project library:
module WordCount
# Returns the number of words in the given input.
def WordCount.count input
input.to_s.split(/\W+/).length
end
end
Add the following code to the bottom of bin/word_count, the main project executable:
input = ARGF.read
total = WordCount.count(input)
puts "There are #{total} words in the input."
Add the following code to the bottom of test/word_count.rb, a unit test for the main project library:
describe WordCount do
it 'handles empty input' do
WordCount.count(nil).must_equal(0)
WordCount.count('').must_equal(0)
WordCount.count(' ').must_equal(0)
end
it 'handles single words' do
WordCount.count('a').must_equal(1)
WordCount.count('foo').must_equal(1)
WordCount.count('bar').must_equal(1)
end
it 'handles multiple words' do
WordCount.count('a b').must_equal(2)
WordCount.count('a-b').must_equal(2)
WordCount.count('a/b').must_equal(2)
end
it 'ignores punctuation and space' do
WordCount.count('!').must_equal(0)
WordCount.count('! @ # % #!@#').must_equal(0)
WordCount.count(' !').must_equal(0)
WordCount.count('! ').must_equal(0)
WordCount.count(' ! ').must_equal(0)
WordCount.count(' ! ').must_equal(0)
end
end
%|paragraph "Goodbye `$LOAD_PATH`, hello `require()`"
Notice that, in the Ruby files that you modified so far, there were no `$LOAD_PATH` manipulations and no explicit `require()` statements to pull in the various parts of your project. That is because <%= $project %> does this for you automatically.
Furthermore, you can always `require()` a sub-library anywhere in your project using its canonical path because <%= $project %> puts your main project libraries on the Ruby load path.
% sub_library = 'word_count/odf/text'
For example, if your project has a sub-library, say, lib/<%= sub_library %>.rb that counts the number of words in an [OpenDocument Text](http://en.wikipedia.org/wiki/OpenDocument) document, then it would be loaded into the main project executable like this:
require '<%= sub_library %>'
Regardless of whether a sub-library is used within your project itself or from within an external application, we always `require()` the sub-library using the same canonical path.
%|section "Test your project"
To reduce the amount of code you have to write, <%= $project %> defines the following convention for unit tests.
%|paragraph "Units and tests"
Every Ruby source file in your project's lib/ directory is considered to be a **unit**. Likewise, every Ruby source file in your project's test/ is considered to be a **test**.
As a result, your project's test/ directory structure *mirrors* the structure of your project's lib/ directory. For example, if your project has a lib/foo/bar.rb unit, then test/foo/bar.rb would be its corresponding test.
%|paragraph "Test execution"
rake testThe above command begins the testing process, during which: * Tests which lack corresponding units are *skipped* and not executed. A message specifying which test file was skipped is printed to the standard error stream whenever this occurs. * Before a test is executed, its corresponding unit is automatically loaded into the Ruby environment using `require()`. The details of test execution are left to the integration libraries specified by the `:test_with` parameter of the `Inochi.rake()` method. Possible values for this parameter are: % test_libs.sort.each do |f| * <%= File.basename(f, '.rb') %> --- %= File.read(f).scan(/^#(.*)/).join %|paragraph "Helper libraries" Your project's main directory is added to Ruby's load path. So if your tests have helper libraries stored in your project's test/ directory, you can load them into your tests by adding a "test/" prefix. For example, if your test/foo/bar.rb test has a test/foo/qux.rb helper library, then you would write the following code inside the test to load the helper library:
require 'test/foo/qux'
%|section "Translate your project"
% phrases_file = "lang/phrases.yaml"
Although English is the *lingua franca* of today, your project's users may prefer to interact with it in their native language. <%= $project %> makes it easy to translate your project and also makes it easy for users to correct and contribute translations to your project.
%|section "Language phrases"
<%= $project %> equips your project module with a `PHRASES` constant (see the `Inochi::Phrases` class) which provides access to translations of language phrases used in your project.
The `Inochi::Phrases#[]` method translates a given language phrase into the user's preferred language, which is automatically inferred from their environment, but may be explictly overridden by the user via the --locale option of <%= xref "Run project executable", "your project's main executable" %>:
your_project::PHRASES['Have a nice day!']
If there is no <%= xref "Translation files", "translation file" %> for the user's preferred language, or it does not define a translation for a particular language phrase, then the language phrase will be used untranslated:
your_project::PHRASES['No translation for this']
#=> 'No translation for this'
%|paragraph "Parameterizing language phrases"
Language phrases can be parameterized using [`printf` format placeholders](http://en.wikipedia.org/wiki/Printf#printf_format_placeholders) to ease translation:
your_project::PHRASES['Good afternoon, %s.', user_name]
your_project::PHRASES['You are %d years old.', user_age]
%|paragraph "Explicit translation into a language"
If a language phrase must be translated into a specific language, regardless of the user's preference, you can invoke the respective method (whose name is the same as the [ISO-639 language code](http://en.wikipedia.org/wiki/ISO_639) of the language into which you wish to translate) on your `PHRASES` object:
# explictly translate into Japanese (ja)
your_project::PHRASES.ja('Goodbye %s!', user_name)
# explictly translate into French (fr)
your_project::PHRASES.fr('Farewell %s!', user_name)
%|section "Translation files"
Translation files are [YAML documents](<%= yaml_addr %>) that reside in your project's lang/ directory. They provide translations for <%= xref "Language phrases", "language phrases" %> used in your project.
For example, suppose that your language phrases are written in English, the lang/es.yaml (Spanish) translation file would appear like this:
# # (this is a comment) # # language phrase : translation # Hello %s! : ¡Hola %s! Money : Dinero Ticket : Tarjeta See you later %s! : ¡Hasta la vista %s! "%s: Quickly, please!" : "%s: ¡Rápidamente, por favor!"On each line, the original language phrase (as used in your project) appears first, then a single semicolon (:), followed by the translation. Also, notice that if a language phrase contains a semicolon, then the entire phrase must be enclosed in quotation marks. The same rule applies to its corresponding translation. %|section "Extracting language phrases"
# rake lang:dump %= verbatim `rake lang:dump`The above command exercises your project's code and extracts all *utilized* language phrases into the <%= phrases_file %> file. Continue reading to learn how this is accomplished. %|paragraph "Dynamic analysis" Because Ruby is a dynamically interpreted language, the easiest way to extract language phrases is to evaluate the code that defines them and keep track of which phrases are defined. But how can <%= $project %> exercise all Ruby code in your project? The answer is through *unit tests*. Because unit tests already exercise your project's code, <%= $project %> can use them to reach and extract all language phrases from your project. However, note that if your unit tests *do not* exercise a part of your project that defines language phrases, then those phrases *will not* be extracted by <%= $project %>. This gives you extra motivation to improve the coverage of your test suite---at least to the point where all code that defines language phrases is covered. %|paragraph "Static analysis" In a future release, I plan to extract language phrases through static analysis of Ruby code. This approach will supplement the current practice of reaching language phrases through unit tests. Patches are welcome! :-) %|section "Translating language phrases" After you have extracted all language phrases from your project (either manually or via <%= xref "Extracting language phrases" %>) into the <%= phrases_file %> file, <%= $project %> can automatically translate them into various languages using the [Yahoo! BabelFish translation service](http://babelfish.yahoo.com):
# rake lang:conv from=LANGUAGE_CODE %= verbatim `rake lang:conv from=LANGUAGE_CODE 2>&1`Notice that you must specify the language in which your phrases are written, via the from= parameter. <%= $project %> cannot determine this automatically. %|section "Publish your project"
# rake pubThe above command performs all automated steps described in the following sections. %|section "Build a RubyGem" Build a RubyGem by running:
# rake gem <%= # prevent Maruku errors about blanks (__________) in the # scaffold-generated documentation for this dummy project Dir['doc/*.erb'].each do |doc| File.open(doc, 'r+') do |f| old = f.read new = old.gsub('__________', '') f.rewind f.write new end end # remove TODO and FIXME words (which come # from the scaffold-generated output for # this dummy project)from the gem spec # that RubyGems does not reject it File.open('rakefile', 'r+') do |f| old = f.read new = old.sub(/^Inochi.rake.*$/) do |header| header + %q{ [:summary, :description].each do |field| old = gem.send(field) new = old.gsub(/\b(TODO|FIXME)\b/o, '') gem.send("#{field}=", new) end } end f.rewind f.write new end `rake gem` %>See the RubyGem contents:
# gem spec pkg/*.gem
%= `gem spec pkg/*.gem`.rstrip
%|section "Publish a RubyGem"
You must first register your project on [RubyForge](http://rubyforge.org) before you can publish a RubyGem. If your RubyForge project name is different from your actual project name, then you should pass the `:rubyforge_project` and `:rubyforge_section` options to the `Inochi.rake()` method.
Publish a RubyGem by running:
# rake pub:gem%|section "Announce a release" You must first provide your <%= xref "Login information" %> to <%= $project %>. If you do not want to do this, then see <%= xref "Manual release announcement" %>. Announce a release by running:
# rake pub:ann%|paragraph "Login information" In order to automate the announcement of releases, <%= $project %> needs to know your login information for the [RAA (Ruby Application Archive)](http://raa.ruby-lang.org) and [RubyForum](http://www.ruby-forum.com/forum/4), which serves as a gateway to the [ruby-talk mailing list](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/). % logins_file = "~/.config/inochi/logins.yaml" This information is expected to be stored in a <%= logins_file %> file (this location can be overridden by passing the `:logins_file` option to the `Inochi.rake()` method), where ~ denotes the path to your home directory. This file is a [YAML document](<%= yaml_addr %>) containing the following parameters:
www.ruby-forum.com:
user: YOUR_USERNAME_HERE
pass: YOUR_PASSWORD_HERE
raa.ruby-lang.org:
pass: YOUR_PASSWORD_HERE
For better security, you should ensure that this file is only readable and writable by you and is not accessible by anyone else. In a UNIX environment, this can be accomplished by running the following command:
# chmod 0600 <%= logins_file %>
%|section "Manual release announcement"
Build release announcements by running:
# rake ann %= `rake ann`This produces the following files in your project directory: %|Dir['ANN*'].each |f| * <%= f %> Now you can manually announce your release using these files. %|section "Publish the documentation" Publish the user manual and API documentation by running:
# rake pub:docIf your documentation website (see the `:docsite` option for the `Inochi.init()` method) is hosted on RubyForge, then the above command will automatically upload your project's documentation to the correct place. %|section "Specific topics" %|section "Build a RubyGem without #{$project} as runtime dependency" <%= $project %> adds itself as a runtime dependency to your project's gem, by default, because it assumes that you want to use its runtime convenience facilities in your project. However, if you only wish to use <%= $project %>'s gem building facilities and your project has no use for its runtime convenience facilities, then you can build your project's gem without <%= $project %> as a runtime dependency as follows. 1. Create your project's rakefile with the following structure and fill in the blanks:
require 'rubygems'
require 'inochi'
Inochi.init __________
Inochi.rake __________, :inochi_consumer => false
Notice the `:inochi_consumer => false` parameter being passed to `Inochi.rake()`. This is what tells <%= $project %> not to add itself as a runtime dependency to your project's gem.
2. Build your project's gem <%= xref "Build a RubyGem", "as you normally would" %>.
Now your project uses <%= $project %> *only* when building its gem and contains <%= $project %>-related code *only* in its rakefile. The remainder of your project is isolated from, and has no knowledge of, <%= $project %>.