inochi 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,3 @@
1
- <% STDERR.puts "hey: #{ERBook::VERSION.object_id}" %>
2
-
3
1
  <% chapter "Setup" do %>
4
2
  <% section "Requirements" do %>
5
3
  Your system needs the following software to run **<%= $project %>**.
@@ -24,7 +24,7 @@
24
24
  output_file=$1
25
25
  shift
26
26
 
27
- kdiff3 --auto "$old_file" "$new_file" --output "$output_file"
27
+ kdiff3 --merge "$old_file" "$new_file" --output "$output_file"
28
28
 
29
29
  2. Make the file executable:
30
30
 
@@ -120,47 +120,60 @@
120
120
  <% cd "word_count" %>
121
121
  </pre>
122
122
 
123
- View the available Rake tasks:
123
+ <% paragraph "View Rake tasks" do %>
124
+ <pre>
125
+ # rake -T
126
+ <%= verbatim `rake -T` %>
127
+ </pre>
128
+ <% end %>
124
129
 
125
- <pre>
126
- # rake -T
127
- <%= verbatim `rake -T` %>
128
- </pre>
130
+ <% paragraph "Run unit tests" do %>
131
+ <pre>
132
+ # rake test
133
+ <%= verbatim `rake test` %>
134
+ </pre>
135
+ <% end %>
129
136
 
130
- Try the main project executable:
137
+ <% paragraph "Run project executable" do %>
138
+ <pre>
139
+ <% command = main_executable %>
140
+ # ruby <%= command %>
141
+ <%= verbatim `ruby #{command}` %>
142
+ </pre>
131
143
 
132
- <pre>
133
- <% command = main_executable %>
134
- # ruby <%= command %>
135
- <%= verbatim `ruby #{command}` %>
136
- </pre>
144
+ See usage information:
137
145
 
138
- See usage information:
146
+ <pre>
147
+ <% command = "#{main_executable} --help" %>
148
+ # ruby <%= command %>
149
+ <%= verbatim `ruby #{command}` %>
150
+ </pre>
139
151
 
140
- <pre>
141
- <% command = "#{main_executable} --help" %>
142
- # ruby <%= command %>
143
- <%= verbatim `ruby #{command}` %>
144
- </pre>
152
+ See project & version information:
145
153
 
146
- See project & version information:
154
+ <pre>
155
+ <% command = "#{main_executable} --version" %>
156
+ # ruby <%= command %>
157
+ <%= verbatim `ruby #{command}` %>
158
+ </pre>
159
+ <% end %>
147
160
 
148
- <pre>
149
- <% command = "#{main_executable} --version" %>
150
- # ruby <%= command %>
151
- <%= verbatim `ruby #{command}` %>
152
- </pre>
161
+ <% paragraph "Show user manual" do %>
162
+ Build the user manual (please disregard any "unclosed span" warnings):
153
163
 
154
- See the user manual:
164
+ <pre>
165
+ # rake doc:man
166
+ </pre>
155
167
 
156
- <pre>
157
- # rake doc:man 2>/dev/null
168
+ Launch the user manual:
158
169
 
159
- <% command = "#{main_executable} --manual" %>
160
- # ruby <%= command %>
161
- </pre>
170
+ <pre>
171
+ <% command = "#{main_executable} --manual" %>
172
+ # ruby <%= command %>
173
+ </pre>
162
174
 
163
- The manual will now appear in your default web browser.
175
+ The manual will now appear in your default web browser.
176
+ <% end %>
164
177
  <% end %>
165
178
 
166
179
  <% section "Configure your project" do %>
@@ -224,7 +237,7 @@
224
237
  <% end %>
225
238
 
226
239
  <% section "Implement your project" do %>
227
- Add the following code to the bottom of the main project library:
240
+ Add the following code to the bottom of <tt>lib/word_count.rb</tt>, the main project library:
228
241
 
229
242
  <code>
230
243
  module WordCount
@@ -235,7 +248,7 @@
235
248
  end
236
249
  </code>
237
250
 
238
- Add the following code to the bottom of the main project executable:
251
+ Add the following code to the bottom of <tt>bin/word_count</tt>, the main project executable:
239
252
 
240
253
  <code>
241
254
  input = ARGF.read
@@ -243,6 +256,39 @@
243
256
  puts "There are #{total} words in the input."
244
257
  </code>
245
258
 
