s4t-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +34 -0
- data/Manifest.txt +52 -0
- data/NOTES.txt +7 -0
- data/README.txt +2 -0
- data/Rakefile +13 -0
- data/Rakefile.hoe +22 -0
- data/bin/make-s4t-project.rb +159 -0
- data/bin/s4t-script-location-file +1 -0
- data/data/make-s4t-project/README-skeleton +30 -0
- data/data/make-s4t-project/Rakefile.example +32 -0
- data/data/make-s4t-project/bin-skeleton +23 -0
- data/data/make-s4t-project/main-lib-skeleton +14 -0
- data/data/make-s4t-project/set-standalone-test-paths.rb +5 -0
- data/data/make-s4t-project/setup.rb +1585 -0
- data/data/make-s4t-project/sub-lib-skeleton +3 -0
- data/data/make-s4t-project/test-skeleton +28 -0
- data/data/make-s4t-project/version-skeleton +3 -0
- data/lib/s4t-utils.rb +23 -0
- data/lib/s4t-utils/capturing-globals.rb +93 -0
- data/lib/s4t-utils/claims.rb +20 -0
- data/lib/s4t-utils/command-line.rb +18 -0
- data/lib/s4t-utils/error-handling.rb +26 -0
- data/lib/s4t-utils/friendly-format.rb +35 -0
- data/lib/s4t-utils/hacks.rb +55 -0
- data/lib/s4t-utils/load-path-auto-adjuster.rb +120 -0
- data/lib/s4t-utils/more-assertions.rb +35 -0
- data/lib/s4t-utils/os.rb +28 -0
- data/lib/s4t-utils/rake-task-helpers.rb +75 -0
- data/lib/s4t-utils/rakefile-common.rb +112 -0
- data/lib/s4t-utils/svn-file-movement.rb +104 -0
- data/lib/s4t-utils/test-util.rb +19 -0
- data/lib/s4t-utils/version.rb +3 -0
- data/setup.rb +1585 -0
- data/test/capturing-globals-tests.rb +42 -0
- data/test/data/make-s4t-project/README-skeleton +30 -0
- data/test/data/make-s4t-project/Rakefile.example +32 -0
- data/test/data/make-s4t-project/bin-skeleton +23 -0
- data/test/data/make-s4t-project/main-lib-skeleton +14 -0
- data/test/data/make-s4t-project/set-standalone-test-paths.rb +5 -0
- data/test/data/make-s4t-project/setup.rb +1585 -0
- data/test/data/make-s4t-project/sub-lib-skeleton +3 -0
- data/test/data/make-s4t-project/test-skeleton +28 -0
- data/test/data/make-s4t-project/version-skeleton +3 -0
- data/test/data/test-data-location-file +1 -0
- data/test/error-handling-tests.rb +43 -0
- data/test/friendly-format-tests.rb +15 -0
- data/test/hacks-tests.rb +42 -0
- data/test/load-path-auto-adjuster-tests.rb +88 -0
- data/test/rake-task-helper-tests.rb +62 -0
- data/test/set-standalone-test-paths.rb +5 -0
- data/test/test-location-file +1 -0
- data/test/test-util-tests.rb +45 -0
- metadata +116 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# This file should be copied into a test ending in 'tests.rb' so that
|
2
|
+
# the Rakefile knows it's a test.
|
3
|
+
|
4
|
+
require "set-standalone-test-paths.rb" unless $started_from_rakefile
|
5
|
+
require 'test/unit'
|
6
|
+
require 's4t-utils'
|
7
|
+
include S4tUtils
|
8
|
+
|
9
|
+
## Require either the particular file under test like this:
|
10
|
+
# require '!REPLACE_ME_FILE!/my-file'
|
11
|
+
## or the entire package:
|
12
|
+
# require '!REPLACE_ME_FILE!'
|
13
|
+
|
14
|
+
class TestName < Test::Unit::TestCase
|
15
|
+
## You probably want to include your library so that you don't have
|
16
|
+
## to tack !REPLACE_ME_MODULE!:: onto every name, but I won't assume
|
17
|
+
## that.
|
18
|
+
# include !REPLACE_ME_MODULE!
|
19
|
+
|
20
|
+
def setup
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_something
|
27
|
+
end
|
28
|
+
end
|
data/lib/s4t-utils.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 's4t-utils/capturing-globals'
|
2
|
+
require 's4t-utils/error-handling'
|
3
|
+
require 's4t-utils/more-assertions'
|
4
|
+
require 's4t-utils/claims'
|
5
|
+
require 's4t-utils/friendly-format'
|
6
|
+
require 's4t-utils/rake-task-helpers'
|
7
|
+
require 's4t-utils/svn-file-movement'
|
8
|
+
require 's4t-utils/hacks'
|
9
|
+
require 's4t-utils/command-line'
|
10
|
+
require 's4t-utils/test-util'
|
11
|
+
require 's4t-utils/os'
|
12
|
+
|
13
|
+
require 'pp'
|
14
|
+
|
15
|
+
# Tolerate typos
|
16
|
+
S4TUtils=S4tUtils
|
17
|
+
S4tUtil=S4tUtils
|
18
|
+
S4TUtil=S4tUtils
|
19
|
+
|
20
|
+
# Note, unless otherwise noted, all methods defined in S4tUtils
|
21
|
+
# are both module functions and instance functions.
|
22
|
+
module S4tUtils
|
23
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module S4tUtils
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Run the block, capturing output to $stderr in a string.
|
8
|
+
# That string is the method's return value.
|
9
|
+
def capturing_stderr
|
10
|
+
old_stderr = $stderr
|
11
|
+
new_stderr = StringIO.new
|
12
|
+
begin
|
13
|
+
$stderr = new_stderr
|
14
|
+
yield
|
15
|
+
ensure
|
16
|
+
$stderr = old_stderr
|
17
|
+
end
|
18
|
+
new_stderr.string
|
19
|
+
end
|
20
|
+
|
21
|
+
# Run the block, replacing the values of environment variables
|
22
|
+
# with the values given in the hash _settings_. The environment
|
23
|
+
# variables are restored when the method returns.
|
24
|
+
def with_environment_vars(settings)
|
25
|
+
begin
|
26
|
+
old = {}
|
27
|
+
settings.each { | key, value |
|
28
|
+
old[key] = ENV[key]
|
29
|
+
ENV[key] = value
|
30
|
+
}
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
settings.each_key { | key |
|
34
|
+
ENV[key] = old[key]
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Run the block with the _HOME_ environment variable set to the
|
40
|
+
# current working directory.
|
41
|
+
def with_home_right_here
|
42
|
+
begin
|
43
|
+
old_home = ENV['HOME']
|
44
|
+
ENV['HOME'] = '.'
|
45
|
+
yield
|
46
|
+
ensure
|
47
|
+
ENV['HOME'] = @old_home
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Run the block with the given _file_ (named by a string) deleted before
|
52
|
+
# and after.
|
53
|
+
def erasing_local_config_file(file)
|
54
|
+
with_home_right_here {
|
55
|
+
begin
|
56
|
+
File.delete(file) if File.exist?(file)
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
File.delete(file) if File.exist?(file)
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Run the block. During the execution, the contents of _file_ (named by
|
65
|
+
# a string) is replaced with _contents_.
|
66
|
+
def with_local_config_file(file, contents)
|
67
|
+
erasing_local_config_file(file) do
|
68
|
+
File.open(file, 'w') do | io |
|
69
|
+
io.puts(contents.to_s)
|
70
|
+
end
|
71
|
+
yield
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Run the block. During execution, _ARGV_'s is set as if the
|
76
|
+
# script had been executed with _string_ as its argument list.
|
77
|
+
# If the block tries to exit, with_command_args will instead throw
|
78
|
+
# a StandardError.
|
79
|
+
def with_command_args(string)
|
80
|
+
begin
|
81
|
+
old_argv = ARGV.dup
|
82
|
+
ARGV.replace(string.split)
|
83
|
+
yield
|
84
|
+
rescue SystemExit => ex
|
85
|
+
replacement = StandardError.new(ex.message)
|
86
|
+
replacement.set_backtrace(ex.backtrace)
|
87
|
+
raise replacement
|
88
|
+
ensure
|
89
|
+
ARGV.replace(old_argv)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module S4tUtils
|
2
|
+
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# A StandardError is thrown if the _fact_ the user claims is true
|
6
|
+
# is actually false. The _block_ is called to provide the exception
|
7
|
+
# message.
|
8
|
+
def user_claims(fact, &block)
|
9
|
+
raise StandardError.new(block.call) unless fact
|
10
|
+
end
|
11
|
+
|
12
|
+
# A StandardError is thrown if the _fact_ the user disputes is
|
13
|
+
# nevertheless true. The _block_ is called to provide the exception
|
14
|
+
# message.
|
15
|
+
def user_disputes(fact, &block)
|
16
|
+
user_claims(!fact, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module S4tUtils
|
2
|
+
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Ask the question contained in the _question_lines_, prompt, and
|
6
|
+
# wait for an answer. If the stripped value read from STDIN is
|
7
|
+
# empty, use the _default_answer_.
|
8
|
+
def ask(default_answer, *question_lines)
|
9
|
+
puts question_lines
|
10
|
+
print "[#{default_answer}] => "
|
11
|
+
answer = STDIN.readline.strip
|
12
|
+
answer = default_answer.to_s if answer == ''
|
13
|
+
answer
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module S4tUtils
|
2
|
+
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Typically used to wrap the execution of an entire script.
|
6
|
+
# If an exception is thrown, a terse message is printed (to $stderr)
|
7
|
+
# instead of a stack dump. The message printed is gotten from the
|
8
|
+
# exception.
|
9
|
+
def with_pleasant_exceptions
|
10
|
+
yield
|
11
|
+
rescue SystemExit
|
12
|
+
raise
|
13
|
+
rescue Exception => ex
|
14
|
+
$stderr.puts(ex.message)
|
15
|
+
end
|
16
|
+
|
17
|
+
# with_pleasant_exceptions swallows the stack trace, which you
|
18
|
+
# want to see during debugging. The easy way to see it is to add
|
19
|
+
# 'out' to that message, producing this one. To reduce the chance
|
20
|
+
# you'll forget to make exceptions pleasant again, a note that
|
21
|
+
# exceptions are turned off is always printed to $stderr.
|
22
|
+
def without_pleasant_exceptions
|
23
|
+
$stderr.puts "Note: exception handling turned off."
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#--
|
2
|
+
# Note: see also <http://englishext.rubyforge.org/>
|
3
|
+
#++
|
4
|
+
|
5
|
+
module S4tUtils
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# Use _connector_ to join _array_ into a human-friendly list.
|
10
|
+
#
|
11
|
+
#
|
12
|
+
# friendly_list("or", [1]) => "'1'"
|
13
|
+
# friendly_list("or"), [1, 2] => "'1' or '2'"
|
14
|
+
# friendly_list("or"), [1, 2, 3] => "'1', '2', or '3'"
|
15
|
+
def friendly_list(connector, array)
|
16
|
+
quoted = array.collect { | elt | "'" + elt.to_s + "'" }
|
17
|
+
case array.length
|
18
|
+
when 0
|
19
|
+
""
|
20
|
+
when 1
|
21
|
+
quoted[0]
|
22
|
+
when 2
|
23
|
+
quoted[0] + " #{connector} " + quoted[1]
|
24
|
+
else
|
25
|
+
quoted[0...-1].join(", ") + ", #{connector} #{quoted.last}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Produces a version of a string that can be typed after a :
|
30
|
+
# (Can also be safely given at a command-line prompt.)
|
31
|
+
def symbol_safe_name(name)
|
32
|
+
name.to_s.gsub(/\W/, '')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module S4tUtils
|
2
|
+
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# The return value of prog1 is the _retval_. Before that's returned,
|
6
|
+
# though, the _retval_ is yielded to the block. This method is an
|
7
|
+
# alternative to stashing a value in a temporary, fiddling around, then
|
8
|
+
# returning the temporary. Here's an example:
|
9
|
+
#
|
10
|
+
# prog1(1+1) { | s | puts "Sum is #{s}."} # => 2
|
11
|
+
#
|
12
|
+
# The name "prog1" is ancient Lisp jargon.
|
13
|
+
def prog1(retval)
|
14
|
+
yield(retval)
|
15
|
+
retval
|
16
|
+
end
|
17
|
+
|
18
|
+
# A way of putting debugging statements in code that requires less
|
19
|
+
# typing than +puts+.
|
20
|
+
#
|
21
|
+
# pi [1, 2, 3], 'input' # => 'input: [1, 2, 3]
|
22
|
+
#
|
23
|
+
# The _arg_ is printed using +inspect+. If _leader_ isn't given,
|
24
|
+
# nothing is printed before _arg_.
|
25
|
+
#
|
26
|
+
# pi returns its _arg_, which is occasionally useful for sticking
|
27
|
+
# debugging into the middle of complicated expressions.
|
28
|
+
def pi(arg, leader=nil)
|
29
|
+
leader = (leader == nil) ? '' : leader + ': '
|
30
|
+
prog1(arg) { puts leader + arg.inspect }
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# An ArgForwarder is associated with a _target_. It forwards messages
|
36
|
+
# sent to it to the target, passing along any arguments to the method.
|
37
|
+
# So far, so boring. But an ArgForwarder is also created with some
|
38
|
+
# _added_args_. When the ArgForwarder forwards, it prepends those
|
39
|
+
# _added_args_ to the message's given arguments.
|
40
|
+
#
|
41
|
+
# array = []
|
42
|
+
# forwarder = ArgForwarder.new(array, 5)
|
43
|
+
# forwarder.push
|
44
|
+
# assert_equal([5], array)
|
45
|
+
class ArgForwarder
|
46
|
+
def initialize(target, *added_args)
|
47
|
+
@target = target
|
48
|
+
@added_args = added_args
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(method, *args) # :nodoc:
|
52
|
+
@target.send(method, *(@added_args + args))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# This is loaded by an executable script in one of two cases:
|
2
|
+
#
|
3
|
+
# It's a development version living in the bin part of this directory
|
4
|
+
# structure (but it can invoked from any place).
|
5
|
+
#
|
6
|
+
# project/
|
7
|
+
# bin/
|
8
|
+
# lib/
|
9
|
+
# project/
|
10
|
+
# third-party/
|
11
|
+
# s4t-utils/
|
12
|
+
# this file
|
13
|
+
#
|
14
|
+
# It's a deployed version living in some random place, with
|
15
|
+
# the site_ruby directory in the page.
|
16
|
+
#
|
17
|
+
# site_ruby/1.8/
|
18
|
+
# project/
|
19
|
+
# third-party/
|
20
|
+
# s4t-utils/
|
21
|
+
# this file
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# In order for this file to have been required in both cases, the following
|
25
|
+
# code is executed in the caller:
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# $:.unshift((Pathname.new(__FILE__).parent.parent + 'lib').to_s)
|
29
|
+
# require 'package/third-party/s4t-utils/load-path-auto-adjuster'
|
30
|
+
#
|
31
|
+
# In the first case, that will put something like "../lib" on the load
|
32
|
+
# path. In the second case, it will put harmless garbage on the path
|
33
|
+
# (harmless because it won't contain this file, which will still be
|
34
|
+
# found somewhere later in the load path).
|
35
|
+
#
|
36
|
+
# The first thing this file does is pop that off, it having done its job.
|
37
|
+
# In the first (development) case, it puts the following on the load path:
|
38
|
+
# project/lib & project/lib/project/third-party & project
|
39
|
+
# ('project' is added so that <require 'test/util-file'> works.)
|
40
|
+
#
|
41
|
+
# In the second, it adds only the third-party library and takes care
|
42
|
+
# to add it just after whatever component in the path contains this
|
43
|
+
# file. (It will thus not interfere with clashing packages earlier
|
44
|
+
# in the path.)
|
45
|
+
# site_ruby/1.8/project/third-party
|
46
|
+
# since site_ruby/1.8 (or the equivalent) is already on there.
|
47
|
+
|
48
|
+
require 'rubygems'
|
49
|
+
require 'pathname'
|
50
|
+
|
51
|
+
module S4tUtils
|
52
|
+
module Hidden # :nodoc: all
|
53
|
+
|
54
|
+
class Arranger
|
55
|
+
def initialize(third_party)
|
56
|
+
@third_party_lib = third_party
|
57
|
+
@project_lib = third_party.parent.parent
|
58
|
+
@test_util_root = @project_lib.parent
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.arrange_path_around(third_party)
|
62
|
+
new(third_party).arrange
|
63
|
+
end
|
64
|
+
|
65
|
+
def arrange
|
66
|
+
add_third_party_gems
|
67
|
+
if project_lib_already_in_path?
|
68
|
+
just_add_third_party_after_project_lib
|
69
|
+
else
|
70
|
+
add_everything_at_front
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def add_third_party_gems
|
76
|
+
# When RubyGems 0.8.11 gets clear_paths, it does not clear the
|
77
|
+
# cache used by Gem's overriding version of require(), so if
|
78
|
+
# this is loaded after the first Gem is required, it will have
|
79
|
+
# no effect on later uses of require(). (But it does affect
|
80
|
+
# require_gem.)
|
81
|
+
#
|
82
|
+
ENV['GEM_PATH']=(@third_party_lib+'gems').to_s
|
83
|
+
Gem.clear_paths
|
84
|
+
end
|
85
|
+
|
86
|
+
def project_lib_already_in_path?
|
87
|
+
$:.include?(@project_lib.to_s)
|
88
|
+
end
|
89
|
+
|
90
|
+
def just_add_third_party_after_project_lib
|
91
|
+
$:.each_with_index do | path_element, index |
|
92
|
+
if path_element == @project_lib.to_s
|
93
|
+
$:[index+1,0] = @third_party_lib.to_s
|
94
|
+
return
|
95
|
+
end
|
96
|
+
end
|
97
|
+
fail "No place to put third_party library."
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_everything_at_front
|
101
|
+
$:.unshift(@test_util_root.to_s)
|
102
|
+
$:.unshift(@third_party_lib.to_s)
|
103
|
+
$:.unshift(@project_lib.to_s) # This is now first
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.auto_adjust_load_path
|
108
|
+
$:.shift # Remove extra element used to find this file.
|
109
|
+
|
110
|
+
# Having loaded us, __FILE__ is something like this:
|
111
|
+
# ...lib.../package/third-party/s4t-utils/load-path-auto-adjuster.rb
|
112
|
+
relative_third_party = Pathname.new(__FILE__).parent.parent
|
113
|
+
# Pathname#real_path doesn't work on Windows (1.8.2). Grr.
|
114
|
+
third_party = Pathname.new(File.expand_path(relative_third_party.to_s))
|
115
|
+
Arranger.arrange_path_around(third_party)
|
116
|
+
end
|
117
|
+
|
118
|
+
auto_adjust_load_path
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
|
4
|
+
# Some additional Test::Unit assertions.
|
5
|
+
module Assertions
|
6
|
+
# Same as +assert+. I just like it better.
|
7
|
+
def assert_true(boolean, message = nil)
|
8
|
+
assert(boolean, message)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Assert that the _boolean_ is false.
|
12
|
+
def assert_false(boolean, message = nil)
|
13
|
+
_wrap_assertion do
|
14
|
+
assert_block(build_message(message, "<?> should be false or nil.", boolean)) { !boolean }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Like +assert_raise+, but the raised exception must contain a
|
19
|
+
# +message+ matching (as in +assert_match+) the _message_ argument.
|
20
|
+
def assert_raise_with_matching_message(exception_class, message, &block)
|
21
|
+
exception = assert_raise(exception_class, &block)
|
22
|
+
assert_match(message, exception.message)
|
23
|
+
end
|
24
|
+
alias_method :assert_raises_with_matching_message,
|
25
|
+
:assert_raise_with_matching_message
|
26
|
+
|
27
|
+
# Assert that the block tried to +exit+.
|
28
|
+
def assert_wants_to_exit
|
29
|
+
assert_raise(SystemExit) do
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|