259
+ Add the following code to the bottom of <tt>test/word_count.rb</tt>, a unit test for the main project library:
260
+
261
+ <code>
262
+ describe WordCount do
263
+ it 'handles empty input' do
264
+ WordCount.count(nil).must_equal(0)
265
+ WordCount.count('').must_equal(0)
266
+ WordCount.count(' ').must_equal(0)
267
+ end
268
+
269
+ it 'handles single words' do
270
+ WordCount.count('a').must_equal(1)
271
+ WordCount.count('foo').must_equal(1)
272
+ WordCount.count('bar').must_equal(1)
273
+ end
274
+
275
+ it 'handles multiple words' do
276
+ WordCount.count('a b').must_equal(2)
277
+ WordCount.count('a-b').must_equal(2)
278
+ WordCount.count('a/b').must_equal(2)
279
+ end
280
+
281
+ it 'ignores punctuation and space' do
282
+ WordCount.count('!').must_equal(0)
283
+ WordCount.count('! @ # % #!@#').must_equal(0)
284
+ WordCount.count(' !').must_equal(0)
285
+ WordCount.count('! ').must_equal(0)
286
+ WordCount.count(' ! ').must_equal(0)
287
+ WordCount.count(' ! ').must_equal(0)
288
+ end
289
+ end
290
+ </code>
291
+
246
292
  <% paragraph "Goodbye `$LOAD_PATH`, hello `require()`" do %>
247
293
  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.
248
294
 
@@ -261,9 +307,41 @@
261
307
  <% end %>
262
308
 
263
309
  <% section "Test your project" do %>
264
- > TODO: show how to write a unit test for the code
310
+ To reduce the amount of code you have to write, **<%= $project %>** defines the following convention for unit tests.
311
+
312
+ <% paragraph "Units and tests" do %>
313
+ Every Ruby source file in your project's <tt>lib/</tt> directory is considered to be a **unit**. Likewise, every Ruby source file in your project's <tt>test/</tt> is considered to be a **test**.
314
+
315
+ As a result, your project's <tt>test/</tt> directory structure *mirrors* the structure of your project's <tt>lib/</tt> directory. For example, if your project has a <tt>lib/foo/bar.rb</tt> unit, then <tt>test/foo/bar.rb</tt> would be its the corresponding test.
316
+ <% end %>
265
317
 
266
- > TODO: integrate minitest tasks into Inochi.rake()
318
+ <% paragraph "Test execution" do %>
319
+ <pre>rake test</pre>
320
+
321
+ The above command begins the testing process, during which:
322
+
323
+ * 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.
324
+
325
+ * Before a test is executed, its corresponding unit is automatically loaded into the Ruby environment using `require()`.
326
+
327
+ As for the details of test execution:
328
+
329
+ * Tests are executed by the [minitest] library, which allows you to write unit tests in a combination of styles: traditional TDD, modern BDD, alternative rSpec BDD, and mock-based validation styles.
330
+
331
+ * Within each test, test cases are executed in random order. This is the default behavior of the [minitest] library.
332
+
333
+ [minitest]: http://rubyforge.org/projects/bfts/
334
+ <% end %>
335
+
336
+ <% paragraph "Helper libraries" do %>
337
+ Your project's main directory is added to Ruby's load path. So if your tests have helper libraries stored in your project's <tt>test/</tt> directory, you can load them into your tests by adding a "test/" prefix.
338
+
339
+ For example, if your <tt>test/foo/bar.rb</tt> test has a <tt>test/foo/qux.rb</tt> helper library, then you would write the following code inside the test to load the helper library:
340
+
341
+ <code>
342
+ require 'test/foo/qux'
343
+ </code>
344
+ <% end %>
267
345
  <% end %>
268
346
 
269
347
  <% section "Publish your project" do %>
@@ -1,16 +1,18 @@
1
- require File.join(File.dirname(File.expand_path(__FILE__)), 'inochi', 'inochi')
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'inochi/inochi'
2
3
 
3
4
  Inochi.init :Inochi,
4
- :version => '0.1.0',
5
- :release => '2009-01-19',
6
- :tagline => 'Gives life to RubyGems-based software',
5
+ :version => '0.2.0',
6
+ :release => '2009-01-25',
7
7
  :website => 'http://snk.tuxfamily.org/lib/inochi',
8
+ :tagline => 'Gives life to RubyGems-based software',
8
9
  :require => {
9
10
  'rake' => '~> 0',
10
- 'rubyforge' => '~> 1', # for publishing gems to RubyForge
11
- 'mechanize' => '~> 0', # for automating web browsing
12
- 'trollop' => '~> 1', # for parsing command-line options
13
- 'launchy' => '~> 0', # for launching a web browser
14
- 'yard' => nil, # for generating API documentation
15
- 'addressable' => '~> 2', # for parsing URIs properly
11
+ 'rubyforge' => '~> 1', # for publishing gems to RubyForge
12
+ 'mechanize' => '~> 0', # for automating web browsing
13
+ 'trollop' => '~> 1', # for parsing command-line options
14
+ 'launchy' => '~> 0', # for launching a web browser
15
+ 'yard' => nil, # for generating API documentation
16
+ 'addressable' => '~> 2', # for parsing URIs properly
17
+ 'minitest' => ['>= 1.3.1', '< 2'], # for unit testing
16
18
  }
@@ -228,9 +228,10 @@ class << self
228
228
  # where "name" is the name of a copyright holder and "info" is
229
229
  # their contact information) is added to the project module.
230
230
  #
231
- # This information is extracted from copyright notices in
232
- # the project license file. NOTE that the first copyright
233
- # notice must correspond to the primary project maintainer.
231
+ # Unless this information is supplied via the :authors option,
232
+ # it is automatically extracted from copyright notices in the
233
+ # project license file, where the first copyright notice is
234
+ # expected to correspond to the primary project maintainer.
234
235
  #
235
236
  # Copyright notices must be in the following form:
236
237
  #
@@ -247,6 +248,11 @@ class << self
247
248
  # @param [Hash] options
248
249
  # Additional method parameters, which are all optional:
249
250
  #
251
+ # [Array] :authors =>
252
+ # A list of project authors and their contact information. This
253
+ # list must have the form "[[name, info]]" where "name" is the name
254
+ # of a project author and "info" is their contact information.
255
+ #
250
256
  # [String] :license_file =>
251
257
  # Path (relative to the main project directory which contains the
252
258
  # project Rakefile) to the file which contains the project license.
@@ -305,24 +311,24 @@ class << self
305
311
 
306
312
  # load the project module
307
313
  program_name = File.basename(program_home)
314
+ project_libs = File.join('lib', program_name)
308
315
 
309
- require File.join('lib', program_name)
316
+ require project_libs
310
317
  project_module = fetch_project_module(project_symbol)
311
318
 
312
319
  # supply default options
313
320
  options[:rubyforge_project] ||= program_name
314
321
  options[:rubyforge_section] ||= program_name
315
- options[:raa_project] ||= program_name
316
- options[:license_file] ||= 'LICENSE'
317
- options[:logins_file] ||= File.join(ENV['HOME'], '.config', 'inochi', 'logins.yaml')
318
- options[:upload_delete] ||= false
319
- options[:upload_options] ||= []
322
+ options[:raa_project] ||= program_name
323
+ options[:license_file] ||= 'LICENSE'
324
+ options[:logins_file] ||= File.join(ENV['HOME'], '.config', 'inochi', 'logins.yaml')
325
+ options[:upload_delete] ||= false
326
+ options[:upload_options] ||= []
320
327
 
321
328
  # add AUTHORS constant to the project module
322
- license = File.read(options[:license_file])
323
-
324
- copyright_holders =
325
- license.scan(/Copyright.*?\d+\s+(.*)/).flatten.
329
+ copyright_holders = options[:authors] ||
330
+ File.read(options[:license_file]).
331
+ scan(/Copyright.*?\d+\s+(.*)/).flatten.
326
332
  map {|s| (s =~ /\s*<(.*?)>/) ? [$`, $1] : [s, ''] }
327
333
 
328
334
  project_module.const_set :AUTHORS, copyright_holders
@@ -333,6 +339,38 @@ class << self
333
339
  Rake::Task[name].instance_variable_set :@comment, nil
334
340
  end
335
341
 
342
+ # testing
343
+ desc 'Run all unit tests.'
344
+ task :test do
345
+ ruby '-w', '-I.', '-Ilib', '-r', program_name, '-e', %q{
346
+ # set title of test suite
347
+ $0 = File.basename(Dir.pwd)
348
+
349
+ require 'minitest/unit'
350
+ require 'minitest/spec'
351
+ require 'minitest/mock'
352
+ MiniTest::Unit.autorun
353
+
354
+ Dir['test/**/*.rb'].sort.each do |test|
355
+ unit = test.sub('test/', 'lib/')
356
+
357
+ if File.exist? unit
358
+ # strip file extension because require()
359
+ # does not normalize its input and it
360
+ # will think that the two paths (with &
361
+ # without file extension) are different
362
+ unit_path = unit.sub(/\.rb$/, '').sub('lib/', '')
363
+ test_path = test.sub(/\.rb$/, '')
364
+
365
+ require unit_path
366
+ require test_path
367
+ else
368
+ warn "Skipped test #{test.inspect} because it lacks a corresponding #{unit.inspect} unit."
369
+ end
370
+ end
371
+ }
372
+ end
373
+
336
374
  # documentation
337
375
  desc 'Build all documentation.'
338
376
  task :doc => %w[ doc:api doc:man ]
@@ -434,9 +472,9 @@ class << self
434
472
  File.delete tmp_file
435
473
  end
436
474
 
437
- # improve readability of list items that span multiple
438
- # lines by adding a blank line between such items
439
- text.gsub! %r{^( *[^\*\s].*)(\r?\n)( *\* \S)}, '\1\2\2\3'
475
+ # improve readability of list items
476
+ # by adding a blank line between them
477
+ text.gsub! %r{(\r?\n)( +\* \S)}, '\1\1\2'
440
478
 
441
479
  text
442
480
  end
@@ -482,6 +520,9 @@ class << self
482
520
  # remove heading navigation menus
483
521
  ann_html.gsub! %r{<div class="nav"[^>]*>(.*?)</div>}, ''
484
522
 
523
+ # remove latex-style heading numbers
524
+ ann_html.gsub! %r"(<(h\d)[^>]*>).+?(?:&nbsp;){2}(.+?)(</\2>)"m, '\1\3\4'
525
+
485
526
  ann_html = resolve_html_links[ann_html]
486
527
  end
487
528
  end
@@ -0,0 +1,106 @@
1
+ describe 'Inochi.calc_program_name' do
2
+ it 'converts input into lower-case' do
3
+ c('foo').must_equal('foo')
4
+ c('foO').must_equal('foo')
5
+ c('Foo').must_equal('foo')
6
+ c('FoO').must_equal('foo')
7
+ end
8
+
9
+ it 'converts camel case into snake case' do
10
+ c('FooBar').must_equal('foo_bar')
11
+ c('AnXMLParser').must_equal('an_xml_parser')
12
+ c('fOo').must_equal('f_oo')
13
+ c('FOo').must_equal('f_oo')
14
+ end
15
+
16
+ private
17
+
18
+ def c *args
19
+ Inochi.calc_program_name(*args)
20
+ end
21
+ end
22
+
23
+ describe 'Inochi.calc_project_symbol' do
24
+ it 'capitalizes first letter like a ruby constant' do
25
+ c('foo').must_equal('Foo')
26
+ end
27
+
28
+ it 'preserves exisitng capitalization' do
29
+ c('FoO').must_equal('FoO')
30
+ c('fooBaR').must_equal('FooBaR')
31
+ end
32
+
33
+ it 'converts non-word characters into underscores' do
34
+ c('a!b#c').must_equal('A_b_c')
35
+ end
36
+
37
+ it 'squeezes mulitple underscores' do
38
+ c('foo!!bar$$qux').must_equal('Foo_bar_qux')
39
+ end
40
+
41
+ it 'ignores surrounding whitespace' do
42
+ c(' a ').must_equal('A')
43
+ end
44
+
45
+ it 'ignores surrounding underscores' do
46
+ c('_a').must_equal('A')
47
+ c('a_').must_equal('A')
48
+ c('_a_').must_equal('A')
49
+ c('__a__').must_equal('A')
50
+ end
51
+
52
+ it 'ignores surrounding non-word characters' do
53
+ c('!a').must_equal('A')
54
+ c('a!').must_equal('A')
55
+ c('!a!').must_equal('A')
56
+ c('!!a!!').must_equal('A')
57
+ c('!@a#$').must_equal('A')
58
+ end
59
+
60
+ private
61
+
62
+ def c *args
63
+ Inochi.calc_project_symbol(*args).to_s
64
+ end
65
+ end
66
+
67
+ describe 'Inochi.camel_to_snake_case' do
68
+ it 'supports empty input' do
69
+ c('').must_equal('')
70
+ end
71
+
72
+ it 'supports normal camel case' do
73
+ c('fooBar').must_equal('foo_Bar')
74
+ c('FooBar').must_equal('Foo_Bar')
75
+ c('Foobar').must_equal('Foobar')
76
+ end
77
+
78
+ it 'supports nested abbreviations' do
79
+ c('AnXMLParser').must_equal('An_XML_Parser')
80
+ c('ANXMLParser').must_equal('ANXML_Parser')
81
+ c('AnXmLPaRsEr').must_equal('An_Xm_L_Pa_Rs_Er')
82
+ end
83
+
84
+ it 'preserves non-word characters' do
85
+ c(' a!!b#c').must_equal(' a!!b#c')
86
+ end
87
+
88
+ it 'preserves exsiting underscores' do
89
+ c('foo_bar').must_equal('foo_bar')
90
+ c('foo_Bar').must_equal('foo_Bar')
91
+ c('Foo_Bar').must_equal('Foo_Bar')
92
+ c('Foo_bar').must_equal('Foo_bar')
93
+
94
+ c('Foo___b_a__r').must_equal('Foo___b_a__r')
95
+ c('_').must_equal('_')
96
+ c('_a').must_equal('_a')
97
+ c('a_').must_equal('a_')
98
+ c('_a_').must_equal('_a_')
99
+ end
100
+
101
+ private
102
+
103
+ def c *args
104
+ Inochi.camel_to_snake_case(*args)
105
+ end
106
+ end